commit 7dc037e39e6bbfa8964d0040e8141dbcf70d726d (HEAD, refs/remotes/origin/master) Author: Stefan Monnier Date: Tue Oct 10 14:57:37 2017 -0400 * lisp/url/url-cookie.el: Fix warning and miscompilation (url-cookie-parse-file-netscape): Remove unused var `match', fix undefined `incf' misuse. diff --git a/lisp/url/url-cookie.el b/lisp/url/url-cookie.el index b0dc40475b..0a3103264d 100644 --- a/lisp/url/url-cookie.el +++ b/lisp/url/url-cookie.el @@ -94,7 +94,7 @@ i.e. 1970-1-1) are loaded as expiring one year from now instead." ;; (message "skipping empty line")) ((= (length fields) 7) (let ((dom (nth 0 fields)) - (match (nth 1 fields)) + ;; (match (nth 1 fields)) (path (nth 2 fields)) (secure (string= (nth 3 fields) "TRUE")) ;; session cookies (expire time = 0) are supposed @@ -112,7 +112,7 @@ i.e. 1970-1-1) are loaded as expiring one year from now instead." s))))) (key (nth 5 fields)) (val (nth 6 fields))) - (incf n) + (cl-incf n) ;;(message "adding <%s>=<%s> exp=<%s> dom=<%s> path=<%s> sec=%S" key val expires dom path secure) (url-cookie-store key val expires dom path secure) )) commit 349c1f93021e49c63f076b602d3d228324105fd6 Author: Aurelien Aptel Date: Fri Oct 6 15:52:39 2017 +0200 Add parser for Netscape/Mozilla cookie file format * lisp/url/url-cookie.el (url-cookie-parse-file-netscape): New function. diff --git a/lisp/url/url-cookie.el b/lisp/url/url-cookie.el index 453d4fe5b6..b0dc40475b 100644 --- a/lisp/url/url-cookie.el +++ b/lisp/url/url-cookie.el @@ -74,6 +74,55 @@ telling Microsoft that." ;; It's completely normal for the cookies file not to exist yet. (load (or fname url-cookie-file) t t)) +(defun url-cookie-parse-file-netscape (filename &optional long-session) + "Load cookies from FILENAME in Netscape/Mozilla format. +When LONG-SESSION is non-nil, session cookies (expiring at t=0 +i.e. 1970-1-1) are loaded as expiring one year from now instead." + (interactive "fLoad Netscape/Mozilla cookie file: ") + (let ((n 0)) + (with-temp-buffer + (insert-file-contents-literally filename) + (goto-char (point-min)) + (when (not (looking-at-p "# Netscape HTTP Cookie File\n")) + (error (format "File %s doesn't look like a netscape cookie file" filename))) + (while (not (eobp)) + (when (not (looking-at-p (rx bol (* space) "#"))) + (let* ((line (buffer-substring (point) (save-excursion (end-of-line) (point)))) + (fields (split-string line "\t"))) + (cond + ;;((>= 1 (length line) 0) + ;; (message "skipping empty line")) + ((= (length fields) 7) + (let ((dom (nth 0 fields)) + (match (nth 1 fields)) + (path (nth 2 fields)) + (secure (string= (nth 3 fields) "TRUE")) + ;; session cookies (expire time = 0) are supposed + ;; to be removed when the browser is closed, but + ;; the main point of loading external cookie is to + ;; reuse a browser session, so to prevent the + ;; cookie from being detected as expired straight + ;; away, make it expire a year from now + (expires (format-time-string + "%d %b %Y %T [GMT]" + (seconds-to-time + (let ((s (string-to-number (nth 4 fields)))) + (if (and (= s 0) long-session) + (seconds-to-time (+ (* 365 24 60 60) (float-time))) + s))))) + (key (nth 5 fields)) + (val (nth 6 fields))) + (incf n) + ;;(message "adding <%s>=<%s> exp=<%s> dom=<%s> path=<%s> sec=%S" key val expires dom path secure) + (url-cookie-store key val expires dom path secure) + )) + (t + (message "ignoring malformed cookie line <%s>" line))))) + (forward-line)) + (when (< 0 n) + (setq url-cookies-changed-since-last-save t)) + (message "added %d cookies from file %s" n filename)))) + (defun url-cookie-clean-up (&optional secure) (let ((var (if secure 'url-cookie-secure-storage 'url-cookie-storage)) new new-cookies) commit 6abff55b5514515c5a28397b34aee478926af232 Author: Philipp Stephani Date: Mon Oct 9 16:08:15 2017 +0200 Revert "Raise an error when detecting old-style backquotes." This reverts commit 9613690f6e51e2f2aa2bcbbede3e209d08cfaaad. diff --git a/etc/NEWS b/etc/NEWS index 0f4c6ae40f..2332ba4d1f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -87,9 +87,6 @@ them through 'format' first. Even that is discouraged: for ElDoc support, you should set 'eldoc-documentation-function' instead of calling 'eldoc-message' directly. -** Old-style backquotes now generate an error. They have been -generating warnings for a decade. - * Lisp Changes in Emacs 27.1 diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 45fa188d6c..590db570c5 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -2048,8 +2048,14 @@ With argument ARG, insert value in current buffer after the form." (not (eobp))) (setq byte-compile-read-position (point) byte-compile-last-position byte-compile-read-position) - (let* ((lread--unescaped-character-literals nil) + (let* ((lread--old-style-backquotes nil) + (lread--unescaped-character-literals nil) (form (read inbuffer))) + ;; Warn about the use of old-style backquotes. + (when lread--old-style-backquotes + (byte-compile-warn "!! The file uses old-style backquotes !! +This functionality has been obsolete for more than 10 years already +and will be removed soon. See (elisp)Backquote in the manual.")) (when lread--unescaped-character-literals (byte-compile-warn "unescaped character literals %s detected!" diff --git a/src/lread.c b/src/lread.c index c073fc4ce6..6bc93b1481 100644 --- a/src/lread.c +++ b/src/lread.c @@ -1003,11 +1003,14 @@ load_error_handler (Lisp_Object data) return Qnil; } -static _Noreturn void -load_error_old_style_backquotes (void) +static void +load_warn_old_style_backquotes (Lisp_Object file) { - AUTO_STRING (format, "Loading `%s': old-style backquotes detected!"); - xsignal1 (Qerror, CALLN (Fformat_message, format, Vload_file_name)); + if (!NILP (Vlread_old_style_backquotes)) + { + AUTO_STRING (format, "Loading `%s': old-style backquotes detected!"); + CALLN (Fmessage, format, file); + } } static void @@ -1279,6 +1282,10 @@ Return t if the file exists and loads successfully. */) version = -1; + /* Check for the presence of old-style quotes and warn about them. */ + specbind (Qlread_old_style_backquotes, Qnil); + record_unwind_protect (load_warn_old_style_backquotes, file); + /* Check for the presence of unescaped character literals and warn about them. */ specbind (Qlread_unescaped_character_literals, Qnil); @@ -3171,7 +3178,10 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) first_in_list exception (old-style can still be obtained via "(\`" anyway). */ if (!new_backquote_flag && first_in_list && next_char == ' ') - load_error_old_style_backquotes (); + { + Vlread_old_style_backquotes = Qt; + goto default_label; + } else { Lisp_Object value; @@ -3222,7 +3232,10 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) return list2 (comma_type, value); } else - load_error_old_style_backquotes (); + { + Vlread_old_style_backquotes = Qt; + goto default_label; + } } case '?': { @@ -3410,6 +3423,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) row. */ FALLTHROUGH; default: + default_label: if (c <= 040) goto retry; if (c == NO_BREAK_SPACE) goto retry; @@ -4982,6 +4996,12 @@ variables, this must be set in the first line of a file. */); doc: /* List of buffers being read from by calls to `eval-buffer' and `eval-region'. */); Veval_buffer_list = Qnil; + DEFVAR_LISP ("lread--old-style-backquotes", Vlread_old_style_backquotes, + doc: /* Set to non-nil when `read' encounters an old-style backquote. +For internal use only. */); + Vlread_old_style_backquotes = Qnil; + DEFSYM (Qlread_old_style_backquotes, "lread--old-style-backquotes"); + DEFVAR_LISP ("lread--unescaped-character-literals", Vlread_unescaped_character_literals, doc: /* List of deprecated unescaped character literals encountered by `read'. diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el index 1f85c26978..30d2a4753c 100644 --- a/test/lisp/emacs-lisp/bytecomp-tests.el +++ b/test/lisp/emacs-lisp/bytecomp-tests.el @@ -534,18 +534,23 @@ literals (Bug#20852)." (ert-deftest bytecomp-tests--old-style-backquotes () "Check that byte compiling warns about old-style backquotes." + (should (boundp 'lread--old-style-backquotes)) (bytecomp-tests--with-temp-file source (write-region "(` (a b))" nil source) (bytecomp-tests--with-temp-file destination (let* ((byte-compile-dest-file-function (lambda (_) destination)) - (byte-compile-debug t) - (err (should-error (byte-compile-file source)))) + (byte-compile-error-on-warn t) + (byte-compile-debug t) + (err (should-error (byte-compile-file source)))) (should (equal (cdr err) - '("Loading `nil': old-style backquotes detected!"))))))) + (list "!! The file uses old-style backquotes !! +This functionality has been obsolete for more than 10 years already +and will be removed soon. See (elisp)Backquote in the manual."))))))) (ert-deftest bytecomp-tests-function-put () "Check `function-put' operates during compilation." + (should (boundp 'lread--old-style-backquotes)) (bytecomp-tests--with-temp-file source (dolist (form '((function-put 'bytecomp-tests--foo 'foo 1) (function-put 'bytecomp-tests--foo 'bar 2) diff --git a/test/src/lread-tests.el b/test/src/lread-tests.el index 3f41982eba..ac730b4f00 100644 --- a/test/src/lread-tests.el +++ b/test/src/lread-tests.el @@ -173,13 +173,13 @@ literals (Bug#20852)." (should (string-suffix-p "/somelib.el" (caar load-history))))) (ert-deftest lread-tests--old-style-backquotes () - "Check that loading doesn't accept old-style backquotes." + "Check that loading warns about old-style backquotes." (lread-tests--with-temp-file file-name (write-region "(` (a b))" nil file-name) - (let ((data (should-error (load file-name nil :nomessage :nosuffix)))) - (should (equal (cdr data) - (list (concat (format-message "Loading `%s': " file-name) - "old-style backquotes detected!"))))))) + (should (equal (load file-name nil :nomessage :nosuffix) t)) + (should (equal (lread-tests--last-message) + (concat (format-message "Loading `%s': " file-name) + "old-style backquotes detected!"))))) (ert-deftest lread-lread--substitute-object-in-subtree () (let ((x (cons 0 1))) commit 2c39565dc046d428127735552db6e7814631d4d4 Author: Mark Oteiza Date: Mon Oct 9 08:12:46 2017 -0400 Disable XTerm titles (Bug#28591) * etc/NEWS: Mention it. * lisp/term/xterm.el (xterm-set-window-title): Disable it. diff --git a/etc/NEWS b/etc/NEWS index adc1085e74..0f4c6ae40f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -67,8 +67,9 @@ globally or for individual definitions. ** Enhanced xterm support -*** New variable 'xterm-set-window-title' controls whether Emacs -sets the XTerm window title. The default is to set the window title. +*** New variable 'xterm-set-window-title' controls whether Emacs sets +the XTerm window title. This feature is experimental and is disabled +by default. * New Modes and Packages in Emacs 27.1 diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el index 6a17d382b0..b7d0cfb479 100644 --- a/lisp/term/xterm.el +++ b/lisp/term/xterm.el @@ -68,7 +68,7 @@ string bytes that can be copied is 3/4 of this value." :version "25.1" :type 'integer) -(defcustom xterm-set-window-title t +(defcustom xterm-set-window-title nil "Whether Emacs should set window titles to an Emacs frame in an XTerm." :version "27.1" :type 'boolean) commit 3c2e8eff8cc9a4a535f473b3e150cb056d8f891d Author: Gemini Lasswell Date: Tue Sep 26 08:14:23 2017 -0700 Stop Testcover from producing spurious 1value errors Fix bug#25351 by copying results of form evaluations for later comparison. * lisp/emacs-lisp/testcover.el (testcover-after): Copy the result of a form's first evaluation and compare subsequent evaluations to the copy. Improve the error message used when a form's value changes. (testcover--copy-object, testcover--copy-object1): New functions. * test/lisp/emacs-lisp/testcover-resources/testcases.el (by-value-vs-by-reference-bug-25351): Remove expected failure tag. (circular-lists-bug-24402): Add another circular list case. diff --git a/lisp/emacs-lisp/testcover.el b/lisp/emacs-lisp/testcover.el index 320c43b59f..3628968974 100644 --- a/lisp/emacs-lisp/testcover.el +++ b/lisp/emacs-lisp/testcover.el @@ -49,11 +49,10 @@ ;; function being called is capable of returning in other cases. ;; Problems: -;; * To detect different values, we store the form's result in a vector and -;; compare the next result using `equal'. We don't copy the form's -;; result, so if caller alters it (`setcar', etc.) we'll think the next -;; call has the same value! Also, equal thinks two strings are the same -;; if they differ only in properties. +;; * `equal', which is used to compare the results of repeatedly executing +;; a form, has a couple of shortcomings. It considers strings to be the same +;; if they only differ in properties, and it raises an error when asked to +;; compare circular lists. ;; * Because we have only a "1value" class and no "always nil" class, we have ;; to treat as potentially 1-valued any `and' whose last term is 1-valued, ;; in case the last term is always nil. Example: @@ -259,26 +258,25 @@ BEFORE-INDEX is the form's index into the code-coverage vector." AFTER-INDEX is the form's index into the code-coverage vector. Return VALUE." (let ((old-result (aref testcover-vector after-index))) - (cond - ((eq 'unknown old-result) - (aset testcover-vector after-index value)) - ((eq 'maybe old-result) - (aset testcover-vector after-index 'ok-coverage)) - ((eq '1value old-result) - (aset testcover-vector after-index - (cons old-result value))) - ((and (eq (car-safe old-result) '1value) - (not (condition-case () - (equal (cdr old-result) value) - ;; TODO: Actually check circular lists for equality. - (circular-list t)))) - (error "Value of form marked with `1value' does vary: %s" value)) - ;; Test if a different result. - ((not (condition-case () - (equal value old-result) - ;; TODO: Actually check circular lists for equality. - (circular-list nil))) - (aset testcover-vector after-index 'ok-coverage)))) + (cond + ((eq 'unknown old-result) + (aset testcover-vector after-index (testcover--copy-object value))) + ((eq 'maybe old-result) + (aset testcover-vector after-index 'ok-coverage)) + ((eq '1value old-result) + (aset testcover-vector after-index + (cons old-result (testcover--copy-object value)))) + ((and (eq (car-safe old-result) '1value) + (not (condition-case () + (equal (cdr old-result) value) + (circular-list t)))) + (error "Value of form expected to be constant does vary, from %s to %s" + old-result value)) + ;; Test if a different result. + ((not (condition-case () + (equal value old-result) + (circular-list nil))) + (aset testcover-vector after-index 'ok-coverage)))) value) ;; Add these behaviors to Edebug. @@ -286,6 +284,53 @@ vector. Return VALUE." (push '(testcover testcover-enter testcover-before testcover-after) edebug-behavior-alist)) +(defun testcover--copy-object (obj) + "Make a copy of OBJ. +If OBJ is a cons cell, copy both its car and its cdr. +Contrast to `copy-tree' which does the same but fails on circular +structures, and `copy-sequence', which copies only along the +cdrs. Copy vectors as well as conses." + (let ((ht (make-hash-table :test 'eq))) + (testcover--copy-object1 obj t ht))) + +(defun testcover--copy-object1 (obj vecp hash-table) + "Make a copy of OBJ, using a HASH-TABLE of objects already copied. +If OBJ is a cons cell, this recursively copies its car and +iteratively copies its cdr. When VECP is non-nil, copy +vectors as well as conses." + (if (and (atom obj) (or (not vecp) (not (vectorp obj)))) + obj + (let ((copy (gethash obj hash-table nil))) + (unless copy + (cond + ((consp obj) + (let* ((rest obj) current) + (setq copy (cons nil nil) + current copy) + (while + (progn + (puthash rest current hash-table) + (setf (car current) + (testcover--copy-object1 (car rest) vecp hash-table)) + (setq rest (cdr rest)) + (cond + ((atom rest) + (setf (cdr current) + (testcover--copy-object1 rest vecp hash-table)) + nil) + ((gethash rest hash-table nil) + (setf (cdr current) (gethash rest hash-table nil)) + nil) + (t (setq current + (setf (cdr current) (cons nil nil))))))))) + (t ; (and vecp (vectorp obj)) is true due to test in if above. + (setq copy (copy-sequence obj)) + (puthash obj copy hash-table) + (dotimes (i (length copy)) + (aset copy i + (testcover--copy-object1 (aref copy i) vecp hash-table)))))) + copy))) + ;;;========================================================================= ;;; Display the coverage data as color splotches on your code. ;;;========================================================================= diff --git a/test/lisp/emacs-lisp/testcover-resources/testcases.el b/test/lisp/emacs-lisp/testcover-resources/testcases.el index d8b8192748..6a9612db05 100644 --- a/test/lisp/emacs-lisp/testcover-resources/testcases.el +++ b/test/lisp/emacs-lisp/testcover-resources/testcases.el @@ -357,7 +357,6 @@ ;; ==== by-value-vs-by-reference-bug-25351 ==== "An object created by a 1value expression may be modified by other code." -:expected-result :failed ;; ==== (defun testcover-testcase-ab () (list 'a 'b)) @@ -491,10 +490,18 @@ regarding the odd-looking coverage result for the quoted form." "Testcover captures and ignores circular list errors." ;; ==== (defun testcover-testcase-cyc1 (a) - (let ((ls (make-list 10 a%%%))) - (nconc ls ls) - ls)) + (let ((ls (make-list 10 a%%%)%%%)) + (nconc ls%%% ls%%%) + ls)) ; The lack of a mark here is due to an ignored circular list error. (testcover-testcase-cyc1 1) (testcover-testcase-cyc1 1) +(defun testcover-testcase-cyc2 (a b) + (let ((ls1 (make-list 10 a%%%)%%%) + (ls2 (make-list 10 b))) + (nconc ls2 ls2) + (nconc ls1%%% ls2) + ls1)) +(testcover-testcase-cyc2 1 2) +(testcover-testcase-cyc2 1 4) ;; testcases.el ends here. commit d79cf638f278e50c22feb53d6ba556f5ce9d7853 Author: Gemini Lasswell Date: Mon Sep 25 13:45:07 2017 -0700 Rewrite Testcover's internals, fixing several bugs * lisp/emacs-lisp/testcover.el: Rewrite the internals of Testcover to analyze instrumented code instead of reinstrumenting it. Use new hooks in Edebug to collect code coverage information at runtime using Edebug's instrumentation. Includes fixes for: (bug#11307) (bug#24509) (bug#24688) (bug#24743) (bug#25316) (bug#25326). (testcover-compose-functions): Remove mapcar. (testcover-start, testcover-this-defun): Analyze code instead of reinstrumenting it. Set edebug-behavior for each definition. (testcover--read, testcover-1value, testcover-reinstrument) (testcover-reinstrument-list, testcover-reinstrument-compose): Deleted. (testcover-after-instrumentation, testcover-init-definition) (testcover-before): New functions. (testcover-enter): Change call signature to match edebug-enter. (testcover-after, testcover-mark): Add handling of 'maybe and 'noreturn. (testcover-analyze-coverage, testcover-analyze-coverage-progn) (testcover-analyze-coverage-edebug-after) (testcover-analyze-coverage-wrapped-form) (testcover-analyze-coverage-wrapped-application) (testcover-analyze-coverage-compose) (testcover-analyze-coverage-backquote) (testcover-analyze-coverage-backquote-form) (testcover-coverage-combine): New functions to analyze instrumented code. * lisp/emacs-lisp/gv.el: Modify edebug-after's gv-expander to instrument in the setter as well as the getter. * test/lisp/emacs-lisp/testcover-tests.el (testcover-tests-run-test-case): Use `edebug-default-enter' instead of `edebug-enter' to detect Edebug invocation during tests. * test/lisp/emacs-lisp/testcover-resources/testcases.el (constants-bug-25316) (customize-defcustom-bug-25326) (1-value-symbol-bug-25316) (quotes-within-backquotes-bug-25316) (backquote-1value-bug-24509) (pcase-bug-24688) (defun-in-backquote-bug-11307-and-24743) (closure-1value-bug) (backquoted-vector-bug-25316) (vector-in-macro-spec-bug-25316) (mapcar-is-not-compose): Remove expected failure tags. (function-with-edebug-spec-bug-25316): Remove expected failure tag and modify expected result. (quoted-backquote): New test. * lisp/textmodes/rst.el: Remove workarounds for bugs in Testcover. (rst-testcover-defcustom): Deleted. * lisp/subr.el (1value): Remove incorrect description of testcover-1value from docstring, replace with description of Testcover's treatment of 1value. diff --git a/lisp/emacs-lisp/gv.el b/lisp/emacs-lisp/gv.el index 892d6e9716..777b955d90 100644 --- a/lisp/emacs-lisp/gv.el +++ b/lisp/emacs-lisp/gv.el @@ -303,7 +303,9 @@ The return value is the last VAL in the list. (lambda (do before index place) (gv-letplace (getter setter) place (funcall do `(edebug-after ,before ,index ,getter) - setter)))) + (lambda (store) + `(progn (edebug-after ,before ,index ,getter) + ,(funcall setter store))))))) ;;; The common generalized variables. diff --git a/lisp/emacs-lisp/testcover.el b/lisp/emacs-lisp/testcover.el index 691860bbd7..320c43b59f 100644 --- a/lisp/emacs-lisp/testcover.el +++ b/lisp/emacs-lisp/testcover.el @@ -33,7 +33,9 @@ ;; that has a splotch. ;; * Basic algorithm: use `edebug' to mark up the function text with -;; instrumentation callbacks, then replace edebug's callbacks with ours. +;; instrumentation callbacks, walk the instrumented code looking for +;; forms which don't return or always return the same value, then use +;; Edebug's before and after hooks to replace its code coverage with ours. ;; * To show good coverage, we want to see two values for every form, except ;; functions that always return the same value and `defconst' variables ;; need show only one value for good coverage. To avoid the brown @@ -89,16 +91,14 @@ these. This list is quite incomplete!" buffer-disable-undo buffer-enable-undo current-global-map deactivate-mark delete-backward-char delete-char delete-region ding forward-char function* insert insert-and-inherit kill-all-local-variables - kill-line kill-paragraph kill-region kill-sexp lambda + kill-line kill-paragraph kill-region kill-sexp minibuffer-complete-and-exit narrow-to-region next-line push-mark put-text-property run-hooks set-match-data signal substitute-key-definition suppress-keymap undo use-local-map while widen yank) - "Functions that always return the same value. No brown splotch is shown -for these. This list is quite incomplete! Notes: Nobody ever changes the -current global map. The macro `lambda' is self-evaluating, hence always -returns the same value (the function it defines may return varying values -when called)." + "Functions that always return the same value, according to `equal'. +No brown splotch is shown for these. This list is quite +incomplete! Notes: Nobody ever changes the current global map." :group 'testcover :type '(repeat symbol)) @@ -111,7 +111,7 @@ them as having returned nil just before calling them." (defcustom testcover-compose-functions '(+ - * / = append length list make-keymap make-sparse-keymap - mapcar message propertize replace-regexp-in-string + message propertize replace-regexp-in-string run-with-idle-timer set-buffer-modified-p) "Functions that are 1-valued if all their args are either constants or calls to one of the `testcover-1value-functions', so if that's true then no @@ -186,19 +186,21 @@ call to one of the `testcover-1value-functions'." ;;;###autoload (defun testcover-start (filename &optional byte-compile) - "Uses edebug to instrument all macros and functions in FILENAME, then -changes the instrumentation from edebug to testcover--much faster, no -problems with type-ahead or post-command-hook, etc. If BYTE-COMPILE is -non-nil, byte-compiles each function after instrumenting." + "Use Edebug to instrument for coverage all macros and functions in FILENAME. +If BYTE-COMPILE is non-nil, byte compile each function after instrumenting." (interactive "fStart covering file: ") - (let ((buf (find-file filename)) - (load-read-function load-read-function)) - (add-function :around load-read-function - #'testcover--read) - (setq edebug-form-data nil - testcover-module-constants nil - testcover-module-1value-functions nil) - (eval-buffer buf)) + (let ((buf (find-file filename))) + (setq edebug-form-data nil + testcover-module-constants nil + testcover-module-1value-functions nil + testcover-module-potentially-1value-functions nil) + (cl-letf ((edebug-all-defs t) + (edebug-after-instrumentation-functions) + (edebug-new-definition-functions)) + (add-hook 'edebug-after-instrumentation-functions 'testcover-after-instrumentation) + (add-hook 'edebug-new-definition-functions 'testcover-init-definition) + (remove-hook 'edebug-new-definition-functions 'edebug-announce-definition) + (eval-buffer buf))) (when byte-compile (dolist (x (reverse edebug-form-data)) (when (fboundp (car x)) @@ -209,229 +211,13 @@ non-nil, byte-compiles each function after instrumenting." (defun testcover-this-defun () "Start coverage on function under point." (interactive) - (let ((x (let ((edebug-all-defs t)) - (symbol-function (eval-defun nil))))) - (testcover-reinstrument x) - x)) - -(defun testcover--read (orig &optional stream) - "Read a form using edebug, changing edebug callbacks to testcover callbacks." - (or stream (setq stream standard-input)) - (if (eq stream (current-buffer)) - (let ((x (let ((edebug-all-defs t)) - (edebug-read-and-maybe-wrap-form)))) - (testcover-reinstrument x) - x) - (funcall (or orig #'read) stream))) - -(defun testcover-reinstrument (form) - "Reinstruments FORM to use testcover instead of edebug. This -function modifies the list that FORM points to. Result is nil if -FORM should return multiple values, t if should always return same -value, `maybe' if either is acceptable." - (let ((fun (car-safe form)) - id val) - (cond - ((not fun) ;Atom - (when (or (not (symbolp form)) - (memq form testcover-constants) - (memq form testcover-module-constants)) - t)) - ((consp fun) ;Embedded list - (testcover-reinstrument fun) - (testcover-reinstrument-list (cdr form)) - nil) - ((or (memq fun testcover-1value-functions) - (memq fun testcover-module-1value-functions)) - ;;Should always return same value - (testcover-reinstrument-list (cdr form)) - t) - ((or (memq fun testcover-potentially-1value-functions) - (memq fun testcover-module-potentially-1value-functions)) - ;;Might always return same value - (testcover-reinstrument-list (cdr form)) - 'maybe) - ((memq fun testcover-progn-functions) - ;;1-valued if last argument is - (testcover-reinstrument-list (cdr form))) - ((memq fun testcover-prog1-functions) - ;;1-valued if first argument is - (testcover-reinstrument-list (cddr form)) - (testcover-reinstrument (cadr form))) - ((memq fun testcover-compose-functions) - ;;1-valued if all arguments are. Potentially 1-valued if all - ;;arguments are either definitely or potentially. - (testcover-reinstrument-compose (cdr form) 'testcover-reinstrument)) - ((eq fun 'edebug-enter) - ;;(edebug-enter 'SYM ARGS #'(lambda nil FORMS)) - ;; => (testcover-enter 'SYM #'(lambda nil FORMS)) - (setcar form 'testcover-enter) - (setcdr (nthcdr 1 form) (nthcdr 3 form)) - (let ((testcover-vector (get (cadr (cadr form)) 'edebug-coverage))) - (testcover-reinstrument-list (nthcdr 2 (cadr (nth 2 form)))))) - ((eq fun 'edebug-after) - ;;(edebug-after (edebug-before XXX) YYY FORM) - ;; => (testcover-after YYY FORM), mark XXX as ok-coverage - (unless (eq (cadr form) 0) - (aset testcover-vector (cadr (cadr form)) 'ok-coverage)) - (setq id (nth 2 form)) - (setcdr form (nthcdr 2 form)) - (setq val (testcover-reinstrument (nth 2 form))) - (setcar form (if (eq val t) - 'testcover-1value - 'testcover-after)) - (when val - ;;1-valued or potentially 1-valued - (aset testcover-vector id '1value)) - (cond - ((memq (car-safe (nth 2 form)) testcover-noreturn-functions) - ;;This function won't return, so set the value in advance - ;;(edebug-after (edebug-before XXX) YYY FORM) - ;; => (progn (edebug-after YYY nil) FORM) - (setcar (cdr form) `(,(car form) ,id nil)) - (setcar form 'progn) - (aset testcover-vector id '1value) - (setq val t)) - ((eq (car-safe (nth 2 form)) '1value) - ;;This function is always supposed to return the same value - (setq val t) - (aset testcover-vector id '1value) - (setcar form 'testcover-1value))) - val) - ((eq fun 'defun) - (setq val (testcover-reinstrument-list (nthcdr 3 form))) - (when (eq val t) - (push (cadr form) testcover-module-1value-functions)) - (when (eq val 'maybe) - (push (cadr form) testcover-module-potentially-1value-functions))) - ((memq fun '(defconst defcustom)) - ;;Define this symbol as 1-valued - (push (cadr form) testcover-module-constants) - (testcover-reinstrument-list (cddr form))) - ((memq fun '(dotimes dolist)) - ;;Always returns third value from SPEC - (testcover-reinstrument-list (cddr form)) - (setq val (testcover-reinstrument-list (cadr form))) - (if (nth 2 (cadr form)) - val - ;;No third value, always returns nil - t)) - ((memq fun '(let let*)) - ;;Special parsing for second argument - (mapc 'testcover-reinstrument-list (cadr form)) - (testcover-reinstrument-list (cddr form))) - ((eq fun 'if) - ;;Potentially 1-valued if both THEN and ELSE clauses are - (testcover-reinstrument (cadr form)) - (let ((then (testcover-reinstrument (nth 2 form))) - (else (testcover-reinstrument-list (nthcdr 3 form)))) - (and then else 'maybe))) - ((eq fun 'cond) - ;;Potentially 1-valued if all clauses are - (when (testcover-reinstrument-compose (cdr form) - 'testcover-reinstrument-list) - 'maybe)) - ((eq fun 'condition-case) - ;;Potentially 1-valued if BODYFORM is and all HANDLERS are - (let ((body (testcover-reinstrument (nth 2 form))) - (errs (testcover-reinstrument-compose - (mapcar #'cdr (nthcdr 3 form)) - 'testcover-reinstrument-list))) - (and body errs 'maybe))) - ((eq fun 'quote) - ;;Don't reinstrument what's inside! - ;;This doesn't apply within a backquote - t) - ((eq fun '\`) - ;;Quotes are not special within backquotes - (let ((testcover-1value-functions - (cons 'quote testcover-1value-functions))) - (testcover-reinstrument (cadr form)))) - ((eq fun '\,) - ;;In commas inside backquotes, quotes are special again - (let ((testcover-1value-functions - (remq 'quote testcover-1value-functions))) - (testcover-reinstrument (cadr form)))) - ((eq fun '1value) - ;;Hack - pretend the arg is 1-valued here - (cond - ((symbolp (cadr form)) - ;;A pseudoconstant variable - t) - ((and (eq (car (cadr form)) 'edebug-after) - (symbolp (nth 3 (cadr form)))) - ;;Reference to pseudoconstant - (aset testcover-vector (nth 2 (cadr form)) '1value) - (setcar (cdr form) `(testcover-1value ,(nth 2 (cadr form)) - ,(nth 3 (cadr form)))) - t) - (t - (setq id (car (if (eq (car (cadr form)) 'edebug-after) - (nth 3 (cadr form)) - (cadr form)))) - (let ((testcover-1value-functions - (cons id testcover-1value-functions))) - (testcover-reinstrument (cadr form)))))) - ((eq fun 'noreturn) - ;;Hack - pretend the arg has no return - (cond - ((symbolp (cadr form)) - ;;A pseudoconstant variable - 'maybe) - ((and (eq (car (cadr form)) 'edebug-after) - (symbolp (nth 3 (cadr form)))) - ;;Reference to pseudoconstant - (aset testcover-vector (nth 2 (cadr form)) '1value) - (setcar (cdr form) `(progn (testcover-after ,(nth 2 (cadr form)) nil) - ,(nth 3 (cadr form)))) - 'maybe) - (t - (setq id (car (if (eq (car (cadr form)) 'edebug-after) - (nth 3 (cadr form)) - (cadr form)))) - (let ((testcover-noreturn-functions - (cons id testcover-noreturn-functions))) - (testcover-reinstrument (cadr form)))))) - ((and (eq fun 'apply) - (eq (car-safe (cadr form)) 'quote) - (symbolp (cadr (cadr form)))) - ;;Apply of a constant symbol. Process as 1value or noreturn - ;;depending on symbol. - (setq fun (cons (cadr (cadr form)) (cddr form)) - val (testcover-reinstrument fun)) - (setcdr (cdr form) (cdr fun)) - val) - (t ;Some other function or weird thing - (testcover-reinstrument-list (cdr form)) - nil)))) - -(defun testcover-reinstrument-list (list) - "Reinstruments each form in LIST to use testcover instead of edebug. -This function modifies the forms in LIST. Result is `testcover-reinstrument's -value for the last form in LIST. If the LIST is empty, its evaluation will -always be nil, so we return t for 1-valued." - (let ((result t)) - (while (consp list) - (setq result (testcover-reinstrument (pop list)))) - result)) - -(defun testcover-reinstrument-compose (list fun) - "For a compositional function, the result is 1-valued if all -arguments are, potentially 1-valued if all arguments are either -definitely or potentially 1-valued, and multi-valued otherwise. -FUN should be `testcover-reinstrument' for compositional functions, - `testcover-reinstrument-list' for clauses in a `cond'." - (let ((result t)) - (mapc #'(lambda (x) - (setq x (funcall fun x)) - (cond - ((eq result t) - (setq result x)) - ((eq result 'maybe) - (when (not x) - (setq result nil))))) - list) - result)) + (cl-letf ((edebug-all-defs t) + (edebug-after-instrumentation-functions) + (edebug-new-definition-functions)) + (add-hook 'edebug-after-instrumentation-functions 'testcover-after-instrumentation) + (add-hook 'edebug-new-definition-functions 'testcover-init-definition) + (remove-hook 'edebug-new-definition-functions 'edebug-announce-definition) + (eval-defun nil))) (defun testcover-end (filename) "Turn off instrumentation of all macros and functions in FILENAME." @@ -444,48 +230,61 @@ FUN should be `testcover-reinstrument' for compositional functions, ;;; Accumulate coverage data ;;;========================================================================= -(defun testcover-enter (testcover-sym testcover-fun) - "Internal function for coverage testing. Invokes TESTCOVER-FUN while -binding `testcover-vector' to the code-coverage vector for TESTCOVER-SYM -\(the name of the current function)." - (let ((testcover-vector (get testcover-sym 'edebug-coverage))) - (funcall testcover-fun))) - -(defun testcover-after (idx val) - "Internal function for coverage testing. Returns VAL after installing it in -`testcover-vector' at offset IDX." - (declare (gv-expander (lambda (do) - (gv-letplace (getter setter) val - (funcall do getter - (lambda (store) - `(progn (testcover-after ,idx ,getter) - ,(funcall setter store)))))))) - (cond - ((eq (aref testcover-vector idx) 'unknown) - (aset testcover-vector idx val)) - ((not (condition-case () - (equal (aref testcover-vector idx) val) - ;; TODO: Actually check circular lists for equality. - (circular-list nil))) - (aset testcover-vector idx 'ok-coverage))) - val) - -(defun testcover-1value (idx val) - "Internal function for coverage testing. Returns VAL after installing it in -`testcover-vector' at offset IDX. Error if FORM does not always return the -same value during coverage testing." - (cond - ((eq (aref testcover-vector idx) '1value) - (aset testcover-vector idx (cons '1value val))) - ((not (and (eq (car-safe (aref testcover-vector idx)) '1value) - (condition-case () - (equal (cdr (aref testcover-vector idx)) val) - ;; TODO: Actually check circular lists for equality. - (circular-list nil)))) - (error "Value of form marked with `1value' does vary: %s" val))) - val) - - +(defun testcover-after-instrumentation (form) + "Analyze FORM for code coverage." + (testcover-analyze-coverage form)) + +(defun testcover-init-definition (sym) + "Mark SYM as under test coverage." + (message "Testcover: %s" edebug-def-name) + (put sym 'edebug-behavior 'testcover)) + +(defun testcover-enter (func _args body) + "Begin execution of a function under coverage testing. +Bind `testcover-vector' to the code-coverage vector for FUNC and +return the result of evaluating BODY." + (let ((testcover-vector (get func 'edebug-coverage))) + (funcall body))) + +(defun testcover-before (before-index) + "Update code coverage before a form is evaluated. +BEFORE-INDEX is the form's index into the code-coverage vector." + (let ((before-entry (aref testcover-vector before-index))) + (when (eq (car-safe before-entry) 'noreturn) + (let* ((after-index (cdr before-entry))) + (aset testcover-vector after-index 'ok-coverage))))) + +(defun testcover-after (_before-index after-index value) + "Update code coverage with the result of a form's evaluation. +AFTER-INDEX is the form's index into the code-coverage +vector. Return VALUE." + (let ((old-result (aref testcover-vector after-index))) + (cond + ((eq 'unknown old-result) + (aset testcover-vector after-index value)) + ((eq 'maybe old-result) + (aset testcover-vector after-index 'ok-coverage)) + ((eq '1value old-result) + (aset testcover-vector after-index + (cons old-result value))) + ((and (eq (car-safe old-result) '1value) + (not (condition-case () + (equal (cdr old-result) value) + ;; TODO: Actually check circular lists for equality. + (circular-list t)))) + (error "Value of form marked with `1value' does vary: %s" value)) + ;; Test if a different result. + ((not (condition-case () + (equal value old-result) + ;; TODO: Actually check circular lists for equality. + (circular-list nil))) + (aset testcover-vector after-index 'ok-coverage)))) + value) + +;; Add these behaviors to Edebug. +(unless (assoc 'testcover edebug-behavior-alist) + (push '(testcover testcover-enter testcover-before testcover-after) + edebug-behavior-alist)) ;;;========================================================================= ;;; Display the coverage data as color splotches on your code. @@ -517,12 +316,13 @@ eliminated by adding more test cases." (while (> len 0) (setq len (1- len) data (aref coverage len)) - (when (and (not (eq data 'ok-coverage)) - (not (eq (car-safe data) '1value)) - (setq j (+ def-mark (aref points len)))) + (when (and (not (eq data 'ok-coverage)) + (not (memq (car-safe data) + '(1value maybe noreturn))) + (setq j (+ def-mark (aref points len)))) (setq ov (make-overlay (1- j) j)) (overlay-put ov 'face - (if (memq data '(unknown 1value)) + (if (memq data '(unknown maybe 1value)) 'testcover-nohits 'testcover-1value)))) (set-buffer-modified-p changed)))) @@ -553,4 +353,284 @@ coverage tests. This function creates many overlays." (goto-char (next-overlay-change (point))) (end-of-line)) + +;;; Coverage Analysis + +;; The top level function for initializing code coverage is +;; `testcover-analyze-coverage', which recursively walks the form it is +;; passed, which should have already been instrumented by +;; edebug-read-and-maybe-wrap-form, and initializes the associated +;; code coverage vectors, which should have already been created by +;; `edebug-clear-coverage'. +;; +;; The purpose of the analysis is to identify forms which can only +;; ever return a single value. These forms can be considered to have +;; adequate code coverage even if only executed once. In addition, +;; forms which will never return, such as error signals, can be +;; identified and treated correctly. +;; +;; The code coverage vector entries for the beginnings of forms will +;; be changed to `ok-coverage.', except for the beginnings of forms +;; which should never return, which will be changed to +;; (noreturn . AFTER-INDEX) so that testcover-before can set the entry +;; for the end of the form just before it is executed. +;; +;; Entries for the ends of forms may be changed to `1value' if +;; analysis determines the form will only ever return a single value, +;; or `maybe' if the form could potentially only ever return a single +;; value. +;; +;; An example of a potentially 1-valued form is an `and' whose last +;; term is 1-valued, in case the last term is always nil. Example: +;; +;; (and (< (point) 1000) (forward-char 10)) +;; +;; This form always returns nil. Similarly, `or', `if', and `cond' +;; are treated as potentially 1-valued if all clauses are, in case +;; those values are always nil. Unlike truly 1-valued functions, it +;; is not an error if these "potentially" 1-valued forms actually +;; return differing values. + +(defun testcover-analyze-coverage (form) + "Analyze FORM and initialize coverage vectors for definitions found within. +Return 1value, maybe or nil depending on if the form is determined +to return only a single value, potentially return only a single value, +or return multiple values." + (pcase form + (`(edebug-enter ',sym ,_ (function (lambda nil . ,body))) + (let ((testcover-vector (get sym 'edebug-coverage))) + (testcover-analyze-coverage-progn body))) + + (`(edebug-after ,(and before-form + (or `(edebug-before ,before-id) before-id)) + ,after-id ,wrapped-form) + (testcover-analyze-coverage-edebug-after + form before-form before-id after-id wrapped-form)) + + (`(defconst ,sym . ,args) + (push sym testcover-module-constants) + (testcover-analyze-coverage-progn args) + '1value) + + (`(defun ,name ,_ . ,doc-and-body) + (let ((val (testcover-analyze-coverage-progn doc-and-body))) + (cl-case val + ((1value) (push name testcover-module-1value-functions)) + ((maybe) (push name testcover-module-potentially-1value-functions))) + nil)) + + (`(quote . ,_) + ;; A quoted form is 1value. Edebug could have instrumented + ;; something inside the form if an Edebug spec contained a quote. + ;; It's also possible that the quoted form is a circular object. + ;; To avoid infinite recursion, don't examine quoted objects. + ;; This will cause the coverage marks on an instrumented quoted + ;; form to look odd. See bug#25316. + '1value) + + (`(\` ,bq-form) + (testcover-analyze-coverage-backquote-form bq-form)) + + ((or 't 'nil (pred keywordp)) + '1value) + + ((pred vectorp) + (testcover-analyze-coverage-compose (append form nil) + #'testcover-analyze-coverage)) + + ((pred symbolp) + nil) + + ((pred atom) + '1value) + + (_ + ;; Whatever we have here, it's not wrapped, so treat it as a list of forms. + (testcover-analyze-coverage-compose form #'testcover-analyze-coverage)))) + +(defun testcover-analyze-coverage-progn (forms) + "Analyze FORMS, which should be a list of forms, for code coverage. +Analyze all the forms in FORMS and return 1value, maybe or nil +depending on the analysis of the last one. Find the coverage +vectors referenced by `edebug-enter' forms nested within FORMS and +update them with the results of the analysis." + (let ((result '1value)) + (while (consp forms) + (setq result (testcover-analyze-coverage (pop forms)))) + result)) + +(defun testcover-analyze-coverage-edebug-after (_form before-form before-id + after-id wrapped-form + &optional wrapper) + "Analyze a _FORM wrapped by `edebug-after' for code coverage. +_FORM should be either: + (edebug-after (edebug-before BEFORE-ID) AFTER-ID WRAPPED-FORM) +or: + (edebug-after 0 AFTER-ID WRAPPED-FORM) + +where BEFORE-FORM is bound to either (edebug-before BEFORE-ID) or +0. WRAPPER may be 1value or noreturn, and if so it forces the +form to be treated accordingly." + (let (val) + (unless (eql before-form 0) + (aset testcover-vector before-id 'ok-coverage)) + + (setq val (testcover-analyze-coverage-wrapped-form wrapped-form)) + (when (or (eq wrapper '1value) val) + ;; The form is 1-valued or potentially 1-valued. + (aset testcover-vector after-id (or val '1value))) + + (cond + ((or (eq wrapper 'noreturn) + (memq (car-safe wrapped-form) testcover-noreturn-functions)) + ;; This function won't return, so indicate to testcover-before that + ;; it should record coverage. + (aset testcover-vector before-id (cons 'noreturn after-id)) + (aset testcover-vector after-id '1value) + (setq val '1value)) + + ((eq (car-safe wrapped-form) '1value) + ;; This function is always supposed to return the same value. + (setq val '1value) + (aset testcover-vector after-id '1value))) + val)) + +(defun testcover-analyze-coverage-wrapped-form (form) + "Analyze a FORM for code coverage which was wrapped by `edebug-after'. +FORM is treated as if it will be evaluated." + (pcase form + ((pred keywordp) + '1value) + ((pred symbolp) + (when (or (memq form testcover-constants) + (memq form testcover-module-constants)) + '1value)) + ((pred atom) + '1value) + (`(\` ,bq-form) + (testcover-analyze-coverage-backquote-form bq-form)) + (`(defconst ,sym ,val . ,_) + (push sym testcover-module-constants) + (testcover-analyze-coverage val) + '1value) + (`(,(or 'dotimes 'dolist) (,_ ,expr . ,result) . ,body) + ;; These always return RESULT if provided. + (testcover-analyze-coverage expr) + (testcover-analyze-coverage-progn body) + (let ((val (testcover-analyze-coverage-progn result))) + ;; If the third value is not present, the loop always returns nil. + (if result val '1value))) + (`(,(or 'let 'let*) ,bindings . ,body) + (testcover-analyze-coverage-progn bindings) + (testcover-analyze-coverage-progn body)) + (`(if ,test ,then-form . ,else-body) + ;; `if' is potentially 1-valued if both THEN and ELSE clauses are. + (testcover-analyze-coverage test) + (let ((then (testcover-analyze-coverage then-form)) + (else (testcover-analyze-coverage else-body))) + (and then else 'maybe))) + (`(cond . ,clauses) + ;; `cond' is potentially 1-valued if all clauses are. + (when (testcover-analyze-coverage-compose clauses #'testcover-analyze-coverage-progn) + 'maybe)) + (`(condition-case ,_ ,body-form . ,handlers) + ;; `condition-case' is potentially 1-valued if BODY-FORM is and all + ;; HANDLERS are. + (let ((body (testcover-analyze-coverage body-form)) + (errs (testcover-analyze-coverage-compose + (mapcar #'cdr handlers) + #'testcover-analyze-coverage-progn))) + (and body errs 'maybe))) + (`(apply (quote ,(and func (pred symbolp))) . ,args) + ;; Process application of a constant symbol as 1value or noreturn + ;; depending on the symbol. + (let ((temp-form (cons func args))) + (testcover-analyze-coverage-wrapped-form temp-form))) + (`(,(and func (or '1value 'noreturn)) ,inner-form) + ;; 1value and noreturn change how the edebug-after they wrap is handled. + (let ((val (if (eq func '1value) '1value 'maybe))) + (pcase inner-form + (`(edebug-after ,(and before-form + (or `(edebug-before ,before-id) before-id)) + ,after-id ,wrapped-form) + (testcover-analyze-coverage-edebug-after inner-form before-form + before-id after-id + wrapped-form func)) + (_ (testcover-analyze-coverage inner-form))) + val)) + (`(,func . ,args) + (testcover-analyze-coverage-wrapped-application func args)))) + +(defun testcover-analyze-coverage-wrapped-application (func args) + "Analyze the application of FUNC to ARGS for code coverage." + (cond + ((eq func 'quote) '1value) + ((or (memq func testcover-1value-functions) + (memq func testcover-module-1value-functions)) + ;; The function should always return the same value. + (testcover-analyze-coverage-progn args) + '1value) + ((or (memq func testcover-potentially-1value-functions) + (memq func testcover-module-potentially-1value-functions)) + ;; The function might always return the same value. + (testcover-analyze-coverage-progn args) + 'maybe) + ((memq func testcover-progn-functions) + ;; The function is 1-valued if the last argument is. + (testcover-analyze-coverage-progn args)) + ((memq func testcover-prog1-functions) + ;; The function is 1-valued if first argument is. + (testcover-analyze-coverage-progn (cdr args)) + (testcover-analyze-coverage (car args))) + ((memq func testcover-compose-functions) + ;; The function is 1-valued if all arguments are, and potentially + ;; 1-valued if all arguments are either definitely or potentially. + (testcover-analyze-coverage-compose args #'testcover-analyze-coverage)) + (t (testcover-analyze-coverage-progn args) + nil))) + +(defun testcover-coverage-combine (result val) + "Combine RESULT with VAL and return the new result. +If either argument is nil, return nil, otherwise if either +argument is maybe, return maybe. Return 1value only if both arguments +are 1value." + (cl-case val + (1value result) + (maybe (and result 'maybe)) + (nil nil))) + +(defun testcover-analyze-coverage-compose (forms func) + "Analyze a list of FORMS for code coverage using FUNC. +The list is 1valued if all of its constituent elements are also 1valued." + (let ((result '1value)) + (dolist (form forms) + (let ((val (funcall func form))) + (setq result (testcover-coverage-combine result val)))) + result)) + +(defun testcover-analyze-coverage-backquote (bq-list) + "Analyze BQ-LIST, the body of a backquoted list, for code coverage." + (let ((result '1value)) + (while (consp bq-list) + (let ((form (car bq-list)) + val) + (if (memq form (list '\, '\,@)) + ;; Correctly handle `(foo bar . ,(baz). + (progn + (setq val (testcover-analyze-coverage (cdr bq-list))) + (setq bq-list nil)) + (setq val (testcover-analyze-coverage-backquote-form form)) + (setq bq-list (cdr bq-list))) + (setq result (testcover-coverage-combine result val)))) + result)) + +(defun testcover-analyze-coverage-backquote-form (form) + "Analyze a single FORM from a backquoted list for code coverage." + (cond + ((vectorp form) (testcover-analyze-coverage-backquote (append form nil))) + ((atom form) '1value) + ((memq (car form) (list '\, '\,@)) + (testcover-analyze-coverage (cadr form))) + (t (testcover-analyze-coverage-backquote form)))) + ;; testcover.el ends here. diff --git a/lisp/subr.el b/lisp/subr.el index d2fefe04f8..c1eae8d752 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -78,8 +78,8 @@ If FORM does return, signal an error." (defmacro 1value (form) "Evaluate FORM, expecting a constant return value. -This is the global do-nothing version. There is also `testcover-1value' -that complains if FORM ever does return differing values." +If FORM returns differing values when running under Testcover, +Testcover will raise an error." (declare (debug t)) form) diff --git a/lisp/textmodes/rst.el b/lisp/textmodes/rst.el index 5534294738..5eb64c82b9 100644 --- a/lisp/textmodes/rst.el +++ b/lisp/textmodes/rst.el @@ -128,27 +128,6 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Support for `testcover' -(when (and (boundp 'testcover-1value-functions) - (boundp 'testcover-compose-functions)) - ;; Below `lambda' is used in a loop with varying parameters and is thus not - ;; 1valued. - (setq testcover-1value-functions - (delq 'lambda testcover-1value-functions)) - (add-to-list 'testcover-compose-functions 'lambda)) - -(defun rst-testcover-defcustom () - "Remove all customized variables from `testcover-module-constants'. -This seems to be a bug in `testcover': `defcustom' variables are -considered constants. Revert it with this function after each `defcustom'." - (when (boundp 'testcover-module-constants) - (setq testcover-module-constants - (delq nil - (mapcar - #'(lambda (sym) - (if (not (plist-member (symbol-plist sym) 'standard-value)) - sym)) - testcover-module-constants))))) - (defun rst-testcover-add-compose (fun) "Add FUN to `testcover-compose-functions'." (when (boundp 'testcover-compose-functions) @@ -1360,7 +1339,6 @@ This inherits from Text mode.") The hook for `text-mode' is run before this one." :group 'rst :type '(hook)) -(rst-testcover-defcustom) ;; Pull in variable definitions silencing byte-compiler. (require 'newcomment) @@ -1557,7 +1535,6 @@ file." (const :tag "Underline only" simple)) (integer :tag "Indentation for overline and underline type" :value 0)))) -(rst-testcover-defcustom) ;; FIXME: Rename this to `rst-over-and-under-default-indent' and set default to ;; 0 because the effect of 1 is probably surprising in the few cases @@ -1574,7 +1551,6 @@ found in the buffer are to be used but the indentation for over-and-under adornments is inconsistent across the buffer." :group 'rst-adjust :type '(integer)) -(rst-testcover-defcustom) (defun rst-new-preferred-hdr (seen prev) ;; testcover: ok. @@ -2013,7 +1989,6 @@ b. a negative numerical argument, which generally inverts the :group 'rst-adjust :type '(hook) :package-version '(rst . "1.1.0")) -(rst-testcover-defcustom) (defcustom rst-new-adornment-down nil "Controls level of new adornment for section headers." @@ -2022,7 +1997,6 @@ b. a negative numerical argument, which generally inverts the (const :tag "Same level as previous one" nil) (const :tag "One level down relative to the previous one" t)) :package-version '(rst . "1.1.0")) -(rst-testcover-defcustom) (defun rst-adjust-adornment (pfxarg) "Call `rst-adjust-section' interactively. @@ -2445,7 +2419,6 @@ also arranged by `rst-insert-list-new-tag'." :tag (char-to-string char) char)) rst-bullets))) :package-version '(rst . "1.1.0")) -(rst-testcover-defcustom) (defun rst-insert-list-continue (ind tag tab prefer-roman) ;; testcover: ok. @@ -2682,7 +2655,6 @@ section headers at all." Also used for formatting insertion, when numbering is disabled." :type 'integer :group 'rst-toc) -(rst-testcover-defcustom) (defcustom rst-toc-insert-style 'fixed "Insertion style for table-of-contents. @@ -2697,19 +2669,16 @@ indentation style: (const aligned) (const listed)) :group 'rst-toc) -(rst-testcover-defcustom) (defcustom rst-toc-insert-number-separator " " "Separator that goes between the TOC number and the title." :type 'string :group 'rst-toc) -(rst-testcover-defcustom) (defcustom rst-toc-insert-max-level nil "If non-nil, maximum depth of the inserted TOC." :type '(choice (const nil) integer) :group 'rst-toc) -(rst-testcover-defcustom) (defconst rst-toc-link-keymap (let ((map (make-sparse-keymap))) @@ -3174,35 +3143,30 @@ These indentation widths can be customized here." "Indentation when there is no more indentation point given." :group 'rst-indent :type '(integer)) -(rst-testcover-defcustom) (defcustom rst-indent-field 3 "Indentation for first line after a field or 0 to always indent for content." :group 'rst-indent :package-version '(rst . "1.1.0") :type '(integer)) -(rst-testcover-defcustom) (defcustom rst-indent-literal-normal 3 "Default indentation for literal block after a markup on an own line." :group 'rst-indent :package-version '(rst . "1.1.0") :type '(integer)) -(rst-testcover-defcustom) (defcustom rst-indent-literal-minimized 2 "Default indentation for literal block after a minimized markup." :group 'rst-indent :package-version '(rst . "1.1.0") :type '(integer)) -(rst-testcover-defcustom) (defcustom rst-indent-comment 3 "Default indentation for first line of a comment." :group 'rst-indent :package-version '(rst . "1.1.0") :type '(integer)) -(rst-testcover-defcustom) ;; FIXME: Must consider other tabs: ;; * Line blocks @@ -3652,7 +3616,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." :version "24.1" :group 'rst-faces :type '(face)) -(rst-testcover-defcustom) (make-obsolete-variable 'rst-block-face "customize the face `rst-block' instead." "24.1") @@ -3667,7 +3630,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." :version "24.1" :group 'rst-faces :type '(face)) -(rst-testcover-defcustom) (make-obsolete-variable 'rst-external-face "customize the face `rst-external' instead." "24.1") @@ -3682,7 +3644,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." :version "24.1" :group 'rst-faces :type '(face)) -(rst-testcover-defcustom) (make-obsolete-variable 'rst-definition-face "customize the face `rst-definition' instead." "24.1") @@ -3699,7 +3660,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." "Directives and roles." :group 'rst-faces :type '(face)) -(rst-testcover-defcustom) (make-obsolete-variable 'rst-directive-face "customize the face `rst-directive' instead." "24.1") @@ -3714,7 +3674,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." :version "24.1" :group 'rst-faces :type '(face)) -(rst-testcover-defcustom) (make-obsolete-variable 'rst-comment-face "customize the face `rst-comment' instead." "24.1") @@ -3729,7 +3688,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." :version "24.1" :group 'rst-faces :type '(face)) -(rst-testcover-defcustom) (make-obsolete-variable 'rst-emphasis1-face "customize the face `rst-emphasis1' instead." "24.1") @@ -3743,7 +3701,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." "Double emphasis." :group 'rst-faces :type '(face)) -(rst-testcover-defcustom) (make-obsolete-variable 'rst-emphasis2-face "customize the face `rst-emphasis2' instead." "24.1") @@ -3758,7 +3715,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." :version "24.1" :group 'rst-faces :type '(face)) -(rst-testcover-defcustom) (make-obsolete-variable 'rst-literal-face "customize the face `rst-literal' instead." "24.1") @@ -3773,7 +3729,6 @@ Region is from BEG to END. With WITH-EMPTY prefix empty lines too." :version "24.1" :group 'rst-faces :type '(face)) -(rst-testcover-defcustom) (make-obsolete-variable 'rst-reference-face "customize the face `rst-reference' instead." "24.1") @@ -3856,7 +3811,6 @@ of your own." (const :tag "transitions" t) (const :tag "section title adornment" nil)) :value-type (face))) -(rst-testcover-defcustom) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -4353,7 +4307,6 @@ string)) to be used for converting the document." (string :tag "Options")))) :group 'rst-compile :package-version "1.2.0") -(rst-testcover-defcustom) ;; FIXME: Must be defcustom. (defvar rst-compile-primary-toolset 'html diff --git a/test/lisp/emacs-lisp/testcover-resources/testcases.el b/test/lisp/emacs-lisp/testcover-resources/testcases.el index edb539f4c2..d8b8192748 100644 --- a/test/lisp/emacs-lisp/testcover-resources/testcases.el +++ b/test/lisp/emacs-lisp/testcover-resources/testcases.el @@ -53,7 +53,6 @@ ;; ==== constants-bug-25316 ==== "Testcover doesn't splotch constants." -:expected-result :failed ;; ==== (defconst testcover-testcase-const "apples") (defun testcover-testcase-zero () 0) @@ -76,7 +75,6 @@ ;; ==== customize-defcustom-bug-25326 ==== "Testcover doesn't prevent testing of defcustom values." -:expected-result :failed ;; ==== (defgroup testcover-testcase nil "Test case for testcover" @@ -135,7 +133,6 @@ ;; ==== 1-value-symbol-bug-25316 ==== "Wrapping a form with 1value prevents splotching." -:expected-result :failed ;; ==== (defun testcover-testcase-always-zero (num) (- num%%% num%%%)%%%) @@ -230,7 +227,6 @@ ;; ==== quotes-within-backquotes-bug-25316 ==== "Forms to instrument are found within quotes within backquotes." -:expected-result :failed ;; ==== (defun testcover-testcase-make-list () (list 'defun 'defvar)) @@ -296,7 +292,6 @@ ;; ==== backquote-1value-bug-24509 ==== "Commas within backquotes are recognized as non-1value." -:expected-result :failed ;; ==== (defmacro testcover-testcase-lambda (&rest body) `(lambda () ,@body)) @@ -320,7 +315,6 @@ ;; ==== pcase-bug-24688 ==== "Testcover copes with condition-case within backquoted list." -:expected-result :failed ;; ==== (defun testcover-testcase-pcase (form) (pcase form%%% @@ -335,7 +329,6 @@ ;; ==== defun-in-backquote-bug-11307-and-24743 ==== "Testcover handles defun forms within backquoted list." -:expected-result :failed ;; ==== (defmacro testcover-testcase-defun (name &rest body) (declare (debug (symbolp def-body))) @@ -348,7 +341,6 @@ ;; ==== closure-1value-bug ==== "Testcover does not mark closures as 1value." -:expected-result :failed ;; ==== ;; -*- lexical-binding:t -*- (setq testcover-testcase-foo nil) @@ -396,9 +388,16 @@ (should (equal '(a b c) (testcover-testcase-dotted-bq nil '(d e)))) (should (equal '(a b c d e) (testcover-testcase-dotted-bq t '(d e)))) +;; ==== quoted-backquote ==== +"Testcover correctly instruments the quoted backquote symbol." +;; ==== +(defun testcover-testcase-special-symbols () + (list '\` '\, '\,@)) + +(should (equal '(\` \, \,@) (testcover-testcase-special-symbols))) + ;; ==== backquoted-vector-bug-25316 ==== "Testcover reinstruments within backquoted vectors." -:expected-result :failed ;; ==== (defun testcover-testcase-vec (a b c) `[,a%%% ,(list b%%% c%%%)%%%]%%%) @@ -415,7 +414,6 @@ ;; ==== vector-in-macro-spec-bug-25316 ==== "Testcover reinstruments within vectors." -:expected-result :failed ;; ==== (defmacro testcover-testcase-nth-case (arg vec) (declare (indent 1) @@ -435,7 +433,6 @@ ;; ==== mapcar-is-not-compose ==== "Mapcar with 1value arguments is not 1value." -:expected-result :failed ;; ==== (defvar testcover-testcase-num 0) (defun testcover-testcase-add-num (n) @@ -450,10 +447,10 @@ ;; ==== function-with-edebug-spec-bug-25316 ==== "Functions can have edebug specs too. -See c-make-font-lock-search-function for an example in the Emacs -sources. The other issue is that it's ok to use quote in an -edebug spec, so testcover needs to cope with that." -:expected-result :failed +See `c-make-font-lock-search-function' for an example in the +Emacs sources. `c-make-font-lock-search-function''s Edebug spec +also contains a quote. See comment in `testcover-analyze-coverage' +regarding the odd-looking coverage result for the quoted form." ;; ==== (defun testcover-testcase-make-function (forms) `(lambda (flag) (if flag 0 ,@forms%%%))%%%) @@ -462,7 +459,7 @@ edebug spec, so testcover needs to cope with that." (("quote" (&rest def-form)))) (defun testcover-testcase-thing () - (testcover-testcase-make-function '((+ 1 (+ 2 (+ 3 (+ 4 5))))))%%%) + (testcover-testcase-make-function '(!!!(+ 1 !!!(+ 2 !!!(+ 3 !!!(+ 4 5)%%%)%%%)%%%)%%%))%%%) (defun testcover-testcase-use-thing () (funcall (testcover-testcase-thing)%%% nil)%%%) diff --git a/test/lisp/emacs-lisp/testcover-tests.el b/test/lisp/emacs-lisp/testcover-tests.el index 0f0ee9a509..2e03488b30 100644 --- a/test/lisp/emacs-lisp/testcover-tests.el +++ b/test/lisp/emacs-lisp/testcover-tests.el @@ -124,14 +124,12 @@ arguments for `testcover-start'." (save-current-buffer (set-buffer (find-file-noselect tempfile)) ;; Fail the test if the debugger tries to become active, - ;; which will happen if Testcover's reinstrumentation - ;; leaves an edebug-enter in the code. This will also - ;; prevent debugging these tests using Edebug. - (cl-letf (((symbol-function #'edebug-enter) + ;; which can happen if Testcover fails to attach itself + ;; correctly. Note that this will prevent debugging + ;; these tests using Edebug. + (cl-letf (((symbol-function #'edebug-default-enter) (lambda (&rest _args) - (ert-fail - (concat "Debugger invoked during test run " - "(possible edebug-enter not replaced)"))))) + (ert-fail "Debugger invoked during test run")))) (dolist (byte-compile '(t nil)) (testcover-tests-unmarkup-region (point-min) (point-max)) (unwind-protect commit 06e452a57287c797cb96a6d4b45220358daab379 Author: Gemini Lasswell Date: Sun Oct 1 09:12:29 2017 -0700 Allow Edebug's instrumentation to be used for other purposes * lisp/emacs-lisp/edebug.el: (edebug-after-instrumentation-functions) (edebug-new-definition-functions): New hook variables. (edebug-behavior-alist): New variable. (edebug-read-and-maybe-wrap-form): Run a hook after a form is wrapped. (edebug-make-form-wrapper): Run a hook after a definition is wrapped. Remove message for each definition. (edebug-announce-definition): New function. (edebug-enter): Rewritten to change behavior of Edebug based on symbol property `edebug-behavior' and `edebug-behavior-alist'. (edebug-default-enter): New function which does what `edebug-enter' used to do. (edebug-run-slow, edebug-run-fast): Modify edebug-behavior-alist. (edebug-before, edebug-after): Function definitions are now set by `edebug-enter'. diff --git a/doc/lispref/edebug.texi b/doc/lispref/edebug.texi index cebf0a3af3..94d61480f1 100644 --- a/doc/lispref/edebug.texi +++ b/doc/lispref/edebug.texi @@ -1690,3 +1690,38 @@ Whether or not to pause for @code{edebug-sit-for-seconds} on reaching a breakpoint. Set to @code{nil} to prevent the pause, non-@code{nil} to allow it. @end defopt + +@defopt edebug-behavior-alist +By default, this alist contains one entry with the key @code{edebug} +and a list of three functions, which are the default implementations +of the functions inserted in instrumented code: @code{edebug-enter}, +@code{edebug-before} and @code{edebug-after}. To change Edebug's +behavior globally, modify the default entry. + +Edebug's behavior may also be changed on a per-definition basis by +adding an entry to this alist, with a key of your choice and three +functions. Then set the @code{edebug-behavior} symbol property of an +instrumented definition to the key of the new entry, and Edebug will +call the new functions in place of its own for that definition. +@end defopt + +@defopt edebug-new-definition-functions +An abnormal hook run by Edebug after it wraps the body of a definition +or closure. After Edebug has initialized its own data, each function +is called with one argument, the symbol associated with the +definition, which may be the actual symbol defined or one generated by +Edebug. This hook may be used to set the @code{edebug-behavior} +symbol property of each definition instrumented by Edebug. + +By default @code{edebug-new-definition-functions} contains +@code{edebug-announce-definition} which prints a message each time a +definition is instrumented. If you are instrumenting a lot of code +and find the messages excessive, remove +@code{edebug-announce-definition}. +@end defopt + +@defopt edebug-after-instrumentation-functions +An abnormal hook run by Edebug after it instruments a form. +Each function is called with one argument, a form which has +just been instrumented by Edebug. +@end defopt diff --git a/etc/NEWS b/etc/NEWS index 75a98d1500..adc1085e74 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -56,6 +56,15 @@ replaced by a double typographic quote. * Changes in Specialized Modes and Packages in Emacs 27.1 +** Edebug + ++++ +*** The runtime behavior of Edebug's instrumentation can be changed +using the new variable 'edebug-behavior-alist' and the new abnormal +hooks 'edebug-after-instrumentation-functions' and +'edebug-new-definition-functions'. Edebug's behavior can be changed +globally or for individual definitions. + ** Enhanced xterm support *** New variable 'xterm-set-window-title' controls whether Emacs diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el index dbc56e272f..a070ff25d1 100644 --- a/lisp/emacs-lisp/edebug.el +++ b/lisp/emacs-lisp/edebug.el @@ -1065,6 +1065,31 @@ circular objects. Let `read' read everything else." (defvar edebug-error-point nil) (defvar edebug-best-error nil) +;; Hooks which may be used to extend Edebug's functionality. See +;; Testcover for an example. +(defvar edebug-after-instrumentation-functions nil + "Abnormal hook run on code after instrumentation for debugging. +Each function is called with one argument, a form which has just +been instrumented for Edebugging.") + +(defvar edebug-new-definition-functions '(edebug-announce-definition) + "Abnormal hook run after Edebug wraps a new definition. +After Edebug has initialized its own data, each hook function is +called with one argument, the symbol associated with the +definition, which may be the actual symbol defined or one +generated by Edebug.") + +(defvar edebug-behavior-alist + '((edebug edebug-default-enter edebug-slow-before edebug-slow-after)) + "Alist describing the runtime behavior of Edebug's instrumented code. +Each definition instrumented by Edebug will have a +`edebug-behavior' property which is a key to this alist. When +the instrumented code is running, Edebug will look here for the +implementations of `edebug-enter', `edebug-before', and +`edebug-after'. Edebug's instrumentation may be used for a new +purpose by adding an entry to this alist and a hook to +`edebug-new-definition-functions' which sets `edebug-behavior' +for the definition.") (defun edebug-read-and-maybe-wrap-form () ;; Read a form and wrap it with edebug calls, if the conditions are right. @@ -1124,47 +1149,48 @@ circular objects. Let `read' read everything else." (eq 'symbol (edebug-next-token-class))) (read (current-buffer)))))) ;;;(message "all defs: %s all forms: %s" edebug-all-defs edebug-all-forms) - (cond - (defining-form-p - (if (or edebug-all-defs edebug-all-forms) - ;; If it is a defining form and we are edebugging defs, - ;; then let edebug-list-form start it. - (let ((cursor (edebug-new-cursor - (list (edebug-read-storing-offsets (current-buffer))) - (list edebug-offsets)))) - (car - (edebug-make-form-wrapper - cursor - (edebug-before-offset cursor) - (1- (edebug-after-offset cursor)) - (list (cons (symbol-name def-kind) (cdr spec)))))) - - ;; Not edebugging this form, so reset the symbol's edebug - ;; property to be just a marker at the definition's source code. - ;; This only works for defs with simple names. - (put def-name 'edebug (point-marker)) - ;; Also nil out dependent defs. - '(mapcar (function - (lambda (def) - (put def-name 'edebug nil))) - (get def-name 'edebug-dependents)) - (edebug-read-sexp))) - - ;; If all forms are being edebugged, explicitly wrap it. - (edebug-all-forms - (let ((cursor (edebug-new-cursor - (list (edebug-read-storing-offsets (current-buffer))) - (list edebug-offsets)))) - (edebug-make-form-wrapper - cursor - (edebug-before-offset cursor) - (edebug-after-offset cursor) - nil))) - - ;; Not a defining form, and not edebugging. - (t (edebug-read-sexp))) - )) - + (let ((result + (cond + (defining-form-p + (if (or edebug-all-defs edebug-all-forms) + ;; If it is a defining form and we are edebugging defs, + ;; then let edebug-list-form start it. + (let ((cursor (edebug-new-cursor + (list (edebug-read-storing-offsets (current-buffer))) + (list edebug-offsets)))) + (car + (edebug-make-form-wrapper + cursor + (edebug-before-offset cursor) + (1- (edebug-after-offset cursor)) + (list (cons (symbol-name def-kind) (cdr spec)))))) + + ;; Not edebugging this form, so reset the symbol's edebug + ;; property to be just a marker at the definition's source code. + ;; This only works for defs with simple names. + (put def-name 'edebug (point-marker)) + ;; Also nil out dependent defs. + '(mapcar (function + (lambda (def) + (put def-name 'edebug nil))) + (get def-name 'edebug-dependents)) + (edebug-read-sexp))) + + ;; If all forms are being edebugged, explicitly wrap it. + (edebug-all-forms + (let ((cursor (edebug-new-cursor + (list (edebug-read-storing-offsets (current-buffer))) + (list edebug-offsets)))) + (edebug-make-form-wrapper + cursor + (edebug-before-offset cursor) + (edebug-after-offset cursor) + nil))) + + ;; Not a defining form, and not edebugging. + (t (edebug-read-sexp))))) + (run-hook-with-args 'edebug-after-instrumentation-functions result) + result))) (defvar edebug-def-args) ; args of defining form. (defvar edebug-def-interactive) ; is it an emacs interactive function? @@ -1332,7 +1358,6 @@ expressions; a `progn' form will be returned enclosing these forms." ;; (message "defining: %s" edebug-def-name) (sit-for 2) (edebug-make-top-form-data-entry form-data-entry) - (message "Edebug: %s" edebug-def-name) ;;(debug edebug-def-name) ;; Destructively reverse edebug-offset-list and make vector from it. @@ -1358,9 +1383,15 @@ expressions; a `progn' form will be returned enclosing these forms." edebug-offset-list edebug-top-window-data )) + (put edebug-def-name 'edebug-behavior 'edebug) + (run-hook-with-args 'edebug-new-definition-functions edebug-def-name) result ))) +(defun edebug-announce-definition (def-name) + "Announce Edebug's processing of DEF-NAME." + (message "Edebug: %s" def-name)) + (defun edebug-clear-frequency-count (name) ;; Create initial frequency count vector. @@ -2167,7 +2198,21 @@ error is signaled again." ;;; Entering Edebug -(defun edebug-enter (function args body) +(defun edebug-enter (func args body) + "Enter Edebug for a function. +FUNC should be the symbol with the Edebug information, ARGS is +the list of arguments and BODY is the code. + +Look up the `edebug-behavior' for FUNC in `edebug-behavior-alist' +and run its entry function, and set up `edebug-before' and +`edebug-after'." + (cl-letf* ((behavior (get func 'edebug-behavior)) + (functions (cdr (assoc behavior edebug-behavior-alist))) + ((symbol-function #'edebug-before) (nth 1 functions)) + ((symbol-function #'edebug-after) (nth 2 functions))) + (funcall (nth 0 functions) func args body))) + +(defun edebug-default-enter (function args body) ;; Entering FUNC. The arguments are ARGS, and the body is BODY. ;; Setup edebug variables and evaluate BODY. This function is called ;; when a function evaluated with edebug-eval-top-level-form is entered. @@ -2198,7 +2243,7 @@ error is signaled again." edebug-initial-mode edebug-execution-mode) edebug-next-execution-mode nil) - (edebug-enter function args body)))) + (edebug-default-enter function args body)))) (let* ((edebug-data (get function 'edebug)) (edebug-def-mark (car edebug-data)) ; mark at def start @@ -2317,22 +2362,27 @@ MSG is printed after `::::} '." value (edebug-debugger after-index 'after value) ))) - (defun edebug-fast-after (_before-index _after-index value) ;; Do nothing but return the value. value) (defun edebug-run-slow () - (defalias 'edebug-before 'edebug-slow-before) - (defalias 'edebug-after 'edebug-slow-after)) + "Set up Edebug's normal behavior." + (setf (cdr (assq 'edebug edebug-behavior-alist)) + '(edebug-default-enter edebug-slow-before edebug-slow-after))) ;; This is not used, yet. (defun edebug-run-fast () - (defalias 'edebug-before 'edebug-fast-before) - (defalias 'edebug-after 'edebug-fast-after)) - -(edebug-run-slow) - + "Disable Edebug without de-instrumenting code." + (setf (cdr (assq 'edebug edebug-behavior-alist)) + '(edebug-default-enter edebug-fast-before edebug-fast-after))) + +(defalias 'edebug-before nil + "Function called by Edebug before a form is evaluated. +See `edebug-behavior-alist' for implementations.") +(defalias 'edebug-after nil + "Function called by Edebug after a form is evaluated. +See `edebug-behavior-alist' for implementations.") (defun edebug--update-coverage (after-index value) (let ((old-result (aref edebug-coverage after-index))) commit 85b4e88194cae541a0093a9166f4306e6fd3109e Author: Stefan Monnier Date: Sun Oct 8 15:44:49 2017 -0400 * lisp/emacs-lisp/checkdoc.el: cl-defstruct + minor simplifications (checkdoc-make-overlay, checkdoc-overlay-put, checkdoc-delete-overlay) (checkdoc-overlay-start, checkdoc-overlay-end, checkdoc-char=) (checkdoc-mode-line-update): Remove old compatibility aliases. (checkdoc, checkdoc-interactive-loop): Consolidate common code in if branches. (checkdoc-error): New struct type. (checkdoc-error-text, checkdoc-error-start, checkdoc-error-end) (checkdoc-error-unfixable): Now defined by cl-defstruct. diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el index 72f82f26f6..fe6cd4160e 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el @@ -171,6 +171,7 @@ (defvar checkdoc-version "0.6.1" "Release version of checkdoc you are currently running.") +(eval-when-compile (require 'cl-lib)) (require 'help-mode) ;; for help-xref-info-regexp (require 'thingatpt) ;; for handy thing-at-point-looking-at @@ -436,23 +437,6 @@ be re-created.") st) "Syntax table used by checkdoc in document strings.") -;;; Compatibility -;; -(defalias 'checkdoc-make-overlay - (if (featurep 'xemacs) #'make-extent #'make-overlay)) -(defalias 'checkdoc-overlay-put - (if (featurep 'xemacs) #'set-extent-property #'overlay-put)) -(defalias 'checkdoc-delete-overlay - (if (featurep 'xemacs) #'delete-extent #'delete-overlay)) -(defalias 'checkdoc-overlay-start - (if (featurep 'xemacs) #'extent-start #'overlay-start)) -(defalias 'checkdoc-overlay-end - (if (featurep 'xemacs) #'extent-end #'overlay-end)) -(defalias 'checkdoc-mode-line-update - (if (featurep 'xemacs) #'redraw-modeline #'force-mode-line-update)) -(defalias 'checkdoc-char= - (if (featurep 'xemacs) #'char= #'=)) - ;;; User level commands ;; ;;;###autoload @@ -475,32 +459,31 @@ the users will view as each check is completed." tmp) (checkdoc-display-status-buffer status) ;; check the comments - (if (not buffer-file-name) - (setcar status "Not checked") - (if (checkdoc-file-comments-engine) - (setcar status "Errors") - (setcar status "Ok"))) - (setcar (cdr status) "Checking...") + (setf (nth 0 status) + (cond + ((not buffer-file-name) "Not checked") + ((checkdoc-file-comments-engine) "Errors") + (t "Ok"))) + (setf (nth 1 status) "Checking...") (checkdoc-display-status-buffer status) ;; Check the documentation (setq tmp (checkdoc-interactive nil t)) - (if tmp - (setcar (cdr status) (format "%d Errors" (length tmp))) - (setcar (cdr status) "Ok")) - (setcar (cdr (cdr status)) "Checking...") + (setf (nth 1 status) + (if tmp (format "%d Errors" (length tmp)) "Ok")) + (setf (nth 2 status) "Checking...") (checkdoc-display-status-buffer status) ;; Check the message text - (if (setq tmp (checkdoc-message-interactive nil t)) - (setcar (cdr (cdr status)) (format "%d Errors" (length tmp))) - (setcar (cdr (cdr status)) "Ok")) - (setcar (cdr (cdr (cdr status))) "Checking...") + (setf (nth 2 status) + (if (setq tmp (checkdoc-message-interactive nil t)) + (format "%d Errors" (length tmp)) + "Ok")) + (setf (nth 3 status) "Checking...") (checkdoc-display-status-buffer status) ;; Rogue spacing - (if (condition-case nil - (checkdoc-rogue-spaces nil t) - (error t)) - (setcar (cdr (cdr (cdr status))) "Errors") - (setcar (cdr (cdr (cdr status))) "Ok")) + (setf (nth 3 status) + (if (ignore-errors (checkdoc-rogue-spaces nil t)) + "Errors" + "Ok")) (checkdoc-display-status-buffer status))) (defun checkdoc-display-status-buffer (check) @@ -592,16 +575,16 @@ style." (while err-list (goto-char (cdr (car err-list))) ;; The cursor should be just in front of the offending doc string - (if (stringp (car (car err-list))) - (setq cdo (save-excursion (checkdoc-make-overlay + (setq cdo (if (stringp (car (car err-list))) + (save-excursion (make-overlay (point) (progn (forward-sexp 1) - (point))))) - (setq cdo (checkdoc-make-overlay + (point)))) + (make-overlay (checkdoc-error-start (car (car err-list))) (checkdoc-error-end (car (car err-list)))))) (unwind-protect (progn - (checkdoc-overlay-put cdo 'face 'highlight) + (overlay-put cdo 'face 'highlight) ;; Make sure the whole doc string is visible if possible. (sit-for 0) (if (and (= (following-char) ?\") @@ -627,10 +610,10 @@ style." (if (not (integerp c)) (setq c ??)) (cond ;; Exit condition - ((checkdoc-char= c ?\C-g) (signal 'quit nil)) + ((eq c ?\C-g) (signal 'quit nil)) ;; Request an auto-fix - ((or (checkdoc-char= c ?y) (checkdoc-char= c ?f)) - (checkdoc-delete-overlay cdo) + ((memq c '(?y ?f)) + (delete-overlay cdo) (setq cdo nil) (goto-char (cdr (car err-list))) ;; `automatic-then-never' tells the autofix function @@ -659,7 +642,7 @@ style." "No Additional style errors. Continuing...") (sit-for 2)))))) ;; Move to the next error (if available) - ((or (checkdoc-char= c ?n) (checkdoc-char= c ?\s)) + ((memq c '(?n ?\s)) (let ((ne (funcall findfunc nil))) (if (not ne) (if showstatus @@ -671,7 +654,7 @@ style." (sit-for 2)) (setq err-list (cons ne err-list))))) ;; Go backwards in the list of errors - ((or (checkdoc-char= c ?p) (checkdoc-char= c ?\C-?)) + ((memq c '(?p ?\C-?)) (if (/= (length err-list) 1) (progn (setq err-list (cdr err-list)) @@ -680,10 +663,10 @@ style." (message "No Previous Errors.") (sit-for 2))) ;; Edit the buffer recursively. - ((checkdoc-char= c ?e) + ((eq c ?e) (checkdoc-recursive-edit (checkdoc-error-text (car (car err-list)))) - (checkdoc-delete-overlay cdo) + (delete-overlay cdo) (setq err-list (cdr err-list)) ;back up the error found. (beginning-of-defun) (let ((ne (funcall findfunc nil))) @@ -695,7 +678,7 @@ style." (sit-for 2)) (setq err-list (cons ne err-list))))) ;; Quit checkdoc - ((checkdoc-char= c ?q) + ((eq c ?q) (setq returnme err-list err-list nil begin (point))) @@ -723,7 +706,7 @@ style." "C-h - Toggle this help buffer."))) (shrink-window-if-larger-than-buffer (get-buffer-window "*Checkdoc Help*")))))) - (if cdo (checkdoc-delete-overlay cdo))))) + (if cdo (delete-overlay cdo))))) (goto-char begin) (if (get-buffer "*Checkdoc Help*") (kill-buffer "*Checkdoc Help*")) (message "Checkdoc: Done.") @@ -1147,6 +1130,15 @@ Prefix argument is the same as for `checkdoc-defun'" ;; features and behaviors, so we need some ways of specifying ;; them, and making them easier to use in the wacked-out interfaces ;; people are requesting + +(cl-defstruct (checkdoc-error + (:constructor nil) + (:constructor checkdoc--create-error (text start end &optional unfixable))) + (text nil :read-only t) + (start nil :read-only t) + (end nil :read-only t) + (unfixable nil :read-only t)) + (defvar checkdoc-create-error-function #'checkdoc--create-error-for-checkdoc "Function called when Checkdoc encounters an error. Should accept as arguments (TEXT START END &optional UNFIXABLE). @@ -1155,7 +1147,7 @@ TEXT is the descriptive text of the error. START and END define the region it is sensible to highlight when describing the problem. Optional argument UNFIXABLE means that the error has no auto-fix available. -A list of the form (TEXT START END UNFIXABLE) is returned if we are not +An object of type `checkdoc-error' is returned if we are not generating a buffered list of errors.") (defun checkdoc-create-error (text start end &optional unfixable) @@ -1171,27 +1163,7 @@ TEXT, START, END and UNFIXABLE conform to (if checkdoc-generate-compile-warnings-flag (progn (checkdoc-error start text) nil) - (list text start end unfixable))) - -(defun checkdoc-error-text (err) - "Return the text specified in the checkdoc ERR." - ;; string-p part is for backwards compatibility - (if (stringp err) err (car err))) - -(defun checkdoc-error-start (err) - "Return the start point specified in the checkdoc ERR." - ;; string-p part is for backwards compatibility - (if (stringp err) nil (nth 1 err))) - -(defun checkdoc-error-end (err) - "Return the end point specified in the checkdoc ERR." - ;; string-p part is for backwards compatibility - (if (stringp err) nil (nth 2 err))) - -(defun checkdoc-error-unfixable (err) - "Return the t if we cannot autofix the error specified in the checkdoc ERR." - ;; string-p part is for backwards compatibility - (if (stringp err) nil (nth 3 err))) + (checkdoc--create-error text start end unfixable))) ;;; Minor Mode specification ;; @@ -1342,7 +1314,7 @@ See the style guide in the Emacs Lisp manual for more details." (if (and (not (nth 1 fp)) ; not a variable (or (nth 2 fp) ; is interactive checkdoc-force-docstrings-flag) ;or we always complain - (not (checkdoc-char= (following-char) ?\"))) ; no doc string + (not (eq (following-char) ?\"))) ; no doc string ;; Sometimes old code has comments where the documentation should ;; be. Let's see if we can find the comment, and offer to turn it ;; into documentation for them. @@ -1471,9 +1443,9 @@ regexp short cuts work. FP is the function defun information." (if (> (point) e) (goto-char e)) ;of the form (defun n () "doc" nil) (forward-char -1) (cond - ((and (checkdoc-char= (following-char) ?\") + ((and (eq (following-char) ?\") ;; A backslashed double quote at the end of a sentence - (not (checkdoc-char= (preceding-char) ?\\))) + (not (eq (preceding-char) ?\\))) ;; We might have to add a period in this case (forward-char -1) (if (looking-at "[.!?]") @@ -1796,7 +1768,7 @@ function,command,variable,option or symbol." ms1)))))) (let ((lim (save-excursion (end-of-line) ;; check string-continuation - (if (checkdoc-char= (preceding-char) ?\\) + (if (eq (preceding-char) ?\\) (line-end-position 2) (point)))) (rs nil) replace original (case-fold-search t)) @@ -2593,12 +2565,12 @@ This function returns non-nil if the text was replaced. This function will not modify `match-data'." (if (and checkdoc-autofix-flag (not (eq checkdoc-autofix-flag 'never))) - (let ((o (checkdoc-make-overlay start end)) + (let ((o (make-overlay start end)) (ret nil) (md (match-data))) (unwind-protect (progn - (checkdoc-overlay-put o 'face 'highlight) + (overlay-put o 'face 'highlight) (if (or (eq checkdoc-autofix-flag 'automatic) (eq checkdoc-autofix-flag 'automatic-then-never) (and (eq checkdoc-autofix-flag 'semiautomatic) @@ -2615,9 +2587,9 @@ This function will not modify `match-data'." (insert replacewith) (if checkdoc-bouncy-flag (sit-for 0)) (setq ret t))) - (checkdoc-delete-overlay o) + (delete-overlay o) (set-match-data md)) - (checkdoc-delete-overlay o) + (delete-overlay o) (set-match-data md)) (if (eq checkdoc-autofix-flag 'automatic-then-never) (setq checkdoc-autofix-flag 'never)) commit 9613690f6e51e2f2aa2bcbbede3e209d08cfaaad Author: Philipp Stephani Date: Tue Oct 3 16:14:54 2017 +0200 Raise an error when detecting old-style backquotes. They have been deprecated for a decade now. * src/lread.c (Fload): Don't use record_unwind_protect to warn about old-style backquotes any more. They now generate a hard error. (read1): Signal an error when detecting old-style backquotes. Remove unused label. (syms_of_lread): Remove unused internal variable 'lread--old-style-backquotes'. (load_error_old_style_backquotes): Rename from 'load_warn_oldstyle_backquotes'. Signal an error. * lisp/emacs-lisp/bytecomp.el (byte-compile-from-buffer): Remove check from byte compiler. It isn't triggered any more. * test/src/lread-tests.el (lread-tests--old-style-backquotes): Adapt unit test. * test/lisp/emacs-lisp/bytecomp-tests.el (bytecomp-tests--old-style-backquotes) (bytecomp-tests-function-put): Adapt unit tests. * etc/NEWS: Document change. diff --git a/etc/NEWS b/etc/NEWS index f961928ffd..75a98d1500 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -77,6 +77,9 @@ them through 'format' first. Even that is discouraged: for ElDoc support, you should set 'eldoc-documentation-function' instead of calling 'eldoc-message' directly. +** Old-style backquotes now generate an error. They have been +generating warnings for a decade. + * Lisp Changes in Emacs 27.1 diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 590db570c5..45fa188d6c 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -2048,14 +2048,8 @@ With argument ARG, insert value in current buffer after the form." (not (eobp))) (setq byte-compile-read-position (point) byte-compile-last-position byte-compile-read-position) - (let* ((lread--old-style-backquotes nil) - (lread--unescaped-character-literals nil) + (let* ((lread--unescaped-character-literals nil) (form (read inbuffer))) - ;; Warn about the use of old-style backquotes. - (when lread--old-style-backquotes - (byte-compile-warn "!! The file uses old-style backquotes !! -This functionality has been obsolete for more than 10 years already -and will be removed soon. See (elisp)Backquote in the manual.")) (when lread--unescaped-character-literals (byte-compile-warn "unescaped character literals %s detected!" diff --git a/src/lread.c b/src/lread.c index 6bc93b1481..c073fc4ce6 100644 --- a/src/lread.c +++ b/src/lread.c @@ -1003,14 +1003,11 @@ load_error_handler (Lisp_Object data) return Qnil; } -static void -load_warn_old_style_backquotes (Lisp_Object file) +static _Noreturn void +load_error_old_style_backquotes (void) { - if (!NILP (Vlread_old_style_backquotes)) - { - AUTO_STRING (format, "Loading `%s': old-style backquotes detected!"); - CALLN (Fmessage, format, file); - } + AUTO_STRING (format, "Loading `%s': old-style backquotes detected!"); + xsignal1 (Qerror, CALLN (Fformat_message, format, Vload_file_name)); } static void @@ -1282,10 +1279,6 @@ Return t if the file exists and loads successfully. */) version = -1; - /* Check for the presence of old-style quotes and warn about them. */ - specbind (Qlread_old_style_backquotes, Qnil); - record_unwind_protect (load_warn_old_style_backquotes, file); - /* Check for the presence of unescaped character literals and warn about them. */ specbind (Qlread_unescaped_character_literals, Qnil); @@ -3178,10 +3171,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) first_in_list exception (old-style can still be obtained via "(\`" anyway). */ if (!new_backquote_flag && first_in_list && next_char == ' ') - { - Vlread_old_style_backquotes = Qt; - goto default_label; - } + load_error_old_style_backquotes (); else { Lisp_Object value; @@ -3232,10 +3222,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) return list2 (comma_type, value); } else - { - Vlread_old_style_backquotes = Qt; - goto default_label; - } + load_error_old_style_backquotes (); } case '?': { @@ -3423,7 +3410,6 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) row. */ FALLTHROUGH; default: - default_label: if (c <= 040) goto retry; if (c == NO_BREAK_SPACE) goto retry; @@ -4996,12 +4982,6 @@ variables, this must be set in the first line of a file. */); doc: /* List of buffers being read from by calls to `eval-buffer' and `eval-region'. */); Veval_buffer_list = Qnil; - DEFVAR_LISP ("lread--old-style-backquotes", Vlread_old_style_backquotes, - doc: /* Set to non-nil when `read' encounters an old-style backquote. -For internal use only. */); - Vlread_old_style_backquotes = Qnil; - DEFSYM (Qlread_old_style_backquotes, "lread--old-style-backquotes"); - DEFVAR_LISP ("lread--unescaped-character-literals", Vlread_unescaped_character_literals, doc: /* List of deprecated unescaped character literals encountered by `read'. diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el index 30d2a4753c..1f85c26978 100644 --- a/test/lisp/emacs-lisp/bytecomp-tests.el +++ b/test/lisp/emacs-lisp/bytecomp-tests.el @@ -534,23 +534,18 @@ literals (Bug#20852)." (ert-deftest bytecomp-tests--old-style-backquotes () "Check that byte compiling warns about old-style backquotes." - (should (boundp 'lread--old-style-backquotes)) (bytecomp-tests--with-temp-file source (write-region "(` (a b))" nil source) (bytecomp-tests--with-temp-file destination (let* ((byte-compile-dest-file-function (lambda (_) destination)) - (byte-compile-error-on-warn t) - (byte-compile-debug t) - (err (should-error (byte-compile-file source)))) + (byte-compile-debug t) + (err (should-error (byte-compile-file source)))) (should (equal (cdr err) - (list "!! The file uses old-style backquotes !! -This functionality has been obsolete for more than 10 years already -and will be removed soon. See (elisp)Backquote in the manual."))))))) + '("Loading `nil': old-style backquotes detected!"))))))) (ert-deftest bytecomp-tests-function-put () "Check `function-put' operates during compilation." - (should (boundp 'lread--old-style-backquotes)) (bytecomp-tests--with-temp-file source (dolist (form '((function-put 'bytecomp-tests--foo 'foo 1) (function-put 'bytecomp-tests--foo 'bar 2) diff --git a/test/src/lread-tests.el b/test/src/lread-tests.el index ac730b4f00..3f41982eba 100644 --- a/test/src/lread-tests.el +++ b/test/src/lread-tests.el @@ -173,13 +173,13 @@ literals (Bug#20852)." (should (string-suffix-p "/somelib.el" (caar load-history))))) (ert-deftest lread-tests--old-style-backquotes () - "Check that loading warns about old-style backquotes." + "Check that loading doesn't accept old-style backquotes." (lread-tests--with-temp-file file-name (write-region "(` (a b))" nil file-name) - (should (equal (load file-name nil :nomessage :nosuffix) t)) - (should (equal (lread-tests--last-message) - (concat (format-message "Loading `%s': " file-name) - "old-style backquotes detected!"))))) + (let ((data (should-error (load file-name nil :nomessage :nosuffix)))) + (should (equal (cdr data) + (list (concat (format-message "Loading `%s': " file-name) + "old-style backquotes detected!"))))))) (ert-deftest lread-lread--substitute-object-in-subtree () (let ((x (cons 0 1))) commit f4995e7d36b576d9ed629b45dd3b09ba6d28cce7 Author: Philipp Stephani Date: Sat Jun 17 00:13:51 2017 +0200 Say that side effect-free functions don't change the matchd data * lisp/help-fns.el (describe-function-1): Add note if a function is known not to change the match data. diff --git a/lisp/help-fns.el b/lisp/help-fns.el index d75fec2b56..788c03e9bf 100644 --- a/lisp/help-fns.el +++ b/lisp/help-fns.el @@ -709,6 +709,10 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED REAL-DEF)." real-function key-bindings-buffer))) (run-hook-with-args 'help-fns-describe-function-functions function) (insert "\n" (or doc "Not documented."))) + (when (or (function-get function 'pure) + (function-get function 'side-effect-free)) + (insert "\nThis function does not change global state, " + "including the match data.")) ;; Avoid asking the user annoying questions if she decides ;; to save the help buffer, when her locale's codeset ;; isn't UTF-8. commit 9b3ce6252115980802adaa562af575bcd73a2c55 Author: Eli Zaretskii Date: Sat Oct 7 15:04:37 2017 +0300 New defcustom 'tooltip-resize-echo-area' * lisp/tooltip.el (tooltip-resize-echo-area): New defcustom. (tooltip-show-help-non-mode): Use it to avoid truncating the tooltip text in the echo area. (Bug#28724) * etc/NEWS: Mention 'tooltip-resize-echo-area'. diff --git a/etc/NEWS b/etc/NEWS index 15661808c7..f961928ffd 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -31,6 +31,12 @@ When you add a new item, use the appropriate mark if you are sure it applies, * Changes in Emacs 27.1 +--- +** The new option 'tooltip-resize-echo-area' avoids truncating tooltip text +on GUI frames when tooltips are displayed in the echo area. Instead, +it resizes the echo area as needed to accommodate the full tool-tip +text. + +++ ** New function 'logcount' calculates an integer's Hamming weight. diff --git a/lisp/tooltip.el b/lisp/tooltip.el index 18ddd25703..44b6938a6f 100644 --- a/lisp/tooltip.el +++ b/lisp/tooltip.el @@ -155,6 +155,18 @@ This variable is obsolete; instead of setting it to t, disable (make-obsolete-variable 'tooltip-use-echo-area "disable Tooltip mode instead" "24.1" 'set) +(defcustom tooltip-resize-echo-area nil + "If non-nil, using the echo area for tooltips will resize the echo area. +By default, when the echo area is used for displaying tooltips, +the tooltip text is truncated if it exceeds a single screen line. +When this variable is non-nil, the text is not truncated; instead, +the echo area is resized as needed to accommodate the full text +of the tooltip. +This variable has effect only on GUI frames." + :type 'boolean + :group 'tooltip + :version "27.1") + ;;; Variables that are not customizable. @@ -347,7 +359,8 @@ It is also called if Tooltip mode is on, for text-only displays." (current-message)))) (setq tooltip-previous-message (current-message))) (setq tooltip-help-message help) - (let ((message-truncate-lines t) + (let ((message-truncate-lines + (or (not (display-graphic-p)) (not tooltip-resize-echo-area))) (message-log-max nil)) (message "%s" help))) ((stringp tooltip-previous-message) commit 53da55b8cc45e76b836ebaadd23f46e92d25abce Merge: 11f9cb522f 9226cf3254 Author: Paul Eggert Date: Fri Oct 6 10:35:07 2017 -0700 Merge from origin/emacs-26 9226cf3254 Fix bug in recent styled_format change fa92f0c447 Cleanup emacs-lisp-mode's use of Flymake 0d0265bf50 Fix @include directive in Flymake doc 295457ae52 Move read-multiple-choice to its own library 560dd9b573 * src/process.c (syms_of_process): Remove duplicated call ... commit 9226cf325421a168b42bd27abf5e171e877b48b9 Author: Paul Eggert Date: Fri Oct 6 10:32:46 2017 -0700 Fix bug in recent styled_format change Problem reported by Kaushal Modi in: http://lists.gnu.org/archive/html/emacs-devel/2017-10/msg00141.html * src/editfns.c (styled_format): Fix bug where USE_SAFE_ALLOCA was not always followed by SAFE_FREE. This bug was introduced in my patch 2017-09-26T23:31:57Z!eggert@cs.ucla.edu entitled "Avoid some unnecessary copying in Fformat etc." diff --git a/src/editfns.c b/src/editfns.c index d88a913c66..e65bd34da8 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -4179,6 +4179,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) multibyte character of the previous string. This flag tells if we must consider such a situation or not. */ bool maybe_combine_byte; + Lisp_Object val; bool arg_intervals = false; USE_SAFE_ALLOCA; sa_avail -= sizeof initial_buffer; @@ -4417,7 +4418,10 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) { if (format == end && format - format_start == 2 && ! string_intervals (args[0])) - return arg; + { + val = arg; + goto return_val; + } /* handle case (precision[n] >= 0) */ @@ -4862,11 +4866,14 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) emacs_abort (); if (! new_result) - return args[0]; + { + val = args[0]; + goto return_val; + } if (maybe_combine_byte) nchars = multibyte_chars_in_text ((unsigned char *) buf, p - buf); - Lisp_Object val = make_specified_string (buf, nchars, p - buf, multibyte); + val = make_specified_string (buf, nchars, p - buf, multibyte); /* If the format string has text properties, or any of the string arguments has text properties, set up text properties of the @@ -4964,6 +4971,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) } } + return_val: /* If we allocated BUF or INFO with malloc, free it too. */ SAFE_FREE (); commit fa92f0c44715fc49e19de001ee8b217ce847d954 Author: João Távora Date: Fri Oct 6 17:51:40 2017 +0100 Cleanup emacs-lisp-mode's use of Flymake * lisp/progmodes/elisp-mode.el (elisp-flymake--checkdoc-1): Delete. (elisp-flymake-checkdoc): Incorporate old elisp-flymake--checkdoc-1. (elisp-flymake--byte-compile-done): Simplify. Don't cleanup here. (elisp-flymake-byte-compile): Remove spurious interactive spec. Simplify. Cleanup on every possible exit. diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 3690f67383..99a4841e31 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -1599,8 +1599,11 @@ ARGLIST is either a string, or a list of strings or symbols." (defvar checkdoc-autofix-flag) (defvar checkdoc-generate-compile-warnings-flag) (defvar checkdoc-diagnostic-buffer) -(defun elisp-flymake--checkdoc-1 () - "Do actual work for `elisp-flymake-checkdoc'." + +;;;###autoload +(defun elisp-flymake-checkdoc (report-fn &rest _args) + "A Flymake backend for `checkdoc'. +Calls REPORT-FN directly." (let (collected) (let* ((checkdoc-create-error-function (lambda (text start end &optional unfixable) @@ -1608,63 +1611,52 @@ ARGLIST is either a string, or a list of strings or symbols." nil)) (checkdoc-autofix-flag nil) (checkdoc-generate-compile-warnings-flag nil) - (buf (generate-new-buffer " *checkdoc-temp*")) - (checkdoc-diagnostic-buffer buf)) + (checkdoc-diagnostic-buffer + (generate-new-buffer " *checkdoc-temp*"))) (unwind-protect (save-excursion (checkdoc-current-buffer t)) - (kill-buffer buf))) + (kill-buffer checkdoc-diagnostic-buffer))) + (funcall report-fn + (cl-loop for (text start end _unfixable) in + collected + collect + (flymake-make-diagnostic + (current-buffer) + start end :note text))) collected)) -;;;###autoload -(defun elisp-flymake-checkdoc (report-fn &rest _args) - "A Flymake backend for `checkdoc'. -Calls REPORT-FN directly." - (unless (derived-mode-p 'emacs-lisp-mode) - (error "Can only work on `emacs-lisp-mode' buffers")) - (funcall report-fn - (cl-loop for (text start end _unfixable) in - (elisp-flymake--checkdoc-1) - collect - (flymake-make-diagnostic - (current-buffer) - start end :note text)))) - (defun elisp-flymake--byte-compile-done (report-fn - origin-buffer - output-buffer - temp-file) - (unwind-protect - (with-current-buffer - origin-buffer - (save-excursion - (save-restriction - (widen) - (funcall - report-fn - (cl-loop with data = - (with-current-buffer output-buffer - (goto-char (point-min)) - (search-forward ":elisp-flymake-output-start") - (read (point-marker))) - for (string pos _fill level) in data - do (goto-char pos) - for beg = (if (< (point) (point-max)) - (point) - (line-beginning-position)) - for end = (min - (line-end-position) - (or (cdr - (bounds-of-thing-at-point 'sexp)) - (point-max))) - collect (flymake-make-diagnostic - (current-buffer) - (if (= beg end) (1- beg) beg) - end - level - string)))))) - (kill-buffer output-buffer) - (ignore-errors (delete-file temp-file)))) + source-buffer + output-buffer) + (with-current-buffer + source-buffer + (save-excursion + (save-restriction + (widen) + (funcall + report-fn + (cl-loop with data = + (with-current-buffer output-buffer + (goto-char (point-min)) + (search-forward ":elisp-flymake-output-start") + (read (point-marker))) + for (string pos _fill level) in data + do (goto-char pos) + for beg = (if (< (point) (point-max)) + (point) + (line-beginning-position)) + for end = (min + (line-end-position) + (or (cdr + (bounds-of-thing-at-point 'sexp)) + (point-max))) + collect (flymake-make-diagnostic + (current-buffer) + (if (= beg end) (1- beg) beg) + end + level + string))))))) (defvar-local elisp-flymake--byte-compile-process nil "Buffer-local process started for byte-compiling the buffer.") @@ -1674,16 +1666,11 @@ Calls REPORT-FN directly." "A Flymake backend for elisp byte compilation. Spawn an Emacs process that byte-compiles a file representing the current buffer state and calls REPORT-FN when done." - (interactive (list (lambda (stuff) - (message "aha %s" stuff)))) - (unless (derived-mode-p 'emacs-lisp-mode) - (error "Can only work on `emacs-lisp-mode' buffers")) (when elisp-flymake--byte-compile-process - (process-put elisp-flymake--byte-compile-process 'elisp-flymake--obsolete t) (when (process-live-p elisp-flymake--byte-compile-process) (kill-process elisp-flymake--byte-compile-process))) (let ((temp-file (make-temp-file "elisp-flymake-byte-compile")) - (origin-buffer (current-buffer))) + (source-buffer (current-buffer))) (save-restriction (widen) (write-region (point-min) (point-max) temp-file nil 'nomessage)) @@ -1703,21 +1690,22 @@ current buffer state and calls REPORT-FN when done." :connection-type 'pipe :sentinel (lambda (proc _event) - (unless (process-live-p proc) + (when (eq (process-status proc) 'exit) (unwind-protect (cond + ((not (eq proc elisp-flymake--byte-compile-process)) + (flymake-log :warning "byte-compile process %s obsolete" proc)) ((zerop (process-exit-status proc)) (elisp-flymake--byte-compile-done report-fn - origin-buffer - output-buffer - temp-file)) - ((process-get proc 'elisp-flymake--obsolete) - (flymake-log :warning "byte-compile process %s obsolete" proc)) + source-buffer + output-buffer)) (t (funcall report-fn :panic :explanation - (format "byte-compile process %s died" proc))))))))) + (format "byte-compile process %s died" proc)))) + (ignore-errors (delete-file temp-file)) + (kill-buffer output-buffer)))))) :stderr null-device :noquery t))) commit 0d0265bf50e190c77e6a06fd677c0114cb8356a6 Author: João Távora Date: Fri Oct 6 16:42:37 2017 +0100 Fix @include directive in Flymake doc * doc/misc/flymake.texi: Don't @include a relative path. diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index 5ff5537d04..a85fe4a30e 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -4,7 +4,7 @@ @set VERSION 0.3 @set UPDATED April 2004 @settitle GNU Flymake @value{VERSION} -@include ../emacs/docstyle.texi +@include docstyle.texi @syncodeindex pg cp @comment %**end of header commit 295457ae52eda341967821ebc5c053db1789b7c9 Author: Mark Oteiza Date: Fri Oct 6 10:42:06 2017 -0400 Move read-multiple-choice to its own library * lisp/emacs-lisp/rmc.el: New file. * lisp/emacs-lisp/subr-x.el (read-multiple-choice): Remove. * lisp/gnus/message.el: * lisp/net/nsm.el: Change required library. diff --git a/lisp/emacs-lisp/rmc.el b/lisp/emacs-lisp/rmc.el new file mode 100644 index 0000000000..417301cde0 --- /dev/null +++ b/lisp/emacs-lisp/rmc.el @@ -0,0 +1,199 @@ +;;; rmc.el --- read from a multiple choice question -*- lexical-binding: t -*- + +;; Copyright (C) 2017 Free Software Foundation, Inc. + +;; Maintainer: emacs-devel@gnu.org + +;; 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 this program. If not, see . + +;;; Commentary: + +;;; Code: + +;;;###autoload +(defun read-multiple-choice (prompt choices) + "Ask user a multiple choice question. +PROMPT should be a string that will be displayed as the prompt. + +CHOICES is an alist where the first element in each entry is a +character to be entered, the second element is a short name for +the entry to be displayed while prompting (if there's room, it +might be shortened), and the third, optional entry is a longer +explanation that will be displayed in a help buffer if the user +requests more help. + +This function translates user input into responses by consulting +the bindings in `query-replace-map'; see the documentation of +that variable for more information. In this case, the useful +bindings are `recenter', `scroll-up', and `scroll-down'. If the +user enters `recenter', `scroll-up', or `scroll-down' responses, +perform the requested window recentering or scrolling and ask +again. + +When `use-dialog-box' is t (the default), this function can pop +up a dialog window to collect the user input. That functionality +requires `display-popup-menus-p' to return t. Otherwise, a text +dialog will be used. + +The return value is the matching entry from the CHOICES list. + +Usage example: + +\(read-multiple-choice \"Continue connecting?\" + \\='((?a \"always\") + (?s \"session only\") + (?n \"no\")))" + (let* ((altered-names nil) + (full-prompt + (format + "%s (%s): " + prompt + (mapconcat + (lambda (elem) + (let* ((name (cadr elem)) + (pos (seq-position name (car elem))) + (altered-name + (cond + ;; Not in the name string. + ((not pos) + (format "[%c] %s" (car elem) name)) + ;; The prompt character is in the name, so highlight + ;; it on graphical terminals... + ((display-supports-face-attributes-p + '(:underline t) (window-frame)) + (setq name (copy-sequence name)) + (put-text-property pos (1+ pos) + 'face 'read-multiple-choice-face + name) + name) + ;; And put it in [bracket] on non-graphical terminals. + (t + (concat + (substring name 0 pos) + "[" + (upcase (substring name pos (1+ pos))) + "]" + (substring name (1+ pos))))))) + (push (cons (car elem) altered-name) + altered-names) + altered-name)) + (append choices '((?? "?"))) + ", "))) + tchar buf wrong-char answer) + (save-window-excursion + (save-excursion + (while (not tchar) + (message "%s%s" + (if wrong-char + "Invalid choice. " + "") + full-prompt) + (setq tchar + (if (and (display-popup-menus-p) + last-input-event ; not during startup + (listp last-nonmenu-event) + use-dialog-box) + (x-popup-dialog + t + (cons prompt + (mapcar + (lambda (elem) + (cons (capitalize (cadr elem)) + (car elem))) + choices))) + (condition-case nil + (let ((cursor-in-echo-area t)) + (read-char)) + (error nil)))) + (setq answer (lookup-key query-replace-map (vector tchar) t)) + (setq tchar + (cond + ((eq answer 'recenter) + (recenter) t) + ((eq answer 'scroll-up) + (ignore-errors (scroll-up-command)) t) + ((eq answer 'scroll-down) + (ignore-errors (scroll-down-command)) t) + ((eq answer 'scroll-other-window) + (ignore-errors (scroll-other-window)) t) + ((eq answer 'scroll-other-window-down) + (ignore-errors (scroll-other-window-down)) t) + (t tchar))) + (when (eq tchar t) + (setq wrong-char nil + tchar nil)) + ;; The user has entered an invalid choice, so display the + ;; help messages. + (when (and (not (eq tchar nil)) + (not (assq tchar choices))) + (setq wrong-char (not (memq tchar '(?? ?\C-h))) + tchar nil) + (when wrong-char + (ding)) + (with-help-window (setq buf (get-buffer-create + "*Multiple Choice Help*")) + (with-current-buffer buf + (erase-buffer) + (pop-to-buffer buf) + (insert prompt "\n\n") + (let* ((columns (/ (window-width) 25)) + (fill-column 21) + (times 0) + (start (point))) + (dolist (elem choices) + (goto-char start) + (unless (zerop times) + (if (zerop (mod times columns)) + ;; Go to the next "line". + (goto-char (setq start (point-max))) + ;; Add padding. + (while (not (eobp)) + (end-of-line) + (insert (make-string (max (- (* (mod times columns) + (+ fill-column 4)) + (current-column)) + 0) + ?\s)) + (forward-line 1)))) + (setq times (1+ times)) + (let ((text + (with-temp-buffer + (insert (format + "%c: %s\n" + (car elem) + (cdr (assq (car elem) altered-names)))) + (fill-region (point-min) (point-max)) + (when (nth 2 elem) + (let ((start (point))) + (insert (nth 2 elem)) + (unless (bolp) + (insert "\n")) + (fill-region start (point-max)))) + (buffer-string)))) + (goto-char start) + (dolist (line (split-string text "\n")) + (end-of-line) + (if (bolp) + (insert line "\n") + (insert line)) + (forward-line 1))))))))))) + (when (buffer-live-p buf) + (kill-buffer buf)) + (assq tchar choices))) + +(provide 'rmc) + +;;; rmc.el ends here diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index 5189cc4a6e..8ed29d8659 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -245,176 +245,6 @@ TRIM-LEFT and TRIM-RIGHT default to \"[ \\t\\n\\r]+\"." (substring string 0 (- (length string) (length suffix))) string)) -(defun read-multiple-choice (prompt choices) - "Ask user a multiple choice question. -PROMPT should be a string that will be displayed as the prompt. - -CHOICES is an alist where the first element in each entry is a -character to be entered, the second element is a short name for -the entry to be displayed while prompting (if there's room, it -might be shortened), and the third, optional entry is a longer -explanation that will be displayed in a help buffer if the user -requests more help. - -This function translates user input into responses by consulting -the bindings in `query-replace-map'; see the documentation of -that variable for more information. In this case, the useful -bindings are `recenter', `scroll-up', and `scroll-down'. If the -user enters `recenter', `scroll-up', or `scroll-down' responses, -perform the requested window recentering or scrolling and ask -again. - -When `use-dialog-box' is t (the default), this function can pop -up a dialog window to collect the user input. That functionality -requires `display-popup-menus-p' to return t. Otherwise, a text -dialog will be used. - -The return value is the matching entry from the CHOICES list. - -Usage example: - -\(read-multiple-choice \"Continue connecting?\" - \\='((?a \"always\") - (?s \"session only\") - (?n \"no\")))" - (let* ((altered-names nil) - (full-prompt - (format - "%s (%s): " - prompt - (mapconcat - (lambda (elem) - (let* ((name (cadr elem)) - (pos (seq-position name (car elem))) - (altered-name - (cond - ;; Not in the name string. - ((not pos) - (format "[%c] %s" (car elem) name)) - ;; The prompt character is in the name, so highlight - ;; it on graphical terminals... - ((display-supports-face-attributes-p - '(:underline t) (window-frame)) - (setq name (copy-sequence name)) - (put-text-property pos (1+ pos) - 'face 'read-multiple-choice-face - name) - name) - ;; And put it in [bracket] on non-graphical terminals. - (t - (concat - (substring name 0 pos) - "[" - (upcase (substring name pos (1+ pos))) - "]" - (substring name (1+ pos))))))) - (push (cons (car elem) altered-name) - altered-names) - altered-name)) - (append choices '((?? "?"))) - ", "))) - tchar buf wrong-char answer) - (save-window-excursion - (save-excursion - (while (not tchar) - (message "%s%s" - (if wrong-char - "Invalid choice. " - "") - full-prompt) - (setq tchar - (if (and (display-popup-menus-p) - last-input-event ; not during startup - (listp last-nonmenu-event) - use-dialog-box) - (x-popup-dialog - t - (cons prompt - (mapcar - (lambda (elem) - (cons (capitalize (cadr elem)) - (car elem))) - choices))) - (condition-case nil - (let ((cursor-in-echo-area t)) - (read-char)) - (error nil)))) - (setq answer (lookup-key query-replace-map (vector tchar) t)) - (setq tchar - (cond - ((eq answer 'recenter) - (recenter) t) - ((eq answer 'scroll-up) - (ignore-errors (scroll-up-command)) t) - ((eq answer 'scroll-down) - (ignore-errors (scroll-down-command)) t) - ((eq answer 'scroll-other-window) - (ignore-errors (scroll-other-window)) t) - ((eq answer 'scroll-other-window-down) - (ignore-errors (scroll-other-window-down)) t) - (t tchar))) - (when (eq tchar t) - (setq wrong-char nil - tchar nil)) - ;; The user has entered an invalid choice, so display the - ;; help messages. - (when (and (not (eq tchar nil)) - (not (assq tchar choices))) - (setq wrong-char (not (memq tchar '(?? ?\C-h))) - tchar nil) - (when wrong-char - (ding)) - (with-help-window (setq buf (get-buffer-create - "*Multiple Choice Help*")) - (with-current-buffer buf - (erase-buffer) - (pop-to-buffer buf) - (insert prompt "\n\n") - (let* ((columns (/ (window-width) 25)) - (fill-column 21) - (times 0) - (start (point))) - (dolist (elem choices) - (goto-char start) - (unless (zerop times) - (if (zerop (mod times columns)) - ;; Go to the next "line". - (goto-char (setq start (point-max))) - ;; Add padding. - (while (not (eobp)) - (end-of-line) - (insert (make-string (max (- (* (mod times columns) - (+ fill-column 4)) - (current-column)) - 0) - ?\s)) - (forward-line 1)))) - (setq times (1+ times)) - (let ((text - (with-temp-buffer - (insert (format - "%c: %s\n" - (car elem) - (cdr (assq (car elem) altered-names)))) - (fill-region (point-min) (point-max)) - (when (nth 2 elem) - (let ((start (point))) - (insert (nth 2 elem)) - (unless (bolp) - (insert "\n")) - (fill-region start (point-max)))) - (buffer-string)))) - (goto-char start) - (dolist (line (split-string text "\n")) - (end-of-line) - (if (bolp) - (insert line "\n") - (insert line)) - (forward-line 1))))))))))) - (when (buffer-live-p buf) - (kill-buffer buf)) - (assq tchar choices))) - (provide 'subr-x) ;;; subr-x.el ends here diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el index ed0b3cb44f..7dc9dd7b13 100644 --- a/lisp/gnus/message.el +++ b/lisp/gnus/message.el @@ -49,7 +49,7 @@ (require 'mm-util) (require 'rfc2047) (require 'puny) -(require 'subr-x) ; read-multiple-choice +(require 'rmc) ; read-multiple-choice (autoload 'mailclient-send-it "mailclient") diff --git a/lisp/net/nsm.el b/lisp/net/nsm.el index e2053a0935..87fa9778b6 100644 --- a/lisp/net/nsm.el +++ b/lisp/net/nsm.el @@ -25,7 +25,7 @@ ;;; Code: (require 'cl-lib) -(require 'subr-x) ; read-multiple-choice +(require 'rmc) ; read-multiple-choice (defvar nsm-permanent-host-settings nil) (defvar nsm-temporary-host-settings nil) commit 560dd9b573d8a470430b23837568892aa7071bd2 Author: Piotr Trojanek Date: Fri Oct 6 17:12:31 2017 +0300 * src/process.c (syms_of_process): Remove duplicated call to DEFSYM. Fixes: Bug#28721 Copyright-paperwork-exempt: yes. diff --git a/src/process.c b/src/process.c index 2733fa3911..05feba7325 100644 --- a/src/process.c +++ b/src/process.c @@ -8097,7 +8097,6 @@ syms_of_process (void) DEFSYM (Qreal, "real"); DEFSYM (Qnetwork, "network"); DEFSYM (Qserial, "serial"); - DEFSYM (Qpipe, "pipe"); DEFSYM (QCbuffer, ":buffer"); DEFSYM (QChost, ":host"); DEFSYM (QCservice, ":service"); commit 11f9cb522fed9aa6552f6315340ca7352661a1e8 Merge: 92045f4546 9655937da4 Author: Stefan Monnier Date: Fri Oct 6 09:50:54 2017 -0400 Merge emacs-26 commit 9655937da4a339300c624addd97674c038a01bc9 Author: Lele Gaifax Date: Fri Oct 6 14:08:49 2017 +0100 Fix typos in Flymake documentation * doc/misc/flymake.texi (Syntax check statuses) (Adding support for a new syntax check tool) (Implementation overview, Locating the buildfile): Fix typos. * lisp/progmodes/flymake-proc.el (flymake-proc--report-fn) (flymake-proc--find-possible-master-files):Fix typos. (flymake-proc--panic) (flymake-proc-legacy-flymake): Fix function reference in doc. * lisp/progmodes/flymake.el (flymake-error) (flymake-diagnostic-functions): Fix typos. (flymake-diagnostic-types-alist): Rephrase and fix typos. (flymake--backend-state): Fix typos and rephrase. (flymake--handle-report): Delete empty line. (flymake--disable-backend) (flymake--run-backend): Fix typos. (flymake-goto-next-error, flymake-goto-prev-error): Rephrase. diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index 5dd72f81e2..5ff5537d04 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -182,7 +182,7 @@ to the first diagnostic when invoked in the end of the buffer. @cindex Syntax check statuses After syntax check is finished, its status is displayed in the mode line. -The following statuses are defined. +The following statuses are defined: @multitable @columnfractions 0.25 0.75 @item @code{Wait} @@ -356,7 +356,7 @@ selected. If no match is found, @code{flymake-mode} is switched off. @code{init-function} is required to initialize the syntax check, usually by creating a temporary copy of the buffer contents. The function must return @code{(list cmd-name arg-list)}. If -@code{init-function} returns null, syntax check is aborted, by +@code{init-function} returns null, syntax check is aborted, but @code{flymake-mode} is not switched off. @item cleanup-function @@ -375,7 +375,7 @@ used as @code{getfname-function}. @end table To add support for a new syntax check tool, write corresponding -@code{init-function}, and, optionally @code{cleanup-function} and +@code{init-function} and, optionally, @code{cleanup-function} and @code{getfname-function}. If the format of error messages reported by the new tool is not yet supported by Flymake, add a new entry to the @code{flymake-proc-err-line-patterns} list. @@ -493,7 +493,7 @@ check-syntax: @code{flymake-proc-legacy-backend} saves a copy of the buffer in a temporary file in the buffer's directory (or in the system temp -directory, for java files), creates a syntax check command and +directory, for Java files), creates a syntax check command and launches a process with this command. The output is parsed using a list of error message patterns, and error information (file name, line number, type and text) is saved. After the process has finished, @@ -631,7 +631,7 @@ include directories for C++. The latter files are syntax checked using some build tool, like Make or Ant. All Make configuration data is usually stored in a file called -@code{Makefile}. To allow for future extensions, flymake uses a notion of +@code{Makefile}. To allow for future extensions, Flymake uses a notion of buildfile to reference the 'project configuration' file. Special function, @code{flymake-proc-find-buildfile} is provided for locating buildfiles. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index d08819713a..52cb198532 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -113,7 +113,7 @@ NAME is the file name function to use, default `flymake-proc-get-real-file-name' "Currently active Flymake process for a buffer, if any.") (defvar flymake-proc--report-fn nil - "If bound, function used to report back to flymake's UI.") + "If bound, function used to report back to Flymake's UI.") (defun flymake-proc-reformat-err-line-patterns-from-compile-el (original-list) "Grab error line patterns from ORIGINAL-LIST in compile.el format. @@ -265,7 +265,6 @@ Return t if so, nil if not." (defun flymake-proc--find-possible-master-files (file-name master-file-dirs masks) "Find (by name and location) all possible master files. - Name is specified by FILE-NAME and location is specified by MASTER-FILE-DIRS. Master files include .cpp and .c for .h. Files are searched for starting from the .h directory and max @@ -626,7 +625,7 @@ Create parent directories as needed." (defun flymake-proc--panic (problem explanation) "Tell Flymake UI about a fatal PROBLEM with this backend. May only be called in a dynamic environment where -`flymake-proc--dynamic-report-fn' is bound" +`flymake-proc--report-fn' is bound." (flymake-log 0 "%s: %s" problem explanation) (if (and (boundp 'flymake-proc--report-fn) flymake-proc--report-fn) @@ -718,7 +717,7 @@ May only be called in a dynamic environment where (defun flymake-proc-legacy-flymake (report-fn &rest args) "Flymake backend based on the original Flymake implementation. This function is suitable for inclusion in -`flymake-diagnostic-types-alist'. For backward compatibility, it +`flymake-diagnostic-functions'. For backward compatibility, it can also be executed interactively independently of `flymake-mode'." ;; Interactively, behave as if flymake had invoked us through its diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index ebd5a1ecee..45f0adfeba 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -203,7 +203,7 @@ generated it." `(flymake--log-1 ,level ',sublog ,msg ,@args))) (defun flymake-error (text &rest args) - "Format TEXT with ARGS and signal an error for flymake." + "Format TEXT with ARGS and signal an error for Flymake." (let ((msg (apply #'format-message text args))) (flymake-log :error msg) (error (concat "[Flymake] " msg)))) @@ -331,7 +331,7 @@ function is called with an arbitrary number of arguments: * the remaining arguments are keyword-value pairs in the form (:KEY VALUE :KEY2 VALUE2...). Currently, Flymake provides no such arguments, but backend functions must be prepared to - accept to accept and possibly ignore any number of them. + accept and possibly ignore any number of them. Backend functions are expected to initiate the buffer check, but aren't required to complete it check before exiting: if the @@ -374,8 +374,8 @@ Currently accepted REPORT-KEY arguments are: * ‘:explanation’: value should give user-readable details of the situation encountered, if any. -* ‘:force’: value should be a boolean suggesting that the Flymake - considers the report even if was somehow unexpected.") +* ‘:force’: value should be a boolean suggesting that Flymake + consider the report even if it was somehow unexpected.") (defvar flymake-diagnostic-types-alist `((:error @@ -384,15 +384,17 @@ Currently accepted REPORT-KEY arguments are: . ((flymake-category . flymake-warning))) (:note . ((flymake-category . flymake-note)))) - "Alist ((KEY . PROPS)*) of properties of Flymake error types. -KEY can be anything passed as `:type' to `flymake-diag-make'. + "Alist ((KEY . PROPS)*) of properties of Flymake diagnostic types. +KEY designates a kind of diagnostic can be anything passed as +`:type' to `flymake-make-diagnostic'. PROPS is an alist of properties that are applied, in order, to -the diagnostics of each type. The recognized properties are: +the diagnostics of the type designated by KEY. The recognized +properties are: * Every property pertaining to overlays, except `category' and `evaporate' (see Info Node `(elisp)Overlay Properties'), used - affect the appearance of Flymake annotations. + to affect the appearance of Flymake annotations. * `bitmap', an image displayed in the fringe according to `flymake-fringe-indicator-position'. The value actually @@ -511,23 +513,22 @@ associated `flymake-category' return DEFAULT." "Buffer-local hash table of a Flymake backend's state. The keys to this hash table are functions as found in `flymake-diagnostic-functions'. The values are structures -of the type `flymake--backend-state', with these slots +of the type `flymake--backend-state', with these slots: `running', a symbol to keep track of a backend's replies via its REPORT-FN argument. A backend is running if this key is -present. If the key is absent if the backend isn't expecting any -replies from the backend. +present. If nil, Flymake isn't expecting any replies from the +backend. -`diags', a (possibly empty) list of diagnostic objects created -with `flymake-make-diagnostic'. This key is absent if the -backend hasn't reported anything yet. +`diags', a (possibly empty) list of recent diagnostic objects +created by the backend with `flymake-make-diagnostic'. `reported-p', a boolean indicating if the backend has replied since it last was contacted. `disabled', a string with the explanation for a previous -exceptional situation reported by the backend. If this key is -present the backend is disabled.") +exceptional situation reported by the backend, nil if the +backend is operating normally.") (cl-defstruct (flymake--backend-state (:constructor flymake--make-backend-state)) @@ -552,7 +553,6 @@ present the backend is disabled.") &key explanation force &allow-other-keys) "Handle reports from BACKEND identified by TOKEN. - BACKEND, REPORT-ACTION and EXPLANATION, and FORCE conform to the calling convention described in `flymake-diagnostic-functions' (which see). Optional FORCE says to handle a report even if TOKEN was @@ -639,7 +639,7 @@ different runs of the same backend." (defun flymake--disable-backend (backend &optional explanation) "Disable BACKEND because EXPLANATION. -If is is running also stop it." +If it is running also stop it." (flymake-log :warning "Disabling backend %s because %s" backend explanation) (flymake--with-backend-state backend state (setf (flymake--backend-state-running state) nil @@ -655,7 +655,7 @@ If is is running also stop it." (flymake--backend-state-disabled state) nil (flymake--backend-state-diags state) nil (flymake--backend-state-reported-p state) nil)) - ;; FIXME: Should use `condition-case-unless-debug' here, for don't + ;; FIXME: Should use `condition-case-unless-debug' here, but don't ;; for two reasons: (1) that won't let me catch errors from inside ;; `ert-deftest' where `debug-on-error' appears to be always ;; t. (2) In cases where the user is debugging elisp somewhere @@ -791,13 +791,11 @@ Do it only if `flymake-no-changes-timeout' is non-nil." (defun flymake-goto-next-error (&optional n filter interactive) "Go to Nth next Flymake error in buffer matching FILTER. +Interactively, always move to the next error. With a prefix arg, +skip any diagnostics with a severity less than ‘:warning’. -Interactively, always move to the next error. Interactively, and -with a prefix arg, skip any diagnostics with a severity less than -‘:warning’. - -If ‘flymake-wrap-around’ is non-nil, resumes search from top -at end of buffer. +If ‘flymake-wrap-around’ is non-nil and no more next errors, +resumes search from top FILTER is a list of diagnostic types found in `flymake-diagnostic-types-alist', or nil, if no filter is to be @@ -847,13 +845,11 @@ applied." (defun flymake-goto-prev-error (&optional n filter interactive) "Go to Nth previous Flymake error in buffer matching FILTER. +Interactively, always move to the previous error. With a prefix +arg, skip any diagnostics with a severity less than ‘:warning’. -Interactively, always move to the previous error. Interactively, -and with a prefix arg, skip any diagnostics with a severity less -than ‘:warning’. - -If ‘flymake-wrap-around’ is non-nil, resumes search from top -at end of buffer. +If ‘flymake-wrap-around’ is non-nil and no more previous errors, +resumes search from bottom. FILTER is a list of diagnostic types found in `flymake-diagnostic-types-alist', or nil, if no filter is to be commit e5bff696bcf5f54d2db9bcc8c5ce93083ac0a0d3 Author: Eli Zaretskii Date: Fri Oct 6 15:42:22 2017 +0300 Revert last change in 'shr-descend' * lisp/net/shr.el (shr-descend): Revert the part of the last change which introduced calls to shr-indirect-call into this function. Add a comment explaining the rationale for that. (Bug#28402) diff --git a/lisp/net/shr.el b/lisp/net/shr.el index fe5197b35f..260ada5422 100644 --- a/lisp/net/shr.el +++ b/lisp/net/shr.el @@ -483,7 +483,11 @@ size, and full-buffer size." (apply 'shr-generic dom args))))) (defun shr-descend (dom) - (let ((tag-name (dom-tag dom)) + (let ((function + (intern (concat "shr-tag-" (symbol-name (dom-tag dom))) obarray)) + ;; Allow other packages to override (or provide) rendering + ;; of elements. + (external (cdr (assq (dom-tag dom) shr-external-rendering-functions))) (style (dom-attr dom 'style)) (shr-stylesheet shr-stylesheet) (shr-depth (1+ shr-depth)) @@ -498,7 +502,17 @@ size, and full-buffer size." (setq style nil))) ;; If we have a display:none, then just ignore this part of the DOM. (unless (equal (cdr (assq 'display shr-stylesheet)) "none") - (shr-indirect-call tag-name dom) + ;; We don't use shr-indirect-call here, since shr-descend is + ;; the central bit of shr.el, and should be as fast as + ;; possible. Having one more level of indirection with its + ;; negative effect on performance is deemed unjustified in + ;; this case. + (cond (external + (funcall external dom)) + ((fboundp function) + (funcall function dom)) + (t + (shr-generic dom))) (when (and shr-target-id (equal (dom-attr dom 'id) shr-target-id)) ;; If the element was empty, we don't have anything to put the commit 6b88f78aa1bcda78793f0254a34a9cc099e7ba72 Author: João Távora Date: Fri Oct 6 00:07:53 2017 +0100 Don't error when turning on Flymake with no known backends Leave it to the mode line indicator to inform the user that there is still some configuration to do. * lisp/progmodes/flymake.el (flymake-mode): Simplify. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index f61face25b..ebd5a1ecee 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -708,18 +708,14 @@ Interactively, with a prefix arg, FORCE is t." (cond ;; Turning the mode ON. (flymake-mode - (cond - ((not flymake-diagnostic-functions) - (flymake-error "No backends to check buffer %s" (buffer-name))) - (t - (add-hook 'after-change-functions 'flymake-after-change-function nil t) - (add-hook 'after-save-hook 'flymake-after-save-hook nil t) - (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) - - (setq flymake--backend-state (make-hash-table)) - - (when flymake-start-syntax-check-on-find-file - (flymake-start))))) + (add-hook 'after-change-functions 'flymake-after-change-function nil t) + (add-hook 'after-save-hook 'flymake-after-save-hook nil t) + (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) + + (setq flymake--backend-state (make-hash-table)) + + (when flymake-start-syntax-check-on-find-file + (flymake-start))) ;; Turning the mode OFF. (t commit 03eab7a05e663dcd5ea07b45e83be1f7fd3d7ade Author: João Távora Date: Thu Oct 5 22:23:24 2017 +0100 Delete a Flymake obsolete alias that can't possibly work The function `flymake-ler-make-ler' can't possibly work as an backward compatible interface to existing extensinos (even purely hypothetical ones, since none are known). This is because every diagnostic considered by Flymake has to passed to a report-fn function. * lisp/progmodes/flymake.el (flymake-ler-make-ler): Delete. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index acc0637ec3..f61face25b 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -223,17 +223,6 @@ TYPE is a key to `flymake-diagnostic-types-alist' and TEXT is a description of the problem detected in this region." (flymake--diag-make :buffer buffer :beg beg :end end :type type :text text)) -(defun flymake-ler-make-ler (file line type text &optional full-file) - (let* ((file (or full-file file)) - (buf (find-buffer-visiting file))) - (unless buf (flymake-error "No buffer visiting %s" file)) - (pcase-let* ((`(,beg . ,end) - (with-current-buffer buf - (flymake-diag-region line nil)))) - (flymake-make-diagnostic buf beg end type text)))) - -(make-obsolete 'flymake-ler-make-ler 'flymake-make-diagnostic "26.1") - (cl-defun flymake--overlays (&key beg end filter compare key) "Get flymake-related overlays. If BEG is non-nil and END is nil, consider only `overlays-at' commit db893ab863770ef3f89102d6837cd3eb021429ba Author: Paul Eggert Date: Thu Oct 5 15:55:10 2017 -0700 Fix bug with unmounted directory on GNU/Linux * src/sysdep.c (emacs_get_current_dir_name): Do not use get_current_dir_name result unless it is absolute (Bug#27871). diff --git a/src/sysdep.c b/src/sysdep.c index 26d381f579..8291a606be 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -232,7 +232,18 @@ emacs_get_current_dir_name (void) bool use_libc = true; # endif if (use_libc) - return get_current_dir_name (); + { + /* GNU/Linux get_current_dir_name can return a string starting + with "(unreachable)" (Bug#27871). */ + char *wd = get_current_dir_name (); + if (wd && ! (IS_DIRECTORY_SEP (*wd) || (*wd && IS_DEVICE_SEP (wd[1])))) + { + free (wd); + errno = ENOENT; + return NULL; + } + return wd; + } # endif char *buf; commit 16dc580aa61832285269f8de081248bac618cf84 Author: Nicolas Petton Date: Thu Oct 5 23:18:23 2017 +0200 ; Update ChangeLog.3 diff --git a/ChangeLog.3 b/ChangeLog.3 index c65cf94a3f..33d04f74ec 100644 --- a/ChangeLog.3 +++ b/ChangeLog.3 @@ -1,3 +1,28147 @@ +2017-10-05 Nicolas Petton + + Update authors.el + + * admin/authors.el (authors-renamed-files-alist) + (authors-valid-file-names): Additions. + +2017-10-05 Gemini Lasswell + + Fix dynamic binding wrapper in iter-lambda (bug#25965) + + * lisp/emacs-lisp/generator.el (cps--make-dynamic-binding-wrapper): + Remove extra evaluation of form. + * test/lisp/emacs-lisp/generator-tests.el + (cps-iter-lambda-with-dynamic-binding): New test. + +2017-10-05 Rasmus + + Update Org to v9.1.2 + + Please note this is a bugfix release. See etc/ORG-NEWS for details. + +2017-10-05 Alan Mackenzie + + Fix irregularities with CC Mode fontification, particularly with "known types" + + * lisp/progmodes/cc-fonts.el (c-font-lock-declarators): Introduce a new + optional parameter, template-class. In "class ", fontify "Y" as a + type. + (c-font-lock-single-decl): New variable template-class, set to non-nil when we + have a construct like the above. Pass this as argument to + c-font-lock-declarators. + (c-font-lock-cut-off-declarators): Check more rigorously that a declaration + being processed starts before the function's starting position. + (c-complex-decl-matchers): Remove the redundant clause which fontified "types + preceded by, e.g., "struct"". + + * lisp/progmodes/cc-langs.el (c-template-typename-kwds) + (c-template-typename-key): New lang defconsts and defvar. + +2017-10-05 Eli Zaretskii + + Fix breakage due to recent change in tabulated-list-print-entry + + * lisp/emacs-lisp/tabulated-list.el (tabulated-list-printer): + Update the doc string. + (tabulated-list-print-entry): Revert to using only 2 arguments. + Update the doc string. + (tabulated-list-entry-lnum-width): New defvar. + (tabulated-list-print): Compute the width of line-number display + once, then store that value in tabulated-list-entry-lnum-width, + for tabulated-list-printer to use. (Bug#28704) + +2017-10-05 Gemini Lasswell + + * lisp/ses.el (ses-print-cell): Fix alignment of text cells. (Bug#27653) + +2017-10-05 Alexander Gramiak + + Set xterm click count to 1 even with no last click + + * lisp/xt-mouse.el (xterm-mouse-event): Move the check for + the last click so that click-count is initialized properly. + Handle the value of t for double-click-time. + (Bug#28658) + +2017-10-05 Vasilij Schneidermann + + Support indirection for all shr-tag-* calls + + The 'shr-external-rendering-functions' variable was previously only + honored in the shr-descend function, now all direct calls to the + shr-tag-* functions have been replaced by a call to + 'shr-indirect-call' which tries using an alternative rendering + function first. + + * lisp/net/shr.el (shr-indirect-call): New helper function. + (shr-descend, shr-tag-object, shr-tag-video): + (shr-collect-extra-strings-in-table): Fix callers to call via + shr-indirect-call. (Bug#28402) + +2017-10-05 Eli Zaretskii + + Speed up list-packages when 'visual' line numbers are displayed + + * lisp/emacs-lisp/tabulated-list.el (tabulated-list-printer): + Update the doc string. + (tabulated-list-print-entry): Accept an additional optional + argument INDENT. Update the doc string. + (tabulated-list-print): Compute the width of line-number display + once, then call tabulated-list-printer with that value as 3rd + argument. (Bug#28704) + +2017-10-05 João Távora + + Misc. minor adjustments to Flymake + + - Add a half-decent minor-mode menu; + - Fix "waiting for backends" mode line message; + - Adjust the flymake-diag-region API; + - Autoload the flymake-log macro; + - Auto-disable the legacy backend in more situations; + - Fix a couple of warnings in legacy backend. + + * lisp/progmodes/flymake-proc.el + (flymake-proc--diagnostics-for-pattern): Use new + flymake-diag-region. + + * lisp/progmodes/flymake-proc.el + (flymake-proc-legacy-flymake): Do error when no + buffer-file-name or not writable. + (flymake-proc-legacy-flymake) + (flymake-proc-simple-cleanup): Don't reference flymake-last-change-time + + * lisp/progmodes/flymake.el (flymake-diag-region): + Autoload. Take buffer as first argument. + + * lisp/progmodes/flymake.el (flymake-switch-to-log-buffer): + New command. + (flymake-menu): Add a simple menu. + (flymake--mode-line-format): Use menu. Fix message. Switch to + log buffer when clicking exceptional warnings. + +2017-10-05 Johan Bockgård + + Fix search for ~/.Xdefaults-HOSTNAME + + * src/xrdb.c (get_environ_db): Fix typo when handling + ~/.Xdefaults-HOSTNAME (Bug#28708). + +2017-10-04 Paul Eggert + + Speed up (format "%s" STRING) and the like + + Although the Lisp manual said that ‘format’ returns a + newly-allocated string, this was not true for a few cases like + (format "%s" ""), and fixing the documentation to allow reuse of + arguments lets us improve performance in common cases like + (format "foo") and (format "%s" "foo") (Bug#28625). + * doc/lispref/strings.texi (Formatting Strings): + * etc/NEWS: + Say that the result of ‘format’ might not be newly allocated. + * src/callint.c (Fcall_interactively): + * src/dbusbind.c (XD_OBJECT_TO_STRING): + * src/editfns.c (Fmessage, Fmessage_box): + * src/xdisp.c (vadd_to_log, Ftrace_to_stderr): + Just use Fformat or Fformat_message, as that’s simpler and no + longer makes unnecessary copies. + * src/editfns.c (styled_format): Remove last argument, as it + is no longer needed: all callers now want it to behave as if it + were true. All remaining callers changed. Make this function + static again. Simplify the function now that we no longer + need to worry about whether the optimization is allowed. + +2017-10-04 Alan Mackenzie + + Fontify untyped function declarations in C Mode correctly. + + Also correct two bugs where deleting WS at a BOL could leave an untyped + function declaration unfontified. + + * lisp/progmodes/cc-engine.el (c-find-decl-spots): Don't set the flag + "top-level" when we're in a macro. + (c-forward-decl-or-cast-1): Recognize top-level "foo(bar)" or "foo()" in C + Mode as a implicitly typed function declaration. + (c-just-after-func-arglist-p): Don't get confused by "defined (foo)" inside a + macro. It's not a function plus arglist. + + * lisp/progmodes/cc-langs.el (c-cpp-expr-functions-key): New defconst and + defvar. + + * lisp/progmodes/cc-mode.el (c-fl-decl-end): After c-forward-declarator, move + over any following parenthesis expression (i.e. parameter list). + (c-change-expand-fl-region): When c-new-END is at a BOL, include that line in + the returned region, to cope with deletions at column 0. + +2017-10-04 Michael Albinus + + * lisp/net/tramp.el (tramp-tramp-file-p): Use `string-match-p'. + + Reported by Clément Pit-Claudel . + +2017-10-04 Eli Zaretskii + + Avoid crashes on C-g when several threads wait for input + + * src/thread.h (m_getcjmp): New member of 'struct thread_state'. + (getcjmp): Define to current thread's 'm_getcjmp'. + * src/thread.c (maybe_reacquire_global_lock): Switch to main + thread, since this is called from a SIGINT handler, which always + runs in the context of the main thread. + * src/lisp.h (sys_jmp_buf, sys_setjmp, sys_longjmp): Move the + definitions before thread.h is included, as thread.h now uses + sys_jmp_buf. + * src/keyboard.c (getcjmp): Remove declaration. + (read_char): Don't call maybe_reacquire_global_lock here. + (handle_interrupt): Call maybe_reacquire_global_lock here, if + invoked from the SIGINT handler, to make sure + quit_throw_to_read_char runs with main thread's Lisp bindings and + uses the main thread's jmp_buf buffer. (Bug#28630) + +2017-10-04 Paul Eggert + + Warn if --without-pop is now the default + + * configure.ac (with_pop): Set to no-by-default if defaulting to "no". + Warn about the change if defaulting to "no". Update URLs. + +2017-10-04 Paul Eggert + + --with-pop is now the default only on MS-Windows + + Problem reported by N. Jackson (Bug#28597). + This improves an earlier suggestion by Robert Pluim (Bug#28597#47). + * INSTALL, configure.ac, etc/NEWS: + Make --with-pop the default only on native MS-Windows. + +2017-10-03 Michael Albinus + + Add support for `file-system-info' in Tramp + + * lisp/net/tramp.el (tramp-file-name-for-operation): + Add `file-system-info'. + + * lisp/net/tramp-adb.el (tramp-adb-handle-file-system-info): New defun. + (tramp-adb-file-name-handler-alist): Use it. + + * lisp/net/tramp-gvfs.el (tramp-gvfs-file-system-attributes) + (tramp-gvfs-file-system-attributes-regexp): New defconst. + (tramp-gvfs-handle-file-system-info): New defun. + (tramp-gvfs-file-name-handler-alist): Use it. + (tramp-gvfs-get-directory-attributes): Fix property name. + (tramp-gvfs-get-root-attributes): Support also file system attributes. + + * lisp/net/tramp-sh.el (tramp-sh-handle-file-system-info): New defun. + (tramp-sh-file-name-handler-alist): Use it. + (tramp-sh-handle-insert-directory): Insert size information. + (tramp-get-remote-df): New defun. + + * lisp/net/tramp-smb.el (tramp-smb-handle-file-system-info): New defun. + (tramp-smb-file-name-handler-alist): Use it. + (tramp-smb-handle-insert-directory): Insert size information. + + * test/lisp/net/tramp-tests.el (tramp-test37-file-system-info): + New test. + (tramp-test38-asynchronous-requests) + (tramp-test39-recursive-load, tramp-test40-remote-load-path) + (tramp-test41-unload): Rename. + +2017-10-03 João Távora + + Merge branch 'scratch/flymake-refactor-clean-for-emacs-26' into emacs-26 + +2017-10-03 João Távora + + Start rewriting Flymake manual + + Missing the parts pertaining to the new customization API. + + * doc/misc/flymake.texi (Overview of Flymake): Rewrite a bit. + (Installing Flymake): Delete most of this. + (Running the syntax check): Mention flymake-start. + (Navigating to error lines): Rewrite. + (Viewing error messages): Commente out. + (Syntax check statuses, Troubleshooting): Rewrite a bit. + (Customizable variables): New section under "Using + Flymake". Don't mention any proc variables here. + (Configuring Flymake): Delete + (Proc backend): New chapter + (Proc customization variables): New chapter. + + * doc/misc/flymake.texi (Overview of Flymake): Rewrite a bit. + (Installing Flymake): Mostly scratch. Flymake comes with Emacs. + (Running the syntax check): Simplify. + (Viewing error messages): Dekete, + (Syntax check statuses): Rewrite. + (Troubleshooting): Simplify. + (Customizable variables): Rewrite. + (Extending Flymake): New chapter, empty for now. + (The legacy Proc backend): New chapter. + (Proc customizable variables) + (Adding support for a new syntax check tool) + (Implementation overview) + (Making a temporary copy) + (Locating a master file) + (Getting the include directories) + (Locating the buildfile) + (Starting the syntax check process) + (Parsing the output) + (Interaction with other modes) + (Example---Configuring a tool called via make) + (Example---Configuring a tool called directly): Rewrite a bit. + +2017-10-03 João Távora + + Minimal tweak as an attempt to future-proof Flymake API + + Discussed with Stefan that this should allow Flymake to request more + from backends in the future, while also allowing backends to report + more accurately. + + * lisp/progmodes/elisp-mode.el (elisp-flymake-checkdoc) + (elisp-flymake-byte-compile): Adjust to new API. + + * lisp/progmodes/flymake-proc.el () + (flymake-proc-legacy-flymake): Adjust to new API. + + * lisp/progmodes/flymake.el (flymake-diagnostic-functions): + Review API again. + (flymake--handle-report): Allow other keys. Change ACTION to + REPORT-ACTION. + +2017-10-03 João Távora + + Integrate Flymake elisp checkers into elisp-mode.el directly + + * lisp/progmodes/elisp-mode.el (emacs-lisp-mode): Use + elisp-flymake-checkdoc and elisp-flymake-byte-compile. + (elisp-flymake--checkdoc-1, elisp-flymake-checkdoc) + (elisp-flymake--byte-compile-done) + (elisp-flymake--byte-compile-process) + (elisp-flymake-byte-compile): Rename from flymake-elisp + counterparts in deleted flymake-elisp.el + (elisp-flymake--batch-compile-for-flymake): New helper. + (checkdoc-create-error-function) + (checkdoc-autofix-flag) + (checkdoc-generate-compile-warnings-flag) + (checkdoc-diagnostic-buffer): Forward declare. + + * lisp/progmodes/flymake-elisp.el: Delete. + +2017-10-03 João Távora + + Hook Flymake onto proper checkdoc and byte-compile interfaces + + The interfaces in bytecomp.el and checkdoc.el are mostly boilerplate, + with little knowledge of actual internals or thought given to the + usefulness of said interfaces in contexts other than Flymake's. + + * lisp/emacs-lisp/bytecomp.el + (byte-compile-log-warning-function): New variable. + (byte-compile-log-warning): Use it. + (byte-compile--log-warning-for-byte-compile): New function. + + * lisp/emacs-lisp/checkdoc.el + (checkdoc-create-error-function): New variable. + (checkdoc-create-error): Use it. + (checkdoc--create-error-for-checkdoc): New function.xo + + * lisp/progmodes/flymake-elisp.el (flymake-elisp--checkdoc-1): + Use checkdoc-create-error-function. + (flymake-elisp--batch-byte-compile): Use + byte-compile-log-warning-function. + +2017-10-03 João Távora + + Tweak Flymake autoloads and dependencies + + * lisp/progmodes/elisp-mode.el (emacs-lisp-mode): Add to + flymake-diagnostic-functions here. + + * lisp/progmodes/flymake-elisp.el[top]: Don't add to + emacs-lisp-mode-hook. Don't call flymake-elisp-setup-backends in + every buffer. (flymake-elisp-checkdoc) (flymake-elisp-byte-compile): + Autoload. (flymake-elisp-setup-backends): Remove. + + * lisp/progmodes/flymake.el: Add some top-level comments. + (flymake-make-diagnostic) + (flymake-mode, flymake-mode-on, flymake-mode-off): Add autoloads + + Where to fixup this shit? + +2017-10-03 João Távora + + Capitalize "Flymake" in docstrings and comments + + * lisp/progmodes/flymake-elisp.el (flymake-elisp-checkdoc) + (flymake-elisp-setup-backends): Capitalize "Flymake" + + * lisp/progmodes/flymake-proc.el: + (flymake-proc-reformat-err-line-patterns-from-compile-el) + (flymake-proc--panic, flymake-proc-legacy-flymake) + (flymake-start-syntax-check, flymake-proc-compile) + (define-obsolete-variable-alias): Capitalize "Flymake" + + * lisp/progmodes/flymake.el (flymake-fringe-indicator-position) + (flymake-make-diagnostic, flymake-delete-own-overlays) + (flymake-diagnostic-functions) + (flymake-diagnostic-types-alist, flymake-is-running) + (flymake-make-report-fn, flymake-mode-on, flymake-mode-off) + (flymake-goto-next-error, flymake-goto-prev-error): Capitalize "Flymake" + +2017-10-03 João Távora + + Flymake backends can report multiple times per check + + Rewrote a significant part of the Flymake backend API. Flymake now + ignores the return value of backend functions: a function can either + returns or errors. If it doesn't error, a backend is no longer + constrained to call REPORT-FN exactly once. It may do so any number + of times, cumulatively reporting diagnostics. Flymake keeps track of + outdated REPORT-FN instances and disconsiders obsolete reports. + Backends should avoid reporting obsolete data by cancelling any + ongoing processing at every renewed call to the backend function. + + Consolidated flymake.el internal data structures to require less + buffer-local variables. Adjusted Flymake's mode-line indicator to the + new semantics. + + Adapted and simplified the implementation of elisp and legacy + backends, fixing potential race conditions when calling backends in + rapid succession. + + Added a new test for a backend that calls REPORT-FN multiple + times. Simplify test infrastructure. + + * lisp/progmodes/flymake-elisp.el (flymake-elisp-checkdoc) + (flymake-elisp-byte-compile): Error instead of returning nil + if not in emacs-lisp-mode. + (flymake-elisp--byte-compile-process): New buffer-local variable. + (flymake-elisp-byte-compile): Mark (and kill) previous process + obsolete process before starting a new one. Don't report if + obsolete process. + + * lisp/progmodes/flymake-proc.el + (flymake-proc--current-process): New buffer-local variable. + (flymake-proc--processes): Remove. + (flymake-proc--process-filter): Don't bind + flymake-proc--report-fn. + (flymake-proc--process-sentinel): Rewrite. Don't report if + obsolete process. + (flymake-proc-legacy-flymake): Rewrite. Mark (and kill) + previous process obsolete process before starting a new + one. Integrate flymake-proc--start-syntax-check-process + helper. + (flymake-proc--start-syntax-check-process): Delete. + (flymake-proc-stop-all-syntax-checks): Don't use + flymake-proc--processes, iterate buffers. + (flymake-proc-compile): + + * lisp/progmodes/flymake.el (subr-x): Require it + explicitly. + (flymake-diagnostic-functions): Reword docstring. + (flymake--running-backends, flymake--disabled-backends) + (flymake--diagnostics-table): Delete. + (flymake--backend-state): New buffer-local variable and new defstruct. + (flymake--with-backend-state, flymake--collect) + (flymake-running-backends, flymake-disabled-backends) + (flymake-reporting-backends): New helpers. + (flymake-is-running): Use flymake-running-backends. + (flymake--handle-report): Rewrite. + (flymake-make-report-fn): Ensure REPORT-FN runs in the correct + buffer or not at all. + (flymake--disable-backend, flymake--run-backend): Rewrite. + (flymake-start): Rewrite. + (flymake-mode): Set flymake--backend-state. + (flymake--mode-line-format): Rewrite. + + * test/lisp/progmodes/flymake-tests.el + (flymake-tests--wait-for-backends): New helper. + (flymake-tests--call-with-fixture): Use it. + (included-c-header-files): Fix whitespace. + (flymake-tests--diagnose-words): New helper. + (dummy-backends): Rewrite for new semantics. Use cl-letf. + (flymake-tests--assert-set): Use quote. + (recurrent-backend): New test. + +2017-10-03 João Távora + + Flymake uses proper idle timers + + Also, flymake-no-changes-timeout can be set to nil to disable + automatic periodic checks. But even in that situation the idle timer + still runs at a reduced rate to detect changes in the variable and + revert that decision. + + * lisp/progmodes/flymake.el (flymake-no-changes-timeout): Improve doc. + (flymake-last-change-time): Delete. + (flymake--schedule-timer-maybe): New helper. + (flymake-after-change-function): Use it. + (flymake-on-timer-event): Delete + (flymake-mode): Don't scheduler timer. + +2017-10-03 João Távora + + Flymake variable flymake-diagnostic-functions now a special hook + + * lisp/progmodes/flymake-proc.el: Use add-hook to affect + flymake-diagnostic-functions. + + * lisp/progmodes/flymake-elisp.el + (flymake-elisp-setup-backends): Use add-hook. + + * lisp/progmodes/flymake.el (flymake-diagnostic-functions): + Revise docstring. + (flymake-start): Use run-hook-wrapped. + +2017-10-03 João Távora + + Batch of minor Flymake cleanup actions agreed to with Stefan + + Discussed with Stefan, in no particular order + + - Remove aliases for symbols thought to be internal to flymake-proc.el + - Don’t need :group in defcustom and defface in flymake.el + - Fix docstring of flymake-make-diagnostic + - Fix docstring of flymake-diagnostic-functions to clarify keywords. + - Mark overlays with just the property ’flymake, not ’flymake-overlay + - Tune flymake-overlays for performance + - Make flymake-mode-on and flymake-mode-off obsolete + - Don’t use hash-table-keys unless necessary. + - Copyright notice in flymake-elisp. + + Added some more + + - Clarify docstring of flymake-goto-next-error + - Clarify a comment in flymake--run-backend complaining about ert-deftest. + - Prevent compilation warnings in flymake-proc.el + - Remove doctring from obsolete aliases + + Now the changelog: + + * lisp/progmodes/flymake-elisp.el: Proper copyright notice. + + * lisp/progmodes/flymake-proc.el (flymake-warning-re) + (flymake-proc-diagnostic-type-pred) + (flymake-proc-default-guess) + (flymake-proc--get-file-name-mode-and-masks): Move up to + beginning of file to shoosh compiler warnings + (define-obsolete-variable-alias): Delete many obsolete aliases. + + * lisp/progmodes/flymake.el (flymake-error-bitmap) + (flymake-warning-bitmap, flymake-note-bitmap) + (flymake-fringe-indicator-position) + (flymake-start-syntax-check-on-newline) + (flymake-no-changes-timeout, flymake-gui-warnings-enabled) + (flymake-start-syntax-check-on-find-file, flymake-log-level) + (flymake-wrap-around, flymake-error, flymake-warning) + (flymake-note): Don't need :group in these defcustom and defface. + (flymake--run-backend): Clarify comment + (flymake-mode-map): Remove. + (flymake-make-diagnostic): Fix docstring. + (flymake--highlight-line, flymake--overlays): Identify flymake + overlays with just ’flymake. + (flymake--overlays): Reverse order of invocation for + cl-remove-if-not and cl-sort. + (flymake-mode-on) + (flymake-mode-off): Make obsolete. + (flymake-goto-next-error, flymake-goto-prev-error): Fix docstring. + (flymake-diagnostic-functions): Clarify keyword arguments in + docstring. + + Maybe squash in that one where I remove many obsoletes + +2017-10-03 João Távora + + Explicitly add a(n empty) keymap for Flymake + + Too early to decide what will be in it, if anything. Though "M-n" and + "M-p" would be great. + + * lisp/progmodes/flymake-ui.el (flymake-mode-map): New variable + +2017-10-03 João Távora + + Flymake uses some new fringe bitmaps + + Also fix behaviour whereby flymake wouldn't react to a change in the + variable. + + * lisp/progmodes/flymake-ui.el (flymake-error-bitmap) + (flymake-warning-bitmap): Update bitmaps. + (flymake-note-bitmap): New defcustom. + (flymake-double-exclamation-mark): New bitmap. + (flymake-error, flymake-warning, flymake-note) + (flymake--highlight-line): 'bitmap property must be a symbol. + Also set default face to flymake-error. + (flymake--fringe-overlay-spec): Bitmap property can be a + variable symbol. + +2017-10-03 João Távora + + Remove old flymake-display-err-menu-for-current-line, it's useless + + See https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00949.html + + * lisp/progmodes/flymake-ui.el + (flymake-popup-current-error-menu): Remove. + +2017-10-03 João Távora + + Treat Flymake errors as just another type of diagnostic + + * lisp/progmodes/flymake.el (flymake--diag-errorp): Remove. + (flymake--handle-report, flymake-popup-current-error-menu): + Don't use it. + +2017-10-03 João Távora + + Fix three Flymake bugs when checking C header files + + The first of these problems is longstanding: if an error-less B.h is + included from error-ridden A.h, flymake's legacy parser will panic + (and disable itself) since it sees a non-zero exit for a clean file. + To fix this, recommend returning 'true' in the documentation for the + check-syntax target. + + Another problem was introduced by the parser rewrite. For error + patterns spanning more than one line, point may be left in the middle + of a line and thus render other patterns useless. Those patterns were + written for the old line-by-line parser. To make them useful again, + move to the beginning of line in those situations. + + The third problem was also longstanding and happened on newer GCC's: + The "In file included from" prefix confused + flymake-proc-get-real-file-name. Fix this. + + Also updated flymake--diag-region to fallback to highlighting a full + line less often. + + Add automatic tests to check this. + + * lisp/progmodes/flymake-proc.el + (flymake-proc--diagnostics-for-pattern): Fix bug when patterns + accidentally spans more than one line. Don't create + diagnostics without error messages. + (flymake-proc-real-file-name-considering-includes): New + helper. + (flymake-proc-allowed-file-name-masks): Use it. + + * lisp/progmodes/flymake.el (flymake-diag-region): Make COL + argument explicitly optional. Only fall back to full line in extreme + cases. + + * test/lisp/progmodes/flymake-tests.el + (included-c-header-files): New test. + (different-diagnostic-types): Update. + + * test/lisp/progmodes/flymake-resources/Makefile + (check-syntax): Always return success (0) error code. + (CC_OPTS): Add -Wextra + + * test/lisp/progmodes/flymake-resources/errors-and-warnings.c + (main): Rewrite comments. + + * test/lisp/progmodes/flymake-resources/errors-and-warnings.c: + Include some dummy header files. + + * test/lisp/progmodes/flymake-resources/no-problems.h: New file. + + * test/lisp/progmodes/flymake-resources/some-problems.h: New file. + + * doc/misc/flymake.texi (Example---Configuring a tool called + via make): Recommend adding "|| true" to the check-syntax target. + +2017-10-03 João Távora + + Add interactive flymake-start function + + * lisp/progmodes/flymake.el (flymake-on-timer-event) + (flymake-after-change-function, flymake-mode): Call + flymake-start. + (flymake-start): Rename from flymake--start-syntax-check. + +2017-10-03 João Távora + + A couple of Flymake backends for emacs-lisp-mode + + Loading flymake-elisp.el doesn't setup flymake-mode to turn on + automatically, but it affects emacs-lisp-mode-hook so that + flymake-diagnostic-functions is setup with a suitable buffer-local + value. The variable flymake-diagnostic-funtions in every live + emacs-lisp-mode buffer is also adjusted. + + * lisp/progmodes/flymake.el (top): Require flymake-elisp. + + * lisp/progmodes/flymake-elisp.el: New file. + +2017-10-03 João Távora + + Fancy Flymake mode-line construct displays status + + Imitates compilation-mode's mode-line a bit, and uses its faces. + + * lisp/progmodes/flymake.el + (flymake-error, flymake-warning, flymake-note): Add + mode-line-face to these flymake error types. + (flymake-note): Notes don't need a noisy fringe bitmap. + (flymake-lighter): Delete. + (flymake--update-lighter): Delete. + (flymake--mode-line-format): New function and variable. + (flymake--diagnostics-table): New buffer-local variable. + (flymake--handle-report): Don't update "lighters". Affect + flymake--diagnostics-table. + (flymake--run-backend): Init flymake--diagnostics-table for backend. + (flymake-mode): Use flymake--mode-line-format. + (flymake-mode): Don't update lighter. + (flymake--highlight-line): Be more careful when overriding a + nil default overlay property. + +2017-10-03 João Távora + + Tweak Flymake commands flymake-goto-[next/prev]-error + + Add filters, useful for backends like the upcoming + flymake-elisp-checkdoc backend, for example, which litters everything + with low-priority notes. + + Also re-implement wraparound for flymake-goto-next-error. Manual + mentions this, so it's probably a good idea to keep it. Added a new + customization variable flymake-wrap-around to control it. + + * lisp/progmodes/flymake.el (flymake-goto-prev-error) + (flymake-goto-next-error): Accept FILTER argument. + (flymake-wrap-around): New variable. + (flymake-goto-next-error): Wrap around according to flymake-wrap-around. + + * test/lisp/progmodes/flymake-tests.el + (different-diagnostic-types, dummy-backends): Pass FILTER to + flymake-goto-prev-error. + (different-diagnostic-types) + (dummy-backends): Use flymake-wrap-around. + +2017-10-03 João Távora + + Flymake's flymake-proc.el backend slightly easier to debug + + Misc cleanup in flymake-proc.el + + Improve description of what this file contains. + + Better name for the backend function. Fix the case where it is run + interactively. + + Keep the output buffer alive iff the external process panics. + + * lisp/progmodes/flymake-proc.el + (flymake-proc-legacy-flymake): Rename from + flymake-proc-start-syntax-check. Allow running interactively. + (flymake-start-syntax-check): Obsolete alias for + flymake-proc-legacy-flymake. + (flymake-proc-start-syntax-check): Delete. + (flymake-diagnostic-functions): Include flymake-proc-legacy-flymake + (flymake-proc--process-sentinel): Keep output buffer alive. + Clarify with comments. + (flymake-proc--diagnostics-for-pattern) + (flymake-proc--process-sentinel) + (flymake-proc--safe-delete-directory) + (flymake-proc--start-syntax-check-process): Use condition-case-unless-debug. + +2017-10-03 João Távora + + Simplify Flymake logging and erroring + + Use display-warning and a dedicated *Flymake log* buffer. + + To ease readability, flymake log messages are now prefixed with a + common prefix and the buffer that originated them. + + Some situations of over-zealous logging are fixed. + + Use byte-compiler info, if available, to determine whence the + flymake-related log message is coming. + + * lisp/progmodes/flymake-proc.el + (flymake-proc--diagnostics-for-pattern): Improve log message. + (flymake-proc--panic): Always flymake-log an error + (flymake-proc--safe-delete-file) + (flymake-proc--safe-delete-directory): + Downgrade warning + (flymake-proc-start-syntax-check): Simplify slightly. + (flymake-proc--start-syntax-check-process): Simplify. + (flymake-proc--init-find-buildfile-dir) + (flymake-proc--init-create-temp-source-and-master-buffer-copy): + No need to warn twice. + + * lisp/progmodes/flymake.el (flymake-log): Convert to macro. + (flymake--log-1): New helper. + (flymake-log-level): Deprecate. + (flymake-error): New helper. + (flymake-ler-make-ler, flymake--handle-report, flymake-mode): + Use flymake-error. + (flymake-on-timer-event) + (flymake--handle-report, flymake--disable-backend) + (flymake--run-backend, flymake-start, flymake-mode-on) + (flymake-mode-off, flymake-after-change-function) + (flymake-after-save-hook, flymake-find-file-hook): Adjust + flymake-log calls. + + * test/lisp/progmodes/flymake-tests.el + (flymake-tests--call-with-fixture): Only log errors. + +2017-10-03 Philipp Stephani + + Work around deprecation of gtk_style_context_get_background_color + + * src/gtkutil.c (xg_check_special_colors): Replace call to + gtk_style_context_get_background_color with its definition. + +2017-10-03 João Távora + + New Flymake API variable flymake-diagnostic-functions + + Lay groundwork for multiple active backends in the same buffer. + + Backends are lisp functions called when flymake-mode sees fit. They + are responsible for examining the current buffer and telling + flymake.el, via return value, if they can syntax check it. + Backends should return quickly and inexpensively, but they are also + passed a REPORT-FN argument which they may or may not call + asynchronously after performing more expensive work. + + REPORT-FN's calling convention stipulates that a backend calls it with + a list of diagnostics as argument, or, alternatively, with a symbol + denoting an exceptional situation, usually some panic resulting from a + misconfigured backend. In keeping with legacy behaviour, + flymake.el's response to a panic is to disable the issuing backend. + + The flymake--diag object representing a diagnostic now also keeps + information about its source backend. Among other uses, this allows + flymake to selectively cleanup overlays based on which backend is + updating its diagnostics. + + * lisp/progmodes/flymake-proc.el (flymake-proc--report-fn): + New dynamic variable. + (flymake-proc--process): New variable. + (flymake-can-syntax-check-buffer): Remove. + (flymake-proc--process-sentinel): Simplify. Use + unwind-protect. Affect flymake-proc--processes here. + Bind flymake-proc--report-fn. + (flymake-proc--process-filter): Bind flymake-proc--report-fn. + (flymake-proc--post-syntax-check): Delete + (flymake-proc-start-syntax-check): Take mandatory + report-fn. Rewrite. Bind flymake-proc--report-fn. + (flymake-proc--process-sentinel): Rewrite and simplify. + (flymake-proc--panic): New helper. + (flymake-proc--start-syntax-check-process): Record report-fn + in process. Use flymake-proc--panic. + (flymake-proc-stop-all-syntax-checks): Use mapc. Don't affect + flymake-proc--processes here. Record interruption reason. + (flymake-proc--init-find-buildfile-dir) + (flymake-proc--init-create-temp-source-and-master-buffer-copy): + Use flymake-proc--panic. + (flymake-diagnostic-functions): Add + flymake-proc-start-syntax-check. + (flymake-proc-compile): Call + flymake-proc-stop-all-syntax-checks with a reason. + + * lisp/progmodes/flymake.el (flymake-backends): Delete. + (flymake-check-was-interrupted): Delete. + (flymake--diag): Add backend slot. + (flymake-delete-own-overlays): Take optional filter arg. + (flymake-diagnostic-functions): New user-visible variable. + (flymake--running-backends, flymake--disabled-backends): New + buffer-local variables. + (flymake-is-running): Now a function, not a variable. + (flymake-mode-line, flymake-mode-line-e-w) + (flymake-mode-line-status): Delete. + (flymake-lighter): flymake's minor-mode "lighter". + (flymake-report): Delete. + (flymake--backend): Delete. + (flymake--can-syntax-check-buffer): Delete. + (flymake--handle-report, flymake--disable-backend) + (flymake--run-backend, flymake--run-backend): New helpers. + (flymake-make-report-fn): Make a lambda. + (flymake--start-syntax-check): Iterate + flymake-diagnostic-functions. + (flymake-mode): Use flymake-lighter. Simplify. Initialize + flymake--running-backends and flymake--disabled-backends. + (flymake-find-file-hook): Simplify. + + * test/lisp/progmodes/flymake-tests.el + (flymake-tests--call-with-fixture): Use flymake-is-running the + function. Check if flymake-mode already active before activating it. + Add a thorough test for flymake multiple backends + + * lisp/progmodes/flymake.el (flymake--start-syntax-check): + Don't use condition-case-unless-debug, use condition-case + + * test/lisp/progmodes/flymake-tests.el + (flymake-tests--assert-set): New helper macro. + (dummy-backends): New test. + +2017-10-03 João Távora + + More Flymake cleanup before advancing to backend redesign + + Diagnostics are reported for buffers, not necessarily files. It’s the + backend’s responsibility to compute the buffer where the diagnostic is + applicable. For now, this has to match the buffer where flymake-mode + is active and which is at the origin of the backend call. + + flymake.el knows nothing about line/column diagnostics (except for + backward-compatible flymake-ler-make-ler, which must yet be tested). + It’s also the backend’s reponsibility to compute a BEG and END + positions for the diagnostic in the relevant buffer. + + * lisp/progmodes/flymake-proc.el + (flymake-proc--diagnostics-for-pattern): Convert LINE/COL to + region here. Check file buffer here. + (flymake-proc--process-sentinel): Don’t kill output buffer if + high enough log level. + + * lisp/progmodes/flymake.el (flymake-diag-region): Make this a utility + function. (flymake--highlight-line): Diagnostic has region now. + (flymake-popup-current-error-menu): Don’t add file and line numbers to + already this silly menu. (flymake--fix-line-numbers): Remove. + (flymake-report): No need to fix diagnostics here. + +2017-10-03 João Távora + + Protect Flymake's eager checks against commands like fill-paragraph + + If flymake-start-syntax-check-on-newline is t, check should start as + soon as a newline is seen by after-change-functions. But don't rush + it: since the buffer state might not be final, we might end up with + invalid diagnostic regions after some commands silently insert and + delete newlines (looking at you, fill-paragraph). + + * lisp/progmodes/flymake.el (flymake-after-change-function): Pass + `deferred' to flymake--start-syntax-check. + (flymake--start-syntax-check): Take optional `deferred' arg. + +2017-10-03 João Távora + + Flymake highlights GCC info/notes as detected by flymake-proc.el + + * lisp/progmodes/flymake-proc.el + (flymake-proc--diagnostics-for-pattern): Rewrite (using cl-loop) to + honour more sophisticated flymake-proc-diagnostic-type-pred. + (flymake-warning-re): Is now an obsolete alias for + flymake-proc-diagnostic-type-pred. + (flymake-proc-diagnostic-type-pred): Rename and augment from + flymake-proc-warning-predicate. (flymake-proc-warning-predicate): + Delete. + + * lisp/progmodes/flymake.el (flymake-note): New face. + (flymake-diagnostic-types-alist): Simplify. + (flymake-note): New overlay category. + (flymake--lookup-type-property): Only lookup single keys, not lists. + (flymake--diag-errorp): Rewrite. + (flymake--highlight-line): Use flymake--lookup-type-property. + + * test/lisp/progmodes/flymake-tests.el + (different-diagnostic-types): Rename from errors-and-warnings. + Check notes. + (flymake-tests--call-with-fixture): Use + flymake-proc-diagnostic-type-pred. + +2017-10-03 João Távora + + Flymake checks file names before considering diagnostics + + The error patterns for gcc picked up errors for the Makefile itself, + for example. These shouldn't count as actual errors. + + * lisp/progmodes/flymake.el (flymake-report): Check + matching file names. + +2017-10-03 João Távora + + Echo Flymake error messages when navigating errors interactively + + Perhaps binding M-n and M-p to flymake-goto-next-error and + flymake-goto-prev-error also wouldn't be a bad idea. + + * lisp/progmodes/flymake.el (flymake-goto-next-error): Use + target overlay's help-echo. + +2017-10-03 João Távora + + Add a new Flymake test for multiple errors and warnings + + * test/lisp/progmodes/flymake-tests.el + (flymake-tests--call-with-fixture): Save excursion. + (errors-and-warnings): New test. + + * test/lisp/progmodes/flymake-resources/errors-and-warnings.c: + New test fixture. + +2017-10-03 João Távora + + Flymake warning face easier to distinguish + + A orange wavy underline is very hard to tell from a red wavy + underline. + + * lisp/progmodes/flymake.el (flymake-warning): Change color to + "deep sky blue" + +2017-10-03 João Távora + + Flymake's flymake-proc.el parses column numbers from gcc/javac errors + + Column numbers are not a great way of marking diagnostic regions, but + that's probably all that can be expected from the flymake-proc.el + backend. For now, try (end-of-thing 'sexp) to discover the + diagnostic's end position. + + * lisp/progmodes/flymake-proc.el () + (flymake-proc-err-line-patterns): Also parse column numbers, + if available, for gcc/javac warnings. + +2017-10-03 João Távora + + New Flymake variable flymake-diagnostic-types-alist and much cleanup + + A new user-visible variable is introduced where different diagnostic + types can be categorized. Flymake backends can also contribute to + this variable. Anything that doesn’t match an existing error type + is considered. + + The variable’s alists are used to propertize the overlays pertaining + to each error type. The user can override the built-in properties by + either by modifying the alist, or by modifying the properties of a + special "category" symbol, named by the `flymake-category' entry in + the alist. + + The `flymake-category' entry is especially useful for, say, the author + of foo-flymake-backend, who issues diagnostics of type :foo-note, that + should behave like notes, except with no fringe bitmap: + + (add-to-list 'flymake-diagnostic-types-alist + '(:foo-note + . ((flymake-category . flymake-note) + (bitmap . nil)))) + + For essential properties like `severity', `priority', etc, a default + value is produced. Some properties like `evaporate' cannot be + overriden. + + * lisp/progmodes/flymake.el (flymake--diag): Rename from + flymake-ler. + (flymake-ler-make): Obsolete alias for flymake-diagnostic-make + (flymake-ler-errorp): Rewrite using flymake--severity. + (flymake--place-overlay): Delete. + (flymake--overlays): Now a cl-defun with &key args. Document. + Use `overlays-at' if BEG is non-nil and END is nil. + (flymake--lookup-type-property): New helper. + (flymake--highlight-line): Rewrite. + (flymake-diagnostic-types-alist): New API variable. + (flymake--diag-region) + (flymake--severity, flymake--face) + (flymake--fringe-overlay-spec): New helper. + (flymake-popup-current-error-menu): Use new flymake-overlays. + (flymake-popup-current-error-menu, flymake-report): Use + flymake--diag-errorp. + (flymake--fix-line-numbers): Use flymake--diag-line. + (flymake-goto-next-error): Pass :key to flymake-overlays + + * lisp/progmodes/flymake-proc.el + (flymake-proc--diagnostics-for-pattern): Use flymake-diagnostic-make. + +2017-10-03 João Távora + + Refactor Flymake tests in preparation for more tests + + Introduce a slightly more generic fixture macro. + + Also make flymake-tests.el friendlier to interactive runs, by not + killing buffers visited by the user. + + * test/lisp/progmodes/flymake-tests.el + (flymake-tests--call-with-fixture): New helper from + flymake-tests--current-face. Don't kill file buffers already + being visited before the test starts. + (flymake-tests--with-flymake): New macro. + (flymake-tests--current-face): Delete. + (warning-predicate-rx-gcc, warning-predicate-function-gcc) + (warning-predicate-rx-perl, warning-predicate-function-perl): + Use flymake-test--with-flymake. + +2017-10-03 João Távora + + Allow running Flymake tests from interactive sessions + + * test/lisp/progmodes/flymake-tests.el (flymake-tests-data-directory): + Expand to reasonable value if no + EMACS_TEST_DIRECTORY. (flymake-tests--current-face): Work around + "weirdness" of bug 17647 with read-event. + +2017-10-03 João Távora + + Flymake diagnostics now apply to arbitrary buffer regions + + Make Flymake UI some 150 lines lighter + + Strip away much of the original implementation's complexity in + manipulating objects representing diagnostics as well as creating and + navigating overlays. + + Lay some groundwork for a more flexible approach that allows for + different classes of diagnostics, not necessarily line-based. + Importantly, one overlay per diagnostic is created, whereas the + original implementation had one per line, and on it it concatenated + the results of errors and warnings. + + This means that currently, an error and warning on the same line are + problematic and the warning might be overlooked but this will soon be + fixed by setting appropriate priorities. + + Since diagnostics can highlight arbitrary regions, not just lines, the + faces were renamed. + + Tests pass and backward compatibility with interactive functions is + maintained, but probably any third-party extension or customization + relying on more than a trivial set of flymake.el internals has stopped + working. + + * lisp/progmodes/flymake-proc.el + (flymake-proc--diagnostics-for-pattern): Use new flymake-ler-make + constructor syntax. + + * lisp/progmodes/flymake.el (flymake-ins-after) + (flymake-set-at, flymake-er-make-er, flymake-er-get-line) + (flymake-er-get-line-err-info-list, flymake-ler-set-file) + (flymake-ler-set-full-file, flymake-ler-set-line) + (flymake-get-line-err-count, flymake-get-err-count) + (flymake-highlight-err-lines, flymake-overlay-p) + (flymake-make-overlay, flymake-region-has-flymake-overlays) + (flymake-find-err-info) + (flymake-line-err-info-is-less-or-equal) + (flymake-add-line-err-info, flymake-add-err-info) + (flymake-get-first-err-line-no) + (flymake-get-last-err-line-no, flymake-get-next-err-line-no) + (flymake-get-prev-err-line-no, flymake-skip-whitespace) + (flymake-goto-line, flymake-goto-next-error) + (flymake-goto-prev-error, flymake-patch-err-text): Delete + functions no longer used. + (flymake-goto-next-error, flymake-goto-prev-error): Rewrite. + (flymake-report): Rewrite. + (flymake-popup-current-error-menu): Rewrite. + (flymake--highlight-line): Rename from + flymake-highlight-line. Call `flymake--place-overlay. + (flymake--place-overlay): New function. + (flymake-ler-errorp): New predicate. + (flymake-ler): Simplify. + (flymake-error): Rename from + flymake-errline. + (flymake-warning): Rename from flymake-warnline. + (flymake-warnline, flymake-errline): Obsoletion aliases. + + * test/lisp/progmodes/flymake-tests.el (warning-predicate-rx-gcc) + (warning-predicate-function-gcc, warning-predicate-rx-perl) + (warning-predicate-function-perl): Use face `flymake-warning'. + +2017-10-03 João Távora + + Move symbols in flymake-proc.el to separate namespace + + Every symbol in this flymake now starts with the prefix flymake-proc-. + + Make obsolete aliases for (almost?) every symbol. + + Furthermore, many flymake-proc.el symbols are prefixed with + "flymake-proc--", that is they were considered internal. + + Some customization variables, interactive functions, and other symbols + considered useful to user customizations or third-party libraries are + considered "public" or "external" and so use a "flymake-proc-" prefix. + + * lisp/progmodes/flymake-proc.el: Every symbol renamed. + + * test/lisp/progmodes/flymake-tests.el + (flymake-tests--current-face): Use + flymake-proc-warning-predicate, not flymake-warning-predicate. + + * lisp/progmodes/flymake-proc.el + (flymake-proc--get-project-include-dirs-function) + (flymake-proc--get-project-include-dirs-imp) + (flymake-proc--get-include-dirs-dot) (flymake-proc--get-tex-args) + (flymake-proc--find-make-buildfile) + (flymake-proc--get-syntax-check-program-args) + (flymake-proc--init-create-temp-source-and-master-buffer-copy) + (flymake-proc--init-find-buildfile-dir) + (flymake-proc--get-full-nonpatched-file-name) + (flymake-proc--get-full-patched-file-name) (flymake-proc--base-dir, + flymake-proc--temp-master-file-name) (flymake-proc--master-file-name) + (flymake-proc--temp-source-file-name) + (flymake-proc--delete-temp-directory) (flymake-proc--kill-process) + (flymake-proc--start-syntax-check-process) + (flymake-proc--compilation-is-running) + (flymake-proc--safe-delete-directory) (flymake-proc--safe-delete-file) + (flymake-proc--get-program-dir) (flymake-proc--restore-formatting) + (flymake-proc--clear-project-include-dirs-cache) + (flymake-proc--project-include-dirs-cache) + (flymake-proc--get-system-include-dirs) + (flymake-proc--get-project-include-dirs) + (flymake-proc--add-project-include-dirs-to-cache) + (flymake-proc--get-project-include-dirs-from-cache) + (flymake-proc--post-syntax-check) (flymake-proc--process-sentinel) + (flymake-proc--process-filter) (flymake-proc--create-master-file) + (flymake-proc--find-buffer-for-file) + (flymake-proc--copy-buffer-to-temp-buffer) + (flymake-proc--read-file-to-temp-buffer) + (flymake-proc--save-buffer-in-file) (flymake-proc--replace-region, + flymake-proc--check-include) + (flymake-proc--check-patch-master-file-buffer) + (flymake-proc--master-file-compare) + (flymake-proc--find-possible-master-files) + (flymake-proc--included-file-name, flymake-proc--same-files) + (flymake-proc--fix-file-name, flymake-proc--find-buildfile) + (flymake-proc--clear-buildfile-cache) + (flymake-proc--add-buildfile-to-cache) + (flymake-proc--get-buildfile-from-cache) + (flymake-proc--find-buildfile-cache) + (flymake-proc--get-real-file-name-function) + (flymake-proc--get-cleanup-function) (flymake-proc--get-init-function) + (flymake-proc--get-file-name-mode-and-masks) + (flymake-proc--processes): Rename to internal symbol from + flymake-proc- version. + +2017-10-03 João Távora + + Completely rewrite Flymake's subprocess output processing + + Instead of parsing and matching regexps line-by-line, insert + subprocess output in a separate buffer and parse using + `search-forward-regexp'. This eventually enables multi-line error + patterns and simplifies code all around. Store per-check information + in the subprocess using `process-get' and `process-put'. Treat error + messages, warnings, etc. more generically as "diagnostics". Create + these objects as soon as possible, reusing existing `flymake-ler' + structure. Fix some whitespace. + + * lisp/progmodes/flymake.el (cl-lib): Require also when + loading. + (flymake--fix-line-numbers): Rename from + flymake-fix-line-numbers. Simplify. + (flymake-report): Call flymake--fix-line-numbers. Rearrange + plain diagnostics list into alist format expected by + flymake-highlight-err-lines. + + * lisp/progmodes/flymake-proc.el (flymake-process-filter): Insert + process output and parse in dedicated output buffer. + (flymake-proc--diagnostics-for-pattern): New helper function. + (flymake-process-sentinel): Call flymake-post-syntax-check with + collected diagnostics. Kill output buffer. + (flymake-post-syntax-check): Receive diagnostics as third argument. + (flymake-parse-output-and-residual, flymake-new-err-info) + (flymake-parse-residual, flymake-parse-err-lines) + (flymake-split-output, flymake-proc-parse-line) + (flymake-output-residual): Delete. + (flymake-start-syntax-check-process): Use make-process. Setup + dedicated an output buffer + +2017-10-03 João Távora + + Flymake provides flymake-report re-entry point for backends + + * lisp/progmodes/flymake-proc.el (flymake-post-syntax-check): + Simplify. Call flymake-report. + + * lisp/progmodes/flymake.el (flymake-report): New function. + +2017-10-03 João Távora + + Split Flymake into flymake.el into flymake-proc.el (again!) + + After deciding that this work would continue on master only, which + caused two commits named + + Revert "Split flymake.el into flymake-proc.el and flymake-ui.el" + + and + + Revert "Add flymake-backends defcustom" + + to be added to the emacs-26 branch, further discussion reversed that + decision. + + See: + + https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg01020.html + https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg01030.html + + This means that those two commits MUST be merged to master AFTER ALL. + + flymke-proc.el contains the main syntax-checking backend, while + flymake.el keeps mostly the UI part. + + * lisp/progmodes/flymake-proc.el: New file. Require flymake. + + * lisp/progmodes/flymake.el: Require flymake-proc.el at the end. + +2017-10-03 Nicolas Petton + + Update authors.el + + * admin/authors.el (authors-ignored-files, authors-valid-file-names) + (authors-renamed-files-alist): Additions. + +2017-10-03 Noam Postavsky + + Give more helpful messages for python completion setup failures + + * lisp/progmodes/python.el (python-shell-completion-native-setup): In + case the completion setup failed with some exception, print out the + exception type and message. If libedit is detected, raise an + exception, since this is known to fail. + +2017-10-02 Eli Zaretskii + + Fix the --without-x build + + * src/frame.c (Ficonify_frame) [HAVE_WINDOW_SYSTEM]: Use + frame_parent only in GUI builds to avoid compilation errors in + --without-x builds. (Bug#28611) + +2017-10-02 Paul Eggert + + Fix customization of zoneinfo-style-world-list + + A customizable variable's initial value cannot depend on that of + another customizable variable, since the variables are initialized + in other than textual order. Problem reported by N. Jackson + (Bug#24291). + * lisp/time.el (display-time-world-list): Default to t, + a special value that expands to zoneinfo-style-word-list + if that works, and to legacy-style-word-list otherwise. + (time--display-world-list): New function. + (display-time-world, display-time-world-timer): Use it. + +2017-10-02 Alan Mackenzie + + Fix a CC Mode brace stack cache bug. + + * lisp/progmodes/cc-engine.el (c-update-brace-stack): Call + c-beginning-of-current-token after a failing search operation, to ensure we + don't cache a point inside a token. + +2017-10-02 Paul Eggert + + * etc/PROBLEMS: Document Bug#26638. + +2017-10-02 Paul Eggert + + Prefer HTTPS to HTTP for gnu.org + + This fixes some URLs I omitted from my previous pass, + notably those in lists.gnu.org. Although lists.gnu.org + does not yet support TLS 1.1, TLS 1.0 is better than nothing. + * lisp/erc/erc.el (erc-official-location): + * lisp/mail/emacsbug.el (report-emacs-bug): + Use https:, not http:. + +2017-10-02 Paul Eggert + + Merge from Gnulib + + This is mostly to change http: to https: in licenses. + * COPYING, build-aux/config.guess, build-aux/config.sub: + * doc/emacs/doclicense.texi, doc/emacs/gpl.texi: + * doc/lispintro/doclicense.texi, doc/lispref/doclicense.texi: + * doc/lispref/gpl.texi, doc/misc/doclicense.texi: + * doc/misc/gpl.texi, etc/COPYING, leim/COPYING: + * lib-src/COPYING, lib/COPYING, lisp/COPYING, lwlib/COPYING: + * msdos/COPYING, nt/COPYING, src/COPYING: + Copy from Gnulib. + +2017-10-01 Simen Heggestøyl + + Keep eww buffer current when looking up CSS on MDN + + * lisp/textmodes/css-mode.el (css-lookup-symbol): Keep the eww buffer + current when looking up CSS documentation on MDN. This fixes a bug + where the eww buffer's content sometimes get mangled when switching + buffers mid-render. + +2017-10-01 Charles A. Roelli + + Workaround for faulty localtime() under macOS 10.6 + + * lisp/org/org-clock.el (org-clock--oldest-date): Only execute + 'decode-time' on times later than year -2**31 under macOS 10.6. + See Bug#27706. + +2017-10-01 Alan Mackenzie + + Doc amendment for syntax-ppss. + + * doc/elisp/syntax.texi (Position Parse): Note, twice, that syntax-ppss is + equivalent to parse-partial-sexp from the beginning of THE VISIBLE PART OF the + buffer. Final part of the fix for bug #22983. + +2017-10-01 Charles A. Roelli + + Remove incorrect NEWS entry about 'find-library' + + * etc/NEWS (Changes in Emacs 26.1): Remove an entry about + 'find-library' taking a prefix argument to pop to a different + window. This behavior was added in "Allow a prefix argument to + find-library to pop to a different window" (commit e1f2d14a), and + then removed in "New commands: find-library-other-window, + find-library-other-frame" (commit 021430f4). + +2017-10-01 Alan Mackenzie + + Remove inadvertent changes to syntax.texi in last commit. + + * doc/lispref/syntax.texi (Position Parse): revert changes. + +2017-10-01 Alan Mackenzie + + Amend documentation for text-quoting-style becoming a user option. + + * doc/lispref/control.texi (Signaling Errors): + * doc/lispref/display.texi (Displaying Messages): + * doc/lispref/strings.texi (Formatting Strings): + Edit for brevity, farming out the details to the new + Text Quoting Style node. + * doc/lispref/help.texi (Text Quoting Style): New section. + Move detailed discussion of text-quoting-style here. + Add discussion about how to output grave accent and apostrophe in + documentation and messages. Adjust xrefs to point to this section + when appropriate. + * etc/NEWS: text-quoting-style semantics have not changed. + +2017-10-01 Alan Mackenzie + + Make the value nil in text-quoting-style mean what it does in Emacs 25. + + This is a partial reversion of yesterday's commit by the same author, which + changed the meaning of nil and introduced the new value t. + + * src/doc.c (text_quoting_style, text-quoting-style) + (internal--text-quoting-flag): Revert yesterday's changes. + + * lisp/cus-start.el: (top level): Amend the entry for text-quoting-style. + + * etc/NEWS: Amend the entry for text-quoting-style. + + * doc/lispref/control.texi (Signalling Errors) + * doc/lispref/display.texi (Displaying Messages) + * doc/lispref/strings.texi (Formatting Strings): Bind text-quoting-style to + grave rather than nil to inhibit translation of quotes. + + * doc/lispref/help.texi (Keys in Documentation): Revert the description of the + proposed new default, t. + +2017-10-01 Alan Mackenzie + + Make text-quoting-style customizable. Introduce t and new meaning for nil. + + A value of nil for text-quoting-style now means "no translation". t means + "Use curved quotes if displayable". + + * src/doc.c (text-quoting-style (function)): modify for new semantics. + (text-quoting-style (variable)): Amend the doc string, set the default value + to t. + + * lisp/cus-start.el: (top level): Create a customize entry for + text-quoting-style in group display. + + * etc/NEWS: Amend the entry for text-quoting-style. + + * doc/emacs/display.texi (Text Display): Describe the translation of ASCII + quotes to curved quotes, and how to influence or inhibit it. + + * doc/lispref/control.texi (Signalling Errors) + * doc/lispref/display.texi (Displaying Messages) + * doc/lispref/strings.texi (Formatting Strings): Describe binding + text-quoting-style to nil to inhibit unwanted quote translation. + + * doc/lispref/help.texi (Keys in Documentation): Change text-quoting-style + from a variable to a user option. Describe its changed set of values. State + that it can be customized freely. + +2017-10-01 Michael Albinus + + eshell.texi improvements + + * doc/misc/eshell.texi (Built-ins): eshell/sudo is a compiled + Lisp function in `em-tramp.el'. Mention also $*, $1, $2, ... + (Aliases): Add $*, $1, $2, ... to the variable index. + +2017-08-15 Alan Third + + Fix ns-win.el on GNUstep + + * lisp/term/ns-win.el: Appkit version check only works on macOS, so + don't try it when not using Cocoa. + +2017-10-01 Martin Rudalics + + Fix reference style in org.texi + + * doc/misc/org.texi (A Texinfo example): Fix reference style. + +2017-10-01 Martin Rudalics + + Improve handling of iconification of child frames (Bug#28611) + + * src/frame.c (Ficonify_frame): Handle `iconify-child-frame' option. + (syms_of_frame): New symbols Qiconify_top_level and Qmake_invisible. + (iconify_child_frame): New option. + * lisp/cus-start.el (iconify-child-frame): Add customization + properties. + * doc/lispref/frames.texi (Child Frames): Describe new option + `iconify-child-frame'. Don't index "top-level frame" twice. + +2017-10-01 Noam Postavsky + + Revert "Don't lose arguments to eshell aliases (Bug#27954)" + + It broke the established argument handling methods provided by eshell + aliases (Bug#28568). + * doc/misc/eshell.texi (Aliases): Fix example, call out use of + arguments in aliases. + * lisp/eshell/em-alias.el (eshell-maybe-replace-by-alias): Ignore + ARGS. + +2017-10-01 Noam Postavsky + + Make "unsafe directory" error message more informative (Bug#865) + + * lisp/server.el (server-ensure-safe-dir): Produce a description for + each "unsafe" condition. + +2017-10-01 Eric Abrahamsen + + Fix slot typecheck in eieio-persistent + + * lisp/emacs-lisp/eieio-base.el (eieio-persistent-slot-type-is-class-p): + An `or' form can specify multiple potential classes (or null) as + valid types for a slot, but previously only the final element of the + `or' was actually checked. Now returns all valid classes in the `or' + form. + (eieio-persistent-validate/fix-slot-value): Check if proposed value + matches any of the valid classes. + * test/lisp/emacs-lisp/eieio-tests/eieio-test-persist.el + (eieio-test-multiple-class-slot): Test this behavior. + +2017-09-30 Dmitry Gutov + + Fix semantic-ia-fast-jump + + * lisp/cedet/semantic/ia.el (semantic-ia--fast-jump-helper): + Use `pop-to-buffer-same-window' (bug#28645). + +2017-09-30 Kaushal Modi + + Bind vc-region-history + + * lisp/vc/vc-hooks.el (vc-prefix-map): + Bind `vc-region-history' to 'C-x v h', which was earlier bound to + `vc-insert-headers' (Bug#27644). + * doc/emacs/maintaining.texi (VC Change Log): Mention the new binding. + * doc/emacs/vc1-xtra.texi (Version Headers): Remove the association of + 'C-x v h' with `vc-insert-headers'. + (http://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00957.html) + +2017-09-30 Allen Li (tiny change) + + Exit macro definition on undefined keys + + * lisp/subr.el (undefined): Error out of kmacro definition, if any. + (Bug#28008) + +2017-09-30 Tim Landscheidt (tiny change) + + Reset bidi-paragraph-direction on article rendering + + * lisp/gnus/gnus-art.el (gnus-request-article-this-buffer): Reset + bidi-paragraph-direction on article rendering. (Bug#28454) + +2017-09-30 Eli Zaretskii + + Fix url-http use of url-current-object + + * lisp/url/url-http.el (url-http): Bind url-current-object before + calling url-http-find-free-connection. (Bug#28515) + +2017-09-30 Andy Moreton + + Avoid assertions in vc-hg.el on MS-Windows + + * lisp/vc/vc-hg.el (vc-hg--pcre-to-elisp-re) + (vc-hg--slurp-hgignore, vc-hg--read-repo-requirements) + (vc-hg-state-fast): Use file-name-absolute-p and directory-name-p + instead of relying on Unix file-name syntax. This avoids + assertion violations on MS-Windows. + +2017-09-30 Eli Zaretskii + + Improve documentation of 'copy-sequence' + + * src/fns.c (Fcopy_sequence): + * doc/lispref/sequences.texi (Sequence Functions): Mention the + exception when copying an empty sequence. (Bug#28627) + +2017-09-30 Eli Zaretskii + + Minor update of ack.texi + + * doc/emacs/ack.texi (Acknowledgments): Update Eli Zaretskii's + contributions. + +2017-09-30 N. Jackson (tiny change) + + * doc/emacs/emacs.texi (Acknowledgments): Add more contributors. + +2017-09-30 Eli Zaretskii + + Improve indexing of multi-file/buffer Isearch commands + + * doc/emacs/maintaining.texi (Identifier Search): Change wording + of index entries to make them different from those for multi-file + isearch commands. (Bug#28584) + * doc/emacs/search.texi (Other Repeating Search): Index the + multi-* commands. (Bug#28584) Rearrange the indexing to keep + each index entry close to its subject. + +2017-09-30 Mark Oteiza + + Add CAM02 JCh and CAM02-UCS J'a'b' conversions + + * src/lcms.c (rad2deg, parse_jch_list, parse_jab_list, xyz_to_jch): + (jch_to_xyz, jch_to_jab, jab_to_jch): New functions. + (lcms-jch->xyz, lcms-jch->xyz, lcms-jch->jab, lcms-jab->jch): New Lisp + functions. + (lcms-cam02-ucs): Refactor. + (syms_of_lcms2): Declare new functions. + * test/src/lcms-tests.el (lcms-roundtrip, lcms-ciecam02-gold): + (lcms-jmh->cam02-ucs-silver): New tests. + * etc/NEWS: Mention new functions. + +2017-09-30 Eli Zaretskii + + Fix uses of @kindex in the Emacs manual + + * doc/emacs/programs.texi (Expressions, Semantic, Hungry Delete): + * doc/emacs/mark.texi (Global Mark Ring) + (Disabled Transient Mark): + * doc/emacs/buffers.texi (Select Buffer): + * doc/emacs/mule.texi (File Name Coding): Fix @kindex entries + which used @key. Reported by Marcin Borkowski . + +2017-09-30 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-09-28 string: code style + 2017-09-25 sys_types: update URL + 2017-09-23 install-sh: do not assume / = // + 2017-09-21 mktime: port to OpenVMS + * build-aux/install-sh, m4/mktime.m4, m4/string_h.m4: + * m4/sys_types_h.m4: Copy from Gnulib. + * lib/gnulib.mk.in: Regenerate. + +2017-09-30 Paul Eggert + + Prefer HTTPS to HTTP for gnu.org + + This catches some URLs I missed in my previous scan, + or perhaps were added after the scan. + +2017-09-30 Noam Postavsky + + Wait for frame visibility with timeout in w32term too + + * src/w32term.c (syms_of_w32term) [x-wait-for-event-timeout]: New + variable. + (x_make_frame_visible): Wait for frame to become visible according to + its value. + (input_signal_count): Remove. + +2017-09-30 Noam Postavsky + + Bring back the busy wait after x_make_frame_visible (Bug#25521) + + But wait specfically for a MapNotify event, and only for a + configurable amount of time. + * src/xterm.c (syms_of_xterm) [x-wait-for-event-timeout]: New + variable. + (x_wait_for_event): Use it instead of hardcoding the wait to 0.1s. + (x_make_frame_visible): Call x_wait_for_event at the end. + * etc/NEWS: Announce x_wait_for_event. + +2017-09-29 Eli Zaretskii + + Fix last doc string change in simple.el + + * lisp/simple.el (shell-command-saved-pos) + (region-extract-function, region-bounds): Doc fixes. (Bug#28609) + +2017-09-29 Eli Zaretskii + + Revert "bug#28609: simple.el" + + This reverts commit a75ab3b3fb8ab69ef38a94403d061f88f3b5b63e. + +2017-09-29 Devon Sean McCullough + + bug#28609: simple.el + + Correct grammar; also, call a pair a pair. + + (cherry picked from commit 25ef543a97a80718cc4eb33734d393420a43f41e) + +2017-09-29 Rasmus + + Merge branch 'emacs-26' into scratch/org-mode-merge + +2017-09-29 Noam Postavsky + + Fix ert backtrace saving for non-`signal'ed errors (Bug#28333) + + * lisp/emacs-lisp/ert.el (ert--run-test-debugger): Take the frames + above the `debugger' frame, rather than assuming there will be a + `signal' frame. + +2017-09-28 Alan Third + + Revert "Fix build on macOS (bug#28571)" + + This reverts commit fec63089d53d2196b0348086aeed70277fbc02c0. + + Prematurely pushed. + +2017-09-28 Alan Third + + Fix build on macOS (bug#28571) + + * src/conf_post.h (HAVE_FUTIMENS, HAVE_FUTIMESAT, HAVE_UTIMENSAT) + [DARWIN_OS]: Undefine. + +2017-09-28 Simen Heggestøyl + + Add tests for `css-current-defun-name' + + * test/lisp/textmodes/css-mode-tests.el (css-test-current-defun-name) + (css-test-current-defun-name-nested) + (css-test-current-defun-name-complex): New tests for + `css-current-defun-name'. + +2017-09-28 Martin Rudalics + + In w32fullscreen_hook don't add decorations to undecorated frames + + * src/w32term.c (w32fullscreen_hook): Do not add (or try to + remove) decorations for undecorated frames. + +2017-09-28 João Távora + + Revert "Split flymake.el into flymake-proc.el and flymake-ui.el" + + In other words, re-coalesce the two files, + lisp/progmodes/flymake-proc.el and lisp/progmodes/flymake-ui.el, back + into a single one, lisp/progmodes/flymake.el. + + The changesets "Prefer HTTPS to FTP and HTTP in documentation" and + "allow nil init in flymake-allowed-file-name-masks to disable flymake" + are kept in place in the new lisp/progmodes/flymake.el. + + This reverts Git commit eb34f7f5a29e7bf62326ecb6e693f28878be28cd. + + Don't merge this back to master as development happening there builds + upon this work. See also + https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00932.html. + +2017-09-28 João Távora + + Revert "Add flymake-backends defcustom" + + This reverts Git commit 13993c46a21495167517f76d2e36b6c09ac5e89e. + + Don't merge this back to master as development happening there builds + upon this work. See also + https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00932.html + +2017-09-27 Paul Eggert + + * src/editfns.c (styled_format): Fix typo in previous change. + +2017-09-27 Paul Eggert + + Avoid some unnecessary copying in Fformat etc. + + This patch is just for performance; it should not affect behavior. + On my platform, it made the microbenchmark (format "%S" load-path) + run about 45% faster. It should also speed up calls like (message + "%s" STRING). + * src/callint.c (Fcall_interactively): + * src/dbusbind.c (XD_OBJECT_TO_STRING): + * src/editfns.c (Fmessage, Fmessage_box): + * src/xdisp.c (vadd_to_log, Ftrace_to_stderr): + Use styled_format instead of Fformat or Fformat_message, + to avoid unnecessary copying. + * src/editfns.c (styled_format): New arg NEW_RESULT. + All uses changed. Reuse an input string if it has the + right value and if !NEW_RESULT. + * src/lisp.h (style_format): New decl. + +2017-09-26 John Wiegley + + lisp/simple.el: Indicate when a list of pairs is meant in a docstring + +2017-09-26 Devon Sean McCullough + + bug#28609: simple.el + + Correct grammar; also, call a pair a pair. + +2017-09-26 Dmitry Gutov + + Use a separate syntax-ppss cache for narrowed buffers + + * lisp/emacs-lisp/syntax.el (syntax-ppss-wide): + New variable, to contain the data from `syntax-ppss-last' and + `syntax-ppss-cache'. + (syntax-ppss-cache, syntax-ppss-last): Remove. + (syntax-ppss-narrow, syntax-ppss-narrow-start): New variables. + (syntax-ppss-flush-cache): Flush both caches. + (syntax-ppss--data): Return the appropriate last result and + buffer cache for the current restriction. + (syntax-ppss, syntax-ppss-debug): Use it (bug#22983). + +2017-09-26 Joerg Behrmann (tiny change) + + Improve python3-compatibility of fallback completion (Bug#28499) + + * lisp/progmodes/python.el (python-eldoc-setup-code): Use + inspect.getfullargspec instead of inspect.getargspec to avoid a + deprecation warning on every usage of eldoc in python-mode. + +2017-09-26 Noam Postavsky + + Fix subr-x-tests when running from elc + + * test/lisp/emacs-lisp/subr-x-tests.el (subr-x-and-let*-test-group-1): + Use `eval' around the `should-error' cases. + +2017-09-26 Noam Postavsky + + * lisp/eshell/esh-util.el (eshell-condition-case): Add debug declaration. + +2017-09-26 Noam Postavsky + + Make sh-indentation into an alias for sh-basic-offset (Bug#21751) + + * lisp/progmodes/sh-script.el (sh-indentation): Redefine as obsolete + variable alias for `sh-basic-offset'. + (sh-mode, sh-smie--indent-continuation) + (sh-smie-rc-rules, sh-basic-indent-line): Replace `sh-indentation' + with `sh-basic-offset'. + +2017-09-26 Noam Postavsky + + Fix loading of smie-config rules (Bug#24848) + + * lisp/emacs-lisp/smie.el (smie-config--setter): Use `set-default' + instead of `setq-default'. + (smie-config): Use `custom-initialize-set' instead of + `custom-initialize-default' as the :initialize argument. + + * lisp/progmodes/sh-script.el (sh-learn-buffer-indent): Mention that + we call `smie-config-guess' so that the user will have a chance to + find the correct docstring to consult. Remove hedging comments + regarding use of abnormal hooks. + +2017-09-26 Dmitry Gutov + + Reset default-directory inside *xref-grep* buffer + + * lisp/progmodes/xref.el (xref-collect-matches): + Reset default-directory, too. (Bug#28575) + +2017-09-25 Michael Albinus + + * test/lisp/tramp-tests.el (tramp-test21-file-links): Special code for smb. + +2017-09-25 Mark Oteiza + + Loosen strict parsing requirement for desktop files + + There are other desktop-looking files, for instance those having to do + with MIME typess, that would benefit from being able to be read by this + function. It helps to have some flexibility. + * lisp/xdg.el (xdg-desktop-read-file): Remove an error condition. + * test/lisp/xdg-tests.el: Remove a test. + +2017-09-25 Mark Oteiza + + * lisp/xdg.el (xdg-thumb-uri): Fix doc string. + +2017-09-25 Martin Rudalics + + Fix documentation of `make-frame' and related variables and hooks + + * lisp/frame.el (before-make-frame-hook) + (after-make-frame-functions, frame-inherited-parameters) + (make-frame): Fix doc-strings. + * doc/lispref/frames.texi (Creating Frames): Fix description + of `make-frame' and related variables and hooks. + +2017-09-24 Eric Abrahamsen + + Accept new `always' value for option `buffer-offer-save' + + Also revert ee512e9a82 + + * lisp/files.el (buffer-offer-save): In addition to nil and t, now + allows a third symbol value, `always'. A buffer where this option is + set to `always' will always be offered for save by + `save-some-buffers'. + (save-some-buffers): Check the exact value of this buffer-local + variable. No longer check the buffer name, or the value of + `write-contents-functions'. + * doc/lispref/buffers.texi (Killing Buffers): Note change in manual. + * doc/lispref/files.texi (Saving Buffers): Remove note about buffer + names. + * etc/NEWS: Mention in NEWS. + +2017-09-24 Alan Third + + Improve new NS scrolling variable names + + * src/nsterm.m (ns-use-system-mwheel-acceleration): Replace with + 'ns-use-mwheel-acceleration'. + (ns-touchpad-scroll-line-height): Replace with + 'ns-mwheel-line-height'. + (ns-touchpad-use-momentum): Replace with 'ns-use-mwheel-momentum'. + * etc/NEWS: Change variable names. + +2017-09-24 Philipp Stephani + + Document 'replace-buffer-contents' in the manual. + + * doc/lispref/text.texi (Replacing): New node. + +2017-09-23 Alan Third + + Fix undecorated frame resizing issues on NS (bug#28512) + + * src/nsterm.m (EmacsView::updateFrameSize): Don't wait for the + toolbar on undecorated frames. + (EmacsView::initFrameFromEmacs): Group window flags correctly. + +2017-09-23 Eli Zaretskii + + Fix doc string of 'dired-listing-switches' + + * lisp/dired.el (dired-listing-switches): Fix the quoting + example. (Bug#28569) + +2017-09-23 Eli Zaretskii + + Documentation improvements for 'display-line-numbers' + + * doc/emacs/display.texi (Display Custom): Document a few more + options for display-line-numbers. (Bug#28533) Fix a typo. + +2017-09-22 Eli Zaretskii + + Fix last change in bat-mode.el + + * lisp/progmodes/bat-mode.el (bat-font-lock-keywords): Fix last + change. (Bug#28311) + +2017-09-22 Eli Zaretskii + + Fix restoring in GUI sessions desktop saved in TTY sessions + + * lisp/frameset.el (frameset-filter-font-param): New function. + (frameset-persistent-filter-alist): Use it for processing the + 'font' frame parameter. (Bug#17352) + +2017-09-22 Eli Zaretskii + + Improve syntax highlighting in bat-mode + + * lisp/progmodes/bat-mode.el (bat-font-lock-keywords): Improve + font-locking of environment variables. Suggested by Achim Gratz + . (Bug#28311) (Bug#18405) + +2017-09-22 Eli Zaretskii + + Document the 'list-FOO' convention + + * doc/lispref/tips.texi (Coding Conventions): Document the + list-FOO convention. + +2017-09-22 Mark Oteiza + + Expose viewing conditions in CAM02-UCS metric + + Also add tests from the colorspacious library. Finally, catch an + errant calculation, where degrees were not being converted to radians. + * src/lcms.c (deg2rad, default_viewing_conditions): + (parse_viewing_conditions): New functions. + (lcms-cam02-ucs): Add comments pointing to references used. Expand + the docstring and explain viewing conditions. JCh hue is given in + degrees and needs to be converted to radians. + (lcms-d65-xyz): Remove. No need to duplicate this in Lisp or make the + API needlessly impure. + * test/src/lcms-tests.el: Reword commentary. + (lcms-rgb255->xyz): New function. + (lcms-cri-cam02-ucs): Fix let-binding. + (lcms-dE-cam02-ucs-silver): New test, assimilated from colorspacious. + +2017-09-21 Alan Third + + Revert "Set frame size to actual requested size (bug#18215)" + + This reverts commit d31cd79b40dbd5459b16505a4ee4340210499277. + + See bug#28536. I misunderstood bug#18215. It wasn't a bug. + +2017-09-21 Gemini Lasswell + + Add tests for Edebug + + * tests/lisp/emacs-lisp/edeug-tests.el: New file. + * tests/lisp/emacs-lisp/edebug-resources/edebug-test-code.el: New file. + +2017-09-21 Gemini Lasswell + + Catch more messages in ert-with-message-capture + + * lisp/emacs-lisp/ert-x.el (ert-with-message-capture): Capture + messages from prin1, princ and print. + (ert--make-message-advice): New function. + (ert--make-print-advice): New function. + +2017-09-21 Tak Kunihiro + + Support setting region from secondary selection and vice versa + + * lisp/mouse.el (secondary-selection-exist-p): New function to + allow callers to tell existence of the secondary selection + in current buffer. + (secondary-selection-to-region): New function to set + beginning and end of the region from those of the secondary + selection. + (secondary-selection-from-region): New function to set + beginning and end of the secondary selection from those of + the region. (Bug#27530) + + * etc/NEWS: Mention the new functions. + +2017-09-20 Paul Eggert + + Fix new copy-directory bug with empty dirs + + Problem reported by Afdam Plaice (Bug#28520) and by Eli Zaretskii + (Bug#28483#34). This is another bug that I introduced in my + recent copy-directory changes. + * lisp/files.el (copy-directory): Work with empty subdirectories, too. + * test/lisp/files-tests.el (files-tests--copy-directory): + Test for this bug. + +2017-09-20 Eli Zaretskii + + * doc/lispref/strings.texi (Formatting Strings): Improve indexing. + +2017-09-20 Eli Zaretskii + + Fix 2 testsuite tests for MS-Windows + + * test/lisp/ibuffer-tests.el (test-buffer-list): Don't try to + create files with "*" in their names. + * test/src/editfns-tests.el (format-time-string-with-zone): Adapt + results to MS-Windows build. Reported by Fabrice Popineau + . + +2017-09-20 Mark Oteiza + + Rename timer-list to list-timers + + * doc/emacs/anti.texi (Antinews): + * doc/lispref/os.texi (Timers): + * etc/NEWS: + * lisp/emacs-lisp/timer-list.el: + (timer-list-mode): Rename timer-list to list-timers. + +2017-09-19 Alan Third + + Provide native touchpad scrolling on macOS + + * etc/NEWS: Describe changes. + * lisp/term/ns-win.el (mouse-wheel-scroll-amount, + mouse-wheel-progressive-speed): Set to smarter values for macOS + touchpads. + * src/nsterm.m (emacsView::mouseDown): Use precise scrolling deltas to + calculate scrolling for touchpads and mouse wheels. + (syms_of_nsterm): Add variables 'ns-use-system-mwheel-acceleration', + 'ns-touchpad-scroll-line-height' and 'ns-touchpad-use-momentum'. + * src/keyboard.c (make_lispy_event): Pass on .arg when relevant. + * src/termhooks.h (event_kind): Update comments re. WHEEL_EVENT. + * lisp/mwheel.el (mwheel-scroll): Use line count. + * lisp/subr.el (event-line-count): New function. + +2017-09-19 Eli Zaretskii + + Fix MinGW64 build broken by recent MinGW64 import libraries + + * configure.ac (W32_LIBS): Put -lusp10 before -lgdi32, as latest + MinGW64 import libraries require that. (Bug#28493) + + * src/Makefile.in: Adjust commentary to the new order of w32 + libraries. + +2017-09-19 Eli Zaretskii + + Fix crashes in 'move-point-visually' in minibuffer windows + + * src/xdisp.c (Fmove_point_visually): Fix off-by-one error in + comparing against the last valid glyph_row of a window glyph + matrix. (Bug#28505) + +2017-09-19 Eli Zaretskii + + * src/emacs.c (usage_message): Don't mention 'find-file'. + +2017-09-19 Eli Zaretskii + + Fix a minor inaccuracy in the Emacs manual + + * doc/emacs/cmdargs.texi (Action Arguments): Don't mention + 'find-file', as the implementation has changed. Reported by + Everton J. Carpes in + http://lists.gnu.org/archive/html/help-gnu-emacs/2017-09/msg00146.html. + +2017-09-19 Eli Zaretskii + + Fix errors in flyspell-post-command-hook + + * lisp/textmodes/ispell.el (ispell-get-decoded-string): Handle the + case of a nil Nth element of the language dictionary slot. This + avoids errors in 'flyspell-post-command-hook' when switching + dictionaries with some spell-checkers. (Bug#28501) + +2017-09-19 Michael Albinus + + Work on Tramp's file-truename + + * lisp/net/tramp-sh.el (tramp-perl-file-truename): + Check also for symlinks. + (tramp-sh-handle-file-truename): Move check for a symlink + cycle to the end. Do not blame symlinks which look like a + remote file name. + + * lisp/net/tramp.el (tramp-handle-file-truename): Expand result. + +2017-09-19 Paul Eggert + + Fix bug with make-directory on MS-Windows root + + * lisp/files.el (files--ensure-directory): Treat any error, not + just file-already-exists, as an opportunity to check whether DIR + is already a directory (Bug#28508). + +2017-09-19 Tom Tromey + + Fix log-view-diff-common when point is after last entry + + Bug#28466 + * lisp/vc/log-view.el (log-view-diff-common): If point is after last + entry, look at the previous revision. + +2017-09-18 Ken Brown + + Adapt fileio-tests--symlink-failure to Cygwin + + * test/src/fileio-tests.el (fileio-tests--symlink-failure) + [CYGWIN]: Skip the case of a symlink target starting with '\'; + this is treated specially on Cygwin. + +2017-09-18 Eric Abrahamsen + + Ignore buffers whose name begins with a space in save-some-buffers + + * lisp/files.el (save-some-buffers): Consider these buffers + "internal", and don't prompt the user to save them. + * doc/lispref/files.texi: Document. + +2017-09-18 Michael Albinus + + Improve tramp-interrupt-process robustness + + * lisp/net/tramp.el (tramp-interrupt-process): Wait, until the + process has disappeared. + +2017-09-18 Michael Albinus + + Minor Tramp doc update + + * doc/misc/tramp.texi (Frequently Asked Questions): + Mention `vc-handled-backends'. + +2017-09-18 Mark Oteiza + + Fix gensym + + * lisp/subr.el (gensym): Actually implement the default prefix. + * test/lisp/subr-tests.el (subr-tests--gensym): New test. + +2017-09-18 Rasmus + + Update Org to v9.1.1 + + Please see etc/ORG-NEWS for major changes. + +2017-09-18 Michael Albinus + + Cleanup in files-tests.el + + * test/lisp/files-tests.el (files-tests--make-directory) + (files-tests--copy-directory): Cleanup temporary directories. + +2017-09-18 Paul Eggert + + Remove old cl-assert calls in 'newline' + + * lisp/simple.el (newline): Remove cl-assert calls + that didn't seem to be helping us debug Bug#18913, + and that caused problems as reported in Bug#28280. + Suggested by Glenn Morris (Bug#28280#8). + +2017-09-18 Paul Eggert + + Avoid crash with C-g C-g in GC + + Problem reported by Richard Stallman (Bug#17406). + Based on fix suggested by Eli Zaretskii (Bug#28279#16). + * src/term.c (tty_send_additional_strings): + Use only safe accessors, to avoid crash when C-g C-g in GC. + +2017-09-18 Paul Eggert + + Fix format-time-string %Z bug with negative tz + + * src/editfns.c (tzlookup): Fix sign error in %Z when a purely + numeric zone is negative (Bug#28746). + * test/src/editfns-tests.el (format-time-string-with-zone): + Add test for this bug. + +2017-09-18 Paul Eggert + + message-citation-line-format %Z is now tz name + + * etc/NEWS: + * lisp/gnus/message.el (message-citation-line-format): + Fix doc to match new behavior (Bug#28476). + +2017-09-18 Mark Oteiza + + Use doc-view or pdf-tools on any window-system + + * lisp/net/mailcap.el (mailcap-mime-data): Simply check for + window-system. + +2017-09-18 Paul Eggert + + Fix bug with min and max and NaNs + + * src/data.c (minmax_driver): Fix bug with (min 0 NaN), which + mistakenly yielded 0. Also, pacify GCC in a better way. + * test/src/data-tests.el (data-tests-min): Test for the bug. + +2017-09-17 Paul Eggert + + Fix recently-introduced copy-directory bug + + Problem reported by Andrew Christianson (Bug#28451): + * lisp/files.el (copy-directory): If COPY-CONTENTS, make the + destination directory if it does not exist, even if it is a + directory name. Simplify, and omit unnecessary test for an + already-existing non-directory target, since make-directory + diagnoses that for us now. + * test/lisp/files-tests.el (files-tests--copy-directory): + Test for this bug. + +2017-09-17 Paul Eggert + + Merge from Gnulib + + This incorporates: + 2017-09-16 manywarnings: port to GCC on 64-bit MS-Windows + 2017-09-13 all: Replace many more http URLs by https URLs + * build-aux/config.guess, build-aux/config.sub: + * build-aux/gitlog-to-changelog, doc/misc/texinfo.tex: + * lib/allocator.h, lib/count-leading-zeros.h: + * lib/count-trailing-zeros.h, lib/dup2.c, lib/filevercmp.c: + * lib/fstatat.c, lib/fsync.c, lib/ftoastr.c, lib/ftoastr.h: + * lib/intprops.h, lib/signal.in.h, lib/stdio-impl.h, lib/stdio.in.h: + * lib/unistd.in.h, lib/utimens.c, m4/alloca.m4, m4/extern-inline.m4: + * m4/fstatat.m4, m4/gnulib-common.m4, m4/manywarnings.m4: + * m4/std-gnu11.m4, m4/sys_types_h.m4, m4/vararrays.m4: + Copy from Gnulib. + * lib/gnulib.mk.in: Regenerate. + +2017-09-17 Michael Albinus + + Fix compatibility problem in Tramp + + * lisp/net/tramp.el (tramp-interrupt-process): Better error handling. + + * lisp/net/tramp-compat.el (default-toplevel-value): Move up. + (top): Do not call `tramp-change-syntax' anymore. + (tramp-compat-directory-name-p): New defalias. + + * lisp/net/tramp-adb.el (tramp-adb-handle-copy-file): + * lisp/net/tramp-sh.el (tramp-sh-handle-copy-directory): + * lisp/net/tramp-smb.el (tramp-smb-handle-copy-directory) + (tramp-smb-handle-copy-file): Use it. + + * test/lisp/net/tramp-tests.el (tramp-test28-interrupt-process): + Modify test. + +2017-09-17 Eli Zaretskii + + Avoid GCC 7 compilation warning in eval.c + + * src/eval.c (push_handler_nosignal): Use CACHEABLE to work around + GCC compilation warning. Suggested by Paul Eggert + in http://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00492.html. + +2017-09-17 Michael Albinus + + Adapt Tramp version. Do not merge + + * doc/misc/trampver.texi: + * lisp/net/trampver.el: Change version to "2.3.3.26.1". + (customize-package-emacs-version-alist): Add Tramp version + integrated in Emacs 26.1. + +2017-09-17 Tom Tromey + + Search for Syntax section when viewing MDN + + * lisp/textmodes/css-mode.el (css--mdn-after-render): Also search for + "Syntax" section. + +2017-09-17 Tom Tromey + + Allow smerge-keep-current to work for empty hunks + + Bug#25555 + * lisp/vc/smerge-mode.el (smerge-get-current): Allow point to be at + match-end. + * test/lisp/vc/smerge-mode-tests.el: New file. + +2017-09-17 Tom Tromey + + Call vc-setup-buffer in vc-git-log-{in,out}going + + Bug#28427: + * lisp/vc/vc-git.el (vc-git-log-incoming, vc-git-log-outgoing): Call + vc-setup-buffer. + +2017-09-17 Stefan Monnier + + Fix last change to textmodes/page-ext.el + + * lisp/textmodes/page-ext.el (pages-directory): Make buffer writable + while we build it (bug#28431). + +2017-09-16 Glenn Morris + + * test/src/lcms-tests.el (lcms-cri-cam02-ucs): Skip if lcms2 not present. + +2017-09-16 Glenn Morris + + * test/src/lcms-tests.el (lcms-whitepoint): Skip if lcms2 not present. + + (cherry picked from commit 8081df26911c63aadfce4ee8f6a7223d814baeaf) + +2017-09-16 Eli Zaretskii + + Fix compilation warning in etags.c + + * lib-src/etags.c (etags_mktmp) [DOS_NT]: Don't dereference a NULL + pointer. Reported by Richard Copley . + +2017-09-16 Mark Oteiza + + Add lisp variable lcms-d65-xyz + + This serves as the default optional argument for functions in this + library. + * src/lcms.c (lcms-d65-xyz): New variable. + (lcms-cam02-ucs): Use it. Use better word in docstring. Fix bug + color1 -> color2. + * test/src/lcms-tests.el: Add some tests for lcms-cri-cam02-ucs. + (lcms-colorspacious-d65): New variable. + +2017-09-16 Gemini Lasswell + + * lisp/emacs-lisp/cl-macs.el (cl-letf): Fix Edebug spec (bug#24765) + +2017-09-16 Andy Moreton + + Avoid MinGW64 compiler warnings in unexw32.c + + * src/unexw32.c (pDWP) [MINGW_W64]: Define to "16llx" only for the + 64-bit build. + +2017-09-16 Eli Zaretskii + + Start emacs-26 release branch + + * configure.ac: + * nt/README.W32: + * README: + * msdos/sed2v2.inp: Increment Emacs version to 26.0.60. + + * lisp/cus-edit.el (customize-changed-options-previous-release): + Update value to "25.3". + +2017-09-16 Alan Mackenzie + + Cope better with C++ and Objective-C protection keywords in class declarations + + This fix fixes the fontification of a method inside a class at the time it is + typed, when there is a protection keyword clause preceding it. + + * lisp/progmodes/cc-engine.el (c-forward-keyword-clause): Handle protection + keywords. + (c-looking-at-decl-block): Avoid scanning forward over protection keyword + clauses too eagerly. + + * lisp/progmodes/cc-langs.el (c-protection-key c-post-protection-token): New + lang defconsts and defvars. + + * lisp/progmodes/cc-mode.el (c-fl-decl-start): When we encounter a protection + keyword following a semicolon or brace, move forward over it before attempting + to parse a type. + +2017-09-16 Eli Zaretskii + + Fix order of sorted overlays returned by 'overlays-at' + + * src/buffer.c (Foverlays_at): If SORTED is non-nil, reverse the + list of results, to have their order as per the documentation. + (Bug#28390) + + * etc/NEWS: Mention the change in the behavior of overlays-at. + +2017-09-16 Eli Zaretskii + + Disable execution of unsafe Lisp by Enriched Text mode + + * src/xdisp.c (handle_display_spec): If the display property is + wrapped in 'disable-eval' form, disable Lisp evaluation while + processing this property. + (handle_single_display_spec): Accept new argument ENABLE_EVAL_P. + If that argument is false, don't evaluate Lisp while processing + display properties. + + * lisp/textmodes/enriched.el + (enriched-allow-eval-in-display-props): New defcustom. + (enriched-decode-display-prop): If + enriched-allow-eval-in-display-props is nil, wrap the display + property with 'disable-eval' to disable Lisp evaluation when the + display property is processed for display. (Bug#28350) + * lisp/gnus/mm-view.el (mm-inline-text): Re-enable processing of + enriched text. + + * doc/lispref/display.texi (Display Property): Document the + 'disable-eval' wrapping of 'display' properties. + * doc/emacs/text.texi (Enriched Properties): Document + 'enriched-allow-eval-in-display-props'. + + * etc/NEWS: Describe the security issues with Enriched Text mode + and their solution. + +2017-09-16 Eli Zaretskii + + Avoid MinGW64 compilation warning in w32.c + + * src/w32.c (sys_strerror): Provide a prototype for MinGW64. + +2017-09-16 Eli Zaretskii + + Fix MS-Windows build broken by recent changes in lcms.c + + * src/lcms.c [WINDOWSNT]: Define types for cmsWhitePointFromTemp + and cmsxyY2XYZ function pointers. + (init_lcms_functions) [WINDOWSNT]: Load cmsWhitePointFromTemp and + cmsxyY2XYZ from liblcms2. + (cmsWhitePointFromTemp, cmsxyY2XYZ) [WINDOWSNT]: Redirect to the + corresponding function pointers. + (Flcms_temp_to_white_point): Minor stylistic changes. Doc fix. + (syms_of_lcms2): Defsubr Slcms_temp_to_white_point. + +2017-09-16 Eli Zaretskii + + Avoid GCC 7 compilation warning in data.c + + * src/data.c (minmax_driver): Use UNINIT to avoid compilation + warnings. Reported by Fabrice Popineau + . + +2017-09-16 Mark Oteiza + + Add lcms-temp->white-point and initial tests + + * src/lcms.c (lcms-temp->white-point): New function. + * test/src/lcms-tests.el: New file. + +2017-09-16 Mark Oteiza + + Use cl-print in timer list + + * lisp/emacs-lisp/timer-list.el (timer-list): Use cl-print + for handling functions. + (timer-list-mode): Capitalize major mode name. Set bidi direction + as in tabulated-list-mode. + +2017-09-15 Vincent Belaïche + + Make landscape layout with geometry package rather than a PostScript special. + + * lisp/calendar/cal-tex.el (cal-tex-preamble): Make 12pt the + default class option. + (cal-tex-year, cal-tex-cursor-month-landscape): Pass landscape + request to `cal-tex-insert-preamble' function call within the + class option string. + (cal-tex-cursor-month): Don't pass any longer "12pt" argument + to `cal-tex-insert-preamble' function, as it is default. + (cal-tex-insert-preamble): Suppress landscape and size + argument, and replace them by a class-options string + argument. Do not insert any longer "\special{landscape}" in + case of landscape layout, as the job is made by the geometry + package. + +2017-09-15 Mark Oteiza + + * lisp/json.el (json-read-keyword): Revert previous change to catch EOL. + +2017-09-15 Eli Zaretskii + + One more attempt to avoid GCC 7 warnings in dispnew.c + + * src/dispnew.c (adjust_glyph_matrix): Use eassume instead of + eassert, to avoid compilation warnings about NULL pointer + dereferences. + +2017-09-15 Mark Oteiza + + Fix color-distance docstring + + Also feed the translated color to the metric argument. + * src/xfaces.c (color-distance): Reword docstring to be more helpful. + Avoid duplicating effort in lcms2 by passing the translated 16 bit RGB + instead of the function's color arguments. + +2017-09-15 Michael Albinus + + Improve Tramp behaviour according to bug#27986 + + * lisp/net/tramp-adb.el (tramp-adb-handle-copy-file): + * lisp/net/tramp-sh.el (tramp-sh-handle-copy-directory): + * lisp/net/tramp-smb.el (tramp-smb-handle-copy-directory) + (tramp-smb-handle-copy-file): Check, that NEWNAME is a + directory name when existing. Use `file-name-as-directory' + where appropriate. + +2017-09-15 Mark Oteiza + + More JSON optimization + + Last I checked, inlining json-skip-whitespace didn't make much + difference. However, changing defsubsts to define-inline results + in roughly 15% reduction in read time on a 200K file. + * lisp/json.el (json-advance, json-peek, json-pop): + (json-skip-whitespace): Inline with define-inline. + (json-read-keyword): Don't use whitespace syntax. + (json-add-to-object): Simpler condition. + +2017-09-15 Eli Zaretskii + + Avoid crashes due to invalid error forms from sentinels/filters + + * src/process.c (exec_sentinel_error_handler): Make sure the error + form passed to cmd_error_internal is a cons cell. (Bug#28430) + +2017-09-15 Eli Zaretskii + + Avoid compilation warnings with GCC 7 on MS-Windows + + * src/w32term.c (w32_setup_relief_color, construct_mouse_click) + (w32_read_socket): Initialize variables to shut up bogus + compilation warnings from GCC 7. + * src/unexw32.c (COPY_CHUNK, COPY_PROC_CHUNK): Cast to DWORD_PTR + to avoid compiler warnings about printing signed values using %x + format spec. + * src/dispnew.c (adjust_glyph_matrix): Add eassert to avoid + compiler warning about possible NULL pointer dereference. + * src/lisp.h (pI): Tweak the definition some more for MinGW64. + +2017-09-15 Martin Rudalics + + Define gnutls_rnd for WINDOWSNT and HAVE_GNUTLS3 case only + + * src/fns.c (gnutls_rnd): Define for WINDOWSNT and HAVE_GNUTLS3 + case only to avoid unused macros warning otherwise. + +2017-09-15 Martin Rudalics + + In w32heap.c bump up DUMPED_HEAP_SIZE + + * src/w32heap.c (DUMPED_HEAP_SIZE): Bump up DUMPED_HEAP_SIZE + to 13*1024*1024 for 32-bit non-wide-integer builds. + +2017-09-15 Mark Oteiza + + Bind n,p in timer-list + + * lisp/emacs-lisp/timer-list.el (timer-list-mode-map): Bind n and p + to next- and previous-line, respectively. + +2017-09-14 Glenn Morris + + * lisp/net/tls.el (tls-program): Fix :version. + +2017-09-14 Eli Zaretskii + + * configure.ac (--with-lcms2, --without-lcms2): New options. + +2017-09-14 Eli Zaretskii + + Avoid 64-bit compilation warnings in unexw32.c + + * src/unexw32.c (pDWP): New macro. + (COPY_CHUNK, COPY_PROC_CHUNK): Declare 'count' as DWORD_PTR. Use + pDWP for printing values that can be either 32-bit or 64-bit wide. + +2017-09-14 Eli Zaretskii + + Fix warnings about formats in printf-like functions on MS-Windows + + * src/lisp.h (pI) [__MINGW32__]: Provide definition that will + hopefully DTRT with both MinGW64 and mingw.org's MinGW. See + http://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00171.html + for the details. + * src/conf_post.h (PRINTF_ARCHETYPE) [MINGW_W64]: Separate + definition specific to MinGW64. + (PRINTF_ARCHETYPE) [__MINGW32__]: For mingw.org's MinGW, use + __mingw_printf__ in ANSI-compatible mode. + +2017-09-14 Eli Zaretskii + + Support lcms2 in MS-Windows builds + + * lisp/term/w32-win.el (dynamic-library-alist): Include + association for the lcms2 library. + + * src/lcms.c [WINDOWSNT]: Include windows.h and w32.h. Use + DEF_DLL_FN to define pointers to dynamically loaded lcms2 + functions. + (cmsCIE2000DeltaE, cmsCIECAM02Init, cmsCIECAM02Forward) + (cmsCIECAM02Done): New macros. + (init_lcms_functions, Flcms2_available_p): New functions. + (Flcms_cie_de2000, Flcms_cam02_ucs) [WINDOWSNT]: Call + init_lcms_functions. + (syms_of_lcms2): Defsubr lcms2-available-p. + * src/w32fns.c (syms_of_w32fns): DEFSYM Qlcms2. + + * configure.ac: Include lcms2 in the final report and in + emacs_config_features. + + * nt/INSTALL: + * nt/INSTALL.W64: Update with the information about lcms2 library. + +2017-09-14 Paul Eggert + + Port renameat_noreplace to openSUSE 12.3 + + Problem reported by M. Nomiya in: + http://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00363.html + * src/sysdep.c (renameat_noreplace): + Call renameat2 only if CYGWIN. + +2017-09-14 Paul Eggert + + Prefer HTTPS to FTP and HTTP in documentation + + Most of this change is to boilerplate commentary such as license URLs. + This change was prompted by ftp://ftp.gnu.org's going-away party, + planned for November. Change these FTP URLs to https://ftp.gnu.org + instead. Make similar changes for URLs to other organizations moving + away from FTP. Also, change HTTP to HTTPS for URLs to gnu.org and + fsf.org when this works, as this will further help defend against + man-in-the-middle attacks (for this part I omitted the MS-DOS and + MS-Windows sources and the test tarballs to keep the workload down). + HTTPS is not fully working to lists.gnu.org so I left those URLs alone + for now. + +2017-09-14 Paul Eggert + + Prefer HTTPS to HTTP for gnu.org + + This patch just changes code files; a followup companion patch + (much larger) will affect the commentary. This part is + separated out to make it easier to review. + * .dir-locals.el (change-log-mode): + * lisp/org/org-info.el (org-info-other-documents) + (org-info-map-html-url): + * lisp/org/ox-html.el (org-html-creator-string): + * lisp/startup.el (fancy-startup-text, fancy-about-text) + (fancy-splash-head): + * test/lisp/ffap-tests.el (ffap-other-window--bug-25352): + * test/lisp/thingatpt-tests.el (thing-at-point-test-data): + Use HTTPS instead of HTTP. + +2017-09-13 Simen Heggestøyl + + Add tests for color.el + + * lisp/color.el (color-name-to-rgb, color-complement): Clarify in + docstrings that RGB triplets should use four digits per component. + (color-rgb-to-hsl): Break line to avoid "Hidden behind deeper element" + warning. + + * test/lisp/color-tests.el: New file. + +2017-09-13 Lars Ingebrigtsen + + Make gnutls-verify-error work again with url-retrieve-synchronously + + * lisp/url/url-gw.el (url-open-stream): Only use :nowait if + we're doing async connections (bug#26835). + + * lisp/url/url-parse.el (url): Add an asynchronous slot. + + * lisp/url/url.el (url-asynchronous): New variable. + (url-retrieve-internal): Store the value. + (url-retrieve-synchronously): Bind the variable. + +2017-09-13 Michael Albinus + + Improve backward compatibility of tramp-tests + + * test/lisp/net/tramp-tests.el (seq): Don't require. + (tramp--test-emacs26-p): New defun. + (tramp-test10-write-region, tramp-test11-copy-file) + (tramp-test12-rename-file, tramp-test15-copy-directory) + (tramp-test21-file-links): Use it. + (tramp-test16-file-expand-wildcards): Use `copy-sequence'. + +2017-09-13 Michael Albinus + + * lisp/net/trampver.el (customize-package-emacs-version-alist): + + Add Tramp version integrated in Emacs 25.3. + +2017-09-13 Mark Oteiza + + Add clarification to if-let* docstring + + Also make its behaviour consistent with and-let* in that empty bindings + results in success, not failure. + * lisp/emacs-lisp/subr-x.el: Edit docstring, change else to then. + +2017-09-13 Lars Ingebrigtsen + + Make fully qualified domain names more fully qualified + + * lisp/gnus/message.el (message-make-fqdn): Don't try to use a + system-name without any periods as a fully qualified domain name. + +2017-09-13 Paul Eggert + + Remove unused file lib/getopt_.h + + * lib/getopt_.h: Remove. It was renamed to lib/getopt.in.h etc. + on 2011-01-08, but I forgot to remove the old file. + +2017-09-13 Mark Oteiza + + Remove "baroque" use of prefix argument from gensym + + 'cl-gensym' was simply moved here, but let us take an opportunity to + shed some historical baggage. + * lisp/subr.el (gensym): Remove special treatment of PREFIX as a + number. Use "g" as prefix to differentiate from cl-gensym defaults. + * doc/lispref/symbols.texi (Creating Symbols): Update accordingly. + * lisp/emacs-lisp/cl-macs.el (cl--gensym-counter, cl-gensym): Restore. + +2017-09-13 Mark Oteiza + + Provide an lcms2 feature + + * src/lcms.c (syms_of_lcms2): Provide "lcms2". + +2017-09-13 Mark Oteiza + + Add lcms2 interface + + configure.ac: Add boilerplate for configuring and detecting liblcms2. + etc/NEWS: Mention new configure option and color-distance change. + src/Makefile.in: Add references to lcms.c and liblcms. + src/emacs.c: Define lcms2 symbols. + src/lcms.c: New file. + src/lisp.h: Add declaration for lcms2. + src/xfaces.c: Add optional METRIC argument. + +2017-09-13 Mark Oteiza + + Add other D series white points and some simple conversions + + * lisp/color.el (color-d75-xyz, color-d55-xyz, color-d50-xyz): New + constants. + (color-xyz-to-xyy, color-xyy-to-xyz, color-lab-to-lch): + (color-lch-to-lab): New functions. + +2017-09-13 Mark Oteiza + + Permit non-integral color gradients + + * lisp/color.el (color-gradient): Float the step-number. + +2017-09-13 Katsumi Yamaoka + + Protect against malformed MIME messages that cause inf-loop (bugfix) + + * lisp/gnus/gnus-art.el (gnus-article-mime-handles): + Protect against malformed MIME messages that cause inf-loop. + +2017-09-13 Paul Eggert + + Merge from Gnulib + + This incorporates: + 2017-09-13 all: prefer https: URLs + This just changes http: to https: in comments, + in files copied from Gnulib. + +2017-09-13 Dmitry Gutov + + Call vc-resynch-buffer in vc-git-resolve-when-done + + * lisp/vc/vc-git.el (vc-git-resolve-when-done): + Call vc-resynch-buffer on the current file (bug#28121). + Move its autoload to before this function. + +2017-09-13 Eric Abrahamsen + + Allow write-contents-functions to short-circuit buffer save + + Bug#28412 + + * lisp/files.el (basic-save-buffer): Re-arrange function so that + write-contents-functions are run earlier. If they return non-nil, + consider the buffer saved without requiring the buffer to be + visiting a file. + (save-some-buffers): This function should consider any buffer with a + buffer-local value for write-contents-functions eligible for + saving. + * test/lisp/files-tests.el (files-test-no-file-write-contents): New + test. + * doc/lispref/files.texi (Saving Buffers): Mention in docs. + * etc/NEWS: And in NEWS. + +2017-09-12 Paul Eggert + + * etc/NEWS.25: Copy from emacs-25 etc/NEWS. + +2017-09-12 Paul Eggert + + Less chatter for ’make info/dir’ + + * Makefile.in (${srcdir}/info/dir): Tweak shell command so + that an ordinary make says just "GEN info/dir" rather than + also having a seemingly-unrelated mv line. + +2017-09-12 Paul Eggert + + Tweak Gnus doc re gnus-copy-file + + * doc/misc/gnus.texi (Saving Articles): + Document behavior with directory name targets (Bug#27986). + Problem reported by Katsumi Yamaoka in: + http://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00216.html + +2017-09-12 Mark Oteiza + + Update uses of if-let and when-let + + * lisp/dom.el (dom-previous-sibling): + * lisp/emacs-lisp/package.el (package--with-work-buffer): + (package--sort-deps-in-alist, package--sort-by-dependence): + (package-install-from-archive, package-install): + (package-menu-execute, package-menu--populate-new-package-list): + * lisp/filenotify.el (file-notify--rm-descriptor): + (file-notify--event-watched-file, file-notify--event-file-name): + (file-notify--event-file1-name, file-notify-rm-watch): + (file-notify-valid-p): + * lisp/gnus/message.el (message-toggle-image-thumbnails): + * lisp/gnus/nnimap.el (nnimap-request-move-article): + * lisp/ibuf-ext.el (ibuffer-repair-saved-filters): + * lisp/mpc.el (mpc-format): + * lisp/net/eww.el (eww-tag-meta, eww-process-text-input): + (eww-save-history): + * lisp/net/shr.el (shr-tag-base, shr-tag-object, shr-make-table-1): + * lisp/progmodes/prog-mode.el (prettify-symbols--post-command-hook): + * lisp/svg.el (svg-remove): + * lisp/textmodes/css-mode.el (css--named-color): + (css--colon-inside-funcall): + * lisp/textmodes/sgml-mode.el (html-current-buffer-classes): + (html-current-buffer-ids): Use if-let* and when-let* instead. + +2017-09-12 Mark Oteiza + + Implement and-let* + + This also includes changes to if-let and when-let. The single tuple + special case is ambiguous, and binding a symbol to nil is not as + useful as binding it to its value outside the lexical scope of the + binding. (Bug#28254) + * etc/NEWS: Mention. + * lisp/emacs-lisp/subr-x.el (internal--listify): + (internal--build-binding-value-form): Extend to account for + solitary symbols and (EXPR) items in binding varlist. + (if-let*, when-let*): Nix single tuple case and incumbent + bind-symbol-to-nil behavior. + (and-let*): New macro. + (if-let, when-let): Mark obsolete. Redefine in terms of if-let*, so + they implicitly gain the new features without breaking existing code. + * test/lisp/emacs-lisp/subr-x-tests.el: Adjust tests for: lack of + single-tuple special case, lack of binding solitary symbols to nil, + and the introduction of uninterned symbols for (EXPR) bindings. Add + SRFI-2 test suite adapted to Elisp. + +2017-09-12 Eli Zaretskii + + Fix minor typos in the Emacs manual + + * doc/emacs/text.texi (Org Organizer): + * doc/emacs/ack.texi (Acknowledgments): Fix spelling of Org nodes. + +2017-09-12 Paul Eggert + + Merge Emacs 25.3 fixes + + The security patches released for Emacs 25.3 were less drastic + than what we had immediately put into master. Adjust master to + match 25.3 (Bug#28350). + * lisp/textmodes/enriched.el (enriched-translations): + Re-enable FUNCTION and display translations that are safe. + (enriched-handle-display-prop): Bring back. + (enriched-decode-display-prop): Bring back, but disable + the unsafe part. + +2017-09-12 Alan Mackenzie + + Don't match C++ template delims starting within a token. FIxes bug #28418. + + * lisp/progmodes/cc-engine.el (c-restore-<>-properties): After failing an + attempted match from the start of a token (in particular, "<<"), move to the + next token rather than the nex character before searching for the next "<". + +2017-09-12 Mark Oteiza + + Move gensym to core Elisp + + * doc/lispref/symbols.texi (Creating Symbols): Mention gensym right + after make-symbol. + * etc/NEWS: Mention. + * lisp/emacs-lisp/cl-macs.el (cl--gensym-counter): Alias to + gensym-counter. + (cl-gensym): Alias to gensym. + * lisp/emacs-lisp/cl.el: Remove gensym from list of aliases. + * lisp/emacs-lisp/edebug.el (edebug-make-enter-wrapper): + * lisp/emacs-lisp/ert-x.el (ert-with-message-capture): + (ert--expand-should-1, ert--expand-should): + (ert--should-error-handle-error): + * lisp/emacs-lisp/generator.el (cps--gensym): + * lisp/emacs-lisp/gv.el (setf): + * lisp/emacs-lisp/inline.el (inline--do-letlisteval): + * lisp/emacs-lisp/pcase.el (pcase--make-docstring, pcase-dolist): + (pcase--funcall, pcase--u1): Use gensym. + * lisp/subr.el (gensym-counter): New variable. + (gensym): New function, assimilated from cl-lib. + +2017-09-12 Mark Oteiza + + Fix cl-gentemp + + * lisp/emacs-lisp/cl-macs.el (cl--gentemp-counter): New variable. + (cl-gentemp): Use it. Change prefix to "T". + +2017-09-12 Sam Steingold + + gnus-score-file-name: Do not append empty suffix. + +2017-09-12 Michael Albinus + + Extend tramp-tests according to bug#27986 + + * test/lisp/net/tramp-tests.el (tramp-test11-copy-file) + (tramp-test12-rename-file, tramp-test15-copy-directory) + (tramp-test21-file-links): Extend tests. + (tramp-test13-make-directory, tramp-test14-delete-directory): + Specifiy error symbol in `should-error'. + +2017-09-12 Mark Oteiza + + Add cl-print method for hash tables + + * lisp/emacs-lisp/cl-print.el (cl-print-object): New method. + +2017-09-12 Mark Oteiza + + Add docstrings to cl-print entry points + + * lisp/emacs-lisp/cl-print.el (cl-print-compiled): Fix docstring. + (cl-prin1, cl-prin1-to-string): Add docstrings. + +2017-09-12 Glenn Morris + + Improve reproducibility of generated leim-list.el + + * lisp/international/quail.el (quail-update-leim-list-file): + Sort the quail directory listing, for more stable output. + +2017-09-11 Mark Oteiza + + Include sxhash of object with printed bytecode + + This printing, while succint, is rather opaque. At least give an + immediate clue of whether different byte code printouts are for the + same or different byte code objects. + * lisp/emacs-lisp/cl-print.el (cl-print-object): Add object sxhash to + printed token "#". + +2017-09-11 Eli Zaretskii + + Update documentation of 'max-lisp-eval-depth' + + * doc/lispref/eval.texi (Eval): Update the documented default + value of 'max-lisp-eval-depth'. + +2017-09-11 Eli Zaretskii + + Another place to produce debugging output in etags + + * lib-src/etags.c (Ruby_functions): One more place to print + debugging output under --debug. + +2017-09-11 Eli Zaretskii + + Improve documentation of etags-related features + + * doc/emacs/maintaining.texi (Looking Up Identifiers): Document + 'xref-prompt-for-identifier'. (Bug#28403) + (Etags Regexps): Document \D back references in etags regexps. + +2017-09-11 Alan Third + + Fix macOS compatibility versions for vibrant dark theme (bug#28415) + + * src/nsterm.m (ns_set_appearance, EmacsView::initFrameFromEmacs): + Change macOS compatibility from 10.9 to 10.10. + +2017-09-11 Michael Albinus + + Further optimization in Tramp's file name decomposition + + * lisp/net/tramp.el (tramp-syntax): Recompute all file name + components. Call `custom-set-variables' after loading. + (tramp-build-prefix-format, tramp-build-prefix-regexp) + (tramp-build-method-regexp) + (tramp-build-postfix-method-format) + (tramp-build-postfix-method-regexp) + (tramp-build-prefix-ipv6-format) + (tramp-build-prefix-ipv6-regexp) + (tramp-build-postfix-ipv6-format) + (tramp-build-postfix-ipv6-regexp) + (tramp-build-postfix-host-format) + (tramp-build-postfix-host-regexp) + (tramp-build-file-name-regexp) + (tramp-build-completion-file-name-regexp): New defuns. + (tramp-prefix-format, tramp-prefix-regexp) + (tramp-method-regexp, tramp-postfix-method-format) + (tramp-postfix-method-regexp, tramp-prefix-ipv6-format) + (tramp-prefix-ipv6-regexp, tramp-postfix-ipv6-format) + (tramp-postfix-ipv6-regexp, tramp-postfix-host-format) + (tramp-postfix-host-regexp) + (tramp-remote-file-name-spec-regexp) + (tramp-file-name-structure, tramp-file-name-regexp) + (tramp-completion-file-name-regexp): Convert defuns into defvars. + (tramp-prefix-regexp-alist) + (tramp-postfix-method-regexp-alist) + (tramp-prefix-ipv6-regexp-alist) + (tramp-postfix-ipv6-regexp-alist) + (tramp-postfix-host-regexp-alist) + (tramp-remote-file-name-spec-regexp-alist): Remove. + (tramp-build-remote-file-name-spec-regexp) + (tramp-build-file-name-structure): Simplify. + (tramp-completion-file-name-regexp-alist): New defconst. + (tramp-tramp-file-p, tramp-dissect-file-name) + (tramp-make-tramp-file-name) + (tramp-completion-make-tramp-file-name) + (tramp-rfn-eshadow-update-overlay-regexp) + (tramp-register-file-name-handlers) + (tramp-completion-handle-file-name-all-completions) + (tramp-completion-dissect-file-name, tramp-clear-passwd): + * lisp/net/tramp-ftp.el (tramp-ftp-file-name-handler): + * lisp/net/tramp-sh.el (tramp-sh-handle-vc-registered) + (tramp-compute-multi-hops): Use variables but functions for + file name components. + + * test/lisp/net/tramp-tests.el (tramp-test24-file-name-completion): + Use variables but functions for file name components. + +2017-09-11 Paul Eggert + + Port tramp-tests to new copy-directory behavior + + * test/lisp/net/tramp-tests.el (tramp-test15-copy-directory): + Use directory name as arg for copy-directory when we want + the special behavior. + +2017-09-11 Paul Eggert + + Adjust thumbs to new rename-file behavior + + * etc/NEWS: Mention this. + * lisp/thumbs.el (thumbs-rename-images): Treat the destination + as special only if it is a directory name. When there is + a marked list, turn the destination into a directory name + if it is not already. + +2017-09-11 Paul Eggert + + Adjust ob-tangle to new copy-file behavior + + * lisp/org/ob-tangle.el (org-babel-tangle-publish): + Port to new copy-file behavior. + +2017-09-11 Paul Eggert + + Make gnus-copy-file act like copy-file etc. + + * etc/NEWS: Mention this. + * lisp/gnus/gnus-util.el (gnus-copy-file): Treat the destination + as special only if it is a directory name. + +2017-09-11 Paul Eggert + + Make write-file act like copy-file etc. + + Change write-file to be consistent with the new behavior + of copy-file, etc. + * etc/NEWS: Mention this. + * lisp/files.el (write-file): Treat the destination as special + only if it is a directory name. + +2017-09-11 Paul Eggert + + Make copy-directory act like copy-file etc. + + Do the special dance with the destination only if it is a + directory name, for consistency with copy-file etc. (Bug#27986). + * doc/emacs/files.texi (Copying and Naming): + * doc/lispref/files.texi (Create/Delete Dirs): + * etc/NEWS: Document this. + * lisp/files.el (copy-directory): Treat NEWNAME as special + only if it is a directory name. + +2017-09-11 Paul Eggert + + Fix some make-directory bugs + + * lisp/files.el (files--ensure-directory): New function. + (make-directory): Use it to avoid bugs when (make-directory FOO t) + is invoked on a non-directory, or on a directory hierarchy that + is being built by some other process while Emacs is running. + * test/lisp/files-tests.el (files-tests--make-directory): New test. + +2017-09-11 Paul Eggert + + Fix race with rename-file etc. with dir NEWNAME + + This changes the behavior of rename-file etc. slightly. + The old behavior mostly disagreed with the documentation, and had + a race condition bug that could allow attackers to modify victims' + write-protected directories (Bug#27986). + * doc/lispref/files.texi (Changing Files): Document that in + rename-file etc., NEWFILE is special if it is a directory name. + * etc/NEWS: Document the change in behavior. + * src/fileio.c (directory_like): Remove. All uses removed. + (expand_cp_target): Test only whether NEWNAME is a directory name, + not whether it is currently a directory. This avoids a race. + (Fcopy_file, Frename_file, Fadd_name_to_file, Fmake_symbolic_link): + Document behavior if NEWNAME is a directory name. + (Frename_file): Simplify now that the destdir behavior occurs + only when NEWNAME is a directory name. + * test/lisp/net/tramp-tests.el (tramp-test11-copy-file) + (tramp-test12-rename-file, tramp--test-check-files): + Adjust tests to match new behavior. + +2017-09-10 Eli Zaretskii + + Extend --debug printouts in etags + + * lib-src/etags.c (regex_tag_multiline, readline): Under + "--debug", print tags found via regexps. + +2017-09-10 Eli Zaretskii + + Add --debug option to etags + + * lib-src/etags.c (make_tag): Print found tags under --debug. + (longopts): Add --debug. + +2017-09-10 Paul Eggert + + Spelling fixes + + * lisp/progmodes/cc-langs.el: + (c-ambiguous-overloadable-or-identifier-prefixes): Rename from + c-ambiguous-overloadable-or-identifier-prefices. Caller changed. + +2017-09-10 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-09-08 stddef: Avoid conflict with system-defined max_align_t + 2017-08-24 warnings: fix compilation with old autoconf + 2017-08-23 glob: merge from glibc with Zanella glob changes + 2017-08-17 random: Fix test compilation failure on Cygwin 1.5.25 + * doc/misc/texinfo.tex, lib/flexmember.h, lib/stddef.in.h: + * lib/stdlib.in.h, m4/manywarnings.m4, m4/stdlib_h.m4: + * m4/warnings.m4: + Copy from Gnulib. + * lib/gnulib.mk.in: Regenerate. + +2017-09-10 Ken Brown + + Implement renameat_noreplace on recent Cygwin + + * src/sysdep.c [CYGWIN]: Include cygwin/fs.h. + (renameat_noreplace) [RENAME_NOREPLACE]: Use renameat2. + (Bug#27986) + +2017-09-10 Eli Zaretskii + + Avoid warnings about file names in autoloads on MS-Windows + + * configure.ac (srcdir) [mingw32]: Downcase the drive letter, to + avoid warnings from find-file-noselect when making autoloads. For + the details, see + http://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00049.html. + +2017-09-10 Mark Oteiza + + Avoid looking at localized strings + + * lisp/xdg.el (xdg-desktop-read-group): Add condition to catch + localized strings. + * test/lisp/xdg-tests.el (xdg-desktop-parsing): Add test to ensure + parsing l10n strings doesn't error but is essentially a no-op. + +2017-09-10 Paul Eggert + + * etc/NEWS.25: Document 25.3 changes. + +2017-09-10 Lars Ingebrigtsen + + Remove unsafe enriched mode translations + + * lisp/gnus/mm-view.el (mm-inline-text): + Do not worry about enriched or richtext type. + * lisp/textmodes/enriched.el (enriched-translations): + Remove translations for FUNCTION, display (Bug#28350). + (enriched-handle-display-prop, enriched-decode-display-prop): Remove. + +2017-09-09 Paul Eggert + + Be more consistent about "directory name" in manual + + This clarifies the documentation, partly in response to the + discussion in Bug#27986. + +2017-09-09 Eli Zaretskii + + Remove more compilation warnings in MinGW64 build + + * src/w32.c (faccessat, map_w32_filename): + * src/w32fns.c (w32_wnd_proc): + * src/w32term.c (w32_horizontal_scroll_bar_handle_click) + (w32_scroll_bar_handle_click): Use FALLTHROUGH to avoid compiler + warnings with GCC 7 and later. + +2017-09-09 Paul Eggert + + Improve --enable-gcc-warnings for MinGW64 + + This partially reverts my 2016-05-30 patch. Apparently MinGW64 + still requires pacifications that GCC 7.1.1 x86-64 (Fedora 26) + does not. Also, pacify tparam.c, which isn’t used on Fedora. + * lib-src/etags.c (process_file_name, TeX_commands): + * src/buffer.c (fix_overlays_before): + * src/data.c (Fmake_variable_buffer_local, cons_to_unsigned) + (cons_to_signed): + * src/editfns.c (Ftranslate_region_internal): + Prefer UNINIT to some stray value, as this simplifies + code-reading later. + * src/eval.c (CACHEABLE): New macro. + (internal_lisp_condition_case): Use it. + * src/tparam.c (tparam1): Use FALLTHROUGH to pacify GCC. + +2017-09-09 Eli Zaretskii + + Fix font-lock in Compilation mode + + * lisp/progmodes/compile.el (compilation-face): Restore function + lost during recent changes. (Bug#28349) + +2017-09-09 Mark Oteiza + + Add function to read all entries in a group + + Use that to extend xdg-desktop-read-file. Also fix a bug where all + entries in all groups were read and returned by xdg-desktop-read-file. + * lisp/xdg.el (xdg-desktop-read-group): New function. + (xdg-desktop-read-file): Use it. + * test/data/xdg/malformed.desktop: New file. + * test/data/xdg/test.desktop: Add another section. + * test/lisp/xdg-tests.el (xdg-desktop-parsing): Test presence of a key + in another group. Test reading a prescribed group. Test detecting a + malformed key=value. + +2017-09-09 Gemini Lasswell + + Reduce Tramp's memory usage + + Construct Tramp syntax strings and regular expressions once instead + of every time they are used, and store them in alists keyed by Tramp + syntax. + * tramp.el (tramp-build-remote-file-name-spec-regexp) + (tramp-build-file-name-structure): New functions. + (tramp-prefix-format-alist, tramp-prefix-regexp-alist) + (tramp-method-regexp-alist) + (tramp-postfix-method-format-alist) + (tramp-postfix-method-regexp-alist) + (tramp-prefix-ipv6-format-alist, tramp-prefix-ipv6-regexp-alist) + (tramp-postfix-ipv6-format-alist) + (tramp-postfix-ipv6-regexp-alist) + (tramp-postfix-host-format-alist) + (tramp-postfix-host-regexp-alist) + (tramp-remote-file-name-spec-regexp-alist) + (tramp-file-name-structure-alist): New constants. + (tramp-lookup-syntax): New function. + (tramp-prefix-format, tramp-prefix-regexp, tramp-method-regexp) + (tramp-postfix-method-format, tramp-postfix-method-regexp) + (tramp-prefix-ipv6-format, tramp-prefix-ipv6-regexp) + (tramp-postfix-ipv6-format, tramp-postfix-ipv6-regexp) + (tramp-postfix-host-format, tramp-postfix-host-regexp) + (tramp-remote-file-name-spec-regexp, tramp-file-name-structure): + Use it. + +2017-09-09 Eli Zaretskii + + Fix compilation warnings in MinGW64 build using GCC 7 + + Reported by Richard Copley . + * src/w32heap.c (init_heap): Declare enable_lfh only for + mingw.org's MinGW build. + + * src/w32console.c (w32con_write_glyphs): + * src/unexw32.c (get_section_info, COPY_CHUNK, unexec): Fix some + mismatches of data type vs format spec. + + * src/w32fns.c (compute_tip_xy): + * src/w32proc.c (stop_timer_thread): + * src/w32notify.c (remove_watch): + * src/eval.c (internal_lisp_condition_case): + * src/editfns.c (Ftranslate_region_internal): + * src/data.c (Fmake_variable_buffer_local, cons_to_unsigned) + (cons_to_signed): + * src/buffer.c (fix_overlays_before): Initialize variables to + avoid compiler warnings. + + * lib-src/etags.c (TeX_commands, process_file_name): Initialize + variables to avoid compilation warnings. + +2017-09-09 Eli Zaretskii + + Avoid infloop when scrolling under scroll-preserve-screen-position + + * src/window.c (window_scroll_pixel_based): If screen position is + to be preserved, make sure its recorded Y coordinate is outside + the scroll margin. (Bug#28342) + +2017-09-09 Michael Albinus + + Clarification in tramp-texi + + * doc/misc/tramp.texi (Connection caching): Two connections are + regarded as different now when they differ in the port number only. + +2017-09-09 Miles Bader + + * admin/quick-install-emacs: Tweak configure.ac parsing + +2017-09-09 Miles Bader + + Use text-property buttons in rcirc-markup-urls + + * lisp/net/rcirc.el (rcirc-markup-urls): Use `make-text-button' + instead of `make-button'; the former is much more efficient in large + buffers, and for the purposes of rcirc, changes no functionality. + +2017-09-08 Eli Zaretskii + + Fix line-pixel-height for lines of variable height + + * src/xdisp.c (Fline_pixel_height): Start moving from the + beginning of the screen line, to capture the full metrics of the + line. (Bug#28391) + +2017-09-08 Alex Branham (tiny change) + + New variable 'dired-confirm-killing-deleted-buffers' + + * lisp/dired-x.el (dired-clean-confirm-killing-deleted-buffers): + New variable. + * lisp/dired.el (dired-clean-up-after-deletion): Kill buffers + visiting deleted files without confirming if + dired-clean-confirm-killing-deleted-buffers is nil. (Bug#28373) + * etc/NEWS: Document the change. + +2017-09-08 Alfred M. Szmidt (tiny change) + + Support SVN files with svn:externals property + + * lisp/vc/vc-svn.el (vc-svn-parse-status): Don't ignore files + marked with the svn:externals property. + +2017-09-08 Alfred M. Szmidt (tiny change) + + List locally removed files in vc-dir with SVN back-end + + * lisp/vc/vc-svn.el (vc-svn-after-dir-status): List files marked + with ?! as needs-update. + +2017-09-08 Ken Olum + + Fix Rmail editing with reapplying encoding to message body + + * lisp/mail/rmailedit.el (rmail-cease-edit): If no + content-type in edited headers, look for one in original + headers and add it to edited headers. (Bug #26918) + Use a marker to track start of new body, so that + content-transfer-encoding gets applied only to body. (Bug #27353). + Ensure blank line at end of message after encoding, not + before. + +2017-09-08 Eli Zaretskii + + Document last change in dired.el + + * etc/NEWS (Dired): Document the last change in dired.el. + (Bug#27435) + +2017-09-08 Tak Kunihiro + + Make mouse clicks in Dired more customizable + + * lisp/dired.el (dired-mouse-find-file): Allow callers to + specify functions to visit file/directory. + (dired-mouse-find-file-other-window) + (dired-mouse-find-file-other-frame): New functions to visit + files in another window/frame. (Bug#27435) + +2017-09-08 Eli Zaretskii + + Avoid compiler warnings on MS-Windows with GCC 6 and 7 + + * src/w32font.c (SUBRANGE): Use unsigned arithmetic for + bit-shifting, to avoid compiler warnings. + (w32font_text_extents): Tell GCC NGLYPHS is non-negative, to avoid + a warning. For details of the warning, see + http://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00093.html. + * src/term.c (keys) [WINDOWSNT]: Don't define, as it is not used + in that build. + * src/sound.c (sound_perror): Ifdef away on WINDOWSNT, as this + function is not used in that build. + + * configure.ac: Disable -Wsuggest-attribute=format on MS-Windows. + +2017-09-08 Eli Zaretskii + + Fix 'directory-file-name' on DOS_NT systems as well + + * src/fileio.c (directory_file_name) [DOS_NT]: Fix the DOS_NT case + to be consistent with last change. + + * test/src/fileio-tests.el (fileio-tests--odd-symlink-chars): + Disable on MS-Windows. + (fileio-tests--directory-file-name-dos-nt) + (fileio-tests--file-name-as-directory-dos-nt): New tests. + +2017-09-08 Wilson Snyder + + Fix various verilog-mode.el issues. + + * lisp/progmodes/verilog-mode.el (verilog-expand-dirnames): Fix expanding + "*/*", msg2284. Reported by Jonathan Ferguson. + (ignore-errors): Fix ignore-errors error on Emacs 22.3, bug1177. Reported + by Victor Lau. + (verilog-getopt, verilog-getopt-file) (verilog-library-flags, + verilog-substitute-file-name-path): Support -F in verilog getopt files, + bug1171. Reported by George Cuan. + (verilog-do-indent): Fix misindenting symbols starting with t, + bug1169. Reported by Hoai Tran. + (verilog-read-auto-template-middle): Fix slow template matching on + AUTOINST. Reported by Jeffrey Huynh. + (verilog-pretty-expr): The extra whitespace addition before "=" operators + is now done only if the whole assignment block contains the 2-character + "<=" operator. Remove the unused argument _myre. Use `unless', + `save-excursion' and `when' functions where possible. Internal variables + refactored for clarity. Follow elisp convention for closing parentheses. + By Kaushal Modi. + (verilog-get-lineup-indent-2): Update docstring. Internal variables + refactored for clarity. Earlier EDPOS argument was expected to be a + marker; it is now renamed to END and is now expected to be a position. + Use `when' instead of `if'. By Kaushal Modi. + (electric-verilog-terminate-line): Remove the unused second argument from + `verilog-pretty-expr' call. By Kaushal Modi. + (verilog-calc-1): Fix indentation of a virtual class definition after a + typedef class, bug1080. By Kaushal Modi. + +2017-09-08 Katsumi Yamaoka + + Don't use summary window to visit group buffer (bugfix) + + * lisp/gnus/gnus-sum.el (gnus-summary-jump-to-group): Make sure that + the window to open the group buffer doesn't visit the summary buffer. + This fixes a bug: `gnus-summary-next-article' sometimes causes an error + by trying to select nonexistent summary window. + +2017-09-08 Paul Eggert + + Fix bug: (directory-file-name "///") returned "//" + + * src/fileio.c (directory_file_name): For "///" and longer, + return "/", not "//", as per POSIX. + * test/src/fileio-tests.el (fileio-tests--directory-file-name) + (fileio-tests--file-name-as-directory): New tests. + +2017-09-08 Paul Eggert + + Remove obsolete vc-mistrust-permissions doc + + * doc/emacs/vc1-xtra.texi (RCS and SCCS): Remove documentation + for vc-mistrust-permissions, which no longer exists. + +2017-09-07 Alan Third + + Set frame size to actual requested size (bug#18215) + + * src/nsterm.m (x_set_window_size): Don't use + FRAME_TEXT_TO_PIXEL_WIDTH or FRAME_TEXT_TO_PIXEL_HEIGHT. + +2017-09-07 Paul Eggert + + autogen.sh: omit bogus chatter if no .git + + Problem reported by Angelo Graziosi in: + http://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00045.html + * autogen.sh (git_config): Do not execut 'git' if $do_git fails. + +2017-09-07 Glenn Morris + + Skip emacsclient tests if --enable-profiling was used + + * test/lib-src/emacsclient-tests.el + (emacsclient-test-call-emacsclient): Make it a macro. + Handle "Profiling timer expired" return from emacsclient. (Bug#28319) + (emacsclient-test-alternate-editor-allows-arguments) + (emacsclient-test-alternate-editor-allows-quotes): Update for above. + +2017-09-06 Eli Zaretskii + + Fix a minor markup problem in ELisp manual + + * doc/lispref/functions.texi (Mapping Functions): Fix the order of + @example and @group. For the details, see + http://lists.gnu.org/archive/html/bug-texinfo/2017-09/msg00007.html. + +2017-09-06 Mark Oteiza + + Add XDG desktop file parsing and tests + + * lisp/xdg.el: Add support for Desktop Entry Specification. + (xdg--user-dirs-parse-line): Check if file is readable. + (xdg-desktop-group-regexp, xdg-desktop-entry-regexp): New variables. + (xdg--desktop-parse-line, xdg-desktop-read-file, xdg-desktop-strings): + New functions. + * test/lisp/xdg-tests.el: + * test/data/xdg/test.desktop: + * test/data/xdg/wrong.desktop: New files. + +2017-09-06 Glenn Morris + + Allow for adjusting line length of test backtraces + + * test/Makefile.in (TEST_BACKTRACE_LINE_LENGTH): New option. + (%.log): Respect backtrace line length. + +2017-09-06 Glenn Morris + + Allow customizing line length of ert backtraces in batch mode + + * lisp/emacs-lisp/ert.el (ert-batch-backtrace-right-margin): + Make it a user option. + (ert-run-tests-batch): Handle ert-batch-backtrace-right-margin nil. + +2017-09-06 Glenn Morris + + Minor emacsclient-tests simplification + + * test/lib-src/emacsclient-tests.el (emacsclient-test-emacs): + Simplify. Also work when running installed. + +2017-09-06 Alan Third + + Revert "Force screen update after drawing cursor glyph (bug#23774)" + + This reverts commit 1b492fa5456e2b6face8d0856f11d17e432693b0. + + See bug#28358 + +2017-09-05 Mark Oteiza + + Refactor some loops in mailcap.el + + * lisp/net/mailcap.el (mailcap-mime-types): + (mailcap-file-default-commands): Convert nested maps to loops. + +2017-09-05 Glenn Morris + + emacsclient-tests: remove some debug statements + + * test/lib-src/emacsclient-tests.el + (emacsclient-test-call-emacsclient): Remove debug statements. + +2017-09-05 Simen Heggestøyl + + Handle non-zero exit status from psql more gracefully + + * lisp/progmodes/sql.el (sql-postgres-list-databases): Handle non-zero + exit statuses from `psql -ltX' more gracefully by returning nil. + + * test/lisp/progmodes/sql-tests.el + (sql-tests-postgres-list-databases-error): New test. + +2017-09-05 Eli Zaretskii + + Avoid losing Ctrl-C keystrokes in compilation mode on MS-Windows + + * src/w32proc.c (sys_kill): Preserve the up/down state of the + Ctrl key across the simulated Ctrl-C keystroke. (Bug#28348) + +2017-09-05 Andreas Schwab + + * src/image.c (Fimagemagick_types): Doc fix. + +2017-09-05 Mark Oteiza + + Move soundex.el test to a proper test + + * test/lisp/soundex-tests.el: New file. + * lisp/soundex.el: Use lexical-binding. Remove commented test. + +2017-09-05 Mark Oteiza + + Add tests for mailcap.el + + * test/data/mailcap/mime.types: New file. + * test/lisp/net/mailcap-tests.el: New file. + +2017-09-05 Michael Albinus + + Doc precisment about remote link targets + + * doc/lispref/files.texi (Truenames): Explain handling of + targets of `file-truename' and `make-symbolic-link', which + look like a remote file name. + + * etc/NEWS: Precise examples for symlinks which look like + remote file names. MUSTBENEW of `write-region' is not + propagated to file name handlers. + +2017-09-05 John Wiegley + + Remove an opinionated section on "What Eshell is not" + + I don't find this information to accurately reflect possible use cases + for Eshell; plus, it doesn't offer much in the way of information, + just opinion. + +2017-09-05 Ken Brown + + Fix configure test for Xpm + + Problem reported by Ashish Shukla in + https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00020.html. + * configure.ac (HAVE_XPM) [HAVE_X11]: Include X11/xpm.h instead of + noX/xpm.h in configure test. + +2017-09-04 Paul Eggert + + Revert recent float→double Motif change + + Problem reported by Martin Rudalics in: + http://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00014.html + * src/xterm.c (xm_scroll_callback, xaw_jump_callback) + (x_set_toolkit_scroll_bar_thumb) + (x_set_toolkit_horizontal_scroll_bar_thumb): + Go back to using ‘float’ temporaries rather than ‘double’. + Although quite possibly this masks an underlying bug, + we lack time to look into that now. + +2017-09-04 Glenn Morris + + emacsclient-tests: add some debug statements + + * test/lib-src/emacsclient-tests.el + (emacsclient-test-call-emacsclient): Add debug statements. + +2017-09-04 Michael Albinus + + Work on Tramp's (symbolic) links + + * doc/misc/tramp.texi (Traces and Profiles): Mention the + backtrace when tramp-verbose is greater than or equal to 10. + + * lisp/net/tramp-adb.el (tramp-adb-file-name-handler-alist): + Use `tramp-handle-add-name-to-file'. + + * lisp/net/tramp-gvfs.el (tramp-gvfs-file-name-handler-alist): Use + `tramp-handle-add-name-to-file' and `tramp-handle-file-truename'. + + * lisp/net/tramp-sh.el (tramp-sh-handle-make-symbolic-link): Improve. + + * lisp/net/tramp-smb.el (tramp-smb-errors): + Add "NT_STATUS_CONNECTION_DISCONNECTED" and + "NT_STATUS_OBJECT_PATH_SYNTAX_BAD". + (tramp-smb-file-name-handler-alist): Use `tramp-handle-file-truename'. + (tramp-smb-do-file-attributes-with-stat): Return non-nil only + if one of the attributes is non-nil. + (tramp-smb-handle-file-local-copy): Use `file-truename'. + (tramp-smb-handle-file-truename): Move to tramp.el. + (tramp-smb-handle-insert-directory): Show symlinks. + (tramp-smb-handle-make-symbolic-link): Improve. + (tramp-smb-read-file-entry): Handle extended file modes in Samba. + + * lisp/net/tramp.el (tramp-handle-add-name-to-file) + (tramp-handle-file-truename): New defuns. + + * test/lisp/net/tramp-tests.el (tramp-test21-file-links): Extend test. + (tramp--test-check-files): Make check for "smb". + +2017-09-04 Mark Oteiza + + Embed JSON readtable into json-read + + Also unroll dispatch into a cond. + * lisp/json.el (json-readtable): Remove. + (json-readtable-dispatch): New macro. Assimilate json-readtable. + (json-read): Use the macro. + +2017-09-04 Mark Oteiza + + Hexify strings in EWW search queries + + Previously, inputting "cats & dogs" would lose dogs because the + ampersand signifies a query parameter. Instead, hexify each word while + preserving quotes with split-string. + * lisp/net/eww.el (eww--dwim-expand-url): Join hexified words together + with + separators, instead of replacing whitespace with +. + +2017-09-03 Glenn Morris + + emacsclient-tests: call-process may return non-integer + + * test/lib-src/emacsclient-tests.el + (emacsclient-test-alternate-editor-allows-arguments) + (emacsclient-test-alternate-editor-allows-quotes): + Handle non-integer return from call-process. + +2017-09-03 Eli Zaretskii + + * lisp/simple.el (visual-line-mode): Doc fix. (Bug#28337) + +2017-09-03 Alan Third + + Force screen update after drawing cursor glyph (bug#23774) + + * src/nsterm.m (ns_draw_window_cursor): Force a screen update after + drawing the glyph over the cursor. + +2017-09-03 Alan Mackenzie + + Correct the fontification of quote marks after buffer changes in CC Mode. + + * lisp/progmodes/cc-defs.el + (c-search-forward-char-property-with-value-on-char): New macro. + + * lisp/progmodes/cc-mode.el (c-parse-quotes-before-change) + (c-parse-quotes-after-change): Rewrite the functions, simplifying + considerably, and removing unnecessary optimisations. Invalidate two caches + after manipulating text properties. + +2017-09-03 Alan Mackenzie + + Fix fontification of "operator~" in C++ Mode. + + * lisp/progmodes/cc-langs.el (c-ambiguous-overloadable-or-identifier-prefices) + (c-ambiguous-overloadable-or-identifier-prefix-re): New c-lang-defconsts/vars. + + * lisp/progmodes/cc-engine.el (c-forward-name): Do not try to parse "~" (and + two other symbols) as a cast without good evidence. Prefer an overloaded + operator in ambiguous cases. + +2017-09-03 Martin Rudalics + + In delete_frame do not delete terminal for any toolkit build + + * src/frame.c (delete_frame): Neither delete terminal for + non-GTK toolkit builds (Bug#5802, Bug#21509, Bug#23499, + Bug#27816). + +2017-09-02 Philipp Stephani + + Improve error messages for improper plists (Bug#27726) + + * src/fns.c (Fplist_put, Flax_plist_get, Flax_plist_put) + (Fplist_member, syms_of_fns): Use ‘plistp’ as pseudo-predicate for + improper plists instead of ‘listp.’ + + * test/src/fns-tests.el (plist-get/odd-number-of-elements) + (lax-plist-get/odd-number-of-elements) + (plist-put/odd-number-of-elements) + (lax-plist-put/odd-number-of-elements) + (plist-member/improper-list): Add unit tests. + +2017-09-02 Eli Zaretskii + + Fix decrypting in plstore.el on MS-Windows + + * lisp/plstore.el (plstore-open): Bind coding-system-for-read to + raw-text, instead of using insert-file-contents-literally. + (Bug#28114) + +2017-09-02 Eli Zaretskii + + * src/fileio.c (Fexpand_file_name): Doc fix. (Bug#27982) + +2017-09-02 Eli Zaretskii + + Rewrite Antinews for Emacs 26 + + * doc/lispref/anti.texi (Antinews): Rewrite for Emacs 26. + * doc/lispref/elisp.texi (Top): Update the top-level menu's + Antinews entry. + * doc/emacs/anti.texi (Antinews): Rewrite for Emacs 26. + * doc/emacs/emacs.texi (Top): Update the top-level menu's Antinews + entry. + + * etc/NEWS: Rearrange some entries in a more reasonable order. + +2017-09-02 Reuben Thomas + + Fix a mis-binding in a test + + * test/lisp/progmodes/python-tests.el + (python-shell-calculate-process-environment-3): Fix binding of + process-environment. A level of parens was missing. + + This was found after Glenn Morris noticed a similar problem with the + patch for Bug#28319. + +2017-09-02 Reuben Thomas + + Fix a mis-binding and a bad defun name in a test (Bug#28319) + + test/lib-src/emacs-client-tests.el (call-emacsclient): Rename + emacsclient-test-call-emacsclient. + (emacsclient-test-alternate-editor-allows-arguments) + (emacsclient-test-alternate-editor-allows-quotes): Fix let-binding of + process-environment. + + Thanks to Glenn Morris for noticing these errors. + +2017-09-02 Glenn Morris + + * test/Makefile.in (check-no-automated-subdir): Silence by default. + + * test/Makefile.in (ELFILES): Sort, for a reproducible order. + +2017-09-01 Mark Oteiza + + Turn off checkdoc complaint about default argument order + + * etc/NEWS: Mention change. + * lisp/emacs-lisp/checkdoc.el (checkdoc-arguments-in-order-flag): + Disable by default, note version. + +2017-09-01 Reuben Thomas + + Stop emacsclient tests hanging (Bug#28319) + + * test/lib-src/emacsclient-tests.el + (emacsclient-test-alternate-editor-allows-arguments): Use a + non-existent file to communicate with server, so that any existing + default server will not be hijacked (in fact, the test does + not need a server). + (emacsclient-test-alternate-editor-allows-quotes): Likewise. + +2017-09-01 Stefan Monnier + + * lisp/obsolete/html2text.el: Don't require CL + + (html2text-clean-anchor): Mark unused arg. + +2017-09-01 Katsumi Yamaoka + + Don't remove undisplayers from inlined MIME parts (bugfix) + + * lisp/gnus/gnus-art.el (gnus-mime-buttonize-attachments-in-header): + Don't remove undisplayers from inlined MIME parts (bugfix); + Simplify criterion that finds attachments. + +2017-08-31 Mark Oteiza + + Make ucs-names a hash table (Bug#28302) + + * etc/NEWS: Mention the type change. + * lisp/descr-text.el (describe-char): Use gethash to access ucs-names. + Hardcode BEL's name into the function instead of needlessly mapping + over the hash table in the spirit of rassoc. + * lisp/international/mule-cmds.el (ucs-names): Fix variable and + function docstrings. Initialize a hash table for ucs-names--the + number of entries is 42845 here. Switch to hash-table + getters/setters. + (mule--ucs-names-annotation): Use hash-table getter. + (char-from-name): Upcase the string if ignore-case is truthy. + * lisp/leim/quail/latin-ltx.el: Use maphash instead of dolist. + +2017-08-31 Alan Third + + Remove unneeded version checks (bug#28222) + + * src/macfont.h (CGContextSetFontSmoothingStyle): Remove version + check. + * src/macfont.m (macfont_draw): Remove version check, and test for + existence of CGContextSetFontSmoothingStyle. + +2017-08-31 Alan Mackenzie + + Fix a glitch in CC Mode's syntactic whitespace cache. + + * lisp/progmodes/cc-engine.el (c-forward-sws): Deal correctly with a block + comment close at the end of a macro. + +2017-08-31 Alan Mackenzie + + Correct the fontification of C++ Mode enclosed declarations. + + * lisp/progmodes/cc-fonts.el (c-font-lock-enclosing-decls): abolish the + spurious check that the character before the start of an enclosed declaration + must be ; or }. It might also be {. + +2017-08-31 Martin Rudalics + + In xterm.c fix some recently introduced compiler warnings + + * src/xterm.c (xaw_jump_callback) + (x_set_toolkit_scroll_bar_thumb): Fix some recently introduced + -Wdouble-promotion warnings. + +2017-08-31 Martin Rudalics + + Restrict fix of Bug#24963 and Bug#25887 to GTK builds + + * src/xterm.c (handle_one_xevent): Restrict earlier fix of + Bug#24963 and Bug#25887 to avoid that a non-GTK Emacs won't + react to state changes received via ConfigureNotify. + +2017-08-31 Katsumi Yamaoka + + Respect directory a user enters (bug#28299) + + * lisp/gnus/mm-decode.el (mm-save-part): + Respect directory a user enters (bug#28299). + +2017-08-31 Samuel Freilich + + Do not split line before width of fill-prefix + + When auto-filling a paragraph, don't split a line before the width of the + fill-prefix, creating a subsequent line that is as long or longer (Bug#20774). + * lisp/simple.el (do-auto-fill): Only consider break-points that are later in + the line than the width of the fill-prefix. This is a more general solution + than the previous logic, which only skipped over the exact fill-prefix. The + fill-prefix doesn't necessarily match the prefix of the first line of a + paragraph in adaptive-fill-mode. + +2017-08-31 Noam Postavsky + + Support lazy loading for autogenerated usage docstrings too (Bug#27748) + + * lisp/emacs-lisp/bytecomp.el (byte-compile-file-form-defmumble): + Consider any documentation that ended up in code as a docstring (e.g., + autogenerated (fn ARG1 ARG2) type things), not just what the user + passed. + +2017-08-31 Noam Postavsky + + Drop docstrings from cl-defsubst produced inline bodies (Bug#27748) + + * lisp/emacs-lisp/cl-macs.el (cl-defsubst): Use macroexp-parse-progn + to drop the docstring. Add a simple docstring to the compiler-macro. + +2017-08-30 Paul Eggert + + Quote file-truename symlink to "../foo:bar:" + + Problem reported by Michael Albinus (Bug#28264#19). + * lisp/files.el (files--splice-dirname-file): Fix bug where + a relative symlink to "../foo:bar:" did not quote the result. + +2017-08-30 Reuben Thomas + + Add support for arguments in emacsclient's ALTERNATE_EDITOR (Bug #25082) + + * lib-src/emacsclient.c (fail): Parse ALTERNATE_EDITOR, or + corresponding command-line argument, into quote- or space-separated + tokens. If a token starts with a quote, then it naturally is expected + to end with a quote; escaping is not supported. This is enough to cope + with the typical case of requiring the initial path to be quoted, + common on Windows where it may contain spaces. + * etc/NEWS: Document. + * doc/emacs/misc.texi: Likewise. + * doc/man/emacsclient.1: Tweak to remove the implication that only an + editor can be specified (the manual already mentions a “command”). + Fix a small error where “EDITOR” is referred to rather than + “ALTERNATE_EDITOR”. + * test/lib-src/emacsclient-tests.el: Add tests. + +2017-08-30 Stefan Monnier + + * lisp/man.el (Man-softhyphen-to-minus): Avoid string-as-multibyte. + +2017-08-30 Devon Sean McCullough (tiny change) + + Correct "hide others" shortcut on macOS (bug#28215) + + * lisp/term/ns-win.el: Fix shortcut for ns-do-hide-others. + +2017-08-30 Eli Zaretskii + + Sync NEWS with the documentation + + * etc/NEWS: Mark entries according to documentation. + + * doc/lispref/functions.texi (Mapping Functions): Document 'mapcan'. + +2017-08-30 Michael Albinus + + Improve symlinks for Tramp + + * lisp/files.el (files--splice-dirname-file): Quote whole file. + + * lisp/net/tramp-sh.el (tramp-sh-handle-make-symbolic-link): + Do not expand TARGET, it could be remote. + (tramp-sh-handle-file-truename): Check for cyclic symlink also + in case of readlink. Quote result if it looks remote. + (tramp-sh-handle-file-local-copy): Use `file-truename'. + + * test/lisp/net/tramp-tests.el (tramp-test08-file-local-copy) + (tramp-test09-insert-file-contents): Test also file missing. + (tramp-test21-file-links): Extend test. + +2017-08-30 Martin Rudalics + + Preserve display's foreground color when clearing internal borders (Bug#28278) + + * src/xterm.c (x_after_update_window_line): Preserve display's + foreground color when clearing internal borders (Bug#28278). + +2017-08-30 Noam Postavsky + + Use cl-print for all values printed by `describe-variable' + + * lisp/help-fns.el (describe-variable): Use cl-prin1 for original and + global values too. + +2017-08-30 Noam Postavsky + + Minor simplification for byte-compile-constant-push + + * lisp/emacs-lisp/bytecomp.el (byte-compile-constant): Move the meat + of the code from here... + (byte-compile-constant-push): ... to here. No need to bind + byte-compile--for-effect anymore. + +2017-08-30 Paul Eggert + + Prefer file-name-quote to concat "/:" + + Suggested by Michael Albinus (Bug#28264#13). + * lisp/files.el (files--splice-dirname-file): Use file-name-quote + rather than attempting to do it by hand. + +2017-08-30 Paul Eggert + + * configure.ac: fix typo in previous change + +2017-08-29 Paul Eggert + + Be more conservative in link time optimization doc + + While testing --enable-link-time-optimization with GCC 7.1.1 + I ran into a serious GCC code-generation bug which makes me + think that --enable-link-time-optimization should be + discouraged for typical installs (Bug#28213). See: + https://bugzilla.redhat.com/show_bug.cgi?id=1486455 + +2017-08-29 Paul Eggert + + Make garbage collection more conservative + + Check for a pointer anywhere within the object, as opposed to just + the start of the object. This is needed for gcc -Os -flto on + x86-64 (Bug#28213). This change means that the garbage collector + is more conservative, and will incorrectly keep objects that it + does not need to, but that is better than incorrectly discarding + objects that should be kept. + * src/alloc.c (ADVANCE, VINDEX): Now functions, not macros; + this is easier to debug. + (setup_on_free_list): Rename from SETUP_ON_FREE_LIST. + Now a function with two args, not a macro with three. + All callers changed. + (live_string_holding, live_cons_holding, live_symbol_holding) + (live_misc_holding, live_vector_holding, live_buffer_holding): + New functions, which check for any object containing the addressed + byte, not just for an object at the given address. + (live_string_p, live_cons_p, live_symbol_p, live_misc_p) + (live_vector_p, live_buffer_p): + Redefine in terms of the new functions. + (live_float_p): Refactor slightly to match the new functions. + (mark_maybe_object, mark_maybe_pointer): Use the new functions. + Don’t bother checking mark bits, as mark_object already does that, + and omitting the checks here simplifies the code. Although + mark_maybe_object can continue to insist that tagged pointers + still address the start of the object, mark_maybe_pointer now is + more conservative and checks for pointers anywhere into an object. + +2017-08-29 Paul Eggert + + Improve stack-top heuristic + + This is needed for gcc -Os -flto on x86-64; otherwise, GC misses part + of the stack when scanning for heap roots, causing Emacs to crash + later (Bug#28213). The problem is that Emacs's hack for getting an + address near the stack top does not work when link-time optimization + moves stack variables around. + * configure.ac (HAVE___BUILTIN_FRAME_ADDRESS): New macro. + * lib-src/make-docfile.c (DEFUN_noinline): New constant. + (write_globals, scan_c_stream): Support noinline. + * src/alloc.c (NEAR_STACK_TOP): New macro. + (SET_STACK_TOP_ADDRESS): Use it. + (flush_stack_call_func, Fgarbage_collect): Now noinline. + +2017-08-29 Paul Eggert + + Align stack bottom properly. + + This is needed for gcc -Os -flto on x86-64 (Bug#28213). + * src/emacs.c (main): Align stack-bottom variable as a pointer, + since mark_memory requires this. + +2017-08-29 Eli Zaretskii + + Avoid spinning waiting for git-gui.exe on Windows + + * src/w32proc.c (waitpid): If GetExitCodeProcess returns + STILL_ACTIVE, and we were called with WNOHANG, pretend that the + process exited. (Bug#28268) + +2017-08-29 Eli Zaretskii + + Document '--module-assertions' + + * doc/emacs/cmdargs.texi (Initial Options): Document the + '--module-assertions' command-line option. + * doc/lispref/loading.texi (Dynamic Modules): Add a + cross-reference to the description of '--module-assertions'. + + * etc/NEWS: Update the NEWS entry for --module-assertions. + +2017-08-29 Alan Third + + Add news entry about new macOS features + + * etc/NEWS: Add entry about ns-appearance, ns-transparent-titlebar and + ns-use-thin-smoothing. + +2017-08-29 Alan Third + + Fix cross macOS version building (bug#28222) + + * src/macfont.h (CGContextSetFontSmoothingStyle): Function + declaration. + * src/macfont.m (macfont_draw): Limit new code to macOS 10.8 and up. + +2017-08-29 Ben Bonfil (tiny change) + + Enable thin font smoothing in macOS (bug#28222) + + * src/nsterm.m (syms_of_nsterm): Define var ns-use-thin-smoothing. + * src/macfont.m (macfont_draw): Use font smoothing. + +2017-08-29 Eli Zaretskii + + Minor improvement in documentation of display-line-numbers + + * doc/emacs/display.texi (Display Custom): Document the + display-line-numbers-mode and related options. + +2017-08-29 Eli Zaretskii + + Avoid aborting in 'waitpid' on MS-Windows + + * src/w32proc.c (waitpid): Don't allow quitting if called with + WNOHANG in OPTIONS. (Bug#28268) + +2017-08-29 Stefan Monnier + + * lisp/progmodes/sh-script.el: Test "in-string" of the right char! + + (sh-syntax-propertize-function): Fix off-by-one error. + Fixes bug#23526. + +2017-08-29 Rasmus + + Update Org to v9.0.10 + + Please see etc/ORG-NEWS for major changes. Note, this is a bugfix + release. + +2017-08-29 Paul Eggert + + Silence false alarms for symlinks to sources + + Problem reported by Glenn Morris (Bug#28264). + * lisp/files.el (files--splice-dirname-file): New function. + (file-truename, file-chase-links): Use it. + +2017-08-29 Paul Eggert + + Simplify remove_slash_colon + + * src/process.c (remove_slash_colon): Simplify + and avoid a special case for "/:" by itself. + +2017-08-28 Tassilo Horn + + Remove font family from minibuffer-prompt face + + * etc/themes/tsdh-light-theme.el (tsdh-light): Remove font family from + minibuffer-prompt face. + +2017-08-28 Michael Albinus + + Further fixes in tramp-smb.el + + * lisp/net/tramp-smb.el (tramp-smb-handle-file-truename): New defun. + (tramp-smb-file-name-handler-alist): Use it. + (tramp-smb-handle-make-symbolic-link): Unquote target. + + * test/lisp/net/tramp-tests.el + (tramp--test-ignore-make-symbolic-link-error): New defmacro. + (tramp-test18-file-attributes, tramp-test21-file-links) + (tramp--test-check-files): Use it. + +2017-08-28 Paul Eggert + + Don’t assume -g3 in .gdbinit + + * src/.gdbinit (EMACS_INT_WIDTH, USE_LSB_TAG): + Use reasonable defaults if not in the symbol table. + +2017-08-28 Robert Pluim (tiny change) + + Use string-match to check for dotfiles in ido + + * lisp/ido.el (ido-make-file-list): Use string-match to check + for dotfiles instead of substring, as when using tramp + simplified syntax ido-temp-list may contain empty strings. + +2017-08-28 Mark Oteiza + + Font-lock FDO desktop files correctly + + Single and double quotes do not have a special meaning in + desktop files. + https://standards.freedesktop.org/desktop-entry-spec/latest/ + * etc/NEWS: Mention new mode. + * lisp/files.el (auto-mode-alist): Split out an entry for handling + the .desktop extension with conf-desktop-mode. + * lisp/textmodes/conf-mode.el (conf-desktop-font-lock-keywords): New + variable with rules for booleans and format specifiers. + (conf-unix-mode): Remove desktop file entry example from docstring. + (conf-desktop-mode): New derived major mode. + +2017-08-27 Tom Tromey + + Fix auto-fill bug in js-mode + + * lisp/progmodes/js.el (js-do-auto-fill): New function. + (js-mode): Set normal-auto-fill-function. + * test/lisp/progmodes/js-tests.el (js-mode-fill-comment-bug): New + test. + +2017-08-27 Noam Postavsky + + Disable completion while entering python multiline statements + + The "legacy" completion mechanism sends newlines to the running python + process to get the list of completions, which confuses things if the + user is in the middle of entering a multiline statement (Bug#28051). + It's better to disable completion in this case. + * lisp/progmodes/python.el (python-shell--block-prompt): New variable. + (python-shell-prompt-set-calculated-regexps): Set it. + (python-shell-completion-at-point): Return 'ignore' as the completion + function when the current prompt is a block prompt. + +2017-08-27 Michael Albinus + + Tramp cleanup + + * lisp/net/tramp-sh.el (tramp-sh-extra-args): Remove compat code. + (tramp-sh-handle-make-symbolic-link): More robust check for + TARGET remoteness. + + * lisp/net/tramp-smb.el (tramp-smb-handle-copy-directory): + Disable copying by tar temporarily, it doesn't work reliably. + (tramp-smb-do-file-attributes-with-stat): Resolve symlink. + (tramp-smb-handle-make-symbolic-link): Fix implementation. + + * lisp/net/tramp.el (tramp-handle-file-symlink-p): Simplify. + + * test/lisp/net/tramp-tests.el (tramp-test21-file-links): + Extend test. + +2017-08-27 Glenn Morris + + Fix previous xterm.h change for non-gtk builds + + * src/xterm.h (GTK_CHECK_VERSION) [!USE_GTK]: Define it. + +2017-08-27 Philipp Stephani + + Fix GdkSettings-related deprecation warnings + + * src/gtkutil.c (xg_initialize): Don’t set deprecated and ignored + gtk-menu-bar-accel setting in new versions of GTK+. Use g_object_set + instead of deprecated gtk_settngs_set_string_property otherwise. + +2017-08-27 Philipp Stephani + + Always use gtk_window_move in new versions + + * src/gtkutil.c (my_log_handler): Don’t define in new versions of + GTK+. + (xg_set_geometry): Always use gtk_window_move in new versions of GTK+. + + * src/xterm.c (syms_of_xterm): Document that x-gtk-use-window-move + is ignored. + + * lisp/subr.el (x-gtk-use-window-move): Make obsolete. + +2017-08-27 Charles A. Roelli + + Fix 'diff-goto-source' when buffer is narrowed (Bug#21262) + + * lisp/vc/diff-mode.el (diff-find-file-name): Save the current + narrowing, and widen the buffer before searching for the name of the + file corresponding to the diff. + + With thanks to Noam Postavsky. + +2017-08-27 Philipp Stephani + + Remove use of a deprecated GTK+ function in new versions + + * src/gtkutil.c (xg_make_tool_item): Use gtk_widget_set_focus_on_click + if available + +2017-08-27 Philipp Stephani + + Stop using deprecated GdkScreen monitor functions in newer GDK + + * src/xfns.c (Fx_display_monitor_attributes_list): Use GdkMonitor + objects instead of the deprecated GdkScreen functions in GDK 3.22+ + +2017-08-27 Philipp Stephani + + Use GdkSeat in new GDK versions + + * src/gtkutil.c (xg_event_is_for_scrollbar): Use GdkSeat instead of + GdkDeviceManager in GDK 3.20+ + +2017-08-27 Philipp Stephani + + * src/xterm.c (XTflash): Don’t use gdk_cairo_create in GDK 3.22+ + +2017-08-27 Philipp Stephani + + Remove call of deprecated GDK function + + * src/xterm.h (XSync): Don’t call gdk_window_process_all_updates in + GDK 3.22 or later. + +2017-08-27 Alan Mackenzie + + Amend the CC Mode macro cache to cope with changes at the macro start + + Fixes bug #28233. + + * lisp/progmodes/cc-engine.el (c-invalidate-macro-cache): Fix an off-by-1 + error. + +2017-08-27 Paul Eggert + + Fix over-protection of byte-compiled files + + Problem reported by Sven Joachim (Bug#28244). + Also, fix similar problem for autoload files. + * lisp/emacs-lisp/autoload.el (autoload--save-buffer): + Set temp file modes to the buffer-file-name file modes (or 666 + if not available) as adjusted by umask. + * lisp/emacs-lisp/bytecomp.el (byte-compile-file): + Set temp file modes to 666 as adjusted by umask. + +2017-08-27 Tom Tromey + + Refine conf-toml-mode font-lock + + Bug#28218 + * lisp/textmodes/conf-mode.el (conf-toml-font-lock-keywords): Use + conf-toml-recognize-section. Use \s- in variable regexp. + (conf-toml-recognize-section): New function. + +2017-08-27 Paul Eggert + + Do not munge contents of local symbolic links + + This lets Emacs deal with arbitrary local symlinks without + mishandling their contents (Bug#28156). For example, + (progn (shell-command "ln -fs '~' 'x'") (rename-file "x" "/tmp/x")) + now consistently creates a symbolic link from '/tmp/x' to '~'. + Formerly, it did that only if the working directory was on the + same filesystem as /tmp; otherwise, it expanded the '~' to + the user's home directory. + * lisp/dired.el (dired-get-filename): Use files--name-absolute-system-p + instead of rolling our own code. + * lisp/files.el (files--name-absolute-system-p): New function. + (file-truename, file-chase-links): Use it to avoid mishandling + symlink contents that begin with ~. + (copy-directory, move-file-to-trash): + Use concat rather than expand-file-name, to avoid mishandling + symlink contents that begin with ~. + * src/fileio.c (Fmake_symbolic_link): Do not expand leading "~" in the + target unless interactive. Strip leading "/:" if interactive. + (emacs_readlinkat): Do not prepend "/:" to the link target if + it starts with "/" and contains ":" before NUL. + * test/src/fileio-tests.el (try-link): Rename from try-char, + and accept a string instead of a char. All uses changed. + (fileio-tests--symlink-failure): Also test leading ~, and "/:", + to test the new behavior. + +2017-08-27 Reuben Thomas + + Remove invalid regexp for shell builtins for wksh + + * lisp/progmodes/sh-script.el (sh-builtins): Shell built-ins have to + be literal strings, so remove a regexp for wksh. In any case, it’s a + defunct proprietary shell. + +2017-08-26 Paul Eggert + + Improve doc for file-name-absolute-p. + +2017-08-26 Michael Albinus + + Fix Tramp part of Bug#28156 + + * lisp/files.el (file-name-non-special): Use `file-name-quote' + instead prefixing "/:", the file could already be quoted. + + * lisp/net/tramp.el (tramp-error): Handle null arguments. + (tramp-handle-make-symbolic-link): + * lisp/net/tramp-sh.el (tramp-sh-handle-make-symbolic-link) + (tramp-sh-handle-add-name-to-file): + * lisp/net/tramp-smb.el (tramp-smb-handle-add-name-to-file) + (tramp-smb-handle-make-symbolic-link): Adapt implementation to + stronger semantics in Emacs. (Bug#28156) + + * test/lisp/net/tramp-tests.el (tramp-test21-file-links): + Extend test. + +2017-08-26 Eli Zaretskii + + Fix bugs merged with bug#25428 + + * lisp/simple.el (auto-fill-mode, visual-line-mode): Doc fix. + (Bug#13926) (Bug#25434) (Bug#25435) + +2017-08-26 Eli Zaretskii + + Improve documentation of Info virtual files and nodes + + * lisp/info.el (Info-virtual-files, Info-virtual-nodes): Doc fix. + (Bug#28237) + +2017-08-26 Eli Zaretskii + + * lisp/delsel.el (delete-selection-mode): Doc fix. (Bug#25428) + +2017-08-26 Grégory Mounié (tiny change) + + Support multi-lingual detection of SEE ALSO man sections + + * lisp/man.el (Man-see-also-regexp): Add support for SEE ALSO + section detection in several langages: French, German, Spanish, + Portugese, Italian, Polish, Turkish, Japanese, Chinese. (Bug#28142) + +2017-08-26 Paul Eggert + + Improve expand-file-name doc + + * doc/lispref/files.texi (Relative File Names, Directory Names) + (File Name Expansion): + * doc/lispref/minibuf.texi (Reading File Names): + Document expand-file-name behavior with ~ more clearly + and accurately. + * doc/misc/org.texi (Batch execution): Simplify example + script so that it does not need expand-file-name and thus + will not mishandle file names with leading ~. + +2017-08-26 Jefferson Carpenter (tiny change) + + Support all perl variable declarators and prefixes (Bug#27613) + + * lisp/progmodes/perl-mode.el (perl-imenu-generic-expression) + (perl-font-lock-keywords-2): Match declators 'anon', 'argument', 'has', + 'local', 'state', 'supersede', 'let', and 'temp'. + +2017-08-25 Paul Eggert + + Fix file-attributes race on GNU hosts + + * doc/lispref/files.texi (File Attributes): + Document file-attributes atomicity. + * etc/NEWS: Document the fix. + * src/dired.c (file_attributes): New args DIRNAME and FILENAME, + for diagnostics. All callers changed. On platforms like + GNU/Linux that support O_PATH, fix a race condition in + file-attributes and similar functions, so that these functions do + not return nonsense if a directory entry is replaced while getting + its attributes. On non-GNU platforms, do a better (though not + perfect) job of detecting the race, and return nil if detected. + +2017-08-25 Paul Eggert + + Simplify expand_and_dir_to_file + + * src/fileio.c (expand_and_dir_to_file): Simplify by omitting 2nd + argument, since in practice it always has the default value. All + callers changed. Prefer C99 style decls in nearby code. + +2017-08-25 Eli Zaretskii + + Fix file-name completion on network shares + + * src/w32.c (faccessat): Don't assume that F_OK is non-zero. + (Bug#28207) + +2017-08-25 Reuben Thomas + + Fix a FIXME with an exegetical comment + + * lisp/progmodes/sh-script.el (sh-builtins): Explain why we have a + regexp for wksh builtins. + +2017-08-25 Reuben Thomas + + Minor docstring language fix + + * lisp/progmodes/sh-script.el (sh-show-indent): Remove spurious “the”. + +2017-08-25 Reuben Thomas + + Remove old commented code from sh-script.el + + * lisp/progmodes/sh-script.el (sh-abbrevs): Remove commented function + and variable, commented since 2001. + +2017-08-25 Stefan Monnier + + * lisp/emacs-lisp/package.el: Don't let failure stop us + + (package-activate-1): Don't throw an error for missing deps. + (package-unpack): Don't bother compiling if activation failed. + (package-initialize): Report failures but keep activating other packages. + +2017-08-25 Paul Eggert + + Prefer ‘double’ for FP temps in xterm.c + + * src/xterm.c (xm_scroll_callback, xaw_jump_callback) + (x_set_toolkit_scroll_bar_thumb) + (x_set_toolkit_horizontal_scroll_bar_thumb): Prefer ‘double’ to + ‘float’ for individual local floating-point temporaries. + +2017-08-24 Reuben Thomas + + Avoid using string-to-multibyte in ispell.el + + * lisp/textmodes/ispell.el (ispell-get-decoded-string): Use + decode-coding-string instead. Note that decode-coding-string returns a + string that satisfies multibyte-string-p even if its input is pure + ASCII and the third argument is t, so the result of + ispell-get-decoded-string is always a multibyte string. + +2017-08-24 Tino Calancha + + Store the regexp just when there are matches + + * lisp/hi-lock.el (hi-lock-set-pattern): When font-lock-mode is + disabled and there are no matches do not store REGEXP + in hi-lock-interactive-patterns. + +2017-08-24 Tino Calancha + + Keep face available if there are no matches + + If font-lock-mode is disabled in the current buffer, and + there are no matches for REGEXP, then keep FACE available + for a next search. + * lisp/hi-lock.el (hi-lock-set-pattern): Add FACE into + hi-lock--unused-faces if font-lock-mode is disabled and + there are no matches. + * test/lisp/hi-lock-tests.el (hi-lock-test-set-pattern): Add test. + +2017-08-24 Michael Albinus + + Minor improvements for tramp-interrupt-process, documentation + + * doc/lispref/processes.texi (Signals to Processes): + * etc/NEWS: Document interrupt-process-functions. + + * lisp/net/tramp.el (tramp-interrupt-process): Test also for + `process-live-p'. + + * src/process.c (Vinterrupt_process_functions): Fix docstring. + + * test/lisp/net/tramp-tests.el (tramp-test28-interrupt-process): + Extend test. + +2017-08-24 Reuben Thomas + + Fix a comment whitespace typo. + + src/fileio.c: A double space was added after "..", used in a code + example. Make it a single space. + +2017-08-24 Reuben Thomas + + Remove old commented code and obsolete comments + + * lisp/files.el (locate-dominating-files): Remove old commented + implementation from 9 years ago. Since the current version + appears (at least to me) not just more efficient but clearer than the + version removed, also delete a comment in the new version referring to + the old version. Remove old commented heuristic code, + and explanatory comments. + +2017-08-24 Reuben Thomas + + Remove old duplicate commented code + + * lisp/files.el (file-relative-name): Remove old commented version, + replaced 14 years ago in commit 753ad9889. + +2017-08-24 Tom Tromey + + Add conf-toml-mode + + * etc/NEWS: Mention conf-toml-mode. + * lisp/files.el (auto-mode-alist): Add entry for .toml. + * lisp/textmodes/conf-mode.el (conf-toml-mode-syntax-table) + (conf-toml-font-lock-keywords): New defvars. + (conf-toml-mode): New mode. + +2017-08-23 Alan Third + + Use lisp type in log message (bug#28176) + + * src/nsimage.m (ns_load_image): Use make_number on index. + +2017-08-23 Alan Third + + Fix PNGs on macOS (bug#28176) + + * src/nsimage.m (ns_load_image): Remove index check. + (EmacsImage::getAnimatedBitmapImageRep): New function. + (EmacsImage::getMetadata): Use getAnimatedBitmapImageRep. + (EmacsImage::setFrame): Use getAnimatedBitmapImageRep and check index + is valid. + +2017-08-23 Alan Third + + Add ability to change macOS WM theme (bug#27973) + + * src/frame.c (make_frame, frame_parms, syms_of_frame) + [NS_IMPL_COCOA]: Add ns-appearance and ns-transparent-titlebar + options. + * src/frame.h (ns_appearance_type) [NS_IMPL_COCOA]: Add enum to + represent NSAppearance options. + (struct frame) [NS_IMPL_COCOA]: Add ns_appearance and + ns_transparent_titlebar frame parameters. + * src/nsfns.m (ns_frame_parm_handlers) [NS_IMPL_COCOA]: Add + ns_set_appearance and ns_set_transparent_titlebar handlers. + (Sx_create_frame): Handle ns-appearance and ns-transparent-titlebar + frame parameters. + (Qdark): Add new symbol for use with ns-appearance. + * src/nsterm.h (ns_set_appearance, ns_set_transparent_titlebar) + [NS_IMPL_COCOA]: Add prototypes. + * src/nsterm.m (ns_set_appearance, ns_set_transparent_titlebar) + [NS_IMPL_COCOA]: New functions. + (initFrameFromEmacs) [NS_IMPL_COCOA]: Handle ns-appearance and + ns-transparent-titlebar frame parameters. + * doc/lispref/frames.texi (Window Management Parameters): Document + ns-apperance and ns-transparent-titlebar. + +2017-08-22 Alan Mackenzie + + When looking for the end of a declarator, prevent macros fouling up the search + + The practical implication of this bug was a random jit-lock chunk remaining + entirely unfontified. + + * lisp/progmodes/cc-mode (c-fl-decl-end): If point starts inside a macro, + restrict two forward searches to the end of that macro. + +2017-08-22 Michael Albinus + + Test `file-expand-wildcards' for Tramp + + * lisp/net/tramp-compat.el (tramp-advice-file-expand-wildcards): + Remove, not needed anymore. + + * test/lisp/net/tramp-tests.el (top): Require seq.el. + (tramp-test16-directory-files): Simplify. + (tramp-test16-file-expand-wildcards): New test. + (tramp-test28-interrupt-process): Skip for older Emacsen. + +2017-08-22 Alexander Gramiak + + Add tests for cl-macs.el (Bug#27559) + + * test/lisp/emacs-lisp/cl-lib-tests.el (cl-lib-test-loop): Remove + duplicate. + (cl-loop-destructuring-with): Move to cl-macs-tests.el. + * test/lisp/emacs-lisp/cl-macs-tests.el: New file. + +2017-08-22 Noam Postavsky + + Optimize skkdic conversion (Bug#28043) + + The primary speedup comes from the optimizing lookup-nested-alist and + set-nested-alist for the case where the key is a string. This brings + the time down to less than half the original. + + * lisp/international/mule-util.el (lookup-nested-alist) + (set-nested-alist): Use `assq' instead of `assoc' when KEYSEQ is a + string. + + * lisp/international/ja-dic-cnv.el (skkdic-collect-okuri-nasi) + (skkdic-convert-okuri-nasi): Use progress-reporter functions instead + of calculating ratio of work done inline. + + (skkdic-reduced-candidates): Call `char-category-set' on the first + character of the string directly, instead of using a regexp for the + character category. + (skkdic--japanese-category-set): New constant. + (skkdic-collect-okuri-nasi): Just set + `skkdic-okuri-nasi-entries-count' at once at the end rather than + updating it throughout the loop. + + (skkdic-convert-postfix skkdic-convert-prefix) + skkdic-get-candidate-list, skkdic-collect-okuri-nasi) + (skkdic-extract-conversion-data): Use `match-string-no-properties' + instead of `match-string'. + +2017-08-22 Reuben Thomas + + Treat tests in lib-src like tests in src + + * test/Makefile.in (test_template): Depend on a .c source file for a + test under lib-src, as for src. (Thanks, Glenn Morris for pointing me + in the right direction.) + +2017-08-22 Paul Eggert + + Port /bin/sh scripts to Solaris 10 + + Its /bin/sh builtin ‘test’ command does not support -e. + * autogen.sh, build-aux/git-hooks/pre-commit: + * build-aux/gitlog-to-emacslog, make-dist: + Use test -r, not test -e. + +2017-08-21 Eli Zaretskii + + Avoid losing the buffer restriction in flyspell-mode + + * src/intervals.c (get_local_map): Don't allow C-g to quit as long + as we have the buffer widened, to make sure the restriction is + preserved. (Bug#28161) + +2017-08-21 Sven Joachim + + Fix the 'versionclean' target in src/Makefile + + * src/Makefile.in (versionclean): Don't accidentally remove + emacs-module.h. (Bug#28169) + +2017-08-21 Michael Albinus + + Implement `interrupt-process-functions' + + * lisp/net/tramp.el (tramp-interrupt-process): Rename from + `tramp-advice-interrupt-process'. Adapt according to changed API. + (top): Add it to `interrupt-process-functions'. + + * src/process.c (Finternal_default_interrupt_process): New defun. + (Finterrupt_process): Change implementation, based on + Vinterrupt_process_functions. + (Vinterrupt_process_functions): New defvar. + + * test/lisp/net/tramp-tests.el (tramp-test40-unload): Do not + test removal of advice. + +2017-08-21 Eli Zaretskii + + Avoid floating-point exceptions while drawing underwave + + * src/w32term.c (x_get_scale_factor): + * src/xterm.c (x_get_scale_factor): Don't let the scale factors + become less than 1. Reported by Yuri D'Elia in + http://lists.gnu.org/archive/html/emacs-devel/2017-08/msg00459.html. + +2017-08-21 Sam Steingold + + mark flymake-mode as safe local variable when the value is nil + +2017-08-21 Sam Steingold + + allow nil init in flymake-allowed-file-name-masks to disable flymake + + (flymake-allowed-file-name-masks): Update doc and :type. + (flymake-get-file-name-mode-and-masks): Handle nil init. + +2017-08-20 Dmitry Gutov + + Remove the workaround for bug#20719 + + * lisp/cedet/semantic/symref/grep.el + (semantic-symref-grep-use-template): Remove the workaround for + bug#20719, it's been fixed for a while now. + +2017-08-20 Dmitry Gutov + + Fix byte-compilation warnings in semantic/symref/grep + + * lisp/cedet/semantic/symref/grep.el (greppattern): Remove. + (grepflags): Rename to semantic-symref-grep-flags. + (semantic-symref-grep-expand-keywords): Update accordingly. + (semantic-symref-grep-use-template): Remove the last two + arguments to make sure they don't shadow the (not renamed) + global variables. + (semantic-symref-perform-search) + (semantic-symref-parse-tool-output-one-line): Use slot names + instead of keywords, like the byte-compiler wants us to. + +2017-08-20 Dmitry Gutov + + Simplify eldoc-message + + * lisp/emacs-lisp/eldoc.el (eldoc-message): Simplify. + Don't use ARGS because no callers pass them. Discussed in bug#27230. + +2017-08-20 Noam Postavsky + + Work around w32-python-2.x bug to fix prompt detection (Bug#21376) + + * lisp/progmodes/python.el (python-shell-prompt-detect): Don't put + carriage returns into the temporary file when running in unbuffered + mode, the w32 build of python 2.7 chokes on them. + +2017-08-20 Reuben Thomas + + Add missing require + + * lisp/textmodes/ispell.el: Require subr-x. (Thanks, Eli Zaretskii.) + +2017-08-20 Michael Albinus + + Implement `interrupt-process' for remote processes (Bug#28066) + + * lisp/net/tramp-sh.el (tramp-sh-handle-start-file-process): + Support sending signals remotely. + (tramp-open-connection-setup-interactive-shell): + Trace "remote-tty" connection property. + + * lisp/net/tramp.el (tramp-advice-interrupt-process): New defun. + (top): Add advice to `interrupt-process'. (Bug#28066) + + * test/lisp/net/tramp-tests.el (tramp-test28-interrupt-process): + New test. + (tramp-test29-shell-command) + (tramp-test30-environment-variables) + (tramp-test30-environment-variables-and-port-numbers) + (tramp-test31-explicit-shell-file-name) + (tramp-test32-vc-registered) + (tramp-test33-make-auto-save-file-name) + (tramp-test34-make-nearby-temp-file) + (tramp-test35-special-characters) + (tramp-test35-special-characters-with-stat) + (tramp-test35-special-characters-with-perl) + (tramp-test35-special-characters-with-ls, tramp-test36-utf8) + (tramp-test36-utf8-with-stat, tramp-test36-utf8-with-perl) + (tramp-test36-utf8-with-ls) + (tramp-test37-asynchronous-requests) + (tramp-test38-recursive-load, tramp-test39-remote-load-path) + (tramp-test40-unload): Rename. + (tramp-test40-unload): Test also removal of advice. + +2017-08-20 Reuben Thomas + + Document Enchant support + + * doc/emacs/fixit.texi: Mention Enchant. + * doc/misc/efaq.texi: Likewise. + * etc/NEWS: Add an item on Enchant support. + +2017-08-20 Reuben Thomas + + Remove old comments and a redundant FIXME + + * lisp/textmodes/ispell.el (ispell-process-line): Remove some old + commented code, a redundant FIXME, and outdated usage instructions. + +2017-08-20 Reuben Thomas + + Add Enchant support to ispell.el (Bug#17742) + + * lisp/textmodes/ispell.el (ispell-program-name): Add “enchant”. + (ispell-really-enchant): Add variable. + (ispell-check-version): If using Enchant, check it’s new enough (at + least 1.6.1). (Like the ispell check, this is absolute: cannot work + without.) + (ispell-enchant-dictionary-alist): Add variable. + (ispell-find-enchant-dictionaries): Add function, based on + ispell-find-aspell-dictionaries. + (ispell-set-spellchecker-params): Allow dictionary auto-detection for + Enchant, and call ispell-find-enchant-dictionaries to find them. Use + old ispell name to locale mapping code for Enchant too. + (ispell-send-replacement): Make it work with Enchant. + +2017-08-20 Noam Postavsky + + * lisp/term.el (term-mode): Use `window-text-height' (Bug#5615). + +2017-08-20 Noam Postavsky + + Stop printing '4' in .elc files after 'define-symbol-prop' calls + + * lisp/emacs-lisp/bytecomp.el (byte-compile-define-symbol-prop): + Return nil in case we have compiled the form, to prevent a redundant + constant from getting added to the compiled output. + +2017-08-20 Paul Eggert + + Change recent symlink tests to just test ASCII + + * test/src/fileio-tests.el (fileio-tests--symlink-failure): + Be less ambitious about testing non-ASCII chars and encoding + errors, as there are too many portability issues. + +2017-08-20 Paul Eggert + + Don’t adjust CRLF in file names + + * doc/misc/gnus.texi (Non-ASCII Group Names): + * etc/NEWS: + * test/lisp/net/tramp-tests.el (tramp--test-utf8): + Use utf-8-unix, not utf-8, for default-file-name-coding-system, so + that CRLF in file names is left alone. + * lisp/international/mule-cmds.el (set-default-coding-systems): + Do not alter CRLF in file name coding systems. + (prefer-coding-system): Ignore differences in CRLF processing when + checking whether we used the user-specified file name coding system. + * test/src/fileio-tests.el: New file. + +2017-08-19 Eli Zaretskii + + Make list-processes support display-line-numbers + + * lisp/simple.el (process-menu-mode): Move the call to + tabulated-list-init-header from here... + (list-processes--refresh): ...to here. (Bug#27895) + +2017-08-19 Eli Zaretskii + + Improve support of display-line-numbers in package.el + + * lisp/emacs-lisp/package.el (package-menu--refresh): Redisplay + the header. (Bug#27895) + * lisp/emacs-lisp/tabulated-list.el + (tabulated-list-line-number-width): Fix the case when + display-line-numbers is nil. + +2017-08-19 Eli Zaretskii + + Improve support of display-line-numbers in tabulated-list-mode + + * lisp/emacs-lisp/tabulated-list.el + (tabulated-list-line-number-width): New function. + (tabulated-list-init-header, tabulated-list-print-entry): Use it. + (Bug#27895) + +2017-08-19 Martin Rudalics + + Fix one more issue reported by Alex (Bug#27999) + + * doc/lispref/windows.texi (Preserving Window Sizes) + (Window Parameters): Use the term `window-preserved-size' + instead of `preserved-size' (Bug#27999). + +2017-08-19 Martin Rudalics + + Rename `no-delete-other-window' to `no-delete-other-windows' + +2017-08-19 Martin Rudalics + + Fix two side window problems noted by Alex (Bug#27999) + + * lisp/window.el (display-buffer-in-side-window): Fix doc-string + typo. + (delete-other-windows): Rename the `no-delete-other-window' + parameter to `no-delete-other-windows' (see the discussion in + Bug#27999 for the rationale of this change). + * doc/lispref/windows.texi (Deleting Windows) + (Frame Layouts with Side Windows, Window Parameters): Rename + `no-delete-other-window' to `no-delete-other-windows'. + +2017-08-19 Alex Schroeder + + Use define-minor-mode for rcirc-omit-mode + +2017-08-19 Paul Eggert + + Clarify behavior of symlinks and directories + + * doc/lispref/files.texi (Saving Buffers): Document how functions + like rename-file work with symlinks and directories. This patch + attempts to document the current behavior better, in preparation + for possibly changing it. See Bug#27986. + +2017-08-19 Paul Eggert + + Fix recently-introduced file descriptor leak + + * src/fileio.c (Fmake_temp_file_internal): + Don’t leak a file descriptor if write_region signals an error. + +2017-08-19 Paul Eggert + + Improve make-temp-file performance on local files + + * lisp/files.el (make-temp-file): Let make-temp-file-internal do + the work of inserting the text. + * src/fileio.c (Fmake_temp_file_internal): New arg TEXT. + All callers changed. + +2017-08-19 Noam Postavsky + + Don't lose arguments to eshell aliases (Bug#27954) + + * lisp/eshell/em-alias.el (eshell-maybe-replace-by-alias): Use ARGS. + +2017-08-19 Ted Zlatanov + + * lisp/files.el (make-temp-file): Fix directory use case. + +2017-08-19 Ted Zlatanov + + Fix and document make-temp-file optional text parameter + + * lisp/files.el (make-temp-file): Fix initial TEXT parameter. + (files--make-magic-temp-file): Support optional TEXT parameter. + * etc/NEWS: Document it. + * doc/lispref/files.texi: Document it. + * test/lisp/auth-source-tests.el: Minor reformat. + +2017-08-19 Ted Zlatanov + + * test/lisp/auth-source-tests.el: Avoid `string-join' to be simple. + + * test/lisp/auth-source-tests.el: Minor cleanups to use CL. + +2017-08-19 João Távora + + Fix default value of electric-pair-pairs and electric-pair-text-pairs + + (Bug#24901) + + A previous change, titled "Add support for curly quotation marks to + electric-pair-mode", attempted to add these characters to the default + value of these variables. But it did so in a quoted list, preventing + evaluation of the relevant expressions and resulting in an invalid + format. + + * lisp/elec-pair.el (electric-pair-pairs, electric-pair-text-pairs): + Use backquote and comma. + +2017-08-19 Noam Postavsky + + * lisp/elec-pair.el (electric-pair-text-pairs): Don't autoload (Bug#24901). + + * lisp/progmodes/elisp-mode.el (emacs-lisp-mode): Require `elec-pair' + explicitly in the interactive case. + +2017-08-19 Mats Lidell + + * etc/tutorials/TUTORIAL.sv: synced with TUTORIAL + +2017-08-19 Ted Zlatanov + + Add auth-source tests and codify its API better + + The auth-source behavior was unclear in some API use cases, so these + extra tests codify and test it. For details see + https://github.com/DamienCassou/auth-password-store/issues/29 + + * lisp/files.el (make-temp-file): Add new initial TEXT parameter. + * test/lisp/auth-source-tests.el (auth-source-test-searches): Add + auth-source tests and simplify them with the new `make-temp-file'. + +2017-08-18 Eli Zaretskii + + Don't call the same hook twice due to obsolete aliases + + * lisp/international/robin.el (robin-activate): + * lisp/international/quail.el (quail-activate): + * lisp/international/mule-cmds.el (deactivate-input-method): + * lisp/emulation/viper-init.el (viper-deactivate-input-method): + Don't call the same hook twice, when the obsolete and the + advertised symbols are aliased. (Bug#28118) + +2017-08-18 Felipe Ochoa (tiny change) + + A new face for show-paren in expression mode + + * lisp/faces.el (show-paren-match-expression): Define the new face. + * lisp/paren.el (show-paren-function): Apply the different face + when in expression mode. (Bug#28047) + +2017-08-18 Eli Zaretskii + + Non-ASCII support for man page section and header names + + * lisp/man.el (Man-name-regexp, Man-page-header-regexp) + (Man-heading-regexp): Replace ASCII character classes by + equivalent classes that allow non-ASCII characters. Suggested by + Grégory Mounié . (Bug#27978) + +2017-08-18 Eli Zaretskii + + Implement HiDPI support for underwave on MS-Windows + + * src/w32term.c (x_get_scale_factor): New function. + (w32_draw_underwave): Use it. + * src/xterm.c (x_draw_underwave): Offset the wave starting point + to make it identical with original code. + +2017-08-18 Stephen Pegoraro (tiny change) + + Support HiDPI displays for wave style underlines + + * src/xterm.c (x_draw_underwave): Compute height, length and thickness + based on scale factor. + (x_get_scale_factor): New function. + +2017-08-18 Bastien + + Delete library-of-babel.org + + * etc/org/library-of-babel.org: Delete file. + +2017-08-18 Glenn Morris + + * doc/emacs/files.texi (Copying and Naming): Avoid confusing texi2pdf. + +2017-08-18 Noam Postavsky + + Remove custom version parsing from epg-config.el (Bug#27963) + + * lisp/epg-config.el (epg-config--compare-version) + (epg-config--parse-version): Remove. + (epg-check-configuration): Use `version<=' instead. + +2017-08-18 Mark Oteiza + + Treat control characters in JSON strings as invalid + + * lisp/json.el (json-peek): Reduce to following-char. + (json-pop, json-read): Zero (null char) means end of file. + (json-read-escaped-char): Delimit URL properly. + (json-read-string): Signal error for ASCII control characters. + * test/lisp/json-tests.el (test-json-peek): Check for zero instead of + :json-eof symbol. + (test-json-read-string): New test for control characters in JSON + strings. + +2017-08-17 Eli Zaretskii + + Support Posix semantics of 'rename' on MS-Windows + + * src/w32.c (sys_rename_replace): Support Posix semantics of + 'rename': return an error if OLD is a directory while NEW is not, + or vice versa. + +2017-08-17 Eli Zaretskii + + * src/w32.c (sys_rename_replace): Support renaming a directory. + +2017-08-17 Eli Zaretskii + + Fix the MS-Windows build + + * nt/gnulib-cfg.mk (OMIT_GNULIB_MODULE_open): Omit Gnulib module + 'open'. + + * lib-src/etags.c (O_CLOEXEC) [WINDOWSNT]: Restore definition. + +2017-08-17 João Távora + + Add flymake-backends defcustom + + * lisp/progmodes/flymake-proc.el (flymake-proc-can-syntax-check-buffer): + Rename from flymake-can-syntax-check-file. Suitable for adding to + flymake-backends. + (flymake-proc-start-syntax-check): Rename from + flymake-start-syntax-check. Don't check again if buffer can be + checked. + (add-to-list flymake-backends): Hook only flymake-ui.el + + * lisp/progmodes/flymake-ui.el (flymake-backends): New + defcustom. + (flymake-on-timer-event, flymake-after-change-function) + (flymake-after-save-hook, flymake-find-file-hook): Call new + flymake--start-syntax-check-buffer and + flymake--can-syntax-check-buffer. + (flymake-mode): Call flymake--can-syntax-check-buffer and set + flymake-backend. + (flymake--backend): New buffer-local variable. + +2017-08-17 João Távora + + Split flymake.el into flymake-proc.el and flymake-ui.el + + flymake.el is now a stub that requires both files. + + * lisp/progmodes/flymake-proc.el: New file. + + * lisp/progmodes/flymake-ui.el: New file. + + * lisp/progmodes/flymake.el: Split into flymake-ui.el and + flymake-proc.el. Require both files. + +2017-08-17 Michael Albinus + + Set `default-directory' for watchdog in tramp-test.el + + * test/lisp/net/tramp-tests.el (tramp-test36-asynchronous-requests): + Set `default-directory' for watchdog. + +2017-08-17 Andreas Schwab + + * lisp/term/konsole.el: New file. + +2017-08-17 Noam Postavsky + + * lisp/woman.el (woman-push, woman-pop): Remove. (Bug#27962) + + (woman2-RS): Use plain `push' instead of `woman-push'. + (woman2-RE): Conditionally `pop' instead of `woman-pop'. + +2017-08-16 Paul Eggert + + Merge from Gnulib; use ‘open’ for O_CLOEXEC + + This incorporates: + 2017-08-15 renameat: ensure declaration in on NetBSD + 2017-08-15 extensions: enable NetBSD specific extensions + 2017-08-14 open: support O_CLOEXEC + 2017-08-13 reallocarray: new module + * admin/merge-gnulib (AVOIDED_MODULES): Remove ‘open’, since + it now supports O_CLOEXEC and this simplifies Emacs. + * build-aux/config.guess, lib/fcntl.in.h, lib/stdio.in.h: + * lib/stdlib.in.h, m4/extensions.m4, m4/stdlib_h.m4: + Copy from Gnulib. + * lib/cloexec.c, lib/cloexec.h, lib/open.c: + * m4/mode_t.m4, m4/open-cloexec.m4, m4/open.m4: + New files, copied from Gnulib. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * lib-src/etags.c (O_CLOEXEC) [WINDOWSNT]: + Remove, as Gnulib does this for us. + * src/filelock.c (create_lock_file): + * src/sysdep.c (emacs_open, emacs_pipe): + Don’t worry about O_CLOEXEC == 0, as Gnulib no longer sets it to 0. + +2017-08-16 Alan Third + Charles A. Roelli + + Allow use of run-time OS version checks on macOS (bug#27810) + + * src/nsterm.h (NSWindowTabbingMode): Define in pre-Sierra macOS. + (MAC_OS_X_VERSION_10_6, MAC_OS_X_VERSION_10_7, MAC_OS_X_VERSION_10_8, + MAC_OS_X_VERSION_10_9, MAC_OS_X_VERSION_10_12, HAVE_NATIVE_FS): Remove + defines. + (NSWindowStyleMaskFullScreen, + NSWindowCollectionBehaviorFullScreenPrimary, + NSApplicationPresentationFullScreen, + NSApplicationPresentationAutoHideToolbar): Define in macOS 10.6. + * src/nsterm.m (colorForEmacsRed, colorUsingDefaultColorSpace, + check_native_fs, ns_read_socket, ns_select, runAlertPanel, + initFrameFromEmacs, windowDidMiniaturize, windowDidEnterFullScreen, + windowDidExitFullScreen, isFullscreen, updateCollectionBehavior, + toggleFullScreen, constrainFrameRect, scrollerWidth, syms_of_nsterm): + Allow use of run-time checks and replace version check macros. + * src/nsfns.m (ns_screen_name): Use run-time OS version checks. + * src/macfont.m (macfont_draw): Use run-time OS version checks. + * src/nsmenu.m (menuWillOpen): Use run-time OS version checks. + +2017-08-16 Alan Third + + Add multiframe image support to NS port (bug#21714) + + * src/nsimage.m (ns_load_image): Handle multiple frames. + (EmacsImage::getMetadata, EmacsImage::setFrame): New functions. + * src/nsterm.h (EmacsImage::getMetadata, EmacsImage::setFrame): New + function prototypes. + +2017-08-16 Tino Calancha + + files-tests.el: Remove unused lexical variable + + * test/lisp/files-tests.el (file-test--do-local-variables-test); + Remove unused var 'files-test-queried'. + +2017-08-16 Michael Albinus + + * doc/emacs/files.texi (Copying and Naming): Mention + + restrictions to add-name-to-file and make-symbolic-link on + remote systems. + +2017-08-16 Michael Albinus + + * lisp/net/ange-ftp.el (ange-ftp-skip-msgs): Further support ftp-ssl. + +2017-08-16 Noam Postavsky + + Add tests for previous commit + + * test/lisp/progmodes/elisp-mode-tests.el + (elisp-mode-tests--face-propertized-string): New function. + (elisp--highlight-function-argument-indexed) + (elisp--highlight-function-argument-keyed-1) + (elisp--highlight-function-argument-keyed-2): New tests. + +2017-08-16 Thierry Volpiatto + + Fix eldoc highlighting for &key args (Bug#27272) + + * lisp/progmodes/elisp-mode.el (elisp--highlight-function-argument): + Only switch to keyword-based searching if INDEX point beyond `&key' in + the argument list. All arguments prior to the `&key' are position + based. Additionally, be more strict about what is a keyword when + searching for the current keyword. + +2017-08-15 Paul Eggert + + Do not assume regular Git .git/hooks dir + + Apparently Gitlab doesn’t create .git/hooks, like regular Git does. + Problem reported by Ted Zlatanov in: + http://lists.gnu.org/archive/html/emacs-devel/2017-08/msg00380.html + * autogen.sh (git_sample_hook_src): New function. Use it to work + even if .git/hooks or its samples do not exist. + +2017-08-15 Paul Eggert + + New manual section "Copying and Naming" + + * doc/emacs/files.texi (Copying and Naming): + New section, split off from Misc File Ops and containing the + operations that copy, name or rename files. This fixes some + confusion caused by the incorrect phrase "The same rule applies + to all the remaining commands in this section" in the old manual. + This change does not affect the confusion about directories (see + Bug#27986 for ongoing discussion). + +2017-08-15 Eli Zaretskii + + Fix the MS-Windows build + + * src/fileio.c (Frename_file): Don't use ENOTSUP if it is equal to + ENOSYS. (Bug#28097) (Bug#27986) + +2017-08-15 Ted Zlatanov + + * .gitlab-ci.yml: run "autogen.sh autoconf" to avoid Git. + + * .gitlab-ci.yml: add Git to the installed packages. + +2017-08-15 Simen Heggestøyl + + Support indentation of detached Less CSS rulesets + + * lisp/textmodes/css-mode.el (css-smie-rules): Provide better support + for indentation of detached rulesets passed to Less mixins. + + * test/manual/indent/less-css-mode.less: New file. + +2017-08-15 Simen Heggestøyl + + Fixes and tweaks for the new Less CSS mode + + * etc/NEWS: Add an entry for the new mode. + + * lisp/textmodes/less-css-mode.el (less-css): Tweak docstring. + (less-css-lessc-command): Tweak docstring. Don't mark it as + safe. Don't autoload. + (less-css-compile-at-save, less-css-lessc-options) + (less-css-output-directory): Tweak docstrings. Don't autoload. + (less-css-output-file-name): Tweak docstring. Don't mark it as safe. + (less-css-input-file-name): Tweak docstring. Don't autoload. + (less-css-compile-maybe): Use `when' for one-armed `if'. + (less-css--output-path): Tweak docstring. + (less-css--maybe-shell-quote-command): Remove function. + (less-css-compile): Don't autoload. Tweak docstring and message. Fix + compiler warning. Use `string-join' instead of `mapconcat'. + (less-css-font-lock-keywords): Use `font-lock-variable-name-face' for + variables. + (less-css-mode-syntax-table, less-css-mode-map): New variables. + (less-css-mode): Change status line mode name from "LESS" to + "Less". Tweak docstring. Move syntax table definitions to + `less-css-mode-syntax-table'. + (less-css-indent-line): Remove function. + +2017-08-15 Steve Purcell + + New major mode: Less CSS mode + + * lisp/textmodes/less-css-mode.el: New file. + +2017-08-15 Tino Calancha + + archive-int-to-mode: Fix order of testing S_ISUID, S_ISGID bits + + * lisp/arc-mode.el (archive-int-to-mode): + Swap order of 2048 and 1024 tests (Bug#28092). + * test/lisp/arc-mode-tests.el (arc-mode-test-archive-int-to-mode): + Update test. + +2017-08-15 Paul Eggert + + Improve rename-file port to macOS + + * src/fileio.c (Frename_file): On macOS, renameat_noreplace can + fail with errno == ENOTSUP on file systems where it is not + supported, according to the Apple documentation. + +2017-08-15 Noam Postavsky + + Speed up ./configure with more caching (Bug#27960) + + * configure.ac: Cache the 'GTK compiles', 'GSettings is in gio', + 'LN_S', '-znocombreloc', 'sysinfo', 'gcc autodepends', '-b link', + 'Xkb', 'Xpm preprocessor', 'tputs library' 'GLib', 'signals via + characters', and 'Windows API header' checks. Remove pause after + warning about GTK bug. + +2017-08-15 Paul Eggert + + Improve rename-file behavior on macOS + + Problem reported by Philipp Stephani (Bug#27986). + * src/fileio.c (Frename_file): + Worry about file name case sensitivity only if CYGWIN or DOS_NT. + * src/sysdep.c (renameat_noreplace): Use renameatx_np on macOS, + since this provides the necessary atomicity guarantees. + +2017-08-14 Glenn Morris + + Clean up temp files after some tests + + * test/lisp/emacs-lisp/bytecomp-tests.el + (bytecomp-tests--with-temp-file): Also delete .elc file if present. + * test/lisp/progmodes/etags-tests.el + (etags-buffer-local-tags-table-list): Delete temp file at end. + +2017-08-14 Eli Zaretskii + + Implement renameat_noreplace for MS-Windows + + * src/sysdep.c (renameat_noreplace) [WINDOWSNT]: Implement minimal + emulation for MS-Windows. (Bug#27986) + +2017-08-14 Eli Zaretskii + + Fix 'rename' on MS-Windows + + * src/w32.c (sys_rename_replace): Use the FORCE argument only if + the primitive rename errors out with EEXIST. + +2017-08-14 Michael Albinus + + * lisp/net/ange-ftp.el (ange-ftp-skip-msgs): Support ftp-ssl. + +2017-08-14 Mark Oteiza + + Tiny JSON performance improvement + + Get rid of some needless uses of apply. Measuring with + (benchmark-run 10 (json-read-file "test.json")) + showed 1.5-2.5% reduction of execution time. + * lisp/json.el (json-peek): Nix let-binding. + (json-read-string): Use concat for making a string from chars. + (json-read-array): Use cond and more appropriate conversion instead + of blindly applying. + +2017-08-13 Paul Eggert + + Be consistent in spelling 'ok-if-already-exists'. + +2017-08-13 Alexander Gramiak + + Use 'header-line-highlight' face in proced and erc + + * lisp/erc/erc-list.el (erc-list-button): + * lisp/proced.el (proced-format): Use the 'header-line-highlight + face. (Bug#28033) + +2017-08-13 Ulf Jasper + + Remove feeds with dead uris from newsticker--raw-url-list-defaults + + * lisp/net/newst-backend.el (newsticker--raw-url-list-defaults): + Remove feeds with dead uris. + +2017-08-13 Eli Zaretskii + + Fix vertical cursor motion when cursor is on the fringe + + * lisp/simple.el (line-move-visual): Fix an off-by-one error in + setting temporary-goal-column when newline overflows into the + fringe. Support that use case in R2L paragraphs as well. + +2017-08-13 Eli Zaretskii + + Fix vertical cursor motion across too wide images + + * src/indent.c (Fvertical_motion): If lines are truncated and we + end up beyond the right margin of the window, don't assume we are + in the next screen line, unless VPOS actually says so. (Bug#28071) + +2017-08-13 Tino Calancha + + Add test suites for arc-mode and tar-mode + + * test/lisp/arc-mode-tests.el (arc-mode-test-archive-int-to-mode) + * test/lisp/tar-mode-tests.el (tar-mode-test-tar-grind-file-mode): + New tests. + +2017-08-13 Tino Calancha + + * lisp/tar-mode.el (tar-grind-file-mode): Fix docstring + +2017-08-13 Ulf Jasper + + Fix uri of Emacs Wiki + + * lisp/net/newst-backend.el (newsticker--raw-url-list-defaults): Fix + uri of Emacs Wiki. (Bug#27981) + +2017-08-13 Paul Eggert + + Fix make-temp-file bug with ""/"."/".." prefix + + The bug with "." and ".." has been present for a while; I + introduced the bug with "" earlier today in my patch for Bug#28023. + * lisp/files.el (make-temp-file): Do not use expand-file-name if + PREFIX is empty or "." or "..", as it does the wrong thing. + Compute absolute-prefix here ... + (files--make-magic-temp-file): ... instead of here ... + * src/fileio.c (Fmake_temp_file_internal): ... or here. + + * lisp/files.el (make-temp-file): If the prefix is empty, append + "/" to the absolute prefix so that the new files are children + rather than siblings of temporary-file-directory. This fixes a + bug introduced in the previous change. + * test/lisp/files-tests.el (files-test-make-temp-file-empty-prefix): + New test, for the bug. + +2017-08-13 Paul Eggert + + Improve make-temp-file performance on local files + + For the motivation behind this patch, please see Bug#28023 and: + http://emacshorrors.com/posts/make-temp-name.html + Although, given the recent changes to Tramp, the related security + problem in make-temp-file is already fixed, make-temp-file still has + several unnecessary system calls. In the typical case on GNU/Linux, + this patch replaces 8 syscalls (symlink, open, close, readlinkat, uname, + getpid, unlink, umask) by 2 (open, close). + * admin/merge-gnulib (GNULIB_MODULES): Add tempname, now + that Emacs is using it directly. + * configure.ac (AUTO_DEPEND): Remove AC_SYS_LONG_FILE_NAMES; + no longer needed. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * lisp/files.el (files--make-magic-temp-file): Rename from + make-temp-file. + (make-temp-file): Use make-temp-file-internal for + non-magic file names. + * src/fileio.c: Include tempname.h. + (make_temp_name_tbl, make_temp_name_count) + (make_temp_name_count_initialized_p, make_temp_name): Remove. + (Fmake_temp_file_internal): New function. + (Fmake_temp_name): Use it. + * src/filelock.c (get_boot_time): Use Fmake_temp_file_internal + instead of make_temp_name. + +2017-08-12 Paul Eggert + + Document internal-use naming conventions + + * doc/lispref/functions.texi (Function Names): + * doc/lispref/variables.texi (Tips for Defining): + Document naming conventions for internal-use functions and vars. + See Bug#28023#59. + +2017-08-12 Paul Eggert + + Simplify re and document 'autoconf.sh all' + + * GNUmakefile (ALL_IF_GIT): Remove; no longer needed, now that + ./autogen.sh defaults to "all". All uses removed. + * README: Mention autoconf.sh's effect on Git configuration. + +2017-08-12 Paul Eggert + + Default autogen.sh to 'all' + + This addresses a problem noted by RMS in: + http://lists.gnu.org/archive/html/emacs-devel/2017-08/msg00052.html + * autogen.sh (do_git): Set to true if this script is invoked + with no arguments and there is a .git subdirectory. + +2017-08-12 Paul Eggert + + Adjust jka-compr to recent Tramp changes. + + * lisp/jka-compr.el (jka-compr-write-region): + Two new args LOCKNAME and MUSTBENEW. + +2017-08-12 Eli Zaretskii + + Improve doc strings of 2 functions in simple.el + + * lisp/simple.el (beginning-of-visual-line) + (move-beginning-of-line): Doc fix. Reported by + Justin Burkett . + +2017-08-12 Eli Zaretskii + + Fix completion on directory names on MS-DOS/MS-Windows + + * src/msdos.c (faccessat): + * src/w32.c (faccessat): Support relative file names, and add D_OK + to 'mode' if the argument is a directory. This unbreaks file-name + completion when the completion result is a directory. + +2017-08-12 Michael Albinus + + Implement EXCL of write-region for Tramp + + * lisp/net/ange-ftp.el (ange-ftp-write-region): + * lisp/net/tramp-adb.el (tramp-adb-handle-write-region) + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-write-region): + * lisp/net/tramp-sh.el (tramp-sh-handle-write-region) + * lisp/net/tramp-smb.el (tramp-smb-handle-write-region): + Implement MUSTBENEW. + + * lisp/net/tramp-gvfs.el (tramp-gvfs-do-copy-or-rename-file) + * lisp/net/tramp-sh.el (tramp-sh-handle-make-symbolic-link) + (tramp-sh-handle-add-name-to-file) + (tramp-do-copy-or-rename-file) + * lisp/net/tramp-smb.el (tramp-smb-handle-make-symbolic-link): + Adapt error message for `file-already-exists'. + + * src/lisp.h: + * src/eval.c (call8): New function. + + * src/fileio.c (write_region): Pass also lockname and + mustbenew to the file name handler. + + * test/lisp/net/tramp-tests.el (tramp-test10-write-region): + Add tests for MUSTBENEW. + +2017-08-12 Eli Zaretskii + + Adapt Proced display to display-line-numbers + + * lisp/proced.el (proced-header-line): Account for the width taken + by display-line-numbers. (Bug#27895) + +2017-08-12 Eli Zaretskii + + Adapt tabulated list when display-line-number is turned on + + * lisp/emacs-lisp/tabulated-list.el (tabulated-list-mode): Add + a hook to revert the display when display-line-numbers is turned + on. (Bug#27895) + +2017-08-12 Eli Zaretskii + + Use Gnulib 'tempname' on MS-Windows + + * lib-src/ntlib.h (mkdir, open): Remove redefinitions. They are + now in nt/inc/ms-w32.h. + * lib-src/ntlib.c (sys_mkdir, sys_open): New functions. + (mkostemp): Remove. + + * src/w32.c (mkostemp): Remove. + (sys_mkdir): Accept a second (unused) argument. + * src/fileio.c (Fmake_directory_internal): Remove the WINDOWSNT + specific call to mkdir. (Bug#28023) + + * nt/inc/ms-w32.h (mkdir): Remove from "#ifdef emacs" and redefine + to accept 2 arguments. + (open): Remove from "#ifdef emacs". + * nt/mingw-cfg.site (ac_cv_func_mkostemp): Remove. + * nt/gnulib-cfg.mk (OMIT_GNULIB_MODULE_mkostemp) + (OMIT_GNULIB_MODULE_tempname): Remove. + +2017-08-12 Alexander Gramiak + + Add new face 'header-line-highlight' + + * lisp/faces.el: Define the face. + * lisp/emacs-lisp/tabulated-list.el (tabulated-list-init-header): + * lisp/info.el (Info-fontify-node): Use the new face. + * doc/emacs/display.texi (Standard Faces): + * etc/NEWS: Document the new face. (Bug#28033) + +2017-08-12 Arash Esbati + + Make a case-sensitive match for strings + + * lisp/textmodes/reftex.el (reftex-typekey-check): Temporarily + let-bind `case-fold-search' to nil in order to be case-sensitive + when matching a string. (Bug#27518) + +2017-08-11 Stephen Berman + + Fix a minor todo-mode regression + + * lisp/calendar/todo-mode.el (todo-get-overlay): Wrap in + save-excursion. This fixes a regression introduced by the fix + for bug#27609, whereby trying to raise the priority of the + first item or lower the priority of the last item, which + should be noops, moves point to the item's start. Clarify + comment. + + * test/lisp/calendar/todo-mode-tests.el + (todo-test-raise-lower-priority): Add test cases for trying to + raise first item and lower last item. + (with-todo-test): Clear abbreviated-home-dir, since we change HOME. + (todo-test-toggle-item-header02): Remove ":expected-result + :failed" and tests of point after todo-next-item, since the + effect when using Todo mode is not reproducible in the test + environment. Add commentary about this. + +2017-08-11 Paul Eggert + + Improve performance for rename-file etc. + + Although this does not fix Bug#27986, it is a step forward. + I plan to propose a more-significant patch later. + * lisp/files.el (directory-name-p): Move from here ... + * src/fileio.c (Fdirectory_name_p): ... to here. + (directory_like, cp_like_target): New static functions. + (Fcopy_file, Frename_file, Fadd_name_to_file) + (Fmake_symbolic_link): + Use them, to avoid directory-testing syscalls on file names that + must be directories if they exist. Omit unnecessary + initializations and CHECK_STRING calls. + (Frename_file): Don't call file_name_case_insensitive_p + twice on the same file. Compare both file names expanded, instead + of the old name expanded and the new one unexpanded. + +2017-08-11 Noam Postavsky + + Respect buffer-local value of tags-table-list (Bug#27772) + + * lisp/progmodes/etags.el (visit-tags-table-buffer): Save the current + buffer around the `tags-table-including' calls so as to get buffer + local variables from the right buffer later. + * test/lisp/progmodes/etags-tests.el (etags-visit-tags-table-buffer): + New test. + * test/lisp/progmodes/etags-tests.el (etags-tests--test-dir): New + constant. + (etags-bug-158, etags-bug-23164): Use it so that when running the test + interactively, setting EMACS_TEST_DIRECTORY is not needed. + +2017-08-10 Tom Tromey + + Fix auto-filling regression + + Bug#28003 + * lisp/newcomment.el (comment-indent-new-line): Check + comment-auto-fill-only-comments. Reverts earlier change. + * lisp/simple.el (internal-auto-fill): Call auto-fill-function, not + do-auto-fill. + +2017-08-09 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-08-09 tempname: do not depend on secure_getenv + 2017-08-08 extensions: add _OPENBSD_SOURCE + 2017-08-06 manywarnings: Add support for C++ + 2017-08-06 warnings, manywarnings: Add support for multiple languages + * admin/merge-gnulib: Don't use m4/manywarnings-c++.m4. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * lib/secure_getenv.c, m4/secure_getenv.m4: Remove. + * lib/tempname.c, m4/extensions.m4, m4/manywarnings.m4, m4/warnings.m4: + Copy from gnulib. + +2017-08-09 Eli Zaretskii + + Fix crashing emacs-module tests on MS-Windows + + * src/w32fns.c (syms_of_w32fns) : New + variable. + (emacs_abort): If w32-disable-abort-dialog is non-nil, abort right + away, without displaying the Abort dialog, which waits for the user. + + * test/src/emacs-module-tests.el (module--test-assertion): Run the + inferior Emacs with the w32 abort dialog disabled. Expect the + status of the aborted Emacs sub-process to be 3 on MS-Windows and + 2 on MS-DOS. + +2017-08-09 Tino Calancha + + dired-delete-file: Dont't ask for empty dirs + + * lisp/dired.el (dired--yes-no-all-quit-help): New defun. + (dired-delete-file): Use it. Dont't ask for empty dirs (Bug#27940). + + * test/lisp/dired-tests.el (dired-test-with-temp-dirs): + New auxiliar macro. + (dired-test-bug27940): Add new test. + +2017-08-09 Tino Calancha + + Ask files for deletion in buffer order: top first, botton later + + * lisp/dired.el (dired-do-flagged-delete, dired-do-delete): + Call `nreverse' t invert the output of `dired-map-over-marks'. + +2017-08-09 Alexander Gramiak + + Use help-mode xrefs in describe-font + + * lisp/international/mule-diag.el (describe-font): Use help-setup-xref + (Bug#27890). + +2017-08-09 Katsumi Yamaoka + + Don't try to jump to non-existent part (bug#28013) + + * lisp/gnus/gnus-art.el (gnus-article-edit-part): Don't try to jump to + the next part if there is the only one part in the article (bug#28013). + +2017-08-08 Mark Oteiza + + Replace some uses of eval + + There are a number of places where eval is used unnecessarily to get + or set the value of a symbol. + * lisp/calendar/calendar.el (diary-date-forms): Use default-value in + custom setter. + * lisp/desktop.el (desktop-clear): Use set-default instead. + * lisp/international/ogonek.el (ogonek-read-encoding): Use + symbol-value. + +2017-08-08 Mark Oteiza + + Convert uses of looking-at in viper-ex to following-char + + * lisp/emulation/viper-ex.el (viper-get-ex-token): Bind + (following-char) and use it in the subsequent cond's clauses. + (viper-ex, ex-quit, viper-get-ex-file): Use following-char instead. + Convert single branch ifs to when + +2017-08-08 Mark Oteiza + + Some cleanup in message.el + + * lisp/gnus/message.el (message-cross-post-insert-note): + (message-strip-forbidden-properties): Mark unused args. + (message-canlock-generate): Remove extinct variable + sha1-maximum-internal-length. + (message-make-mail-followup-to): Use loop's thereis clause. + +2017-08-08 Paul Eggert + + Document make-temp-name magic limitations + + * doc/lispref/files.texi (Unique File Names): + * src/fileio.c (Fmake_temp_name): Document that make-temp-name + does not guarantee uniqueness on magic file names. + +2017-08-08 Tom Tromey + + Show number of errors in compilation-mode mode-line + + Bug#25354 + * lisp/progmodes/compile.el (compilation-num-errors-found): Provide + default value. + (compilation-num-warnings-found, compilation-num-infos-found): New + defvars. + (compilation-mode-line-errors): New defconst. + (compilation-face): Remove. + (compilation-type, compilation--note-type): New functions. + (compilation-parse-errors): Call compilation--note-type. + (compilation-start): Include compilation-mode-line-errors in + mode-line-process. + (compilation-setup): Initialize compilation-num-* variables to 0. + (compilation-handle-exit): Include compilation-mode-line-errors in + mode-line-process. + * doc/emacs/building.texi (Compilation): Document new feature. + +2017-08-08 Mark Oteiza + + Do some cleanup in mailcap.el + + * lisp/net/mailcap.el: Use lexical-binding. + (mailcap--set-user-mime-data, mailcap-possible-viewers): Use pcase + destructuring. + (mailcap-mime-data): Remove some entries for ancient functions. + (mailcap-parse-mailcaps, mailcap-mime-info): Nix single-branch ifs. + (mailcap-parse-mimetype-file): Just use append. + (mailcap-command-p): Remove unused function. + +2017-08-08 Tino Calancha + + query-replace: Undo replacements performed with 'comma + + During a `query-replace', the char ',' replaces the character + at point and doesn't move point; right after, the char 'u' + must undo such replacement (Bug#27268). + * lisp/replace.el (replace--push-stack): + New macro extracted from `perform-replace'. + (perform-replace): Use it. + * test/lisp/replace-tests.el (query-replace--undo): Add test. + +2017-08-08 Noam Postavsky + + Don't define gv expanders in compiler's runtime (Bug#27016) + + This prevents definitions being compiled from leaking into the current + Emacs doing the compilation. + * lisp/emacs-lisp/gv.el (gv-define-expander): Use function-put instead + of `put' with `eval-and-compile'. + * test/lisp/emacs-lisp/gv-tests.el: New tests. + +2017-08-08 Noam Postavsky + + Let the cl-typep effects of defclass work during compilation (Bug#27718) + + * lisp/emacs-lisp/eieio.el (defclass): Use `define-symbol-prop' + instead of `put'. + * test/lisp/emacs-lisp/eieio-tests/eieio-tests.el + (eieio-tests--dummy-function): Remove. + (eieio-test-25-slot-tests, eieio-test-23-inheritance-check): Don't + expect to fail if compiled. + +2017-08-08 Stefan Monnier + Noam Postavsky + + Let `define-symbol-prop' take effect during compilation + + * src/fns.c (syms_of_fns): New variable `overriding-plist-environment'. + (Fget): Consult it. + * lisp/emacs-lisp/bytecomp.el (byte-compile-close-variables): Let-bind + it to nil. + (byte-compile-define-symbol-prop): New function, handles compilation + of top-level `define-symbol-prop' and `function-put' calls by putting + the symbol setting into `overriding-plist-environment'. + +2017-08-08 Gemini Lasswell + + Add a test of handling of circular values to testcover-tests + + * test/lisp/emacs-lisp-testcover-resources/testcases.el + (testcover-testcase-cyc1): New function. + (testcover-tests-circular-lists-bug-24402): New test. + +2017-08-08 Noam Postavsky + + Don't error on circular values in testcover + + * lisp/emacs-lisp/testcover.el (testcover-after, testcover-1value): + Consider circular lists to be non-equal instead of signaling error. + +2017-08-08 Alexander Gramiak + + Catch argument and macroexpansion errors in ert + + This kludge catches errors caused by evaluating arguments in ert's + should, should-not, and should-error macros; it also catches + macroexpansion errors inside of the above macros (Bug#24402). + + * lisp/emacs-lisp/ert.el: (ert--should-signal-hook): New function. + (ert--expand-should-1): Catch macroexpansion errors. + * test/lisp/emacs-lisp/ert-tests.el (ert-test-should-error-argument) + (ert-test-should-error-macroexpansion): Tests for argument and + expansion errors. + +2017-08-07 Reuben Thomas + + Revert "Add Enchant support to ispell.el (Bug#17742)" + + This reverts commit 7136e6723d87b51ae3089f5ceef6b14621bfaf87. + +2017-08-07 Reuben Thomas + + Revert "Add support for arguments in ALTERNATE_EDITOR to emacsclient" + + This reverts commit 28f1fe97daa13e13714e6c43c9a6fbb0c0e99a26. + +2017-08-07 Reuben Thomas + + Add support for arguments in ALTERNATE_EDITOR to emacsclient + + * lib-src/emacsclient.c (fail): Parse ALTERNATE_EDITOR, or + corresponding command-line argument, into space-separated tokens. + * etc/NEWS: Document. + * test/lib-src/emacsclient-tests.el: Add a test. + +2017-08-07 Reuben Thomas + + Add Enchant support to ispell.el (Bug#17742) + + * lisp/textmodes/ispell.el (ispell-program-name): Add “enchant”. + (ispell-really-enchant): Add variable. + (ispell-check-version): If using Enchant, check it’s new enough (at + least 1.6.1). (Like the ispell check, this is absolute: cannot work + without.) + (ispell-enchant-dictionary-alist): Add variable. + (ispell-find-enchant-dictionaries): Add function, based on + ispell-find-aspell-dictionaries. + (ispell-set-spellchecker-params): Allow dictionary auto-detection for + Enchant, and call ispell-find-enchant-dictionaries to find them. Use + old ispell name to locale mapping code for Enchant too. + (ispell-send-replacement): Make it work with Enchant. + +2017-08-07 Reuben Thomas + + Allow async command output buffer to be shown only on output + + * lisp/simple.el (async-shell-command-display-buffer): Add + defcustom. + (shell-command): Use the new defcustom to determine whether to show + the buffer immediately, or add a process filter that shows it only + when there is some output. + * etc/NEWS: Document the new variable. + * doc/emacs/misc.texi: Likewise. + + Thanks to Juri Linkov and Eli Zaretskii for advice and guidance. + +2017-08-07 Eli Zaretskii + + Fix infinite recursion under prettify-symbols-mode and linum-mode + + * src/xdisp.c (get_overlay_strings_1) + (handle_single_display_spec, push_prefix_prop): Invalidate the + composition information before starting to iterate on a string. + Otherwise we might think in set_iterator_to_next that we are + delivering characters from a composition, and do all kinds of + nonsensical things, like over-step the string end. (Bug#27761) + +2017-08-07 Stefan Monnier + + * lisp/gnus/gnus-bcklg.el (gnus-backlog-request-article): Fix thinko. + +2017-08-07 Martin Rudalics + + Fix doc-string of `delete-other-windows' + + * lisp/window.el (delete-other-windows): Fix doc-string. + +2017-08-07 Paul Eggert + + Fix a couple more make-temp-file races + + * lisp/files.el (basic-save-buffer-2, move-file-to-trash): + Use make-temp-name, not make-temp-file with retry. + (basic-save-buffer-2): Use condition-case, instead of + unwind-protect with a success flag. + +2017-08-07 Noam Postavsky + + Merge null and without-null regexp alists (Bug#27840, Bug#27873) + + * lisp/progmodes/grep.el (grep-mode-font-lock-keywords): Allow for NUL + characters following filename in grep context lines. + (grep--regexp-alist-column, grep--regexp-alist-bin-matcher) + (grep-with-null-regexp-alist, grep-fallback-regexp-alist): Remove. + (grep-regexp-alist): Recombine their contents here. + (grep-mode): + * lisp/cedet/semantic/symref/grep.el + (semantic-symref-parse-tool-output-one-line): + * lisp/progmodes/xref.el (xref-collect-matches): Use the variable + `grep-regexp-alist' rather than the function. + +2017-08-07 Paul Eggert + + Fix some crashes on self-modifying Elisp code + + Prompted by a problem report by Alex in: + http://lists.gnu.org/archive/html/emacs-devel/2017-08/msg00143.html + * src/eval.c (For, Fprogn, Fsetq, FletX, eval_sub): + Compute XCDR (x) near XCAR (x); although this doesn't fix any bugs, + it is likely to run a bit faster with typical hardware caches. + (Fif): Use Fcdr instead of XCDR, to avoid crashing on + self-modifying S-expressions. + (Fsetq, Flet, eval_sub): Count the number of arguments as we go + instead of trusting an Flength prepass, to avoid problems when the + code is self-modifying. + (Fquote, Ffunction, Fdefvar, Fdefconst): Prefer !NILP to CONSP + where either will do. This is mostly to document the fact that + the value must be a proper list. It's also a tiny bit faster on + typical machines nowadays. + (Fdefconst, FletX): Prefer XCAR+XCDR to Fcar+Fcdr when either will do. + (eval_sub): Check that the args are a list as opposed to some + other object that has a length. This prevents e.g. (if . "string") + from making Emacs dump core in some cases. + * test/src/eval-tests.el (eval-tests--if-dot-string) + (eval-tests--let-with-circular-defs, eval-tests--mutating-cond): + New tests. + +2017-08-06 Eli Zaretskii + + * etc/tutorials/TUTORIAL.he: Update to match recent changes to TUTORIAL. + +2017-08-06 Tino Calancha + + Minor tweak in a dired test + + * test/lisp/dired-tests.el (dired-test-bug27968): + Ensure the new header has different length than the original one. + +2017-08-06 Tino Calancha + + dired-delete-file: Do not TAB complete the user answer + + This action might delete directories containing valuable information. + Before previous commit, we prompted users with `yes-or-no-p' + which doesn't TAB complete the user answer. Let's play safe and + keep requiring full answers. + * emacs-master/lisp/dired.el (dired-delete-file): Use `read-string' + instead of `completing-read' to read the user answers. + +2017-08-06 Tino Calancha + + dired-do-delete: Allow to delete dirs recursively without prompts + + * lisp/dired.el (dired-delete-file): Accept 2 additional answers: + 'all', to delete all directories recursively and no prompt anymore. + 'quit', to cancel directory deletions (Bug#27940). + Show help message when user inputs 'help'. + (dired-do-flagged-delete): Bind locally dired-recursive-deletes + so that we can overwrite its global value. + Wrapp the loop within a catch '--delete-cancel to catch when + the user abort the directtry deletion. + * doc/emacs/dired.texi (Dired Deletion): Update manual. + * etc/NEWS (Changes in Specialized Modes and Packages in Emacs 26.1): + Announce this change. + +2017-08-06 Paul Eggert + + Fix a couple of make-temp-file races + + * lisp/emacs-lisp/autoload.el (autoload--save-buffer): + * lisp/emacs-lisp/bytecomp.el (byte-compile-file): + Use make-temp-file, not make-temp-name, to avoid an unlikely race + that could lose data. Remove the deletion hook as quickly as + possible after the file is renamed; though a race still remains + here, it is smaller than before. + +2017-08-06 Tino Calancha + + Dired w/ eshell-ls: Handle shell wildcards in file name + + * lisp/eshell/em-ls.el (eshell-ls--insert-directory): + Use eshell-extended-glob (Bug#27844). + * test/lisp/dired-tests.el (dired-test-bug27844): Add test. + +2017-08-06 Tino Calancha + + dired-revert: save line numbers instead of positions + + Positions might change if the length of one dired header line + changes; this happen, for instance, if we add new files. + Instead, line numbers are invariant under shrinks/enlargements + of the file header. + https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg01092.html + * lisp/dired.el (dired-save-positions): Save the line numbers at point. + (dired-restore-positions): Use forward-line to restore the original + position (Bug#27968). + * test/lisp/dired-tests.el (dired-test-bug27968): Add test. + +2017-08-06 Tom Tromey + + Respect comment-auto-fill-only-comments + + Respect comment-auto-fill-only-comments when auto-filling and a + comment syntax is defined. + + * lisp/newcomment.el (comment-indent-new-line): Do not check + comment-auto-fill-only-comments. + * lisp/simple.el (internal-auto-fill): New defun. + * src/cmds.c (internal_self_insert): Call Qinternal_auto_fill, not + auto_fill_function. + (syms_of_cmds): Define Qinternal_auto_fill. + +2017-08-05 Richard Stallman + + * etc/tutorials/TUTORIAL: Update. + +2017-08-05 Eli Zaretskii + + Unify CNS11643-15 in a way that avoids segfaults + + * lisp/international/mule-conf.el: Redo unification of + cns11643-15. (Bug#27964) + (chinese-cns11643-15): Add the missing :unify-map attribute. + +2017-08-05 Eli Zaretskii + + Avoid segfaults while producing Punct.el + + * lisp/international/mule-conf.el: Undo unification of + cns11643-15, as that causes segfaults during bootstrap. + (Bug#27964) + +2017-08-05 Eli Zaretskii + + Make header line in some modes be sensitive to display-line-numbers + + * lisp/ruler-mode.el (ruler-mode-ruler, ruler-mode-window-col): + * lisp/emacs-lisp/tabulated-list.el (tabulated-list-init-header) + (tabulated-list-print-entry): Account for the width taken by + line-number display. (Bug#27895) + +2017-08-05 Eli Zaretskii + + Fix a bug in 'generate-new-buffer-name' + + * src/buffer.c (Fgenerate_new_buffer_name): Test IGNORE for being + nil before calling string-equal, since the latter will compare + "nil and 'nil' as equal. (Bug#27966) + + * test/src/buffer-tests.el + (test-generate-new-buffer-name-bug27966): New test. + +2017-08-05 Eli Zaretskii + + Unify CNS11643-15 + + * lisp/international/mule-conf.el (chinese-cns11643-15): Add a + unify-charset form for it. (Bug#27964) + +2017-08-05 Eli Zaretskii + + Improve test of error message when Emacs cannot be suspended + + * lisp/term/x-win.el (x-win-suspend-error): + * lisp/term/ns-win.el (ns-suspend-error): Improve the error + message. (Bug#27901) + +2017-08-05 Alexander Gramiak + + Make "C-h o" show faces as well as variables + + * lisp/faces.el (describe-face): Return (buffer-string). Reorder + the placement of variables/faces in describe-symbol, to put more + emphasis on the variable entry rather than the face. (Bug#24543) + +2017-08-05 Eli Zaretskii + + Fix files-tests.el for MS-Windows + + * test/lisp/files-tests.el + (files-tests--file-name-non-special--subprocess): Fix this test + for MS-Windows. + +2017-08-05 Eli Zaretskii + + Improve documentation of 'region-extract-function' + + * lisp/simple.el (region-extract-function): Rename the argument to + METHOD. Doc fix. (Bug#27927) + +2017-08-05 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-08-04 manywarnings: port to 64-bit GCC builds of Emacs + 2017-08-01 manywarnings: port to 32-bit GCC bug + * lib/gnulib.mk.in: Regenerate. + * m4/manywarnings.m4: Copy from gnulib. + +2017-08-05 Paul Eggert + + Port recent rename changes to Ubuntu 14.04 + + * src/sysdep.c (renameat_noreplace) [!RENAME_NOREPLACE]: + Don’t use syscall. Problem reported by Tino Calancha (Bug#27946#10). + +2017-08-05 Tino Calancha + + insert-directory-wildcard-in-dir-p: Tweak regexp + + This function must return non-nil for a wildcard like '/*/*.txt'. + * lisp/files.el (insert-directory-wildcard-in-dir-p): Adjust regexp. + * test/lisp/files-tests.el (files-tests--insert-directory-wildcard-in-dir-p): + Add test. + +2017-08-04 Toby S. Cubitt + + Implement iterator generator for avl-trees. + + * lisp/emacs-lisp/avl-tree.el (avl-tree-iter): New iter-defun. + +2017-08-04 Tino Calancha + + ls-lisp: Drop eshell dependencies + + Use 'file-expand-wildcards' instead of 'eshell-extended-glob' to + expand the wildcards. + Suggested by Fabrice Popineau in: + https://lists.gnu.org/archive/html/emacs-devel/2017-08/msg00108.html + * lisp/ls-lisp.el (ls-lisp--dired): Use file-expand-wildcards. + +2017-08-04 Tino Calancha + + Fix dired-test-bug27631 on MS-Windows + + Skip the test if Dired use 'ls' emulation with lisp. The same + bug is tested in their respective test suites: ls-lisp-tests.el + and em-ls-tests.el. + * test/lisp/dired-tests.el (dired-test-bug27631): Skip test if 'ls-lisp' + or 'eshell' features are enabled. + +2017-08-04 Eli Zaretskii + + Fix dired-test-bug25609 on MS-Windows + + * test/lisp/dired-tests.el (dired-test-bug25609): On MS-Windows, + pass temporary files through file-truename, to avoid bogus + failures due to file-name comparison as strings. + +2017-08-04 Tino Calancha + + Fix 2 tests that fail in MS-Windows + + https://lists.gnu.org/archive/html/emacs-devel/2017-08/msg00018.html + * test/lisp/vc/ediff-ptch-tests.el (ediff-ptch-test-bug26084): + Add comments to explain the test logic. + Pass '--binary' option to 'patch' program in windows environments. + Check explicitely that a backup is created before compare file contents. + + * test/lisp/dired-tests.el (dired-test-bug25609): + Declare variable 'dired-dwim-target' right before the test. + Add comments to explain the test logic. + Ensure, before test the bug condition, that we are displaying the + 2 dired buffers created in this test, and no other dired buffer + is shown. + +2017-08-04 Stefan Monnier + + * lisp/shell.el (explicit-shell-file-name): Mention shell-file-name + + * lisp/files.el (insert-directory): Don't hardcode "-c". + * lisp/term.el (term, ansi-term): Use shell-file-name. + +2017-08-04 Paul Eggert + + Fix version numbers for some GnuTLS features + + Problem reported by Glenn Morris (Bug#27708#58). + * src/gnutls.c (HAVE_GNUTLS_X509_SYSTEM_TRUST): + New macro. Use it instead of low-level version number checks. + (HAVE_GNUTLS_AEAD): Move here from gnutls.h, and rename from + HAVE_GNUTLS3_AEAD. All uses changed. Indent preprocessor lines. + * src/gnutls.h (HAVE_GNUTLS3_CIPHER, HAVE_GNUTLS3_DIGEST) + (HAVE_GNUTLS3_HMAC): Remove, since these were available + before GnuTLS 3.0.0 and the code checks them only if HAVE_GNUTLS3 + is defined. Remove all uses; this simplifies the code a bit. + +2017-08-04 Paul Eggert + + Port recent rename changes to RHEL 7 + NFS + + Problem reported by Ted Zlatanov in: + http://lists.gnu.org/archive/html/emacs-devel/2017-08/msg00082.html + * src/fileio.c (Frename_file): On RHEL 7 + NFS, renameat2 can fail + with errno == EINVAL when it is not supported. So treat that case + like errno == ENOSYS. Also, when ok_if_already_exists is neither + nil nor an integer, just call plain rename; this avoids an extra + syscall to renameat2 when the latter fails with errno == EINVAL or + ENOSYS or ENOENT. + +2017-08-03 Paul Eggert + + Port GnuTLS usage to Ubuntu 16.04.2 LTS + + * src/gnutls.h (HAVE_GNUTLS3_AEAD): Define only if GnuTLS 3.5.1 or + later, as opposed to the old 3.4.0 or later. + +2017-08-03 Paul Eggert + + Simplify configuration of HAVE_GNUTLS3 etc. + + There's only one GnuTLS, so configuring these symbols at + 'configure' time is overkill. Simplify things by moving their + configuration to src/gnutls.h (Bug#27708). + * configure.ac (HAVE_GNUTLS3, HAVE_GNUTLS3_HMAC, HAVE_GNUTLS3_AEAD) + (HAVE_GNUTLS3_CIPHER, HAVE_GNUTLS3_DIGEST): Move these definitions + from here ... + * src/gnutls.h: ... to here, and simplify. + +2017-08-03 Paul Eggert + + Default to --with-mailutils if it is installed + + * configure.ac (with_mailutils): Default to 'yes' if GNU Mailutils + is installed. See: + http://lists.gnu.org/archive/html/emacs-devel/2017-08/msg00054.html + +2017-08-02 Paul Eggert + + Clarify when autogen.sh should run only autoconf + + * Makefile.in (configure, bootstrap): Run ‘./autogen.sh autoconf’, + not plain ‘./autogen.sh’, to make it clear that only + autoconf-related tools should be run here. + +2017-08-02 Toon Claes + + .gitlab-ci.yml: Use stretch Debian image instead of unstable + +2017-08-02 Stephen Berman + + Add debugging messages to a Dired test + + * test/lisp/dired-tests.el (dired-test-bug27243-01): Log + positions saved and restored by dired-revert to try and find + out why the test fails on Hydra. + +2017-08-02 Tino Calancha + + ls-lisp: Autoload call instead of cookie + + * lisp/ls-lisp.el (eshell-extended-glob): autoload call instead of cookie. + +2017-08-02 Paul Eggert + + When renaming a file, ask only if EEXIST or ENOSYS + + * src/fileio.c (Frename_file): Avoid calling Ffile_directory_p + more than once on FILE. Use renameat_noreplace, so that we can + ask the user (and unlink and retry) only if this fails with errno + == EEXIST or ENOSYS. This avoids the need to ask the user for + permission to do an operation that will fail anyway. Simplify + computation of ok_if_already_exists for subsidiary functions. + * src/filelock.c (rename_lock_file): Prefer renameat_noreplace + if it works, as this avoids the need to link and unlink. + * src/lisp.h (renameat_noreplace): New decl. + * src/sysdep.c [HAVE_LINUX_FS_H]: Include linux/fs.h and sys/syscall.h. + (renameat_noreplace): New function. + +2017-08-02 Paul Eggert + + When creating a link, ask only if EEXIST + + * src/fileio.c (Fadd_name_to_file, Fmake_symbolic_link): + Ask the user (and unlink and retry) only if link creation fails + with errno == EEXIST. This avoids the need to ask the user for + permission to do an operation that will fail anyway. + +2017-08-02 Tino Calancha + + dired-align-file: Inherit text properties in inserted spaces + + * lisp/dired.el (dired-align-file): Inherit text + properties in inserted spaces (Bug#27899). + * test/lisp/dired-tests.el (dired-test-bug27899): Add test. + +2017-08-02 Tino Calancha + + Don't assume /bin/sh as the 'sh' location in the local host + + * lisp/dired.el (dired-insert-directory): Use executable-find in + a local host. + +2017-08-02 Tino Calancha + + Move dired tests using ls emulation to different files + + Suggested in: + https://lists.gnu.org/archive/html/emacs-devel/2017-08/msg00018.html + * test/lisp/dired-tests.el (dired-test-bug27693) + (dired-test-bug27762, dired-test-bug27817) + (dired-test-bug27631, dired-test-bug27843): Delete those + parts requiring either ls-lisp or eshell-ls. + + * test/lisp/ls-lisp-tests.el (ls-lisp-test-bug27762) + (ls-lisp-test-bug27631, ls-lisp-test-bug27693): + Add all dired tests using ls-lisp here. + + * test/lisp/eshell/em-ls-tests.el (em-ls-test-bug27631) + (em-ls-test-bug27817, em-ls-test-bug27843): New test file. Add + all dired tests using eshell-ls here. + +2017-08-02 Tino Calancha + + * test/lisp/ls-lisp-tests.el: Rename it from ls-lisp.el + +2017-08-02 Katsumi Yamaoka + + * lisp/gnus/mm-uu.el (mm-uu-org-src-code-block-extract): + Say the handle is already decoded. + cf. in the info-gnus-english list. + +2017-08-02 Paul Eggert + + Don’t worry about unlink if errno == ENOENT + + * src/fileio.c (Fdelete_file): + * src/keyboard.c (Fopen_dribble_file): Do not report failure to + remove a file if unlink fails with errno == ENOENT. This can + happen even if Emacs is the only program removing the file, in + case an NFS cache overflows. The file does not exist if errno == + ENOENT, so it is OK to proceed. + +2017-08-01 Tino Calancha + + Fix misalignment in Dired when dired-directory is a cons + + * lisp/dired.el (dired--need-align-p, dired--align-all-files): + New defuns. + (dired-internal-noselect): Call dired--align-all-files when + dired-directory is a cons (Bug#27762). + * test/lisp/dired-tests.el (dired-test-bug27762): Test should pass. + +2017-08-01 Eli Zaretskii + + Fix some dired-tests.el on MS-Windows + + * test/lisp/dired-tests.el (dired-test-bug27243-01) + (dired-test-bug27243-02): On MS-Windows, pass test-dir through + file-truename, to avoid bogus failures due to file-name comparison + as strings. + +2017-08-01 Tino Calancha + + Insert subdir content if dir-or-list is a string w/o wildcards + + * lisp/eshell/em-ls.el (eshell-ls--insert-directory): + Append '("-d") into 'eshell-ls-dired-initial-args' + if 'dired-directory' is a cons or there are wildcars (Bug#27843). + * test/lisp/dired-tests.el (dired-test-bug27843): Add test. + +2017-08-01 Stephen Berman + + Update todo-mode defcustoms in a less hideous way + + * lisp/calendar/todo-mode.el (todo-reevaluate-filelist-defcustoms) + (todo-reevaluate-default-file-defcustom) + (todo-reevaluate-category-completions-files-defcustom) + (todo-reevaluate-filter-files-defcustom): Delete these functions. + (todo-update-filelist-defcustoms): New function. This replaces + todo-reevaluate-filelist-defcustoms, using the 'custom-type' + property instead of re-evaluating the defcustoms. + (todo-add-file, todo-rename-file, todo-delete-file) + (todo-delete-category, todo-move-category) + (todo-convert-legacy-files, todo-check-file): Replace call of + todo-reevaluate-filelist-defcustoms by + todo-update-filelist-defcustoms. + (todo-show, todo-category-completions): Replace call of + todo-reevaluate-* function by use of 'custom-type' property. + +2017-08-01 Tino Calancha + + Add more should form calls in a failing dired test + + Some dired tests fail intermittently in hydra. Add few + more should form calls for debugging. + See: + https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg01092.html + * test/lisp/dired-tests.el (dired-test-bug27243-01): Add few more should + forms for debugging. + +2017-08-01 Michael Albinus + + Follow SAUNA recommendations for display-line-numbers-type + + * lisp/display-line-numbers.el (display-line-numbers-type): Do not autoload. + + * lisp/menu-bar.el (display-line-numbers-type): Declare. + +2017-07-31 Paul Eggert + + Avoid most stat calls when completing file names + + * admin/merge-gnulib (GNULIB_MODULES): Add d-type. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * m4/d-type.m4: New file, copied from gnulib. + * src/dired.c (DT_UNKNOWN, DT_DIR, DT_LINK) + [!HAVE_STRUCT_DIRENT_D_TYPE]: New constants. + (dirent_type): New function. + (file_name_completion): Use it, to avoid unnecessary calls to + stat-like functions on GNU/Linux and other platforms with d_type. + (file_name_completion_stat): Just follow the link; there is no + need to try first with AT_SYMLINK_NOFOLLOW since the directory + entry was already checked to exist. + +2017-07-31 Tino Calancha + + dired-tests: Unload tested features after test them + + Some tests are for Dired with ls-lisp or eshell-ls. + Requiring these features add an advice on `dired' and + might affect other tests. + Do not require these features at the top of the file; require + then inside the tests and unload then at the end. + * test/lisp/dired-tests.el (dired-test-bug27693) + (dired-test-bug7131, dired-test-bug27817, dired-test-bug27631): + require ls-lisp and/or eshell-ls inside the test; unload the + features at the end. + +2017-07-31 Michael Albinus + + Small adaptions for directory wildcards + + * lisp/dired.el (dired-insert-directory): Remove "--dired" + when there are wildcards, and the directory is remote. + + * test/lisp/net/tramp-tests.el (tramp--test-make-temp-name): + Adapt docstring. + (tramp-test17-dired-with-wildcards): Skip for all methods but + those from tamp-sh.p. + +2017-07-31 Tino Calancha + + * lisp/dired (dired-trivial-filenames): Use \` and \' to match string bounds + +2017-07-31 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-07-30 Don't interpret EOVERFLOW to mean nonexistence + * lib/tempname.c: Copy from gnulib. + +2017-07-30 Tino Calancha + + ls-lisp: Do not require em-glob at top of the file + + Require em-glob inside 'ls-lisp--dired'. This is necessary to + not break the Emacs build. + See following thread for details: + https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg01083.html + * lisp/ls-lisp.el (dired-goto-next-file) + (dired-read-dir-and-switches, eshell-extended-glob): + Add function declarations. + * lisp/eshell/em-ls.el (dired-goto-next-file): Fix function declaration. + +2017-07-30 Michael Albinus + + * lisp/dired.el (dired-insert-directory): Move `file-remote-p' check up. + + * test/lisp/net/tramp-tests.el (tramp-test17-dired-with-wildcards): New test. + +2017-07-30 Simen Heggestøyl + + Change default CSS property face + + * lisp/textmodes/css-mode.el (css-property): Inherit from + `font-lock-keyword-face' instead of `font-lock-variable-name-face' to + distinguish CSS properties from variables. + +2017-07-30 Tino Calancha + + Dired: Handle posix wildcards in directory part + + Allow Dired to handle calls like + \(dired \"~/foo/*/*.el\"), that is, with wildcards within + the directory part of the file argument (Bug#27631). + * lisp/files.el (insert-directory-wildcard-in-dir-p): New predicate. + (insert-directory-clean): New defun extracted from insert-directory. + (insert-directory) + * lisp/dired.el (dired-internal-noselect) + (dired-insert-directory): Use the new predicate; when it's true, + handle the directory wildcards with a shell call. + * lisp/eshell/em-ls.el (eshell-ls-use-in-dired): Add/remove both advices. + (eshell-ls-unload-hook): New defun. Use it in + eshell-ls-unload-hook instead of an anonymous function. + (eshell-ls--dired) + * lisp/ls-lisp.el (ls-lisp--dired): + Advice dired to handle wildcards in the directory part with both + eshell-ls and ls-lisp. + * etc/NEWS: Announce it. + * doc/emacs/dired.texi (Dired Enter): Update manual. + * test/lisp/dired-tests.el (dired-test-bug27631): Add test. + +2017-07-29 Tino Calancha + + * lisp/find-lisp.el: Enable lexical binding + + * lisp/find-dired.el: Enable lexical binding + +2017-07-29 Mark Oteiza + + * lisp/kmacro.el: Use lexical binding. + +2017-07-29 Mark Oteiza + + Use lexical-binding in dired-aux.el + + * lisp/dired.el: Use lexical binding. + (dired-do-shell-command): Remove unused bindings. + +2017-07-29 Mark Oteiza + + * lisp/ido.el: Use lexical binding. + + * lisp/whitespace.el: Use lexical binding. + +2017-07-29 Stephen Berman + + artist.el: Avoid error with keyboard command invocation + + * lisp/textmodes/artist.el (artist-mouse-choose-operation): + Call x-popup-menu with t instead of last-nonmenu-event as the + value of the position argument; this allows invoking the + command from the keyboard without raising an error (bug#27819). + +2017-07-29 Stephen Berman + + Preserve point under 'dired-auto-revert-buffer' (third case) + + * lisp/files.el (find-file): Use pop-to-buffer-same-window + instead of switch-to-buffer. This preserves Dired window + point when dired-auto-revert-buffer is non-nil. (Bug#27243) + + * test/lisp/dired-tests.el (dired-test-bug27243-01) + (dired-test-bug27243-02, dired-test-bug27243-03): New tests. + The first two replace a previous test that combined them; that + test intermittently fails in the Hydra build system, so maybe + separating the two cases will help locate the point of + failure. The third test involves find-file but is here + because it, like the others, is testing the effect of + dired-auto-revert-buffer. + +2017-07-29 Allen Li (tiny change) + + Do not unset user key remaps in dired-x + + * lisp/dired-x.el (dired-x-bind-find-file): Don't map any keys if user + sets dired-x-hands-off-my-keys. (Bug#27828) + +2017-07-29 Eli Zaretskii + + Improve documentation of 'occur' + + * doc/emacs/search.texi (Other Repeating Search): + * lisp/replace.el (occur): Make the documentation of 'occur' be + more accurate when matches overlap. (Bug#27818) + +2017-07-29 Eli Zaretskii + + Minor copyedits of comments in faces.el + + * lisp/faces.el (face-font-family-alternatives): More info about + requirements from "Monospace Serif". + +2017-07-29 Paul Eggert + + Do not worry about paxctl on newer NetBSD + + Problem reported privately by Thomas Klausner. + * configure.ac (emacs_uname_r): New var. Use it to avoid paxctl + on newer NetBSD platforms, where it is not needed. Also use it to + simplify Cygwin diagnostic. + +2017-07-29 Eli Zaretskii + + Clarify documentation of ':inherit' face attribute + + * doc/lispref/display.texi (Face Attributes): Document the special + treatment of 'unspecified' in the ':inherit' attribute. + +2017-07-28 Stefan Monnier + + * lisp/password-cache.el (password-data): Use a hash-table + + * lisp/auth-source.el (auth-source-magic): Remove. + (auth-source-forget+, auth-source-forget-all-cached): Adjust to new + format of password-data. + (auth-source-format-cache-entry): Just use a cons. + + (password-cache-remove, password-cache-add, password-reset) + (password-read-from-cache, password-in-cache-p): Adjust accordingly. + + (Bug#26699) + +2017-07-28 Stefan Monnier + + * lisp/subr.el (define-symbol-prop): New function + + (symbol-file): Make it find symbol property definitions. + + * lisp/emacs-lisp/pcase.el (pcase-defmacro): + * lisp/emacs-lisp/ert.el (ert-set-test): Use it instead of `put'. + (ert-describe-test): Adjust call to symbol-file accordingly. + +2017-07-28 Stefan Monnier + + * lisp/subr.el (method-files): Move function to cl-generic.el + + * lisp/emacs-lisp/cl-generic.el (cl-generic-p): New function. + (cl--generic-method-files): New function, moved from subr.el. + * lisp/emacs-lisp/edebug.el (edebug-instrument-function): Use them. + * test/lisp/emacs-lisp/cl-generic-tests.el: + * test/lisp/subr-tests.el: Move and adjust method-files tests accordingly. + +2017-07-28 Eli Zaretskii + + Preserve this-command-keys across recursive-edit invocations + + * src/minibuf.c (read_minibuf, read_minibuf_unwind): Save and + restore this-command-keys, to preserve it across recursive-edit. + (Bug#27470) + +2017-07-28 Eli Zaretskii + + Improve doc string of 'locate-dominating-file' + + * lisp/files.el (locate-dominating-file): Doc fix. (Bug#27798) + +2017-07-28 Drew Adams + + New commands 'apropos-local-variable', 'apropos-local-value' + + * lisp/apropos.el (apropos-local-variable, apropos-local-value): + New functions. (Bug#27424) + + * doc/emacs/help.texi (Apropos): Document 'apropos-local-variable' + and 'apropos-local-value'. + * etc/NEWS: Mention the new commands. + +2017-07-28 Stefan Monnier + + * lisp/loadhist.el (unload-feature): Remove ad-hoc ELP code + + * lisp/emacs-lisp/elp.el (loadhist-unload-element): Un-instrument functions. + +2017-07-27 Alan Mackenzie + + Fix C++ class initializers not always being fontified at mode start. + + The problem here happened when an "outer list" of declarations moved beyond an + "inner list" containing class initializers. These weren't being checked for + by the code. + + Also, fix places in c-get-fontification-context where point is undefined. + + * lisp/progmodes/cc-fonts.el (c-get-fontification-context): when argument + not-front-decl is set, test for class initializers. Also, anchor point in + places where it is moved and is otherwise undefined. + +2017-07-27 Alan Mackenzie + + Fix variables in C++ "for" statement not always being fontified. + + The error happened when there was a comma inside template delimiters. + + * lisp/progmodes/cc-fonts.el (c-get-fontification-context): In "for" + statements, recognise template delimiters containing "," and "&". + +2017-07-27 Michael Albinus + + Add watchdog process to tramp-test36-asynchronous-requests + + * test/lisp/net/tramp-tests.el (tramp--test-timeout-handler): + New defun. + (tramp-test36-asynchronous-requests): Use a watchdog process, + listening for SIGUSR1. + +2017-07-27 Alan Mackenzie + + CC Mode: Fix declarator being cut off from terminator by end of jit-lock chunk + + If a declarator is so cut off, extend the fontification chunk to include it. + + * lisp/progmodes/cc-mode.el (c-fl-decl-end): New function. + (c-change-expand-fl-region, c-context-expand-fl-region): Use the new function. + +2017-07-27 Stefan Monnier + + * lisp/vc/smerge-mode.el: Avoid N² blow up in degenerate cases + + (smerge--refine-long-words): New var. + (smerge--refine-chopup-region): Use it. + +2017-07-27 Stefan Monnier + + * lisp/url/url-cookie.el: Use lexical-binding + + (url-cookie-host-can-set-p): Remove unused var `last'. + Use string-suffix-p. + (url-cookie-list): De morgan. + (url-cookie-quit): Remove. + (url-cookie-mode): Inherit from special-mode. + (url-cookie-mode-map): Simplify accordingly. + +2017-07-27 Stefan Monnier + + * lisp/calendar/todo-mode.el (todo-print-buffer-function): Rework docstring. + + * lisp/ruler-mode.el (ruler-mode-ruler): Document problem. + +2017-07-27 Stefan Monnier + + * lisp/emacs-lisp/cl-generic.el (cl-generic-define-method): + + Record this as the function's definition site if it's the first def. + +2017-07-26 Glenn Morris + + * doc/lispref/loading.texi (When to Autoload): New section. + +2017-07-26 Glenn Morris + + Stop using unibyte buffers for ert backtraces + + * lisp/emacs-lisp/ert.el + (ert-results-pop-to-backtrace-for-test-at-point): + Set multibyte true, not false. This copies a + debugger-setup-buffer change from 2009-08-30, and stops the + "Backtrace for" header line containing ^X and ^Y. + +2017-07-26 Dmitry Gutov + + Fix semantic-symref-parse-tool-output-one-line after 644cdd1aa0 + + * lisp/cedet/semantic/symref/grep.el + (semantic-symref-grep--line-re): Delete. + (semantic-symref-parse-tool-output-one-line): + Use regexp and group numbers from (grep-regexp-alist). + +2017-07-26 Grégoire Jadi + + Fix cl-defmethod indentation + + * lisp/emacs-lisp/cl-generic.el (cl-defmethod): + Declare (indent defun). Fixes bug#23994. + +2017-07-26 Martin Rudalics + + Fix two customization types in frame.el + + * lisp/frame.el (window-divider-default-bottom-width) + (window-divider-default-right-width): Fix customization types. + +2017-07-26 Tino Calancha + + Dired: Support eshell-ls from the beginning if the user wants to + + * lisp/dired.el (dired-insert-directory): Check for eshell-ls + as well (Bug#27817). + * test/lisp/dired-tests.el (dired-test-bug27817): Add test. + +2017-07-26 Mark Oteiza + + * lisp/progmodes/sh-script.el (sh-mode): Recognize mkshrc. + +2017-07-25 Stefan Monnier + + * lisp/emacs-lisp/eieio-compat.el (eieio--defgeneric-init-form): + + Adjust to change in cl-generic-ensure-function. + +2017-07-25 Tino Calancha + + ls-lisp: Add an unload function and enable lexical binding + + Enable lexical binding. + * lisp/ls-lisp.el (ls-lisp-unload-function): New defun. + * test/lisp/ls-lisp.el (ls-lisp-unload): Add test. + +2017-07-25 Tino Calancha + + register-read-with-preview: Quit if user input C-g or ESC + + * lisp/register.el (register-read-with-preview): + Quit if user input C-g or ESC (bug#27634). + * doc/emacs/regs.texi (Registers): Update manual. + * test/lisp/register-tests.el (register-test-bug27634): Add test. + +2017-07-25 Mark Oteiza + + Recognize MirBSD Korn shell rc file + + * lisp/files.el (auto-mode-alist): Add .mkshrc to the list. + +2017-07-25 Glenn Morris + + * configure.ac: Be explicit about ImageMagick version in summary. + +2017-07-25 Andreas Schwab + + Properly align global lispsym + + * lib-src/make-docfile.c (close_emacs_globals): Wrap struct + Lisp_Symbols inside struct. + * src/alloc.c (sweep_symbols): Update use of lispsym. + * src/lisp.h (builtin_lisp_symbol): Likewise. + +2017-07-25 Paul Eggert + + Do not use ImageMagick 7 and later + + Suggested by Glenn Morris (Bug#25967#15). + * configure.ac (IMAGEMAGICK_MODULE): Reject 7 and later. + +2017-07-25 Stefan Monnier + + * lisp/progmodes/perl-mode.el: Add support for indented here docs + + * lisp/progmodes/perl-mode.el (perl-syntax-propertize-function): + Recognize the new <<~ syntax for indented here docs. + (perl-syntax-propertize-special-constructs): Adjust search of the + end of here docs accordingly. + + * test/manual/indent/perl.perl: Add test for indented here docs. + +2017-07-24 Stefan Monnier + + (loadhist-unload-element): Move ERT and cl-generic methods + + * lisp/loadhist.el (loadhist-unload-element): Don't define cl-generic + and ert methods here. + (loadhist-unload-element) <(head define-type)>: Remove unused var `slots'. + + * lisp/emacs-lisp/cl-generic.el (loadhist-unload-element): Define + unload method for cl-defmethod. + (cl-generic-ensure-function): Remove redundant `defalias'. + + * lisp/emacs-lisp/ert.el (ert-set-test): Move the current-load-list + setting here... + (ert-deftest): ...from here. + (loadhist-unload-element): Define unload method for ert-deftest. + +2017-07-24 Michael Albinus + + Fix Bug#27371 + + * lisp/loadhist.el (loadhist-unload-element): Declare for + different entry types of `load-history'. + (loadhist--restore-autoload): New variable. + (loadhist--unload-function): New defun. + (unload-feature): Use `loadhist-unload-element'. Recommended by + Stefan Monnier. (Bug#27371) + + * test/lisp/net/tramp-tests.el (tramp-test39-unload): + Check, that the `tramp-file-name' structure has been unloaded. + +2017-07-24 Grégoire Jadi + + Ensure that we parse images right in shr.el + + * lisp/net/shr.el (shr-image-fetched): Go back to the + beginning of the buffer before trying to parse the image + fetched. + +2017-07-24 Paul Eggert + + Update .gitignore for Valgrind and no Automake + + * .gitignore: Remove .deps/ since we no longer use Automake. + Add vgcore.*[0-9], for debugging Emacs with Valgrind+GDB. + +2017-07-24 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-07-23 Rename module 'strftime' to 'nstrftime' + * admin/merge-gnulib (GNULIB_MODULES): Add nstrftime, remove strftime. + * build-aux/config.guess: Copy from gnulib. + * lib/nstrftime.c: Rename from lib/strftime.c. + * m4/nstrftime.m4: Rename from m4/strftime.m4. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + +2017-07-23 Philipp Stephani + + Add 'rx' pattern for pcase. + + * lisp/emacs-lisp/rx.el (rx): New pcase macro. + * test/lisp/emacs-lisp/rx-tests.el (rx-pcase): Add unit test. + +2017-07-23 Mark Oteiza + + Use a named function for global minor mode turn-on argument + + * lisp/display-line-numbers.el (turn-on-display-line-numbers-mode): + New function. + (global-display-line-numbers-mode): Use it. + +2017-07-23 Charles A. Roelli + + Enable GUI Emacs without 'make install' on macOS (Bug #27645) + + * nextstep/INSTALL: Correct it, and mention that Emacs can be run + from 'src/emacs'. + + * src/nsterm.m (applicationDidFinishLaunching:): When Emacs is + launched outside of a macOS application bundle, change its + activation policy from the default 'prohibited' to 'regular'. + +2017-07-23 Alan Mackenzie + + Convert CC Mode's c-found-types from an obarray to a hash table. + + * lisp/progmodes/cc-engine.el (c-clear-found-types): create a hash table + rather than an obarray. + (c-copy-found-types): Remove. + (c-add-type, c-unfind-type, c-check-type, c-list-found-types): Amend to use + the new hash table. + (c-forward-<>-arglist): Use copy-hash-table rather than c-copy-found-types. + +2017-07-23 Lars Ingebrigtsen + + Fix image/svg+xml display in shr + + * lisp/net/shr.el (shr-put-image): Display svg images as svg + (bug#27799). I suspect the previous change was checked in by + accident in conjuction with some other svg changes. + +2017-07-23 Michael Albinus + + * lisp/display-line-numbers.el (display-line-numbers-type): Autoload it. + +2017-07-23 Glenn Morris + + Don't automatically enable Gconf if Gsettings was found + + * configure.ac (HAVE_GCONF) [HAVE_GSETTINGS]: + Don't test for Gconf unless specifically requested. + Gconf was deprecated in favor of Gsettings several years ago. + +2017-07-23 Glenn Morris + + * configure.ac (MODULES_SUFFIX): Always give it a value. + + This prevents a Makefile thinko like "rm *${MODULE_SUFFIX}". + +2017-07-23 Glenn Morris + + * doc/emacs/frames.texi (Fonts): Mention Gsettings. + +2017-07-22 Michael Albinus + + Add line numbers display to the Options menu + + * lisp/menu-bar.el (toggle-display-line-numbers): Remove. + (menu-bar-display-line-numbers-mode): New defun. + (menu-bar-showhide-line-numbers-menu): New defvar. + (menu-bar-showhide-menu): Use `menu-bar-showhide-line-numbers-menu' + +2017-07-22 Noam Postavsky + + Signal error for symbol names with strange quotes (Bug#2967) + + * src/lread.c (read1): Signal an error when a symbol starts with a + non-escaped quote-like character. + * test/src/lread-tests.el (lread-tests--funny-quote-symbols): New + test. + * etc/NEWS: Announce change. + +2017-07-22 Noam Postavsky + + Revert "Let delete-selection-mode work with popup-menu commands (Bug#27569)" + + It turns out that this change is not needed, and it leaves several + command loops settings not done. + + https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00757.html + https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00840.html + +2017-07-22 Alexander Gramiak + + Add a minor mode interface for display-line-numbers + + * lisp/cus-start.el: Use the new display-line-numbers group. + * lisp/display-line-numbers.el: New file. + + * doc/emacs/custom.texi (Init Rebinding): Re-add entry that used to + belong to linum-mode. + * doc/emacs/modes.texi (Minor Modes): Summarize the mode. + * etc/NEWS: Document display-line-numbers-mode and its customization + variables, and mention that display-line-numbers-width is + buffer-local. + + * src/xdisp.c (syms_of_xdisp) : Fix a + typo. + +2017-07-22 vividsnow (tiny change) + + Support indented HERE-DOCs in cperl-mode + + * lisp/progmodes/cperl-mode.el (cperl-find-pods-heres): Support + indented here-docs. (Bug#27254) (Bug#27697) + +2017-07-22 Eli Zaretskii + + Document the support for "scrollBar" X resource + + * doc/emacs/xresources.texi (Table of Resources): Document the new + 'scrollBar' setting. + * etc/NEWS: Document the new 'scrollBar' setting. + +2017-07-22 Matthew Bauer (tiny change) + + Add 'scroll-bar-mode' to settings in 'x-apply-session-resources' + + * lisp/startup.el (x-apply-session-resources): Add scroll-bar-mode + settings. + +2017-07-22 Alexander Kuleshov + + Update ld-script mode (bug#27629) + + * lisp/progmodes/ld-script.el: (ld-script-keywords): New commands + NOCROSSREFS_TO and HIDDEN added. Fix documentation sections + numbers for PROVIDE/PROVIDE_HIDDEN commands. + (ld-script-builtins): New builtin function LOG2CEIL added. + +2017-07-22 Eli Zaretskii + + Index 'rectangle' in the ELisp manual + + * doc/lispref/text.texi (Registers): Index the "rectangle" value. + (Bug#27541) + +2017-07-22 Eli Zaretskii + + * lisp/subr.el (add-to-history): Doc fix. (Bug#27494) + +2017-07-22 Eli Zaretskii + + Doc fixes for kmacro.el functions + + * lisp/kmacro.el (kmacro-start-macro, kmacro-call-macro) + (kmacro-end-and-call-macro): Don't use "permanent name", as that + could be misinterpreted. (Bug#27492) + +2017-07-22 Charles A. Roelli + + ElDoc: add docstrings and minor refactoring + + * lisp/emacs-lisp/eldoc.el (eldoc-edit-message-commands): Add + docstring. + (turn-on-eldoc-mode): Fix capitalization. + (eldoc--supported-p): Add docstring. + (eldoc-schedule-timer): Add docstring and use + 'eldoc--supported-p'. + (eldoc-message): Add docstring and make calling convention + clearer. + (eldoc--message-command-p): + (eldoc-pre-command-refresh-echo-area): + (eldoc-display-message-p): + (eldoc-display-message-no-interference-p): + (eldoc-print-current-symbol-info): + (eldoc-docstring-format-sym-doc): + (eldoc-add-command, eldoc-add-command-completions): + (eldoc-remove-command, eldoc-remove-command-completions): + Add docstring. (Bug#27230) + +2017-07-22 Fabrice Bauzac (tiny change) + + Mention 'C-M-i' as key binding for 'ispell-complete-word' + + * doc/emacs/fixit.texi (Spelling): ispell-complete-word + can also be invoked by C-M-i. (Bug#27349) + +2017-07-22 Fabrice Bauzac (tiny change) + + Fix the eww-search-words description in the Emacs manual + + * doc/emacs/search.texi (Word Search): + Include the key binding for eww-search-words in the manual. + Fix the spelling of the 'eww-search-words' command. + +2017-07-22 Andrew L. Moore + + Introduce defcustom 'executable-prefix-env' + + * lisp/progmodes/executable.el (executable-prefix): Update the doc + string. + (executable-prefix-env): New defcustom. + (executable-set-magic): Use executable-prefix-env. + + * etc/NEWS: Document the new variable. + +2017-07-22 Glenn Morris + + * test/lisp/ibuffer-tests.el: Delete temporary files. + +2017-07-21 Glenn Morris + + Further attempt to avoid hang in network-stream-tests + + * test/lisp/net/network-stream-tests.el (connect-to-tls-ipv6-nowait): + Limit the time we wait for the external process. + +2017-07-21 Glenn Morris + + Stop skipping many ibuffer tests by default + + * test/lisp/ibuffer-tests.el (ibuffer-0autoload): + Rename so it sorts first. + (ibuffer-save-filters, ibuffer-filter-inclusion-1) + (ibuffer-filter-inclusion-2, ibuffer-filter-inclusion-3) + (ibuffer-filter-inclusion-4, ibuffer-filter-inclusion-5) + (ibuffer-filter-inclusion-6, ibuffer-filter-inclusion-7) + (ibuffer-filter-inclusion-8, ibuffer-decompose-filter) + (ibuffer-and-filter, ibuffer-or-filter, ibuffer-format-qualifier) + (ibuffer-unary-operand): Require ibuf-ext so tests not skipped. + +2017-07-21 Stefan Monnier + + Use lexical-binding in todo-mode.el + + Adjust code accordingly and make various minor improvements. + + * lisp/calendar/todo-mode.el: Enable lexical-binding. + (dayname, monthname, day, month, year): Make forward defvars + of these keywords from macros defined in calendar.el; wrap + them in with-no-warnings. + (todo-files, todo-files-function, todo-date-pattern) + (todo-mode-line-function, todo-show, todo-forward-category) + (todo-edit-item--header, todo-set-category-number) + (todo-adjusted-category-label-length) + (todo-total-item-counts, todo-filter-items) + (todo-print-buffer-function, todo-convert-legacy-date-time) + (todo-category-number, todo-category-completions) + (todo-read-file-name, todo-read-category) + (todo-validate-name, todo-read-date) + (todo-set-show-current-file, todo-modes-set-1) + (todo-modes-set-2, todo-modes-set-3, todo-mode): + Use #' instead of ' to quote functions. + (todo-files): Use \' instead of $ in regexp. + (todo--files-type-list): New function. + (todo-default-todo-file, todo-category-completions-files) + (todo-filter-files, todo-multiple-filter-files) + (todo-reevaluate-default-file-defcustom) + (todo-reevaluate-category-completions-files-defcustom) + (todo-reevaluate-filter-files-defcustom): Use it. + (todo-show, todo-rename-file, todo-move-category) + (todo-edit-item--text, todo-edit-quit, todo-edit-item--header) + (todo-item-undone, todo-unarchive-items, todo-search) + (todo-filter-items, todo-filter-items-1, todo-find-item) + (todo-category-select, todo-read-date) + (todo-nondiary-marker-matcher, todo-date-string-matcher) + (todo-diary-expired-matcher, todo-convert-legacy-files) + (todo-read-category): Reformat to avoid code hiding behind a + more deeply embedded element. + (todo-forward-category, todo-set-category-number): + Use 'funcall' instead of 'apply'. + (todo-toggle-mark-item, todo-edit-item--diary-inclusion) + (todo-edit-category-diary-inclusion) + (todo-insert-sort-button, todo-insert-category-line) + (todo-multiple-filter-files): Mark unused local variables. + (todo-edit-item--header, todo-move-item, todo-print-buffer) + (todo-edit-item--header, todo-move-item, todo-check-file) + (todo-edit-item--next-key): Remove unused local variables. + (todo-insert-sort-button, todo-insert-category-line): + Use a closure instead of a backquoted lambda. + (todo-update-categories-display, todo-print-buffer): Simplify code. + (todo-print-buffer-function): Document calling convention. + (todo-category-completions): Use cl-pushnew instead of add-to-list. + (todo-mode-map, todo-archive-mode-map) + (todo-categories-mode-map, todo-filtered-items-mode-map): + Remove superfluous call of suppress-keymap, since it's already + in the parent special-mode-map. + +2017-07-21 Tino Calancha + + dired: Revert buffer when DIRNAME is a cons + + * lisp/dired.el (dired-internal-noselect): Revert buffer if DIR-OR-LIST + is a cons, or dired-directory is a cons and DIR-OR-LIST a string (Bug#7131). + Update the comments. + * test/lisp/dired-tests.el (dired-test-bug7131): Test should pass. + +2017-07-21 Tino Calancha + + Handle when dired-directory is a cons in some Dired functions + + * lisp/dired-aux.el (dired-rename-subdir-1) + * lisp/dired-x.el (dired-mark-omitted): + Handle when dired-directory is a cons. + +2017-07-21 Noam Postavsky + + Make eshell-next-prompt more reliable (Bug#27405) + + * lisp/eshell/em-prompt.el (eshell-next-prompt): Search for + `eshell-prompt-regexp' (and `read-only' text-property if + `eshell-highlight-prompt' is set) rather than trying to use + `forward-paragraph'. + (eshell-previous-prompt): Don't count prompt on current line. + +2017-07-21 Paul Eggert + + Simplify recent gnutls.c changes + + * src/gnutls.c (clear_storage) [HAVE_GNUTLS3_AEAD]: Remove. + All uses replaced by calls to explicit_bzero; that’s clear enough. + (gnutls_symmetric_aead) [HAVE_GNUTLS3_AEAD]: Simplify by + coalescing duplicate actions. There is no need to invoke + SAFE_FREE before calling ‘error’. + +2017-07-20 Michael Albinus + + Stylistic changes in tramp-cache.el + + * test/lisp/net/tramp-cache.el (tramp-get-file-property) + (tramp-set-file-property): Use `bound-and-true-p'. Add + counter variables to `tramp-cache-unload-hook'. + +2017-07-20 Glenn Morris + + * admin/notes/hydra: Small updates. + +2017-07-20 Glenn Morris + + Make tramp unloading handle debug counter variables + + * lisp/net/tramp-cache.el (tramp-get-file-property) + (tramp-set-file-property): Add counter variables to tramp-unload-hook. + +2017-07-20 Eli Zaretskii + + Fix hscrolling calculations when display-line-numbers is set + + * src/xdisp.c (move_it_in_display_line_to): Account for line + numbers in hscrolled lines. (Bug#27756) + +2017-07-20 Katsumi Yamaoka + + Fix the bogus change made 13 years ago (bug#27084) + + * lisp/gnus/gnus-sum.el (gnus-summary-toggle-header): + Fix the way to test if there is no visible header (bug#27084). + +2017-07-20 Noam Postavsky + + Use grep's --null option (Bug#6843) + + * lisp/progmodes/grep.el (grep-use-null-filename-separator): New option. + (grep--regexp-alist-column, grep--regexp-alist-bin-matcher) + (grep-with-null-regexp-alist, grep-fallback-regexp-alist): New + constants, replacing `grep-regexp-alist'. + (grep-regex-alist): Mark the variable obsolete, add a new function of + the same name to replace it. + (grep-compute-defaults): Compute default for + `grep-use-null-filename-separator'. + (grep-mode): Set compilation-error-regexp-alist (buffer locally) to the + value of `grep-with-null-regexp-alist' or `grep-fallback-regexp-alist' + according to `grep-use-null-filename-separator'. + * lisp/progmodes/xref.el (xref-collect-matches): Call + `grep-regex-alist' instead of the obsolete variable. Don't hardcode + grep-regexp-alist match groups. + * etc/NEWS: Announce new use of --null. Move 'grep-save-buffers' + item under "Grep" heading as well. + +2017-07-19 Philipp Stephani + + * src/gnutls.c (clear_storage): Define only if needed. + +2017-07-19 Stephen Berman + + Adjust todo-quit to recent change in dired + + * lisp/calendar/todo-mode.el (todo-quit): Use quit-window instead of + bury-buffer to exit todo-mode. This restores the desired behavior + of not immediately returning to the exited todo-mode buffer on + quitting another buffer, which a dired bug fix had changed (see + http://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00739.html). + +2017-07-19 Tino Calancha + + Add test for bugs 7131, 27762 + + Require 'ls-lisp' at top of the file. + * test/lisp/dired-tests.el (dired-test-bug7131, dired-test-bug27762): + New tests. + (dired-test-bug27693): Delete Dired buffer at the end. + +2017-07-18 Michael Albinus + + * admin/notes/hydra: Mention environment variable EMACS_HYDRA_CI. + +2017-07-18 Stefan Monnier + + * lisp/emacs-lisp/nadvice.el (advice--defalias-fset): Strip advices + + This tries to make sure that (defalias F (symbol-function F)) stays a no-op. + +2017-07-18 Glenn Morris + + Use a more specific test for running on hydra.nixos.org + + * lisp/emacs-lisp/ert.el (ert-summarize-tests-batch-and-exit): + * test/Makefile.in (WRITE_LOG): + * test/lisp/filenotify-tests.el: + * test/lisp/emacs-lisp/eieio-tests/eieio-test-methodinvoke.el + (eieio-test-method-order-list-6): + * test/lisp/emacs-lisp/eieio-tests/eieio-tests.el + (eieio-test-37-obsolete-name-in-constructor): + * test/lisp/net/tramp-tests.el: Replace NIX_STORE with EMACS_HYDRA_CI. + +2017-07-18 Eli Zaretskii + + Avoid infloop due to Eshell's "smart" redisplay + + * src/xdisp.c (pos_visible_p): Save and restore the window's + mode-line and header-line height. (Bug#27752) + +2017-07-18 Stefan Monnier + + * emacs-lisp/cl-lib.el (cl--old-struct-type-of): Accept `[]' + +2017-07-18 Eli Zaretskii + + Fix indentation when display-line-numbers is non-nil + + * src/xdisp.c (x_produce_glyphs): Fix a typo in deciding whether + to go one more tab stop to display a TAB. (Bug#27743) + +2017-07-18 Lars Ingebrigtsen + + Don't use gtk_widget_get_scale_factor on old GTK3 versions + + * src/gtkutil.c (xg_get_scale): gtk_widget_get_scale_factor is + only present since GTK 3.10. + +2017-07-18 Noam Postavsky + + Let delete-selection-mode work with popup-menu commands (Bug#27569) + + * lisp/menu-bar.el (popup-menu): Run `pre-command-hook' with + `this-command' set to the selected command. + +2017-07-18 Paul Eggert + + Port gnutls.c to older (buggier?) GnuTLS + + Problem reported for GnuTLS 3.2.1 by Glenn Morris in: + http://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00716.html + http://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00742.html + Although I don't see how this bug can occur with vanilla GnuTLS 3.2.1, + perhaps hydra was using a modified GnuTLS. + * src/gnutls.c (Fgnutls_ciphers): Don't assume GNUTLS_CIPHER_NULL + is at the end of the list returned by gnutls_cipher_list, + or that the earlier ciphers all have non-null names. + +2017-07-17 Vincent Belaïche + + Fix relocation with named cell referred to by a one-symbol formula. + + * lisp/ses.el (ses-replace-name-in-formula): Fix bug for it to + work also with one symbol formulas. + + * test/lisp/ses-tests.el + (ses-tests-renaming-cell-with-one-symbol-formula): Add new + test for renaming with relocating a one symbol formula. + +2017-07-17 Vincent Belaïche + + Fix symbol completion and document it. + + * doc/misc/ses.texi (Configuring what printer function + applies): Add description of keys for completing local printer + symbols and listing local printers in a help buffer. + (Formulas): Add decription for key to list the named cell + symbols in a help buffer. + + * lisp/ses.el (ses-completion-keys): New constant. + (ses--completion-table): New defvar. + (ses--list-orig-buffer): New defvar. + (ses-mode-edit-map): Fixed for symbol completion, plus add + help functions to list named cells or local printers. + (ses-edit-cell-complete-symbol) + (ses--edit-cell-completion-at-point-function): New defuns for + completion during formula edition. + (ses-edit-cell): Redefine dynamically edit keymap for + completion keys to point at the right function. + (ses-read-printer-complete-symbol) + (ses--read-printer-completion-at-point-function): New defuns + for completion during printer edition. + (ses-read-printer): Redefine dynamically edit keymap for + completion keys to point at the right function. + (ses-list-local-printers): New defun. + (ses-list-named-cells): New defun. + +2017-07-17 Lars Ingebrigtsen + + Move comments around + +2017-07-17 Lars Ingebrigtsen + + Make scaling work (?) on pre-GTK3 systems + + * src/gtkutil.c (xg_get_gdk_scale): Reinstate function. + (xg_get_scale): Use it on non-GTK3 systems. + +2017-07-17 Lars Ingebrigtsen + + Always return the GDK scale + + * src/gtkutil.c (xg_get_scale): Return the GDK scale always. + +2017-07-17 Lars Ingebrigtsen + + Remove usage of the GDK_SCALE variable + + * src/gtkutil.c (xg_get_gdk_scale): Remove. + (xg_get_default_scrollbar_height) + (xg_get_default_scrollbar_width): Pass in a frame to check for + scaling. + (xg_frame_set_char_size): Use the API for querying scale + instead of looking at the GDK_SCALE variable. + (xg_get_default_scrollbar_width): Ditto. + (xg_get_default_scrollbar_height): Ditto. + (xg_update_scrollbar_pos): Ditto. + + * src/xfns.c (x_set_scroll_bar_default_height): Pass in the + frame to get the width. + +2017-07-17 Lars Ingebrigtsen + + Get positions of menus and tooltips right on HiDPI + + * src/gtkutil.c (xg_get_scale): New function. + (xg_show_tooltip): Use it. + + * src/xmenu.c (create_and_show_popup_menu): Put menus in the + right place. + +2017-07-17 Eli Zaretskii + + Allow user control on what starts and ends a paragraph for bidi + + * src/buffer.h (struct buffer): New members + bidi_paragraph_separate_re_ and bidi_paragraph_start_re_. + * src/buffer.c (bset_bidi_paragraph_start_re) + (bset_bidi_paragraph_separate_re): New setters/ + (Fbuffer_swap_text): Swap the values of bidi-paragraph-start-re and + bidi-paragraph-separate-re. + (init_buffer_once): Init the values of bidi-paragraph-start-re and + bidi-paragraph-separate-re. + (syms_of_buffer) : + New per-buffer variables. + * src/bidi.c (bidi_at_paragraph_end, bidi_find_paragraph_start): + Support bidi-paragraph-start-re and bidi-paragraph-separate-re. + (bidi_move_to_visually_next): Handle correctly the case when the + separator matches an empty string. (Bug#27526) + + * doc/emacs/mule.texi (Bidirectional Editing): + * doc/lispref/display.texi (Bidirectional Display): Document + bidi-paragraph-start-re and bidi-paragraph-separate-re. + + * etc/NEWS: Mention bidi-paragraph-start-re and + bidi-paragraph-separate-re. + +2017-07-17 Tino Calancha + + * lisp/emacs-lisp/map.el (map-put): Fix redundancy in docstring. + +2017-07-17 Tino Calancha + + alist-get: Add optional arg TESTFN + + If TESTFN is non-nil, then it is the predicate to lookup + the alist. Otherwise, use 'eq' (Bug#27584). + * lisp/subr.el (alist-get): Add optional arg FULL. + * lisp/emacs-lisp/map.el (map-elt, map-put): Add optional arg TESTFN. + * lisp/emacs-lisp/gv.el (alist-get): Update expander. + * doc/lispref/lists.texi (Association Lists): Update manual. + * etc/NEWS: Announce the changes. + * test/lisp/emacs-lisp/map-tests.el (test-map-put-testfn-alist) + (test-map-elt-testfn): New tests. + +2017-07-17 Michael Albinus + + Fix `tramp-test39-unload' + + * test/lisp/net/tramp-tests.el (tramp--test-instrument-test-case-p) + (tramp--test-instrument-test-case): Rename. Adapt all callees. + (tramp-test36-asynchronous-requests): Bind `timer-max-repeats'. + (tramp-test39-unload): Expect it to pass. Ignore buffer-local + variables and autoload functions; they are not removed. Check + also for `-function(s)'. + +2017-07-17 Stephen Berman + + Preserve point under 'dired-auto-revert-buffer' (second case) + + * lisp/dired.el (dired): Use pop-to-buffer-same-window instead + of switch-to-buffer. This preserves Dired window point when + dired-auto-revert-buffer is non-nil. (Bug#27243) + + * test/lisp/dired-tests.el (dired-test-bug27243): New test. + +2017-07-17 Martin Rudalics + + Have Fgnutls_available_p return Qnil when GNUTLS is undefined + + * src/gnutls.c (Fgnutls_available_p): Return Qnil when GNUTLS is + undefined to allow --with-gnutls=no builds to proceed. + +2017-07-17 Paul Eggert + + * src/gnutls.c: Restore some comments. + +2017-07-17 Paul Eggert + + Use memset, not bzero + + * src/ftcrfont.c (ftcrfont_glyph_extents): Use memset instead + of the (less-portable) bzero. + +2017-07-17 Paul Eggert + + Use explicit_bzero to clear GnuTLS keys + + * admin/merge-gnulib (GNULIB_MODULES): Add explicit_bzero. + * lib/explicit_bzero.c, m4/explicit_bzero.m4: New files. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * src/gnutls.c (clear_storage): New function. + (gnutls_symmetric_aead): Use it instead of memset. + +2017-07-17 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-07-16 explicit_bzero: new module + 2017-07-15 getdtablesize: Add minimal support for OpenVMS. + * lib/getdtablesize.c, lib/string.in.h, m4/getdtablesize.m4: + * m4/string_h.m4: + Copy from Gnulib. + * lib/gnulib.mk.in: Regenerate. + +2017-07-17 Dieter Deyke + + Fix vc-src-dir-status-files + + * lisp/vc/vc-src.el (vc-src-dir-status-files): Fix broken + copy-paste from b1a765b3 (bug#27641). + +2017-07-16 Wilfred Hughes + + Fix mismatched parens + + * etc/NEWS.21: Remove excess parenthesis in code example + +2017-07-16 Alan Third + + Add missing declare-function for new function + + * lisp/frame.el: Add declare function for + ns-mouse-absolute-pixel-position. + +2017-07-16 R. Bernstein + + Realgud for tango themes + +2017-07-16 Noam Postavsky + + Fix test when running from test/lisp/subr-tests.elc + + * test/lisp/subr-tests.el (subr-test-backtrace-simple-tests): Don't + assume a lambda expression will be `equal' to its quoted form. That's + not true if the lambda expression has been compiled. + +2017-07-15 Alan Third + + Fix some frame handling issues on NS + + * lisp/frame.el (mouse-absolute-pixel-position): Use new NS function. + * src/nsfns.m (Sns_mouse_absolute_pixel_position): New function. + * src/nsterm.m (x_make_frame_visible): Re-establish parent-child + relationship if it's broken. + +2017-07-15 Tino Calancha + + ls-lisp: Fix file size format + + * lisp/ls-lisp.el (ls-lisp-filesize-d-fmt, ls-lisp-filesize-f-fmt) + (ls-lisp-filesize-b-fmt): Add space in front (Bug#27693). + * test/lisp/dired-tests.el (dired-test-bug27693): Add test. + +2017-07-15 Eli Zaretskii + + Avoid link errors with older versions of GnuTLS + + * src/gnutls.c (Fgnutls_ciphers, gnutls_symmetric_aead) + (Fgnutls_macs, Fgnutls_digests): Conditionally compile code that + calls GnuTLS functions which might be unavailable in older + versions of GnuTLS. + +2017-07-15 Eli Zaretskii + + Improve comments in faces.el + + * lisp/faces.el (face-font-family-alternatives): Improve + commentary. + +2017-07-15 Eli Zaretskii + + Improve some GnuTL error messages + + * src/gnutls.c (gnutls_symmetric_aead, gnutls_symmetric): + * src/fns.c (Fsecure_hash_algorithms): Fix error messages. + +2017-07-15 Eli Zaretskii + + Fix the Elisp manual wrt GnuTL cryptography + + * doc/lispref/elisp.texi (Top): Update the master menu. + * doc/lispref/text.texi (GnuTLS Cryptography): Add a @menu, to + avoid errors in makeinfo. + +2017-07-15 Eli Zaretskii + + Fix compilation of gnutls.c with older GnuTLS + + * src/gnutrls.c (syms_of_gnutls): Condition some defsubr's + on HAVE_GNUTLS3, to avoid compilation errors when GnuTLS + v3.X is not available. Reported by Colin Baxter . + +2017-07-15 rocky + + Realgud for two more light themes + +2017-07-15 Eli Zaretskii + + Rearrange MS-Windows code that dynamically loads GnuTLS functions + + * src/gnutls.c [WINDOWSNT]: Reorganize definitions and loading + of functions using the same preprocessing directives as in the code. + +2017-07-15 Eli Zaretskii + + Fix the MS-Windows build broken in gnutls.c + + * src/gnutls.c (Fgnutls_available_p) [WINDOWSNT]: Move the DLL + loading code to after 'capabilities' has been calculated. Remove + redundant comments. + +2017-07-15 Lars Ingebrigtsen + + src/image.c (compute_image_size): Remove superfluous checks. + + * src/image.c (compute_image_size): Remove superfluous checks. + +2017-07-15 Lars Ingebrigtsen + + Make combinations of :width/:max-height image specs work reliably + + * doc/lispref/display.texi (ImageMagick Images): Document + :width/:max-height combinations (etc) (bug #25583). + + * src/image.c (compute_image_size): Handle :width/:max-height + (etc) combinations consistently (by letting "max" win and + preserve ratio). + + * test/manual/image-size-tests.el (image-size-tests): Add + tests for :width/:max-height (etc) combinations. + +2017-07-15 Glenn Morris + + Fix recent theme changes + + * etc/themes/manoj-dark-theme.el, etc/themes/tsdh-dark-theme.el: + Fix typos in recent changes. + +2017-07-15 Paul Eggert + + GnuTLS integer-overflow and style fixes + + This tweaks the recently-added GnuTLS improvements so that + they avoid some integer-overflow problems and follow typical + Emacs style a bit better. + * configure.ac (HAVE_GNUTLS3_HMAC, HAVE_GNUTLS3_AEAD) + (HAVE_GNUTLS3_CIPHER): Use AC_CACHE_CHECK so that the + configure-time results are displayed. + * src/fns.c (extract_data_from_object): Return char *, not char + const *, since one gnutls caller wants a non-const pointer. Use + CONSP rather than !NILP when testing for conses. Use CAR_SAFE + instead of rolling our own code. Prefer signed types to unsigned + when either will do. Report problems for lengths out of range, + instead of silently mishandling them. + * src/gnutls.c (emacs_gnutls_strerror): New function, to simplify + callers. All callers of gnutls_sterror changed. + (Fgnutls_boot): Check for integers out of range rather than + silently truncating them. + (gnutls_symmetric_aead): Check for integer overflow in size + calculations. + (gnutls_symmetric_aead, Fgnutls_macs, Fgnutls_digests): + Prefer signed to unsigned integers where either will do. + (gnutls_symmetric_aead, gnutls_symmetric): + Work even if ptrdiff_t is wider than ‘long’. + (gnutls_symmetric, Fgnutls_hash_mac, Fgnutls_hash_digest): + Check for integer overflow in algorithm selection. + +2017-07-14 Noam Postavsky + + * .gitlab-ci.yml: Don't install a C++ compiler. Suppress apt interaction. + +2017-07-14 Eli Zaretskii + + Fix the MS-Windows build due to added GnuTLS functions + + * src/gnutls.c [WINDOWSNT]: Add DEF_DLL_FN for new functions. + (init_gnutls_functions) [WINDOWSNT]: Add LOAD_DLL_FN for new + functions. Add #define redirections for new functions. + (gnutls_symmetric_aead): Fix format specs to be more portable when + printing ptrdiff_t arguments. + * src/fns.c (gnutls_rnd) [WINDOWSNT]: Redirect to w32_gnutls_rnd + wrapper. + * src/gnutls.h [WINDOWSNT]: Add prototype for w32_gnutls_rnd. + + * test/lisp/net/gnutls-tests.el (gnutls-tests-tested-macs) + (gnutls-tests-tested-digests, gnutls-tests-tested-ciphers): Call + gnutls-available-p, otherwise GnuTLS functions might not be loaded + from the DLL on MS-Windows. + +2017-07-14 Stefan Monnier + + * lisp/emacs-lisp/bytecomp.el: Fix bug#14860. + + * lisp/emacs-lisp/bytecomp.el (byte-compile--function-signature): New fun. + Dig into advice wrappers to find the "real" signature. + (byte-compile-callargs-warn, byte-compile-arglist-warn): Use it. + (byte-compile-arglist-signature): Don't bother with "new-style" arglists, + since bytecode functions are now handled in byte-compile--function-signature. + + * lisp/files.el (create-file-buffer, insert-directory): + Remove workaround introduced for (bug#14860). + + * lisp/help-fns.el (help-fns--analyse-function): `nadvice` is preloaded. + + * lisp/help.el (help-function-arglist): + Dig into advice wrappers to find the "real" signature. + +2017-07-14 Ted Zlatanov + + GnuTLS HMAC and symmetric cipher support + + * etc/NEWS: Add news for new feature. + + * doc/lispref/text.texi (GnuTLS Cryptography): Add + documentation. + + * configure.ac: Add macros HAVE_GNUTLS3_DIGEST, + HAVE_GNUTLS3_CIPHER, HAVE_GNUTLS3_AEAD, HAVE_GNUTLS3_HMAC. + + * src/fns.c (Fsecure_hash_algorithms): Add function to list + supported `secure-hash' algorithms. + (extract_data_from_object): Add data extraction function that + can operate on buffers and strings. + (secure_hash): Use it. + (Fsecure_hash): Mention `secure-hash-algorithms'. + + * src/gnutls.h: Include gnutls/crypto.h. + + * src/gnutls.c (Fgnutls_ciphers, gnutls_symmetric_aead) + (gnutls_symmetric, Fgnutls_symmetric_encrypt, Fgnutls_symmetric_decrypt) + (Fgnutls_macs, Fgnutls_digests, Fgnutls_hash_mac, Fgnutls_hash_digest) + (Fgnutls_available_p): Implement GnuTLS cryptographic integration. + + * test/lisp/net/gnutls-tests.el: Add tests. + +2017-07-14 Stefan Monnier + + * lisp/emacs-lisp/cl-lib.el (cl--random-time): Remove as well + + It's also defined in cl-extra.el. + +2017-07-14 Paul Eggert + + Do not convert ij and IJ to compatibility chars + + * lisp/leim/quail/latin-alt.el: Omit lines for ij and IJ in Dutch. + Problem reported by James Cloos (Bug#518#10). + +2017-07-14 Toon Claes + + Remove Turkish ligatures from Dutch input method + + * lisp/leim/quail/latin-alt.el: Remove Turkish ligatures (Bug#518). + +2017-07-14 Paul Eggert + + Improve stack-overflow heuristic on GNU/Linux + + Problem reported by Steve Kemp (Bug#27585). + * src/eval.c (near_C_stack_top): Remove. All uses replaced + by current_thread->stack_top. + (record_in_backtrace): Set current_thread->stack_top. + This is for when the Lisp interpreter calls itself. + * src/lread.c (read1): Set current_thread->stack_top. + This is for recursive s-expression reads. + * src/print.c (print_object): Set current_thread->stack_top. + This is for recursive s-expression printing. + * src/thread.c (mark_one_thread): Get stack top first. + * src/thread.h (struct thread_state.stack_top): Now void *, not char *. + +2017-07-14 Paul Eggert + + Remove duplicate cl--random-state definition + + * lisp/emacs-lisp/cl-lib.el (cl--random-state): Remove. + This variable is now defined in cl-extra.el (Bug#27617). + +2017-07-14 Michael Albinus + + Adjust timer in tramp-test36-asynchronous-requests + + * test/lisp/net/tramp-tests.el (tramp-test36-asynchronous-requests): + Adjust timer if it takes too much time. + +2017-07-14 Eli Zaretskii + + Always display rmail progress report under user control + + * lisp/mail/rmail.el (rmail-show-message-1): Delete the second + copy of '(message "Showing message %d..." msg)'. (Bug#27535) + +2017-07-14 Eli Zaretskii + + Avoid byte-compilation warnings for advised functions + + * lisp/files.el (insert-directory, create-file-buffer): Add an + advertised-calling-convention form to shut up byte-compilation + warnings. (Bug#14860) + +2017-07-14 Eli Zaretskii + + Add assertion related to display-line-numbers + + * src/xdisp.c (maybe_produce_line_number): Add assertion for the + condition regarding IT->glyph_row->used[TEXT_AREA] expected by the + code. (Bug#27668) + +2017-07-14 Eli Zaretskii + + Prevent display corruption when display-line-numbers is set + + * src/xdisp.c (try_window_reusing_current_matrix): If giving up + due to display-line-numbers, clear the window's desired glyph + matrix before returning, as the following call to try_window will + call display_line, which expects rows of the desired matrix + cleared. (Bug#27668) + +2017-07-14 Eli Zaretskii + + Revert "Use fixed-pitch font for display-line-numbers" + + This reverts commit d014a5e15c1110af77e7a96f06ccd0f0cafb099f. + * lisp/faces.el (line-number): Don't use a fixed-pitch font, by + popular demand. For relevant discussions, see + + http://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00433.html + http://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00445.html + +2017-07-14 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-07-13 Improve cross-compilation guesses for native Windows + 2017-07-11 More systematic m4 quoting and indentation + 2017-07-10 Make sure $host and $host_os are defined when used + 2017-07-03 stdioext: Port to OpenVMS + 2017-06-24 xalloc-oversized: port to icc + * doc/misc/texinfo.tex, lib/fpending.c, lib/stdio-impl.h: + * lib/xalloc-oversized.h, m4/dirfd.m4, m4/gettimeofday.m4: + * m4/lstat.m4, m4/mktime.m4, m4/pselect.m4, m4/putenv.m4: + * m4/stdint.m4, m4/strtoimax.m4, m4/utimes.m4: + Copy from Gnulib. + +2017-07-13 Alan Mackenzie + + C++ Mode. Fix anomaly occurring when a ">" is deleted then reinserted. + + This fontification anomaly happened because after deleting the ">", + c-forward-<>-arglist parses the preceding identifier as a putative type but + stores it in c-found-types before it becomes clear it is not an unambiguous + type. c-forward-<>-arglist fails, leaving the spurious type id in + c-found-types. Fix this by "binding" c-found-types "to itself" in + c-forward-<>-arglist, and restoring the original value when that function call + fails. + + * lisp/progmodes/cc-engine.el (c-copy-found-types): New function. + (c-forward-<>-arglist): Record the original value of c-found-types at the + beginning of the function, and restore it at the end on failure. + + * lisp/progmodes/cc-mode.el (c-unfind-coalesced-tokens): Rewrite more + accurately. + +2017-07-13 Vincent Belaïche + + Add tests for SES, and fix one more cell renaming bug. + + * lisp/ses.el (ses-relocate-all): In case of insertion, do not + relocate value for named cells as they keep the same symbol. + (ses-rename-cell): Set new cell name symbol to cell value --- do not + rely on recalculating. Push cells with updated data --- cell name, + cell reference list, or cell formula --- to deferred write list. + + * test/lisp/ses-tests.el: New file, with 7 tests for SES. + +2017-07-12 Alan Mackenzie + + Fix some bugs in c-defun-name. This fixes bug #25623. + + * lisp/progmodes/cc-cmds.el (c-defun-name): Fix some bugs to do with structs, + etc. + +2017-07-12 Vasilij Schneidermann + + Make prog-mode-map the parent of c-mode-base-map. Fixes bug #26658. + + * lisp/progmodes/cc-mode.el (top level): Make prog-mode-map the parent of + c-mode-base-map if possible. + +2017-07-12 Alan Mackenzie + + CC Mode: create and use c-set-keymap-parent. + + * lisp/progmodes/cc-defs.el (c-set-keymap-parent): New macro. + + * lisp/progmodes/cc-mode.el (top-level): Remove cc-bytecomp-defun for + set-keymap-parents. + (c-make-inherited-keymap): Use c-set-keymap-parent in place of inline code. + +2017-07-12 Martin Rudalics + + Minor tweaks of new line number display variables + + * src/xdisp.c (Vdisplay_line_numbers): Tweak doc-string. + (Vdisplay_line_number_width): Rename to + Vdisplay_line_numbers_width. + (maybe_produce_line_number): Comply with above rename. + * lisp/cus-start.el (standard): + * lisp/frame.el (top-level): + * etc/NEWS: Comply with renaming of + `display-line-number-width' to `display-line-numbers-width'. + +2017-07-12 Eli Zaretskii + + Avoid assertion violations in close_infile_unwind + + * src/lread.c (close_infile_unwind): A temporary band-aid solution + for bug#27642: allow 'infile' be NULL. + +2017-07-11 Eli Zaretskii + + Use fixed-pitch font for display-line-numbers + + * lisp/faces.el (line-number): Use a fixed-pitch font by default, + even if the default face uses a variable-pitch font. Reported by + James Cloos . + +2017-07-11 Eli Zaretskii + + Improve documentation of display-line-numbers + + * src/xdisp.c (syms_of_xdisp) : Improve the + doc string. Suggested by Alex . + +2017-07-11 Nicolas Petton + + Add an optional testfn parameter to assoc + + * src/fns.c (assoc): New optional testfn parameter used for comparison + when provided. + * test/src/fns-tests.el (test-assoc-testfn): Add tests for the new + 'testfn' parameter. + * src/buffer.c: + * src/coding.c: + * src/dbusbind.c: + * src/font.c: + * src/fontset.c: + * src/gfilenotify.c: + * src/image.c: + * src/keymap.c: + * src/process.c: + * src/w32fns.c: + * src/w32font.c: + * src/w32notify.c: + * src/w32term.c: + * src/xdisp.c: + * src/xfont.c: Add a third argument to Fassoc calls. + * etc/NEWS: + * doc/lispref/lists.texi: Document the new 'testfn' parameter. + +2017-07-10 Michael Albinus + + Use `with-demoted-errors' in Tramp + + * lisp/net/tramp.el (tramp-with-demoted-errors): New defmacro. + + * lisp/net/tramp-sh.el (tramp-sh-handle-vc-registered): Use it. + +2017-07-10 Michael Albinus + + Add Quick Start Guide to Tramp manual + + * doc/misc/tramp.texi: Use consequently "@value{tramp}" and + "MS Windows". + (Quick Start Guide): New node. + + * doc/misc/trampver.texi: + * lisp/net/trampver.el: Change version to "2.3.3-pre". + +2017-07-10 Glenn Morris + + Fix failing module tests on GNU/Linux + + * test/src/emacs-module-tests.el + (module--test-assertions--load-non-live-object) + (module--test-assertions--call-emacs-from-gc): + Avoid test failures due to backtraces. + +2017-07-10 Paul Eggert + + Fix core dump in substitute-object-in-subtree + + Without this fix, (substitute-object-in-subtree #0=(#0# 'a) 'a) + would dump core, since the C code would recurse indefinitely through + the infinite structure. This patch adds an argument to the function, + and renames it to lread--substitute-object-in-subtree as the function + is not general-purpose and should not be relied on by outside code. + See Bug#23660. + * src/intervals.c (traverse_intervals_noorder): ARG is now void *, + not Lisp_Object, so that callers need not cons unnecessarily. + All callers changed. Also, remove related #if-0 code that was + “temporary” in the early 1990s and has not been compilable for + some time. + * src/lread.c (struct subst): New type, for substitution closure data. + (seen_list): Remove this static var, as this info is now part of + struct subst. All uses removed. + (Flread__substitute_object_in_subtree): Rename from + Fsubstitute_object_in_subtree, and give it a 3rd arg so that it + doesn’t dump core when called from the top level with an + already-cyclic structure. All callers changed. + (SUBSTITUTE): Remove. All callers expanded and then simplified. + (substitute_object_recurse): Take a single argument SUBST rather + than a pair OBJECT and PLACEHOLDER, so that its address can be + passed around as part of a closure; this avoids the need for an + AUTO_CONS call. All callers changed. If the COMPLETED component + is t, treat every subobject as potentially circular. + (substitute_in_interval): Take a struct subst * rather than a + Lisp_Object, for the closure data. All callers changed. + * test/src/lread-tests.el (lread-lread--substitute-object-in-subtree): + New test, to check that the core dump does not reoccur. + +2017-07-10 Philipp Stephani + + Minor simplification of module_free_global_ref + + * src/emacs-module.c (module_free_global_ref): Remove unused variable + 'hashcode'. Inline variable 'value' that's only used once. + +2017-07-10 Philipp Stephani + + Re-add a useful assertion + + * src/emacs-module.c (module_free_global_ref): Re-add assertion that + the reference count is zero. This assertion was removed in commit + 8afaa1321f8088bfb877fe4b6676e8517adb0bb7, but it's not included in the + test performed by XFASTINT before, because the previous reference + count could have been zero already in the case of a buggy + implementation. This assertion might have detected Bug#27587. + +2017-07-10 Valentin Gatien-Baron (tiny change) + + Fix bug in module_free_global_ref (Bug#27587) + + * src/emacs-module.c (module_free_global_ref): Actually remove entry + from hash table. + +2017-07-09 Philipp Stephani + + Further improve electric quote support for Markdown (Bug#24709) + + Markdown sets both 'comment-start' and 'comment-use-syntax' to non-nil + values. Therefore 'electric-quote-mode' recognized it as a + programming mode. Fix this by first checking whether the current + major mode is derived from 'text-mode'. + + * lisp/electric.el (electric-quote-post-self-insert-function): Treat + 'text-mode' as stronger signal than comment syntax. + + * test/lisp/electric-tests.el (electric-quote-markdown-in-text) + (electric-quote-markdown-in-code): Adapt unit tests. + +2017-07-09 Philipp Stephani + + Remove pointless code in 'electric-quote-mode' + + * lisp/electric.el (electric-quote-post-self-insert-function): Remove + pointless form. + +2017-07-09 Philipp Stephani + + Refactor 'electric-quote-mode' + + * lisp/electric.el (electric-quote-post-self-insert-function): Remove + local variable 'start', which was misnamed and only used once. + +2017-07-09 Saulius Menkevičius (tiny change) + + Avoid crashes on MS-Windows starting 64-bit .NET executables + + * src/w32proc.c (w32_executable_type): Don't assume that the + import directory in a DLL will always be non-NULL. (Bug#27527) + +2017-07-09 Eli Zaretskii + + Avoid compilation warning in files.el + + * lisp/files.el (auto-save-visited-file-name): Avoid obsoletion + warning due to its use in auto-save-visited-mode. + +2017-07-09 Eli Zaretskii + + Improve indexing of VC-related stuff in the Emacs manual + + * doc/emacs/maintaining.texi (Version Control): Add a "VC" index + entry. (Bug#27627) + +2017-07-09 Eli Zaretskii + + Speed up display of line numbers for very large buffers + + * src/xdisp.c (maybe_produce_line_number): Speed up line counting + using values cached by mode-line display of line numbers. + (Bug#27622) + +2017-07-09 Alexander Kuleshov + + Define internal_border_parts for window systems only (Bug#27615) + + * src/keyboard.c: (internal_border_parts): Define only + when HAVE_WINDOW_SYSTEM is enabled. (Bug#27615) + +2017-07-09 R. Bernstein + + Add realgud faces faces to whiteboard... + + Adjust wheatgrass to use underline for enabled/disabled breakpoints + +2017-07-08 Noam Postavsky + + Optimize UCS normalization tests + + Brings the the time for `ucs-normalize-part1' from 200s down to 130s. + * test/lisp/international/ucs-normalize-tests.el + (ucs-normalize-tests--parse-column): Use character instead of string + of length 1 for terminator. Convert return value into string since + all callers need that form anyway. + (ucs-normalize-tests--normalization-equal-p): Rename from + ucs-normalize-tests--normalize. Use dedicated buffer instead of + messing with narrowing. Take string to compare against and insert it + into buffer so that compare-buffer-substrings can be used instead of + allocating a new string from buffer contents. + (ucs-normalize-tests--normalization-chareq-p): New macro, specialized + for comparing single character. + (ucs-normalize-tests--rule1-holds-p) + (ucs-normalize-tests--rule2-holds-p): Turn into defsubst. + (ucs-normalize-tests--rule1-failing-for-partX): Use `eq' instead of + `='. + +2017-07-08 Noam Postavsky + + Update failing lines for UCS normalize tests + + * test/lisp/international/ucs-normalize-tests.el + (ucs-normalize-tests--failing-lines-part2): Update for new + admin/unidata/NormalizationTest.txt version. + +2017-07-08 Noam Postavsky + + Semi-automate the procedure for updating UCS normalize test bad lines + + * test/lisp/international/ucs-normalize-tests.el: Remove incorrect + commentary describing a manual procedure for producing the updated + failing lines, it did not actually work. Replace it with pointer to + new function which prints the updated values. + (ucs-normalize-tests--rule1-holds-p): Renamed from + ucs-normalize-tests--invariants-hold-p. + (ucs-normalize-tests--rule2-holds-p): Renamed from + ucs-normalize-tests--invariants-rule2-hold-p. + (ucs-normalize-tests--rule1-failing-for-partX): Renamed from + ucs-normalize-tests--invariants-failing-for-part. + (ucs-normalize-tests--rule1-failing-for-lines): Renamed from + ucs-normalize-tests--invariants-failing-for-lines. + (ucs-normalize-tests--part2-rule1-failed-lines): New variable. + (ucs-normalize-part2): Set it. + (ucs-normalize-part1): Always run through to end of test before + checking for failures. + (ucs-normalize-tests--insert-failing-lines) + (ucs-normalize-check-failing-lines): New functions, used to update + the *--failing-lines-part* variables. + +2017-07-08 Paul Eggert + + * lisp/cus-start.el (standard): Spelling fix. + +2017-07-08 Philipp Stephani + + Module assertions: check for garbage collections + + It's technically possible to write a user pointer finalizer that calls + into Emacs module functions. This would be disastrous because it + would allow arbitrary Lisp code to run during garbage collection. + Therefore extend the module assertions to check for this case. + + * src/emacs-module.c (module_assert_thread): Also check whether a + garbage collection is in progress. + + * test/data/emacs-module/mod-test.c (invalid_finalizer) + (Fmod_test_invalid_finalizer): New test module functions. + (emacs_module_init): Register new test function. + + * test/src/emacs-module-tests.el (module--test-assertion) + (module--with-temp-directory): New helper macros. + (module--test-assertions--load-non-live-object): Rename existing + unit test, use helper macros. + (module--test-assertions--call-emacs-from-gc): New unit test. + +2017-07-08 Eli Zaretskii + + Capitalize the menu entry for display-line-numbers + + * lisp/menu-bar.el (menu-bar-showhide-menu): Capitalize menu item + for display-line-numbers. Suggested by Martin Rudalics + . + +2017-07-08 Eli Zaretskii + + Update Unicode data and files to Unicode 10.0 + + * admin/notes/unicode: + * admin/unidata/README: + * admin/unidata/BidiBrackets.txt: + * admin/unidata/BidiMirroring.txt: + * admin/unidata/Blocks.txt: + * admin/unidata/IVD_Sequences.txt: + * admin/unidata/NormalizationTest.txt: + * admin/unidata/SpecialCasing.txt: + * admin/unidata/UnicodeData.txt: + * lisp/international/characters.el: + * lisp/international/fontset.el (script-representative-chars): + * lisp/international/mule-cmds.el (ucs-names): Update per Unicode 10.0. + +2017-07-08 Alexander Gramiak + + Support '=' in Scheme and Lisp tags in 'etags' + + * lib-src/etags.c (get_lispy_tag): New function. + (L_getit, Scheme_functions): Use get_lispy_tag (Bug#5624). + * test/manual/etags/CTAGS.good: + * test/manual/etags/ETAGS.good_1: + * test/manual/etags/ETAGS.good_2: + * test/manual/etags/ETAGS.good_3: + * test/manual/etags/ETAGS.good_4: + * test/manual/etags/ETAGS.good_5: + * test/manual/etags/ETAGS.good_6: + * test/manual/etags/Makefile: + * test/manual/etags/el-src/TAGTEST.EL: Update tests. + * test/manual/etags/scm-src/test.scm: New tests for Scheme. + +2017-07-08 Alexander Kuleshov + + Avoid compiler warnings in xdisp.c debugging code + + * src/xdisp.c (dump_glyph, dump_glyph_row, Fdump_glyph_matrix): + Use pD directives for ptrdiff_t values instead of pI, to avoid + compilation warnings on 64-bit hosts. (Bug#27597) + +2017-07-08 Eli Zaretskii + + Commentary enhancement in frame.el + + * lisp/frame.el: Explain how to test whether a variable needs to + be added to the list of those which are watched for buffer + redisplay. + +2017-07-08 Eli Zaretskii + + Support display of line numbers natively + + This merges branch 'line-numbers'. + * src/buffer.c (disable_line_numbers_overlay_at_eob): New + function. + * src/lisp.h (disable_line_numbers_overlay_at_eob): Add prototype. + * src/dispextern.h (struct it): New members pt_lnum, lnum, + lnum_bytepos, lnum_width, and lnum_pixel_width. + * src/indent.c (line_number_display_width): New function, + refactored from line-number width calculations in vertical-motion. + (Fvertical_motion): Call line_number_display_width when the width + of line-number display is needed. + (Fline_number_display_width): New defun. + (syms_of_indent): Defsubr it. + * src/indent.c (Fvertical_motion): Help C-n/C-p estimate correctly + the width used up by line numbers by looking near the window-start + point. If window-start is outside of the accessible portion, + temporarily widen the buffer. + * src/term.c (produce_glyphs): Adjust tab stops for the horizontal + space taken by the line-number display. + * src/xdisp.c (display_count_lines_logically) + (display_count_lines_visually, maybe_produce_line_number) + (should_produce_line_number, row_text_area_empty): New functions. + (try_window_reusing_current_matrix): Don't use this method when + display-line-numbers is in effect. + (try_window_id, try_cursor_movement): Disable these optimizations + when the line-number-current-line face is different from + line-number face and for relative line numbers. + (try_window_id, redisplay_window, try_cursor_movement): For + visual line-number display, disable the same redisplay + optimizations as for relative. + (x_produce_glyphs): Adjust tab stops for the horizontal + space taken by the line-number display. + (hscroll_window_tree): Adjust hscroll calculations to line-number + display. + (DISP_INFINITY): Renamed from INFINITY to avoid clashes with + math.h; all users changed. + (set_cursor_from_row): Fix calculation of cursor X coordinate in + R2L rows with display-produced glyphs at the beginning. + (display_line): Use should_produce_line_number to determine + whether a line number should be produced for each glyph row, and + maybe_produce_line_number to produce line numbers. + Don't display line numbers in the minibuffer and in tooltip + frames. + Call row_text_area_empty to verify that a glyph + row's text area is devoid of any glyphs that came from a buffer or + a string. This fixes a bug with empty-lines indication + disappearing when line numbers or line-prefix are displayed. + (syms_of_xdisp) + : New buffer-local variables. + : New variable. + + * lisp/cus-start.el (standard): Provide customization forms for + display-line-numbers and its sub-features. + * lisp/faces.el (line-number, line-number-current-line): New faces. + * lisp/frame.el: Add display-line-numbers, display-line-numbers-widen, + display-line-numbers-current-absolute, and + display-line-number-width to the list of variables that should + trigger redisplay of the current buffer. + * lisp/menu-bar.el (menu-bar-showhide-menu): Add menu-bar item to + turn display-line-numbers on and off. + (toggle-display-line-numbers): New function. + * lisp/simple.el (last--line-number-width): New internal variable. + (line-move-visual): Use it to adjust temporary-goal-column when + line-number display changes its width. + + * doc/emacs/basic.texi (Position Info): Add cross-reference to + "Display Custom", for line-number display. + * doc/emacs/custom.texi (Init Rebinding): + * doc/emacs/modes.texi (Minor Modes): Remove references to + linum-mode. + * doc/emacs/display.texi (Display Custom): Describe the + line-number display. + * doc/lispref/display.texi (Size of Displayed Text): Document + line-number-display-width. + + * etc/NEWS: Document display-line-numbers and its customizations. + +2017-07-08 Paul Eggert + + Fix more ungetc bugs with encoding errors + + * src/lread.c (infile): New variable, replacing ... + (instream): ... this. All uses changed. + (readbyte_from_stdio): New function, which deals with lookahead. + (readbyte_from_file, Fget_file_char): Use it. + (Fget_file_char): When misused, signal an error instead of + relying on undefined behavior. + (close_infile_unwind): New function. + (Fload): Use it. + (readevalloop): 2nd arg is now struct infile *, not FILE *. + All callers changed. + (read1): Handle lookahead when copying doc strings with + encoding errors. + +2017-07-08 Paul Eggert + + Avoid ungetc when loading charset maps from files + + * src/charset.c (read_hex): New args LOOKAHEAD and TERMINATOR, + replacing the old EOF. All callers changed. This avoids the + need to call ungetc. + +2017-07-08 Paul Eggert + + Fix ungetc bug when reading an encoding error + + * src/lread.c (readchar, read_emacs_mule_char): Fix off-by-one + error when reading an encoding error from a file, e.g., a symbol + in an .elc file whose name is "\360\220\200\360". + +2017-07-07 Stefan Monnier + + * lisp/wid-edit.el (widget-color--choose-action): Use a closure + + * lisp/window.el (display-buffer--special-action): Use a closure. + +2017-07-07 Stephen Berman + + Add new todo-mode.el tests + + * test/lisp/calendar/todo-mode-tests.el (with-todo-test): + Declare an Edebug spec. Restore pre-test-run state of test files. + (todo-test--show, todo-test--move-item) + (todo-test--insert-item): New functions. + (todo-test-get-archive): Remove, as subsumed by + todo-test--show. Adjust all callers. + (todo-test--is-current-buffer): Rename from + todo-test-is-current-buffer and adjust uses. + (todo-test-item-highlighting): Use todo-test--show. + (todo-test-revert-buffer01, todo-test-revert-buffer02) + (todo-test-raise-lower-priority) + (todo-test-todo-mark-unmark-category, todo-test-move-item01) + (todo-test-move-item02, todo-test-move-item03) + (todo-test-move-item04, todo-test-move-item05) + (todo-test-toggle-item-header01) + (todo-test-toggle-item-header02) + (todo-test-toggle-item-header03) + (todo-test-toggle-item-header04) + (todo-test-toggle-item-header05) + (todo-test-toggle-item-header06) + (todo-test-toggle-item-header07): New tests. + + * test/lisp/calendar/todo-mode-resources/todo-test-1.toda: + * test/lisp/calendar/todo-mode-resources/todo-test-1.todo: + Modify to accommodate new tests. + +2017-07-07 Stephen Berman + + todo-mode.el: Fix handling of hidden item headers (bug#27609) + + * lisp/calendar/todo-mode.el (todo--item-headers-hidden): New variable. + (todo-toggle-item-header): Use it. Make this command a noop + if the file has no items. + (todo-move-item, todo-item-done): Instead of concatenating the + items to move into one string, make a list of them to + facilitate handling hidden headers. Adjust insertion accordingly. + (todo-archive-done-item): Handle hidden headers in archive file. + (todo-unarchive-items): Handle hidden headers in todo file. + (todo-backward-item): Use todo--item-headers-hidden and handle + moving backward work when item date-time headers are hidden. + (todo-remove-item): Delete date-time header overlay. + (todo-get-overlay, todo-insert-with-overlays): Make them work + with hidden date-time headers. + (todo-modes-set-2): Make todo--item-headers-hidden buffer local. + +2017-07-07 Stephen Berman + + Fix several todo-mode bugs found while debugging bug#27609 + + * lisp/calendar/todo-mode.el (todo-toggle-mark-item): Calculate + current category only once. + (todo-mark-category): Update number of marked items to avoid + spurious duplication in todo-categories-with-marks alist and + corruption of the todo-categories alist. Handle empty line + when there are no todo items and done items are shown. + (todo-set-item-priority): Make noop if called from + todo-raise-item-priority or todo-lower-item-priority when + point is on a done todo item or an empty line. + (todo-move-item): Use markers instead of integer positions to + correctly handle deleting the now moved items from the source + category (without markers an infinite loop arises when moving + marked item to a preceding category). + (todo-unarchive-items): Put point on the (first) restored done + item, instead of leaving it at the end of the done items + separator string. + (todo-revert-buffer): Ensure buffer remains read-only after + reverting. + +2017-07-07 Eli Zaretskii + + Exclude blank columns from value of line-number-display-width + + * src/indent.c (Fline_number_display_width): Don't add 2 to the + number of columns we return, to make this consistent with + display-line-number-width. + +2017-07-07 Eli Zaretskii + + Fix vertical-motion across the place where line-number width changes + + * src/indent.c (line_number_display_width): New function, + refactored from line-number width calculations in vertical-motion. + (Fvertical_motion): Call line_number_display_width when the width + of line-number display is needed. + (Fline_number_display_width): New defun. + (syms_of_indent): Defsubr it. + + * doc/lispref/display.texi (Size of Displayed Text): Document + line-number-display-width. + + * etc/NEWS: Mention line-number-display-width. + + * lisp/simple.el (last--line-number-width): New internal variable. + (line-move-visual): Use it to adjust temporary-goal-column when + line-number display changes its width. + +2017-07-07 Martin Rudalics + + Remove Vwindow_text_change_functions and related code + + Vwindow_text_change_functions had been provided for implementing + line numbers but apparently was never functional or in use. + + * src/xdisp.c (redisplay_window): Remove handling of + Vwindow_text_change_functions. + (syms_of_xdisp): Remove Qwindow_text_change_functions. + (Vwindow_text_change_functions): Remove variable. + * doc/lispref/hooks.texi (Standard Hooks): Remove entry for + `window-text-change-functions'. + +2017-07-07 Mark Oteiza + + Convert more uses of looking-at to following-char + + More followup to Karl Fogel's commit a84da83c1. + * lisp/dired-aux.el (dired-add-entry, dired-subdir-hidden-p): + * lisp/dired-x.el (dired-mark-unmarked-files, dired-mark-sexp): + * lisp/help-fns.el (doc-file-to-man, doc-file-to-info): + * lisp/proced.el (proced-toggle-marks): + * lisp/progmodes/f90.el (f90-indent-line): + * lisp/ses.el (ses-load): + * lisp/tar-mode.el (tar-expunge): Replace instances of looking-at with + char comparisons using following-char. + +2017-07-07 Noam Postavsky + + Don't skip epg tests (Bug#23561) + + * test/lisp/epg-tests.el (with-epg-tests): Ignore REQUIRE-PASSPHRASE + parameter, since we supply the passphrase via pinentry-program for all + GPG versions (as of 2017-02-28 "Fix epg-tests with dummy-pinentry + program (Bug#23619)"). + (epg-tests-program-alist-for-passphrase-callback): Remove. + +2017-07-06 Eli Zaretskii + + Implement line numbers that disregard narrowing + + * src/xdisp.c (display_count_lines_logically): New function, + counts line numbers disregarding narrowing. Suggested by Andy + Moreton . + (maybe_produce_line_number): Call display_count_lines_logically + instead of display_count_lines. Adapt BEGV, ZV, etc. to + display-line-numbers-widen. + (syms_of_xdisp) : New buffer-local + variable. + + * lisp/cus-start.el (standard): Provide a customization form for + display-line-numbers-widen. + * lisp/frame.el: Add display-line-numbers-widen, + display-line-numbers-current-absolute, and + display-line-number-width to the list of variables that should + trigger redisplay of the current buffer. + + * doc/emacs/display.texi (Display Custom): Document + display-line-numbers-widen. + +2017-07-06 Noam Postavsky + + Fix lisp-comment-indent for single-semicolon case + + * lisp/emacs-lisp/lisp-mode.el (lisp-comment-indent): Only check for + open paren if we're looking at multiple comment characters. + * test/lisp/emacs-lisp/lisp-mode-tests.el (lisp-comment-indent-1) + (lisp-comment-indent-2): New tests. + +2017-07-06 Paul Eggert + + Spelling fixes + + * lisp/org/org-table.el (org-table-sort-lines): + Fix misspelling in prompt. + * lisp/org/ox-ascii.el (org-ascii--describe-datum): + Fix misspelling in call to org-element-lineage. + +2017-07-06 Noam Postavsky + + Don't put whitespace between open paren and comment in Lisp modes (Bug#19740) + + * lisp/emacs-lisp/lisp-mode.el (lisp-comment-indent): If current + line's code ends in open paren, set comment indentation exactly to + column following it. + (lisp-mode-variables): Set `comment-indent-function' to + `lisp-comment-indent'. + +2017-07-06 Noam Postavsky + + Allow comment-indent-functions to specify exact indentation (Bug#385) + + * lisp/newcomment.el (comment-choose-indent): Interpret a cons of two + integers as indicating a range of acceptable indentation. + (comment-indent): Don't apply `comment-inline-offset', + `comment-choose-indent' already does that. + (comment-indent-function): + * doc/emacs/programs.texi (Options for Comments): Document new + acceptable return values. + * etc/NEWS: Announce it. + +2017-07-06 Paul Eggert + + Check for integer overflow in xbm images + + * src/image.c (XBM_TK_OVERFLOW): New constant. + (xbm_scan): Check for integer overflow instead of relying on + undefined behavior. Check that octal digits are actually octal. + +2017-07-06 Paul Eggert + + Convert hex digits more systematically + + This makes the code a bit smaller and presumably faster, as + it substitutes a single lookup for conditional jumps. + * src/character.c (hexdigit): New constant. + (syms_of_character) [HEXDIGIT_IS_CONST]: Initialize it. + * src/character.h (HEXDIGIT_CONST, HEXDIGIT_IS_CONST): New macros. + (hexdigit): New decl. + (char_hexdigit): New inline function. + * src/charset.c: Do not include c-ctype.h. + * src/charset.c (read_hex): + * src/editfns.c (styled_format): + * src/image.c (xbm_scan): + * src/lread.c (read_escape): + * src/regex.c (ISXDIGIT) [emacs]: + Use char_hexdigit insted of doing it by hand. + +2017-07-06 Paul Eggert + + Don’t use -Woverride-init + + I have some further changes in mind that would also need to + disable the -Woverride-init warnings. In practice these warnings + seem to be more trouble than they’re worth, so disable them in the + cc command line. + * configure.ac: Disable -Woverride-init here ... + * src/bytecode.c: ... rather than here. + +2017-07-05 Glenn Morris + + * lisp/progmodes/python.el (auto-mode-alist): Add .pyi. (Bug#27847) + + * lisp/org/ox-html.el (org-html-infojs-template): Update copyright. + +2017-07-05 Glenn Morris + + Small fix for bug-reference.el + + * lisp/progmodes/bug-reference.el (bug-reference-bug-regexp): + Autoload safety property. (Bug#27481) + +2017-07-05 Michael Albinus + + Suppress timers in Tramp + + * lisp/net/tramp.el (tramp-file-name-handler): Don't trigger timers. + + * test/lisp/net/tramp-tests.el + (tramp-test36-asynchronous-requests): Trigger timers. + (tramp-test37-recursive-load, tramp-test38-remote-load-path): + Set `default-directory' to a trustworthy value. + +2017-07-05 rocky + + Add realgud face definitions + + Add realgud faces to tdsh-dark-theme + +2017-07-05 Stefan Monnier + + * lisp/progmodes/cc-awk.el: Mark unused args + + * lisp/progmodes/cc-bytecomp.el: Mark unused args + +2017-07-05 Stefan Monnier + + * lisp/progmodes/cc-defs.el (lookup-syntax-properties): Move ... + + ... before first use + +2017-07-05 Stefan Monnier + + * lisp/progmodes/cc-vars.el: Mark unused args + +2017-07-05 Stefan Monnier + + * lisp/progmodes/cc-langs.el: Mark unused args + + (c-primary-expr-regexp): Remove unused vars ambiguous-prefix-ops and + unambiguous-prefix-ops. + +2017-07-05 Stefan Monnier + + * lisp/progmodes/cc-engine.el: Mark unused args + + (c-beginning-of-statement-1, c-guess-basic-syntax): + Remove unused var c-in-literal-cache. + (c-debug-sws-msg): Silence byte-compiler, even if we don't use the arg. + (c-append-to-state-cache): Remove unused var `bra+1s'. + (c-remove-stale-state-cache): Remove unused var `pps-point-state'. + (c-invalidate-state-cache-1): Remove unused var `pa'. + (c-forward-decl-or-cast-1): Change comments so they don't look like + outline headers. + (c-restricted-<>-arglists, c-parse-and-markup-<>-arglists): + Declare before first use. + (c-forward-decl-or-cast-1): Remove unused var `backup-kwd-sym'. + (c-backward-over-enum-header): Remove unused var `up-sexp-pos'. + +2017-07-05 Stefan Monnier + + * lisp/progmodes/cc-cmds.el: Remove unused vars + + (c-syntactic-context): Declare as dynbound. + (c-beginning-of-defun, c-end-of-defun): Remove unused var `start'. + +2017-07-05 Stefan Monnier + + * lisp/progmodes/cc-guess.el: Remove unused var + + (c-guess-view-reorder-offsets-alist-in-style): Remove redundantly bound + and computed variable `guessed-syntactic-symbols'. + +2017-07-05 Stefan Monnier + + * lisp/progmodes/cc-align.el: Mark unused arguments + +2017-07-05 Stefan Monnier + + * lisp/progmodes/cc-mode.el: Mark unused arguments + + (c-parse-quotes-before-change, c-parse-quotes-after-change): + Remove unused vars. + +2017-07-05 Noam Postavsky + + Mention `ffap-url-unwrap-local' in find-file-at-point's docstring (Bug#27564) + + * lisp/ffap.el (find-file-at-point): Mention `ffap-url-unwrap-local' + and `ffap-url-unwrap-remote'. + +2017-07-05 Noam Postavsky + + Fix infloop in uncomment-region-default (Bug#27112) + + When `comment-continue' has only blanks, `comment-padright' produces a + regexp that matches the empty string, so `uncomment-region-default' + will loop infinitely. + * lisp/newcomment.el (comment-padright): Only return a regexp if STR + has nonblank characters. + +2017-07-04 Eli Zaretskii + + Fix display of current line number in visual mode + + * src/xdisp.c (maybe_produce_line_number): Fix visual-mode display + of current line when line-number-current-line face was customized. + Reported by Filipe Silva . + +2017-07-03 Philipp Stephani + + Use hook instead of face list to inhibit electric quoting + + This is more flexible and doesn't couple electric quoting to font + locking. + Give that 'electric-quote-code-faces' was just introduced, remove it + without formal deprecation. + + * lisp/electric.el (electric-quote-inhibit-functions): New abnormal + hook variable. + (electric-quote-post-self-insert-function): Run the hook. Remove + use of old 'electric-quote-code-faces' variable. + + * test/lisp/electric-tests.el (electric-quote-markdown-in-text) + (electric-quote-markdown-in-code): Adapt unit tests. + +2017-07-03 Ingo Lohmar + + Offer non-aligned indentation in lists in js-mode (Bug#27503) + + * lisp/progmodes/js.el (js--proper-indentation): + New customization option 'js-indent-align-list-continuation'. + Affects argument lists as well as arrays and object properties. + * test/manual/indent/js-indent-align-list-continuation-nil.js: + Test the change. + +2017-07-03 Eli Zaretskii + + Avoid errors in vertical-motion when buffer is narrowed + + * src/indent.c (Fvertical_motion): If need to start from + window-start, and it is outside of the accessible portion, + temporarily widen the buffer. This avoids errors in evil-mode. + Reported by James Nguyen . + +2017-07-03 Michael Albinus + + (Re-)activate remote tests of filenotify-tests.el + + * test/lisp/filenotify-tests.el + (file-notify-test-remote-temporary-file-directory): + Declare default host for mock method. Offer home directory + for mock method if it doesn't exist. + (file-notify-test09-watched-file-in-watched-dir-remote): + Remove, it doesn't work reliably. + +2017-07-03 Noam Postavsky + + Reset ansi escape context before printing eshell prompt (Bug#27407) + + * lisp/eshell/em-prompt.el (eshell-emit-prompt): Reset + `ansi-color-context-region'. + +2017-07-03 Noam Postavsky + + Let ansi-color overlay hooks work in eshell (Bug#27407) + + * lisp/ansi-color.el (ansi-color-make-extent): Add + `ansi-color-freeze-overlay' to `insert-behind-hooks' as well. + * lisp/eshell/esh-mode.el (eshell-output-filter): Let-bind + `inhibit-modification-hooks' to nil while inserting the string. + +2017-07-03 Noam Postavsky + + Fix and simplify ansi escape detection (Bug#21381) + + * lisp/ansi-color.el (ansi-color-regexp, ansi-color-drop-regexp): + Remove. + (ansi-color-control-seq-regexp): New constant, matches all escape + sequences. + (ansi-color-filter-apply, ansi-color-apply) + (ansi-color-filter-region, ansi-color-apply-on-region): Use it instead + of matching color sequences separately from ignored sequences. + Differentiate color sequences simply by checking the last character. + +2017-07-03 Damien Cassou + + Add absolute optional parameter to line-number-at-pos (Bug#26417) + + * lisp/simple.el (line-number-at-pos): Add a second optional + argument 'absolute'. + * test/list/simple-tests.el: Add tests for 'line-number-at-pos'. + +2017-07-03 R. Bernstein + + Add realgud faces + +2017-07-03 Michael Albinus + + Fix tramp-tests.el for hydra + + * test/Makefile.in: Remove instrumentation for tramp-tests. + + * test/lisp/net/tramp-tests.el (tramp-test36-asynchronous-requests): + Remove instrumentation. Wrap with a timeout. Give hydra + another timer value. Set `default-directory' in timer. + +2017-07-03 Bastien + + Merge branch 'master' into scratch/org-mode-merge + + Merge branch 'master' into scratch/org-mode-merge + +2017-07-03 Tino Calancha + + dired-do-shell-command: Fix check for wildcards + + * lisp/dired-aux.el (dired-do-shell-command): Replace just '?', '*' + and '`?' i.e., keep the whitespaces. + * test/lisp/dired-aux-tests.el (dired-test-bug27496): Add test. + +2017-07-02 Noam Postavsky + + Split shr-copy-url dwim behavior into separate functions (Bug#26826) + + * lisp/net/shr.el (shr-url-at-point, shr-probe-url) + (shr-probe-and-copy-url, shr-maybe-probe-and-copy-url): New functions, + split out from `shr-copy-url'. + (shr-copy-url): Only copy the url, don't fetch it. + (shr-map): Bind 'w' and 'u' to `shr-maybe-probe-and-copy-url', which + has the same behavior as the old `shr-copy-url'. + * etc/NEWS: Announce changes. + +2017-07-02 Alex Branham (tiny change) + + Make eww-search-words prompt for query if nothing selected + + * lisp/net/eww.el (eww-search-words): Make eww-search-words prompt the + user for a search query if the region is inactive or if the region is + just whitespace. + +2017-07-02 Noam Postavsky + + * lisp/emacs-lisp/cl-print.el (cl-print-compiled-button): t by default. + + * lisp/emacs-lisp/debug.el (debugger-insert-backtrace): + * lisp/help-fns.el (describe-variable): No need to let-bind + `cl-print-compiled-button' to t anymore. + +2017-07-02 Philipp Stephani + + Electric quotes: Improve support for Markdown mode (Bug#24709) + + Introduce a new user option 'electric-quote-context-sensitive'. If + non-nil, have ' insert an opening quote if sensible. + + Also introduce a new variable 'electric-quote-code-faces'. Major + modes such as 'markdown-mode' can add faces to this list to treat text + as inline code and disable electric quoting. + + * lisp/electric.el (electric-quote-context-sensitive): New user + option. + (electric-quote-code-faces): New variable. + (electric-quote-post-self-insert-function): Treat ' as ` if + desired and applicable; disable electric quoting for given faces. + + * test/lisp/electric-tests.el (electric-quote-opening-single) + (electric-quote-closing-single, electric-quote-opening-double) + (electric-quote-closing-double) + (electric-quote-context-sensitive-backtick) + (electric-quote-context-sensitive-bob-single) + (electric-quote-context-sensitive-bob-double) + (electric-quote-context-sensitive-bol-single) + (electric-quote-context-sensitive-bol-double) + (electric-quote-context-sensitive-after-space-single) + (electric-quote-context-sensitive-after-space-double) + (electric-quote-context-sensitive-after-letter-single) + (electric-quote-context-sensitive-after-letter-double) + (electric-quote-context-sensitive-after-paren-single) + (electric-quote-context-sensitive-after-paren-double) + (electric-quote-markdown-in-text) + (electric-quote-markdown-in-code): New unit tests. + +2017-07-02 Michael Albinus + + * doc/misc/tramp.texi: Replace ftp:// and http:// URLs by https://. + +2017-07-02 Eli Zaretskii + + Avoid off-by-one errors in column C-n/C-p calculations + + * src/indent.c (Fvertical_motion): Help C-n/C-p estimate correctly + the width used up by line numbers by looking near the window-start + point. + +2017-07-02 Tino Calancha + + Ask confirmation for all suspicious wildcards + + * lisp/dired-aux.el (dired-do-shell-command): Check that all + the wildcards are right. Otherwise, ask for confirmation (Bug#27496). + +2017-07-02 Tino Calancha + + Extend dired-do-shell-command substitutions + + Substitute "`?`" inside command with the current file name. + See details in: + https://lists.gnu.org/archive/html/emacs-devel/2017-06/msg00618.html + * lisp/dired-aux.el (dired-quark-subst-regexp, dired-star-subst-regexp): + Mark as obsolete. + (dired-isolated-string-re): New defun. + (dired--star-or-qmark-p): New predicate. + (dired-do-shell-command): Use dired--star-or-qmark-p. Substitute "`?`" + with the current file name. + * doc/emacs/dired.texi (Shell Commands in Dired): Update manual. + +2017-07-02 Alan Mackenzie + + Fix bug in yesterday's CC Mode commit. + + * lisp/progmodes/cc-mode.el (c-quoted-number-head-before-point): Check a + search has succeded before using the match data. + (c-quoted-number-head-before-point, c-quoted-number-head-after-point): + Specify that the position of the extremity of the head or tail is in the + match data. + +2017-07-02 Philipp Stephani + + Remove FIXME comments about sentinel values + + These FIXMEs can't be addressed because they would require breaking + changes to the module API. Furthermore, other module functions don't + return sentinel values as well, so users generally have to call + non_local_exit_check anyway. + + * src/emacs-module.c (module_set_user_ptr) + (module_set_user_finalizer, module_vec_set, module_vec_size): Remove + FIXME comments. + +2017-07-02 Philipp Stephani + + Adapt Lisp reference to reader changes + + The reader now warns about some unescaped character literals, but + still allows them for compatibility reasons. Slightly adapt the + manual to forbid them officially. + + * doc/lispref/objects.texi (Basic Char Syntax): Document that + backslashes are now required before some characters. + +2017-07-02 Michael Albinus + + Fix Bug#27502 + + * lisp/autorevert.el (auto-revert-find-file-function): New defun. + (find-file-hook): Use it. (Bug#27502) + (auto-revert-remove-current-buffer): New defun. + (auto-revert-mode, auto-revert-buffers): Use it. + +2017-07-02 Noam Postavsky + + Let test summary go through even if some logs were not generated + + * lisp/emacs-lisp/ert.el (ert-summarize-tests-batch-and-exit): Check + for existence of log files before reading. + +2017-07-01 Philipp Stephani + + * src/module-env-25.h (copy_string_contents): Fix comment. + +2017-07-01 Philipp Stephani + + Also mark module init function as noexcept if possible + + * src/emacs-module.h.in (emacs_module_init): Mark as noexcept if + possible. + +2017-07-01 Philipp Stephani + + Improve C++98 compatibility + + * src/emacs-module.h.in (emacs_funcall_exit): Lose trailing comma. + C++98 doesn't allow trailing commas in enumerations. + +2017-07-01 Eli Zaretskii + + Minor copyedits of manuals regarding bidi conformance + + * doc/emacs/mule.texi (Bidirectional Editing): + * doc/lispref/display.texi (Bidirectional Display): Update the + bidi conformance text. + +2017-07-01 Alan Mackenzie + + Make C++ digit separators work. Amend the handling of single quotes generally + + Single quotes, even in strings and comments, are now marked with the + "punctuation" syntax-table property, except where they are validly bounding a + character literal. They are font locked with font-lock-warning-face except + where they are valid. This is done in C, C++, ObjC, and Java Modes. + + * lisp/progmodes/cc-defs.el (c-clear-char-property-with-value-on-char-function) + (c-clear-char-property-with-value-on-char, c-put-char-properties-on-char): New + functions/macros. + + * lisp/progmodes/cc-fonts.el (c-font-lock-invalid-single-quotes): New function. + (c-basic-matchers-before): invoke c-font-lock-invalid-single-quotes. + + * lisp/progmodes/cc-langs.el (c-get-state-before-change-functions): Remove + c-before-after-change-digit-quote from wherever it occurs. Insert + c-parse-quotes-before-change into the entries for the languages where it is + needed. + (c-before-font-lock-functions): Remove c-before-after-change-digit-quote from + wherever it occurs. Insert c-parse-quotes-after-change into the entries for + the languages which need it. + (c-has-quoted-numbers): New lang-defconst/-defvar. + + * lisp/progmodes/cc-mode.el (c-before-after-change-digit-quote): Remove. + (c-maybe-quoted-number-head, c-maybe-quoted-number-tail) + (c-maybe-quoted-number): New defconsts. + (c-quoted-number-head-before-point, c-quoted-number-tail-after-point) + (c-quoted-number-straddling-point, c-parse-quotes-before-change) + (c-parse-quotes-after-change): New functions. + +2017-07-01 Noam Postavsky + + Ignore mouse-movement for describe-key-briefly (Bug#12204) + + * lisp/help.el (help-read-key-sequence): Add optional argument ot + ignore `mouse-movement' events. + (describe-key-briefly): Use it. + * doc/emacs/help.texi (Key Help): + * etc/NEWS: Mention that mouse movement is ignored. + +2017-07-01 Noam Postavsky + + Refactor key describing commands + + * lisp/help.el (help-read-key-sequence, help--analyze-key): New + functions, extracted from `describe-key' and `describe-key-briefly'. + (describe-key, describe-key-briefly): Use them. + +2017-07-01 Eli Zaretskii + + Improve display of tabs with line numbers + + * src/xdisp.c (x_produce_glyphs): Improve calculation of next tab + stop in hscrolled lines. Prevent aborts in compute_line_metrics. + +2017-07-01 Alan Third + + Fix threads on NS (bug#25265) + + src/nsterm.h (ns_select): Compiler doesn't like sigmask being const. + (ns_run_loop_break) [HAVE_PTHREAD]: New function. + src/nsterm.m (ns_select): Call thread_select from within ns_select. + (ns_run_loop_break) [HAVE_PTHREAD]: New function. + (ns_send_appdefined): Don't wait for main thread when sending app + defined event. + src/process.c (wait_reading_process_output): Call thread_select from + within ns_select. + src/systhread.c (sys_cond_broadcast) [HAVE_NS]: Break ns_select out of + its event loop using ns_run_loop_break. + +2017-07-01 Eli Zaretskii + + Avoid slow redisplay under 'visual' mode of line numbers + + * src/xdisp.c (display_count_lines_visually): Avoid very slow + redisplay when this function is invoked very far from point. + Reported by Alex . + +2017-07-01 Noam Postavsky + + * lisp/help-fns.el (describe-variable): Let-bind cl-print-compiled-button. + +2017-07-01 Stefan Monnier + + * lisp/emacs-lisp/cl-extra.el (cl--random-state): New defstruct + + (cl--random-state, cl--random-time): Move from cl-lib.el. + (cl-random): Use struct accessors. + (cl-random-state-p): Remove, provided by the defstruct. + (cl-make-random-state): Rewrite to struct constructor. + +2017-07-01 Stefan Monnier + + * lisp/emacs-lisp/debug.el (debugger-list-functions): Remove obsolete msg + +2017-06-30 Eli Zaretskii + + Improve documentation of faces related to display-line-numbers + + * lisp/faces.el (line-number, line-number-current-line): Warn + against using non-monospaced fonts. + +2017-06-30 Eli Zaretskii + + Fix relative-number display with non-nil display-line-number-width + + * src/xdisp.c (maybe_produce_line_number): Don't treat a zero + value of display-line-number-width as acceptable. + Handle the case of 'relative' with display-line-number-width + non-nil and smaller than the absolute line number requires. + Reported by Alex . + +2017-06-30 Michael Albinus + + Release Tramp 2.3.2 + + * doc/misc/tramp.texi (Android shell setup): Show default file name. + Structure section. + + * doc/misc/trampver.texi: + * lisp/net/trampver.el: Change version to "2.3.2". + + * test/lisp/net/tramp-tests.el (tramp-test-temporary-file-directory): + Offer home directory for mock method if it doesn't exist. + +2017-06-30 Eli Zaretskii + + Add documentation for display-line-numbers + + * doc/emacs/custom.texi (Init Rebinding): + * doc/emacs/modes.texi (Minor Modes): Remove references to + linum-mode. + * doc/emacs/display.texi (Display Custom): Describe the + line-number display. + (Optional Mode Line): Fix the index entry to not conflict with + that in "Display Custom". + * doc/emacs/basic.texi (Position Info): Add cross-reference to + "Display Custom", for line-number display. + + * src/xdisp.c (syms_of_xdisp): : Mention + display-line-numbers-disable in the doc string. + + * lisp/cus-start.el (standard): Fix lst change. + +2017-06-30 Eli Zaretskii + + Support displaying zero as the number of the current line + + * src/xdisp.c (syms_of_xdisp) + : New variable. + : Doc fix. + (maybe_produce_line_number): Support nil value of + display-line-numbers-current-absolute. + + * lisp/cus-start.el (standard): Add customization form for + display-line-numbers-current-absolute. + + * etc/NEWS: Document recently introduced features. + +2017-06-30 Eli Zaretskii + + Speed up the visual-mode relative line numbers + + * src/xdisp.c (display_count_lines_visually): Introduce a + shortcut: if a relative line number was already calculated for + this iterator object, just increase it instead of the + expensive call to move_it_to. Argument list changed to pass a + pointer to the iterator object. + (maybe_produce_line_number): Adjust for change in signature of + display_count_lines_visually. Record the relative line number and + the corresponding byte position in the iterator object also in the + 'visual' mode. + +2017-06-30 Eli Zaretskii + + Fix hscrolling with line numbers on TTY frames + + * src/xdisp.c (hscroll_window_tree): Correct the X offset + calculations on TTY frames. + * src/term.c (produce_glyphs): Use it->lnum_pixel_width instead of + a kludge using it->lnum_width. + +2017-06-30 Eli Zaretskii + + Fix TAB display when the line-number face uses a smaller/larger font + + * src/dispextern.h (struct it): New member lnum_pixel_width. + * src/xdisp.c (maybe_produce_line_number): Compute the width of + the line-number display in pixels. + (x_produce_glyphs): Use it->lnum_pixel_width instead of a kludge + that used it->lnum_width and made assumptions about pixel width. + +2017-06-30 Noam Postavsky + + Escape NUL bytes in X selections (Bug#6991) + + * lisp/term/w32-win.el (w32--set-selection): + * lisp/select.el (xselect--encode-string): Replace NUL bytes with + "\0". + * doc/emacs/killing.texi: Document new behavior. + * etc/NEWS (times): Announce it. + +2017-06-30 Noam Postavsky + + Hide byte code in backtraces (Bug#6991) + + * lisp/emacs-lisp/debug.el (debugger-print-function): New defcustom, + defaulting to `cl-print'. + (debugger-insert-backtrace, debugger-setup-buffer): Use it instead of + `prin1'. + * etc/NEWS: Announce it. + +2017-06-30 Noam Postavsky + + Don't redundantly cl-print arglist in function docstring again + + * lisp/emacs-lisp/cl-print.el (cl-print-object): Don't print arglist + part of docstring. + * test/lisp/emacs-lisp/cl-print-tests.el (cl-print-tests-1): Update + test accordingly. + +2017-06-30 Noam Postavsky + + Escape control characters in backtraces (Bug#6991) + + * src/print.c (syms_of_print): Add new variable, + print-escape-control-characters. + (print_object): Print control characters with octal escape codes when + print-escape-control-characters is true. + * lisp/subr.el (backtrace): + * lisp/emacs-lisp/debug.el (debugger-setup-buffer): Bind + `print-escape-control-characters' to t. + +2017-06-30 Noam Postavsky + + Improve ert backtrace recording + + Change ert to use the new `backtrace-frames' function instead of + collecting frames one by one with `backtrace-frame'. Additionally, + collect frames starting from `signal' instead the somewhat arbitrary + "6 from the bottom". Skipping 6 frames would skip the expression that + actually caused the signal that triggered the debugger. Possibly 6 + was chosen because in the case of a failed test, the triggering frame + is an `ert-fail' call, which is not so interesting. But in case of a + test throwing an error, this drops the `error' call which is too much. + + * lisp/emacs-lisp/debug.el (debugger-make-xrefs): Remove. + * lisp/emacs-lisp/ert.el (ert--make-xrefs-region): Bring in relevant + code from `debugger-make-xrefs'. + (ert--print-backtrace): Add DO-XREFS parameter, delegate to + `debugger-insert-backtrace'. + (ert--run-test-debugger): Record the backtrace frames starting from + the instigating `signal' call. + (ert-run-tests-batch): Pass nil for `ert--print-backtrace's new + DO-XREFS parameter. + (ert-results-pop-to-backtrace-for-test-at-point): Pass t as DO-XREFS + to `ert--print-backtrace' and remove call to `debugger-make-xrefs'. + * test/lisp/emacs-lisp/ert-tests.el (ert-test-record-backtrace): Check + the backtrace list instead of comparing its string representation. + Expect `signal' to be the first frame. + +2017-06-30 Noam Postavsky + + Operate on frame list instead of printed backtrace + + * lisp/emacs-lisp/debug.el (debugger-insert-backtrace): New function, + prints the given backtrace frames. + (debugger-setup-buffer): Use it instead of editing the backtrace + buffer text. + +2017-06-29 Eli Zaretskii + + Minor fixes + + * src/xdisp.c (maybe_produce_line_number): Fix bug that caused + line numbers to be displayed in empty lines beyond ZV. + (x_produce_glyphs): Start fixing TAB display in truncated lines. + +2017-06-29 Michael Albinus + + Improve timer handling when Tramp accepts output + + * lisp/net/tramp-compat.el: Avoid compiler warning. + + * lisp/net/tramp-sh.el (tramp-sh-file-name-handler): + Remove lock machinery. + + * lisp/net/tramp.el (tramp-locked, tramp-locker): Move up. + (tramp-file-name-handler): Add lock machinery from + `tramp-sh-file-name-handler'. Allow timers to run. + (tramp-accept-process-output): Remove nasty workaround. + Suppress timers. + + * test/lisp/net/tramp-tests.el (shell-command-sentinel): + Suppress run in tests. + (tramp--instrument-test-case-p): New defvar. + (tramp--instrument-test-case): Use it in order to allow nested calls. + (tramp--test-message, tramp--test-backtrace): New defsubst, + will be used for occasional test instrumentation. + (tramp-test00-availability, tramp-test31-vc-registered): Use them. + (tramp-test28-shell-command) + (tramp--test-shell-command-to-string-asynchronously): Suppress + nasty messages. Don't overwrite sentinel. + (tramp-test36-asynchronous-requests): Rewrite major parts. + Expect :passed. + +2017-06-28 Stefan Monnier + + * lisp/url/url-history.el: Use lexical-binding + + (url-completion-function): Mark as obsolete. + Mark unused args accordingly. + +2017-06-28 Noam Postavsky + + Don't assume url structs are vectors (Bug#27333) + + * lisp/url/url-history.el (url-history-update-url): Use `url-p' + instead of `vectorp'. + +2017-06-28 Mark Oteiza + + Replace with dolist some uses of while + + * lisp/calc/calc-units.el (calc-permanent-units): + (math-compare-unit-names, math-simplify-units-quotient): + (math-build-units-table-buffer): Use dolist to replace extra bindings + and some while loops. + +2017-06-28 Noam Postavsky + + Make tcl-auto-fill-mode obsolete (Bug#10772) + + * lisp/progmodes/tcl.el (tcl-auto-fill-mode): Declare obsolete. + * etc/NEWS: Announce it. + +2017-06-28 Noam Postavsky + + Don't read eshell/which output from *Help* buffer (Bug#26894) + + * lisp/help-fns.el (help-fns--analyse-function) + (help-fns-function-description-header): New functions, extracted from + describe-function-1. + (describe-function-1): Use them. + * lisp/eshell/esh-cmd.el (eshell/which): Use + `help-fns-function-description-header' instead of + `describe-function-1'. + +2017-06-27 Eli Zaretskii + + Support default-text-properties + + * src/xdisp.c (should_produce_line_number): Call get-char-property + at ZV as well, to support default-text-properties. + +2017-06-27 Eli Zaretskii + + Avoid segfaults when some display vector is an empty string + + * src/xdisp.c (next_element_from_display_vector): Don't try + accessing the dpvec[] array if its size is zero. (Bug#27504) + +2017-06-26 Eli Zaretskii + + Initial support for visually-relative line numbers + + Works very slowly. + + * src/xdisp.c (display_count_lines_visually): New function. + (maybe_produce_line_number): Support 'visual' mode of line-number + display. + * src/xdisp.c (maybe_produce_line_number): Update IT's metrics + also when glyph_row is NULL. This is important for move_it_* + functions. + (syms_of_xdisp) : Now buffer-local. + (try_window_id, redisplay_window, try_cursor_movement): For + 'visual' line-number display, disable the same redisplay + optimizations as for 'relative'. + + * lisp/cus-start.el (standard): Add new value for the + customization form of display-line-numbers. + +2017-06-26 Eli Zaretskii + + Update IT's metrics while simulating display + + * src/xdisp.c (maybe_produce_line_number): Update IT's metrics + also when glyph_row is NULL. This is important for move_it_* + functions. + +2017-06-26 Teemu Likonen + + Fix bug in handling GnuPG's TRUST_MARGINAL status + + * lisp/epg.el (epg--status-TRUST_MARGINAL): Change symbol `marginal' + to `good'. + +2017-06-26 Tino Calancha + + Prefer `when' instead of 1-branch `if' + + * lisp/dired-aux.el (dired-do-shell-command): Store condition value + in local variable ok. + Use `when' instead of 1-branch `if'. + +2017-06-26 Tino Calancha + + Use #' instead of (function ...) + + * lisp/dired-aux.el (dired-do-chxxx, dired-clean-directory) + (dired-mark-confirm, dired-query, dired-byte-compile) + (dired-load, dired-update-file-line, dired-after-subdir-garbage) + (dired-relist-file, dired-rename-subdir, dired-do-create-files) + (dired-mark-read-file-name, dired-do-copy, dired-do-symlink) + (dired-do-hardlink, dired-do-rename, dired-do-rename-regexp) + (dired-do-copy-regexp, dired-do-hardlink-regexp) + (dired-do-symlink-regexp, dired-create-files-non-directory) + (dired-upcase, dired-downcase) + + * lisp/dired.el (dired-mode, dired-copy-filename-as-kill) + (dired-internal-do-deletions, dired-internal-do-deletions): + Prefer #' instead of (function ...). + +2017-06-26 Tino Calancha + + Don't quote lambda forms + + * lisp/dired.el (dired-re-maybe-mark, dired-map-over-marks) + (dired-mark, dired-desktop-buffer-misc-data) + + * lisp/dired-aux.el (dired-do-create-files, dired-do-create-files-regexp) + (dired-create-files-non-directory, dired-insert-subdir-validate) + (dired-alist-sort, dired-do-shell-command): Don't quote lambda forms. + +2017-06-26 Stefan Monnier + + * lisp/progmodes/cc-fonts.el: Remove/mark unused vars + + (c-font-lock-declarators): Remove unused vars `id-end', `paren-depth', + and `brackets-after-id'. + (c-font-lock-objc-methods): Mark unused args. + +2017-06-25 Paul Eggert + + Omit null-pointer test in intervals.h FRAME + + * src/intervals.h (ROOT_INTERVAL_P, ONLY_INTERVAL_P) + (INTERVAL_LAST_POS): Omit unnecessary parens. + (LENGTH): Omit test for null pointer. The argument is never null. + The unnecessary test causes GCC 7.1.0 to assume that the argument + might be null, and therefore to issue false alarms when the + argument is dereferenced in other expressions. + +2017-06-25 Paul Eggert + + Parenthesize frame.h macro definitions + + * src/frame.h (FRAME_TOOL_BAR_POSITION) + (FRAME_VERTICAL_SCROLL_BAR_TYPE, FRAME_HAS_VERTICAL_SCROLL_BARS) + (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT) + (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_RIGHT) + (FRAME_OVERRIDE_REDIRECT, FRAME_UNDECORATED, FRAME_PARENT_FRAME) + (FRAME_SKIP_TASKBAR, FRAME_NO_FOCUS_ON_MAP) + (FRAME_NO_ACCEPT_FOCUS, FRAME_NO_SPECIAL_GLYPHS, FRAME_Z_GROUP) + (FRAME_Z_GROUP_NONE, FRAME_Z_GROUP_ABOVE, FRAME_Z_GROUP_BELOW) + (FRAME_HAS_HORIZONTAL_SCROLL_BARS): Parenthesize macro definiens + to allow arbitrary expression arguments. + +2017-06-25 Paul Eggert + + Port recent frame changes to GCC 7 + + * src/frame.c (keep_ratio): New arg P. Caller changed. Since it + is non-null, it avoids a GCC 7 warning that FRAME_PARENT_FRAME + might return null. This also avoids a run-time test. + +2017-06-25 Eli Zaretskii + + Minor aesthetic fix of last change. + +2017-06-25 Eli Zaretskii + + Allow to disable display of line numbers beyond EOB + + * src/buffer.c (disable_line_numbers_overlay_at_eob): New + function. + * src/lisp.h (disable_line_numbers_overlay_at_eob): Add prototype. + * src/xdisp.c (should_produce_line_number): When at ZV, call + disable_line_numbers_overlay_at_eob to determine whether line + numbers should be displayed beyond ZV. + +2017-06-25 Michael Albinus + + Fix Android 6/7 problems in Tramp + + * lisp/net/tramp-adb.el (tramp-adb-ls-toolbox-regexp): Fix link part. + (tramp-adb-handle-directory-files-and-attributes) + (tramp-adb-handle-file-name-all-completions): Insert "." and + ".." only when needed. + (tramp-adb-get-ls-command): Force one column output for toybox. + +2017-06-25 Stefan-W. Hahn (tiny change) + + * lisp/subr.el (setq-local): Add debug declaration (Bug#27408). + +2017-06-25 Eli Zaretskii + + Fix line number display for overlay/display strings with newlines + + * src/xdisp.c (maybe_produce_line_number): Fix the condition for + producing space glyphs instead of a line number to include the + case of display strings and overlays. + +2017-06-25 Alan Mackenzie + + Make CC Mode load cl-lib rather than cl in Emacs 26. + + * lisp/progmodes/cc-cmds.el (c-declaration-limits): Remove unused local + variable. + + * lisp/progmodes/cc-defs.el (c--mapcan-status): Remove. + (c--cl-library): New variable. + (Top level): Amend the form which requires library cl or cl-lib. + (c--mapcan, c--set-difference, c--intersection, c--macroexpand-all) + (c--delete-duplicate): Amend to use c--cl-library instead of + c--mapcan-status. + + * lisp/progmodes/cc-engine.el (c-syntactic-skip-backward) + (c-back-over-compound-identifier): Remove unused local variables. + + * lisp/progmodes/cc-fonts.el (c-font-lock-declarations): Remove an unused + local variable. + + * lisp/progmodes/cc-langs.el (Top level): Amend to use c--cl-library instead + of c--mapcan-status. + + * lisp/progmodes/cc-styles.el (Top level): Add a cc-bytecomp-defun to try to + silence a compiler warning. + +2017-06-25 Martin Rudalics + + Provide additional support for child frames + + Provide mouse dragging and resizing of frames. Allow resizing + frames proportionally. Provide additional functionality for + child frames. Minor bug fixes. + + * lisp/frame.el (frame-border-width, frame-pixel-width) + (frame-pixel-height): Alias to `frame-internal-border-width', + `frame-native-width' and `frame-native-height'. + (frame-inner-width, frame-inner-height, frame-outer-width) + (frame-outer-height): New functions. + * lisp/minibuffer.el (completion-auto-help): Fix typo. + * lisp/mouse.el (mouse-drag-line, mouse-drag-mode-line) + (mouse-drag-header-line): Allow moving a frame by dragging the + mode line of its bottommost window (on a minibuffer-less frame) + or the header line of its topmost window. + (mouse-drag-vertical-line): Mention argument in doc-string. + (mouse-resize-frame, mouse-drag-frame, mouse-drag-left-edge) + (mouse-drag-top-left-corner, mouse-drag-top-edge) + (mouse-drag-top-right-corner, mouse-drag-right-edge) + (mouse-drag-bottom-right-corner, mouse-drag-bottom-edge) + (mouse-drag-bottom-left-corner): New functions for resizing a + frame by dragging its internal border together with + corresponding key bindings. + * lisp/tooltip.el (tooltip-frame-parameters): Add + 'no-special-glyphs' to default parameters and update version + tag. + * lisp/window.el (frame-auto-hide-function): Add choice to make + frame invisible and update version tag. + (window--delete): Handle 'auto-hide-function' frame parameter. + (window--maybe-raise-frame): Respect 'no-focus-on-map' and + 'no-accept-focus' frame parameters. + (display-buffer--action-function-custom-type): Add + `display-buffer-in-child-frame'. + (display-buffer): Mention `display-buffer-in-child-frame' in + doc-string. + (display-buffer-in-child-frame): New action function for + `display-buffer'. + (window--sanitize-margin): Return zero when MARGIN cannot be + sanitized. + (fit-frame-to-buffer): Major rewrite to handle child frames and + 'fit-frame-to-buffer-sizes' and 'fit-frame-to-buffer-margins' + frame parameters. + (window-largest-empty-rectangle--maximums-1) + (window-largest-empty-rectangle--maximums) + (window-largest-empty-rectangle--disjoint-maximums) + (window-largest-empty-rectangle): New functions. + + * src/dispextern.h (WINDOW_WANTS_MODELINE_P) + (WINDOW_WANTS_HEADER_LINE_P): Remove. Functionality is now + provided by corresponding functions window_wants_modeline and + window_wants_header_line in window.c. Adjust users. + * src/dispnew.c (adjust_glyph_matrix) + (buffer_posn_from_coords): Use window_wants_modeline and + window_wants_header_line instead of WINDOW_WANTS_MODELINE_P and + WINDOW_WANTS_HEADER_LINE_P. + * src/frame.c (keep_ratio): New function. + (adjust_frame_size): Call keep_ratio for each of F's child + frames. + (make_frame): Initialize no_special_glyphs slot. + (frame_internal_border_part): New function. + (Fframe_pixel_width, Fframe_pixel_height, Fborder_width): Rename + to Fframe_native_width, Fframe_native_height mand + Fframe_internal_border_width. + (frame_parm_table): Add Qno_special_glyphs entry. + (frame_float_type): New enumeration type. + (frame_float): New function to handle frame size and position + ratios. + (x_set_frame_parameters): Handle size and position ratios. + (x_set_no_special_glyphs): New function + (x_figure_window_size): Handle size and position ratios. + (syms_of_frame): Add Qdisplay_monitor_attributes_list, + Qno_special_glyphs, Qframe_edges, Qkeep_ratio, Qwidth_only, + Qheight_only, Qleft_only and Qtop_only. + * src/frame.h (internal_border_part): New enumeration type. + (struct frame): New slot no_special_glyphs. + (FRAME_NO_SPECIAL_GLYPHS): New macro. + * src/gtkutil.c (xg_frame_restack): Return immediately for + GTK versions before 2.18.0. + * src/keyboard.c (internal_border_parts): New array constant. + (make_lispy_position): For frames with border dragging enabled + return internal border part. + (syms_of_keyboard): New symbols Qdrag_internal_border, + Qleft_edge, Qtop_left_corner, Qtop_edge, Qtop_right_corner, + Qright_edge, Qbottom_right_corner, Qbottom_edge and + Qbottom_left_corner. + * src/minibuf.c (read_minibuf_unwind): When exiting the + minibuffer deal with frames that have the 'minibuffer-exit' + parameter set. + (syms_of_minibuf): New symbol Qminibuffer_exit. + * src/nsfns.m (frame_parm_handler): Add entry for + x_set_no_special_glyphs. + (Fx_create_frame): Handle 'no-special-glyphs' parameter. + Intitialize new cursor types for dragging frame borders. + * src/nsterm.h (struct ns_output): Add new cursor types for + dragging frame borders. + * src/w32fns.c (w32_frame_parm_handlers): Add entry for + x_set_no_special_glyphs. + (Fx_create_frame): Handle 'no-special-glyphs' parameter. + Intitialize new cursor types for dragging frame borders. + * src/w32term.h (struct w32_output): Add new cursor types for + dragging frame borders. + * src/window.c (coordinates_in_window) + (Fwindow_line_height, window_internal_height): Use + window_wants_modeline and window_wants_header_line instead of + WINDOW_WANTS_MODELINE_P and WINDOW_WANTS_HEADER_LINE_P. + (Fwindow_lines_pixel_dimensions): New function. + (window_parameter): New function. + (Fwindow_parameter): Call window_parameter. + (window_wants_mode_line, window_wants_header_line): New + functions replacing the macros WINDOW_WANTS_MODELINE_P and + WINDOW_WANTS_HEADER_LINE_P from dispextern.h. + (syms_of_window): New symbols Qmode_line_format and + Qheader_line_format. + * src/window.h: Reorganize and re-comment macros. Use + window_wants_modeline and window_wants_header_line instead of + WINDOW_WANTS_MODELINE_P and WINDOW_WANTS_HEADER_LINE_P. + (MINI_NON_ONLY_WINDOW_P, MINI_ONLY_WINDOW_P): Minor rewrite. + (WINDOW_BUFFER): New macro. + (WINDOW_BOX_LEFT_EDGE_COL, WINDOW_BOX_RIGHT_EDGE_COL): Remove. + * src/xdisp.c (window_text_bottom_y, window_box_height) + (window_box, start_display) + (compute_window_start_on_continuation_line) + (try_cursor_movement, redisplay_window) + (try_window_reusing_current_matrix, try_window_id) + (display_line, expose_window): Use window_wants_modeline and + window_wants_header_line instead of WINDOW_WANTS_MODELINE_P and + WINDOW_WANTS_HEADER_LINE_P. + (pos_visible_p, display_mode_lines): Respect W's + 'mode-line-format' and 'header-line-format' window parameters. + (init_iterator): Use window_wants_modeline and + window_wants_header_line instead of WINDOW_WANTS_MODELINE_P and + WINDOW_WANTS_HEADER_LINE_P. For tip frames respect + no_special_glyphs value. + (note_mouse_highlight): Set frame border cursors when on + internal border. + (x_draw_right_divider, x_draw_bottom_divider): Try to improve + drawing of window dividers. + * src/xfns.c (mouse_cursor): Add entries for border parts. + (mouse_cursor_types): Add entries for cursor types to drag + frame borders. + (INSTALL_CURSOR): Add entries for new cursor types to drag + frame borders. + (Fx_create_frame): Handle 'no-special-glyphs' parameter. + (x_frame_parm_handlers): Add entry for + x_set_no_special_glyphs. + (Vx_window_left_edge_shape, Vx_window_top_left_corner_shape) + (Vx_window_top_edge_shape, Vx_window_top_right_corner_shape) + (Vx_window_right_edge_shape) + (Vx_window_bottom_right_corner_shape) + (Vx_window_bottom_edge_shape) + (Vx_window_bottom_left_corner_shape): New variables. + (x_frame_restack): Call xg_frame_restack only for GTK versions + starting with 2.18.0. + * src/xterm.c (x_free_frame_resources): Remove new cursors for + dragging frame borders. + * src/xterm.h (struct x_output): Add new cursor types for + dragging frame borders. + + * doc/lispref/display.texi (Size of Displayed Text): Document + `window-lines-pixel-dimensions'. + * doc/lispref/elisp.texi (Top): Add entry for "Mouse Dragging + Parameters". + * doc/lispref/frames.texi (Frame Size): Replace + frame-pixel-width/-height by frame-native-width/-height. Add + frame-inner-width/-height and frame-outer-width/-height docs. + (Position Parameters): Describe specifying position as ratios. + Clarify remark about positions relative to bottom/ridge display + edge. + (Size Parameters): Describe specifying sizes as ratios. + Describe 'fit-frame-to-buffer-margins' and + 'fit-frame-to-buffer-sizes' parameters. + (Layout Parameters): Describe 'no-special-glyphs' parameter. + (Frame Interaction Parameters): Describe 'auto-hide-function', + 'minibuffer-exit' and 'keep-ratio' parameters. + (Mouse Dragging Parameters): New section describing + 'drag-internal-border', 'drag-with-header-line', + 'drag-with-mode-line', 'snap-width', 'top-visible' and + 'bottom-visible' parameters. + (Management Parameters): Mention that `override-redirect' has + no effect on MS Windows. + (Font and Color Parameters): Mention child frames for `alpha' + parameter. + (Child Frames): Rewrite section with description and cross + references to new frame parameters added. + * doc/lispref/modes.texi (Mode Line Basics): Mention + 'mode-line-format' and 'header-line-format' window parameters. + * doc/lispref/windows.texi (Resizing Windows): Mention effect + of `fit-frame-to-buffer-margins' for child frames. + (Display Action Functions): New action function + `display-buffer-in-child-frame'. + (Quitting Windows): Mention `make-frame-invisible' as optional + value of `frame-auto-hide-function' and `auto-hide-function' + frame paameter. + (Coordinates and Windows): Describe new function + `window-largest-empty-rectangle'. + (Window Parameters): Describe new parameters 'mode-line-format' + and 'header-line-format'. Index all window parameters described + in this section. + +2017-06-25 Paul Eggert + + Adjust lm-verify to accept current notices + + Problem reported by Mike Kupfer in: + http://lists.gnu.org/archive/html/emacs-devel/2017-06/msg00512.html + * lisp/emacs-lisp/lisp-mnt.el (lm-crack-copyright): + Do not require later lines in a copyright notice to have more + indentation than earlier lines. + +2017-06-24 Eli Zaretskii + + Minor change in NEWS. + + Improve documentation in NEWS. + +2017-06-24 Eli Zaretskii + + Move additional hscrolling code into a suitable 'if' + + * src/xdisp.c (hscroll_window_tree): Make additional calculations + regarding glyphs produced for line numbers conditional on + line-number display. + +2017-06-24 Eli Zaretskii + + Partial fix of hscroll of truncated lines with line numbers + + * src/xdisp.c (x_produce_glyphs, hscroll_window_tree): Adjust + hscroll calculations to line-number display. + * src/term.c (produce_glyphs): Adjust tab stop to window's + hscroll. These two changes fix horizontal scrolling when line + numbers are displayed. But there's still a bug: the horizontal + shift of lines that begin with a TAB is different from the rest. + * src/xdisp.c (move_it_in_display_line_to): Call + should_produce_line_number to determine whether a line number + should be produced for this screen line. + +2017-06-24 Noam Postavsky + + Don't change byte-compile-delete-errors at runtime (Bug#27340) + + * lisp/emacs-lisp/eieio-core.el: Confine `cl-declaim' calls to compile + time. + +2017-06-24 Eli Zaretskii + + Allow Lisp program to disable line-number display for specific lines + + * etc/NEWS: Update the documentation. + + * src/xdisp.c (syms_of_xdisp) : New + symbol. + (should_produce_line_number): New function. + (display_line): Use should_produce_line_number to determine + whether a line number should be produced for each glyph row. + +2017-06-24 Stefan Monnier + + * lisp/net/html2text.el: Move to obsolete/. + +2017-06-24 Eli Zaretskii + + Support a separate face for displaying the current line's number + + * lisp/faces.el (line-number-current-line): New face. + + * src/xdisp.c (syms_of_xdisp) : New + symbol. + (try_window_id, try_cursor_movement): Disable these optimizations + when the line-number-current-line face is different from + line-number face. + (maybe_produce_line_number): Display the current line in the + line-number-current-line face, if it's different from line-number. + +2017-06-24 Eli Zaretskii + + Change display of current line in relative mode + + * src/xdisp.c (maybe_produce_line_number): In relative mode + display the current line number as its absolute value, not as zero. + +2017-06-24 Eli Zaretskii + + Rename display-line-width + + * etc/NEWS: + * src/xdisp.c (syms_of_xdisp, maybe_produce_line_number): + * lisp/cus-start.el: Rename display-line-width to + display-line-number-width. + +2017-06-24 Eli Zaretskii + + Fix tab stops when line numbers are displayed + + * src/xdisp.c (x_produce_glyphs): + * src/term.c (produce_glyphs): Adjust tab stops for the horizontal + space taken by the line-number display. + +2017-06-24 Eli Zaretskii + + Fix crashes on TTY frames due to negative lnum_width. + + Don't display line numbers in the minibuffer and in tooltip frames. + +2017-06-24 Eli Zaretskii + + Fix problems with line-number updates in Follow mode + + * src/xdisp.c (redisplay_window): If forced window-start requires + to move a window's point, and the window is under relative + line-number display, force another round of redisplay to update + the relative line numbers. This fixes follow-mode "redisplay" of + its window group. + + * lisp/frame.el: Add display-line-numbers to the list of variables + that should trigger redisplay of the current buffer. + +2017-06-24 Eli Zaretskii + + Fix display of line numbers with fonts larger than the default + + * src/xdisp.c (maybe_produce_line_number): Update the metrics in + IT, not in IT->glyph_row, since the latter gets overwritten in + display_line. Fixes display of line numbers when the font used + for them is larger than that of the default face. + +2017-06-23 Eli Zaretskii + + Fix background color beyond EOB and cursor display + + * src/xdisp.c: (maybe_produce_line_number): Use the default face + for background of the blank glyphs in the line-number area which + are drawn beyond EOB. + (display_line): Reset the glyph row's displays_text_p flag only on + empty lines that don't display line numbers. This fixes cursor + display beyond EOB. Fix the bidi information in the glyphs + produced for line numbers. Set the avoid_cursor_p flag of glyphs + produced for line numbers. + +2017-06-23 Eli Zaretskii + + Fix display of indicate-empty-lines when line numbers are displayed + + * src/xdisp.c (row_text_area_empty): New function. + (display_line): Call row_text_area_empty to verify that a glyph + row's text area is devoid of any glyphs that came from a buffer or + a string. This fixes a bug with empty-lines indication + disappearing when line numbers or line-prefix are displayed. + (display_line): Delete the argument FORCE; all callers changed. + Remove the condition for actually producing the glyphs for the + line number, as even if the number didn't change we need to + produce empty space. + +2017-06-23 Vincent Belaïche + + Fix symbol relocation when the relocated cell is renamed. + + * lisp/ses.el (ses-sym-rowcol): Check that the renamed cell + hashmap has been instantiated before getting data from it. When + editing several spreadsheets, and you have spreadsheet #1 with a + cell named `foo', and no renamed cell in spreadsheet #2, then if + you make a formula with `foo' in spreadsheet #2, not doing this + check will make an error. + (ses-cell-set-formula): Robustify versus incorrect cell references + given in the user provided formula. An explicit error message is + provided after the action when the user gives an incorrect cell + reference, but the formula edition is not changed. This means that + if the incorrect reference is to a cell that is created someday, + then this new cell will not have the edited cell in its reference + list. Fixing this can still be done by editing again the first + cell formula. + (ses-relocate-symbol): Do not create symbol of referred-to cell + when this is a renamed cell. + +2017-06-23 Rasmus + + Synchronize with the "emacs-sync" branch from Org + +2017-06-22 Paul Eggert + + Remove getc_unlocked configure-time check + + * configure.ac (getc_unlocked): Remove check, as unlocked-io now + does this for us. + +2017-06-22 Paul Eggert + + Use unlocked stdio more systematically + + This can improve performance significantly on stdio-bottlenecked code. + E.g., make-docfile is 3x faster on my Fedora 25 x86-64 desktop. + * admin/merge-gnulib (GNULIB_MODULES): Add unlocked-io. + * lib-src/ebrowse.c, lib-src/emacsclient.c, lib-src/etags.c: + * lib-src/hexl.c, lib-src/make-docfile.c, lib-src/movemail.c: + * lib-src/profile.c, lib-src/update-game-score.c: + Include unlocked-io.h instead of stdio.h, since these programs are + single-threaded. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * lib/unlocked-io.h, m4/unlocked-io.m4: New files, copied from Gnulib. + * src/charset.c, src/cm.c, src/emacs.c, src/image.c, src/keyboard.c: + * src/lread.c, src/term.c: + Include sysstdio.h, possibly instead of stdio.h, to define + the unlocked functions if the system does not provide them. + * src/charset.c, src/lread.c (getc_unlocked): + Remove, since sysstdio.h now defines it if needed. + * src/cm.c (cmputc, cmcheckmagic): + * src/dispnew.c (update_frame, update_frame_with_menu) + (update_frame_1, Fsend_string_to_terminal, Fding, bitch_at_user): + * src/emacs.c (main, Fdump_emacs): + * src/fileio.c (Fdo_auto_save, Fset_binary_mode): + * src/image.c (slurp_file, png_read_from_file, png_load_body) + (our_stdio_fill_input_buffer): + * src/keyboard.c (record_char, kbd_buffer_get_event, handle_interrupt): + * src/lread.c (readbyte_from_file): + * src/minibuf.c (read_minibuf_noninteractive): + * src/print.c (printchar_to_stream, strout) + (Fredirect_debugging_output): + * src/sysdep.c (reset_sys_modes, procfs_ttyname) + (procfs_get_total_memory): + * src/term.c (tty_ring_bell, tty_send_additional_strings) + (tty_set_terminal_modes, tty_reset_terminal_modes) + (tty_update_end, tty_clear_end_of_line, tty_write_glyphs) + (tty_write_glyphs_with_face, tty_insert_glyphs) + (tty_menu_activate): + * src/xfaces.c (Fx_load_color_file): + Use unlocked stdio when it should be safe. + * src/sysstdio.h (clearerr_unlocked, feof_unlocked, ferror_unlocked) + (fflush_unlocked, fgets_unlocked, fputc_unlocked, fputs_unlocked) + (fread_unlocked, fwrite_unlocked, getc_unlocked, getchar_unlocked) + (putc_unlocked, putchar_unloced): Provide substitutes if not declared. + +2017-06-22 Glenn Morris + + * lisp/net/shr.el (shr-fill-text): Actually fill the text. (Bug#27399) + +2017-06-22 Michal Nazarewicz + + unidata: don’t check special casing in unidata-check (bug#26656) + + * admin/unidata/unidata-gen.el (unidata-check): Do not test special + casing mapping of characters since that mapping is not constructed from + the unidata.txt file. + Also, check for integer decoder and cons char earlier so that less + unnecessary processing is performed. + +2017-06-22 Stefan Monnier + + * lisp/descr-text.el (describe-char): Avoid string-*-multibyte + + Avoid string-to-multibyte and string-as-unibyte. + Don't make *Help* unibyte just because the char was in a unibyte buffer. + +2017-06-22 Rasmus + + Add Org schemas.xml contents to Emacs schemas.xml + + Entries from the Org version of schemas.xml have been added to + the Emacs version of schemas.xml. + +2017-06-22 Rasmus + + Update Org to v9.0.9 + + Please see etc/ORG-NEWS for details. + +2017-06-22 Vincent Belaïche + + Do not hard code A1 cell reference, as it may be renamed. + + * lisp/ses.el (ses-recalculate-all): `A1' -> `(ses-cell-symbol 0 0)' + +2017-06-22 Martin Rudalics + + Fix make_hash_table calls in lread.c + + * src/lread.c (readevalloop, read_internal_start): Fix + make_hash_table calls to make build succeed. + +2017-06-22 Ken Raeburn + + Merge several Lisp reader speedups. + +2017-06-22 Ken Raeburn + + Create less garbage to collect while reading symbols. + + * src/lread.c (read1): When interning a symbol, only create a new + string object for the name if we're going to use it for a new symbol + object. + +2017-06-22 Ken Raeburn + + Replace read_objects assoc list with two hash tables. + + For larger input files with lots of shared data structures, an + association list is too slow. + + * src/lread.c (read_objects_map, read_objects_completed): New + variables, replacing read_objects. + (readevalloop): Initialize them with hash tables before starting a + top-level read, if they're not already empty hash tables, and reset + them to Qnil afterwards if something was added to the hash tables. + (read_internal_start): Likewise. + (read1): Store first the placeholder and later the newly read object + into read_objects_map under the specified object number. If the new + object can contain a reference to itself, store it in + read_objects_completed. + (substitute_objects_recurse): Check read_objects_completed instead of + read_objects for the known possibly-recursive objects. + (syms_of_lread): Update initializations. + +2017-06-22 Ken Raeburn + + Use getc_unlocked. + + * configure.ac: Check for getc_unlocked. + * src/charset.c (read_hex, load_charset_map_from_file): Use + getc_unlocked instead of getc. + (getc_unlocked) [!HAVE_GETC_UNLOCKED]: Fall back to getc. + * src/lread.c (readbyte_from_file, Fget_file_char, read1, + getc_unlocked): Likewise. + +2017-06-22 Stefan Monnier + + Reduce lread substitutions. + + * src/lread.c (read1): After reading an object using the "#n=" syntax, + if the read object is a cons cell, instead of recursively substituting + the placeholder with the new object, mutate the placeholder cons cell + itself to have the correct car and cdr values. + +2017-06-22 Ken Raeburn + + Short-circuit substitutions for some simple types. + + Values that don't contain other values cannot be circular, so checking + for circular objects is a waste of cycles. + + * src/lread.c (substitute_object_recurse): If the subtree being + examined is a symbol, number, or property-less string, just return + it. + +2017-06-22 Paul Eggert + + Limit style_format to MAX_ALLOCA + + * src/editfns.c (styled_format): Subtract initial buffer size + from sa_avail, since it is nontrivial. + +2017-06-22 Paul Eggert + + Limit bidi_find_bracket_pairs to MAX_ALLOCA + + * src/bidi.c (MAX_BPA_STACK): Now a constant, not a macro. + Shrink it to allow for the two struct bidi_it objects in + the same frame. + (PUSH_BPA_STACK): Avoid integer overflow with enormous bidi cache. + (bidi_find_bracket_pairs): Use compile-time check instead of runtime. + +2017-06-22 Paul Eggert + + Limit insert-file-contents to MAX_ALLOCA + + * src/fileio.c (READ_BUF_SIZE): Don’t allocate more than + MAX_ALLOCA bytes in a single stack array. + +2017-06-21 Paul Eggert + + Remove malloc_find_address relic + + * src/gmalloc.c (register_heapinfo, _malloc_internal_nolock): + Omit unnecessary initialization. + +2017-06-21 Paul Eggert + + Fix temacs hybrid_malloc core dump + + Without this patch, ./temacs would dump core sometimes on Fedora + 25 x86-64. The problem was that the hybrid allocator assumed that + all pointers into bss_sbrk_buffer are allocated via gmalloc. This + assumption is not true on Fedora, because the standard memory + allocator calls gdefault_morecore, which means its blocks are + interleaved with our blocks. Usually the code happened to work, + because our data structures agreed with the glibc data structures, + but this was merely luck due to a shared pedigree, and as glibc + mutates our luck has run out. + * src/gmalloc.c (ALLOCATED_BEFORE_DUMPING) [HYBRID_MALLOC]: + Remove; no longer needed. + (BLOCK): Use unsigned division, as that does the right thing near zero. + (register_heapinfo, __malloc_internal_nolock, __free_internal_nolock) + (_realloc_internal_nolock): + Big blocks now have type -1, not 0, as 0 now means the block is + not ours. + (morecore_nolock): Omit now-unnecessary casts to size_t. + (allocated_via_gmalloc) [HYBRID_MALLOC]: New function. + (hybrid_free, hybrid_realloc) [HYBRID_MALLOC]: Use it, to + avoid calling the wrong free or realloc function in some cases. + +2017-06-21 Katsumi Yamaoka + + Make gnus-article-date-user work + + * lisp/gnus/gnus-art.el (article-date-ut): + Work for unfolded multi-line Date header. + (article-transform-date): + Refactor; add header name if it is missing in user-defined date line. + (article-date-user): Fix name of date type. + +2017-06-21 Noam Postavsky + + Keep order of completion candidates (Bug#25995, Bug#24676) + + * lisp/minibuffer.el (completion-pcm--filename-try-filter) + (completion-pcm--all-completions): Use nreverse to undo the reversing + caused by using push in the loop. + +2017-06-21 Glenn Morris + + * src/lread.c (syms_of_lread) : Doc fix. + +2017-06-21 Alex Gramiak + + Mark prolog indent variables as safe (bug#27369) + + * lisp/progmodes/prolog.el (prolog-indent-width) + (prolog-left-indent-regexp, prolog-paren-indent-p) + (prolog-paren-indent): Add :safe property. + +2017-06-20 Simen Heggestøyl + + Remove `:options' from `css-electric-keys' + + * lisp/textmodes/css-mode.el (css-electric-keys): Remove `:options` + since it just duplicates the default value. + +2017-06-20 Paul Eggert + + Fix crash when built by GNU Gold linker on x86 + + Problem reported by Andrés Musetti (Bug#27248). + * src/widget.c (emacsFrameClassRec): Do not initialize superclass here. + (emacsFrameClass): Now a function (which initializes the + superclass) instead of a variable. All uses changed. + +2017-06-20 Paul Eggert + + Simplify autogen.sh version checking + + * autogen.sh (get_version): Simplify and make more reliable + by using expr rather than echo | sed. Check exit status of program. + Run program in subshell in case it cannot be executed. + (check_version): Check exit status of command rather than its output. + Check return status of get_version. + +2017-06-20 Katsumi Yamaoka + + Delete old Date header in a simple way + + * lisp/gnus/gnus-art.el (article-date-ut): Don't rely on text prop + when searching the old Date header boundary in order to delete it. + +2017-06-20 Bastien + + Revert "Don't bind org-agenda key to an anonymous function" + + This reverts commit 49c0ff29c2e0243ba35ec17e3e3af49369be43db. + +2017-06-20 Katsumi Yamaoka + + Bind enable-local-variables to nil globally (fix dbe3e41) + + * lisp/gnus/mm-view.el (mm-display-inline-fontify): + Bind enable-local-variables to nil globally instead of making it + buffer-local; remove let-bind of local-enable-local-variables. + cf. in the emacs-devel list. + +2017-06-20 Glenn Morris + + kill-matching-buffers to optionally not confirm + + * lisp/files.el (kill-matching-buffers): + Add option to not confirm killing. (Bug#27286) + +2017-06-20 Glenn Morris + + * lisp/files.el (local-enable-local-variables): Doc fix. + +2017-06-20 Glenn Morris + + autogen.sh: try to check for tool being present but broken + + * autogen.sh (get_version): Check return status of "--version". + (check_version): Try to distinguish between a missing tool + and a broken one. (Bug#27288) + +2017-06-19 Glenn Morris + + Avoid a custom-variable-type error (bug#27363) + + * lisp/cus-edit.el (custom-variable-type): + Avoid an error due to plist-put becoming stricter of late. + +2017-06-19 Glenn Morris + + Don't put deleted packages in the trash (bug#14967) + + * lisp/emacs-lisp/package.el (package-delete): + Don't pay attention to delete-by-moving-to-trash. + +2017-06-19 Nicolas Petton + + Revert "Add current-line in simple.el" + + This reverts commit ae98cdf9431604d0f722f1db217ca06debfbb7b6. + +2017-06-19 Damien Cassou + + Add current-line in simple.el + + * lisp/simple.el (current-line): New function. + * test/list/simple-tests.el: Add tests for current-line. + +2017-06-19 Katsumi Yamaoka + + Don't try to eval local variables in Gnus article + + * lisp/gnus/mm-view.el (mm-display-inline-fontify): Disable local vars. + +2017-06-18 Paul Eggert + + Fix emacs-module.h cleaning + + * src/Makefile.in (clean): Do not remove emacs-module.h.in. + (bootstrap-clean): Remove emacs-module.h. + +2017-06-18 Andreas Schwab + + * url/url-util.el (url-get-url-at-point): Add missing group in + regex. + +2017-06-18 Paul Eggert + + * lib/gettext.h: Merge from gnulib. + +2017-06-18 Paul Eggert + + Merge from gnulib + + This (and my previous patch) incorporate: + 2017-06-17 diffseq: port to GCC 7 with --enable-gcc-warnings + 2017-06-15 gettext-h: Update comment + * lib/diffseq.h: Copy from gnulib. + +2017-06-17 Noam Postavsky + + * test/Makefile.in: Don't suppress test failure for single tests. + +2017-06-17 Philipp Stephani + + emacs-module.h: Create emacs_env_26 + + This was part of the original design of the module + API (https://lists.gnu.org/archive/html/emacs-devel/2015-02/msg00960.html), + but I didn't take it into account when adding the should_quit + function. + + Instead of duplicating the environment fields or using the C + preprocessor, use configure to build emacs-module.h. + + * configure.ac: Expand emacs-module.h template. + +2017-06-17 Eli Zaretskii + + Improve documentation of replace-buffer-contents + + * etc/NEWS (replace-buffer-contents): Fix formatting. + + * src/editfns.c (Freplace_buffer_contents): Doc fix. + +2017-06-17 Eli Zaretskii + + Finish up native display of line numbers + + * src/xdisp.c (maybe_produce_line_number): Produce a blank before + the number, for R2L rows. Increment 'g' in the loop even if + glyph_row is NULL. Accept 2nd argument FORCE and produce the + line-number glyphs if it is non-zero. + (move_it_in_display_line_to): Account for the space taken by the + line-number glyphs. Call maybe_produce_line_number with 2nd + argument non-zero. + (set_cursor_from_row): Fix calculation of cursor X coordinate in + R2L rows with display-produced glyphs at the beginning. + (syms_of_xdisp) : New face symbol. + : New symbols. + (maybe_produce_line_number): Use the line-number face for + displaying line numbers. Support relative line-number display. + Support user-defined width for displaying line numbers. + (try_cursor_movement, try_window_id): Disable these optimizations + when displaying relative line numbers. + * src/dispextern.h (struct it): New member 'pt_lnum'. + + * lisp/faces.el (line-number): New face. + * lisp/cus-start.el (standard): Provide customization forms for + display-line-numbers and display-line-width. + * lisp/menu-bar.el (menu-bar-showhide-menu): Add menu-bar item to + turn display-line-numbers on and off. + + * etc/NEWS: Document the new feature. + +2017-06-17 Philipp Stephani + + Allow local variables section to begin with a square bracket + + Fixes Bug#27391. + + * lisp/international/mule.el (find-auto-coding): Fix regular + expression for "Local Variables" section. + + * test/lisp/international/mule-tests.el (find-auto-coding--bug27391): + Add unit test. + +2017-06-17 Philipp Stephani + + Remove unnecessary point motion + + * src/editfns.c (Freplace_buffer_contents): Remove unnecessary point + motion. + +2017-06-17 Philipp Stephani + + Add command to replace buffer contents + + Add a new command 'replace-buffer-contents' that uses the Myers diff + algorithm to non-destructively replace the accessible portion of the + current buffer. The Myers algorithm is implemented in Gnulib. + + * src/editfns.c (Freplace_buffer_contents): New command. + (set_bit, bit_is_set, buffer_chars_equal): New helper functions. + (syms_of_editfns): Define new command. + + * test/src/editfns-tests.el (replace-buffer-contents-1) + (replace-buffer-contents-2): New unit tests. + + * src/buffer.h (BUF_FETCH_CHAR_AS_MULTIBYTE): New helper macro. + + * admin/merge-gnulib (GNULIB_MODULES): Add diffseq.h and minmax.h. + +2017-06-17 Andreas Schwab + + * international/characters.el: Update list of zero and full width + characters according to Unicode 9.0.0. + +2017-06-17 Simen Heggestøyl + + Complete CSS property values less eagerly (Bug#27392) + + * lisp/textmodes/css-mode.el (css--complete-property-value): Be less + eager by looking for a colon after the property which values are being + completed for. + + * test/lisp/textmodes/css-mode-tests.el (css-test-complete-property): + Add a test case ensuring that properties that are prefixes of other + properties don't hinder further completion. + +2017-06-17 Noam Postavsky + + Handle integer indices for eshell variables (Bug#26055) + + * lisp/eshell/esh-var.el (eshell-index-value): Convert index to number + if it's been marked as one, just like `eshell-lisp-command' does. + +2017-06-17 Mark Oteiza + + Don't bind org-agenda key to an anonymous function + + * lisp/org/org-agenda.el: Bind "g" to named command. + (org-agenda-redo-all): New command. Extend the previous functionality + through a prefix argument. + +2017-06-17 Dmitry Gutov + + Add test for the fix in the parent commit + + * test/src/undo-tests.el (undo-test-skip-invalidated-markers): + New test, for the fix in the parent commit. + +2017-06-17 Nitish Chandra (tiny change) + + primitive-undo: Update only the currently valid markers + + * lisp/simple.el (primitive-undo): + Update only the currently valid markers (bug#25599). + +2017-06-16 Eli Zaretskii + + Initial version of native display of line numbers + + * src/xdisp.c (syms_of_xdisp) : New + buffer-local variable. + Include . + (maybe_produce_line_number): New function. + (DISP_INFINITY): Rename from INFINITY, since math.h defines INFINITY. + (try_window_reusing_current_matrix): Don't use this method when + display-line-numbers is in effect. + * src/dispextern.h (struct it): New members 'lnum'. + +2017-06-16 Philipp Stephani + + Correctly detect URLs surrounded by parentheses in comments + + * lisp/thingatpt.el (thing-at-point--bounds-of-well-formed-url): + Make parentheses match work inside comments. + + * test/lisp/thingatpt-tests.el (thing-at-point-url-in-comment): Add + unit test. + +2017-06-16 Michael Albinus + + Fix load-path issue when it contains remote directories + + * lisp/net/tramp.el (tramp-file-name-handler): Use `autoloadp'. + (tramp-use-absolute-autoload-file-names): New defun. Call it + after loading tramp.el. + + * test/lisp/net/tramp-tests.el (tramp-test38-remote-load-path): + New test. + (tramp-test39-unload): Rename. + +2017-06-16 Alan Mackenzie + + Ensure C++ initializer lists don't get fontified. + + * lisp/progmodes/cc-cmds.el (c-block-comment-flag): Move declaration to solve + compiler warning. + + * lisp/progmodes/cc-fonts.el (c-get-fontification-context): Add an extra + clause to handle C++ member initialization lists. + (c-font-lock-single-decl): New function, extracted from + c-font-lock-declarations. + (c-font-lock-declarations): Call c-font-lock-single-decl in place of inline + code. + (c-font-lock-cut-off-declarators): Make more rigorous by calling + c-get-fontification-context, c-forward-decl-or-cast-1, and + c-font-lock-single-decl in place of rather approximate code. + +2017-06-16 Alan Mackenzie + + Fix hang in CC Mode when ":" is typed after identifier at EOB. + + * list/progmodes/cc-engine.el (c-forward-declarator): Fix coding error + confusing ":" and EOB. + +2017-06-15 Alan Mackenzie + + Create a toggle between block and line comments in CC Mode. + + Also (unrelated change) initialize the modes' keymaps at each loading. + + * lisp/progmodes/cc-cmds.el (c-update-modeline): amend for the new information + on the modeline. + (c-block-comment-flag): New variable. + (c-toggle-comment-style): New function. + + * lisp/progmodes/cc-langs.el (c-block-comment-starter) + (c-line-comment-starter): Make them c-lang-defvars. + (c-block-comment-is-default): New c-lang-defvar. + (comment-start, comment-end): Make the default values dependent on + c-block-comment-is-default. + + * lisp/progmodes/cc-mode.el (c-mode-base-map): Define C-c C-k in this map. + (c-basic-common-init): Initialize c-block-comment-flag. + (c-mode-map, c++-mode-map, objc-mode-map, java-mode-map, idl-mode-map) + (pike-mode-map, awk-mode-map): Make entries in these key maps each time the + mode is loaded rather than just once per Emacs session. + + * doc/misc/cc-mode.texi (Comment Commands): Introduce the notion of comment + style. + (Minor Modes): Define comment style. Describe how comment style influences + the information displayed on the modeline. Document c-toggle-comment-style. + (FAQ): Add a question about toggling the comment style. + +2017-06-15 Paul Eggert + + Pacify clang without munging C source + + * configure.ac (WARN_CFLAGS): With Clang, use + -Wno-tautological-compare regardless of --enable-gcc-warnings. + (WERROR_CFLAGS): Simplify assignments, and guarantee it’s always set. + * lib/strftime.c: Copy from gnulib, reverting Clang-specific + change which I hope is no longer needed. + * src/emacs.c (main): Revert rlim_t change, as rlim_t is signed on + some older non-POSIX hosts. + +2017-06-15 Paul Eggert + + No need to complicate make-docfile.c for Clang + + * lib-src/make-docfile.c (put_filename): Undo recent change. + The Clang false alarm occurs only with CFLAGS=-save-temps and + we needn’t worry about pacifying unusual compiler configurations. + +2017-06-15 Paul Eggert + + Port './configure CC=clang' to Fedora 25 + + * configure.ac (HAVE_IMAGEMAGICK): Disable if even a + standard function like MagickRelinquishMemory does not link. + +2017-06-15 Paul Eggert + + Don’t worry about __STDC_VERSION__ in emacs-module + + * src/emacs-module.h: Remove __STDC_VERSION__ check. In the past + we’ve found that some compilers do not define this symbol even + when they work well enough. If necessary features like stdbool.h + are missing the compiler will complain eventually anyway. + +2017-06-14 Paul Eggert + + Port cleanup check to Oracle Studio 12.5 + + * src/conf_post.h (__has_attribute_cleanup): Resurrect. + * src/emacs-module.c: Verify __has_attribute (cleanup), but in an + #if this time. + +2017-06-14 Bastien + + Fix misformatted changelog entry + +2017-06-14 Eli Zaretskii + + Avoid compiler warning in image.c on MS-Windows + + * src/image.c (x_create_x_image_and_pixmap) [HAVE_NTGUI]: Avoid + compilation warning under -Warray-bounds by temporarily disabling + the -Warray-bounds option. + +2017-06-14 Michael Albinus + + Fix Bug#27315 + + * lisp/net/tramp-cache.el (tramp-cache-read-persistent-data): + New defvar. + (top): Use it. + + * lisp/net/tramp.el (tramp-handle-file-name-case-insensitive-p): + Check for connected, not for connectable. (Bug#27315) + (tramp-process-actions): + * lisp/net/tramp-gvfs.el (tramp-gvfs-maybe-open-connection): + Use `tramp-cache-read-persistent-data'. + + * test/lisp/net/tramp-tests.el (top): Set also + `tramp-cache-read-persistent-data'. + +2017-06-14 Noam Postavsky + + Give a fixed default value for icomplete-prospects-height (Bug#26939) + + * lisp/icomplete.el (icomplete-prospects-height): Default to 2. + (icomplete-prospects-length): Remove. + * etc/NEWS: Announce removal. + +2017-06-14 Philipp Stephani + + Remove some tautological comparisons involving rlim_t + + Clang on macOS warns about these with -Wtautological-compare. POSIX + guarantees that rlim_t is + unsigned (cf. + http://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/resource.h.html), + so these resource limits can never be negative. + + * src/emacs.c (main): Remove tautological comparisons. + +2017-06-14 Philipp Stephani + + Use --module-assertions if modules are available + + Using --module-assertions helps us find bugs in the test module. But + we can use it only if Emacs was compiled with module support. + + * test/Makefile.in (MODULES_EMACSOPT): New variable. + (emacs): Use it. + +2017-06-14 Philipp Stephani + + Define --module-assertions only of modules are available + + Fixes Bug#27352. + + * src/emacs.c (usage_message, standard_args): Define + --module-assertions only if Emacs has been compiled with module + support. + +2017-06-14 Katsumi Yamaoka + + gnus-article-read-summary-keys: Don't move point for WDD and WDW commands + + * lisp/gnus/gnus-art.el (gnus-article-read-summary-keys): + No need to restore window config for WDD and WDW commands. + +2017-06-14 Thomas Fitzsimmons + + lisp/net/soap-client.el: Bump version to 3.1.3 + + * lisp/net/soap-client.el: Bump version to 3.1.3. + (soap-name-p): Fix checkdoc issue. + +2017-06-14 Alex Harsanyi + + Fix an HTTP encoding error in soap-client.el + + * lisp/net/soap-client.el (soap-invoke-internal): Make + SOAPAction header a UTF-8 encoded string. + +2017-06-14 Paul Eggert + + Port cleanup attribute to Oracle Studio 12.5 + + * INSTALL (--with-modules): List cleanup attribute as prereq. + * src/conf_post.h (__has_attribute_cleanup): Remove; no longer needed. + * src/emacs-module.c (MODULE_SETJMP_1): Don’t attempt to verify + (__has_attribute (cleanup)), as Oracle Studio 12.5 supports + __has_attribute only inside preprocessor expressions. The C + compiler should check the cleanup attribute in the next line anyway. + (module_reset_handlerlist): Remove an unnecessary ‘const’ + that causes Oracle Studio 12.5 to refuse to compile. + +2017-06-14 Glenn Morris + + Fix running tests in without-modules builds + + * test/Makefile.in (EMACSOPT): Remove option that is only defined + with-modules. emacs-module-tests.el passes it where needed. + +2017-06-13 Glenn Morris + + * test/Makefile.in (src/emacs-module-tests.log): Out-of-tree fix. + +2017-06-13 Philipp Stephani + + Inline test module Makefile into main test Makefile + + The test/data/emacs-module/Makefile only built a single target, and + inlining it into test/Makefile simplifies dependency tracking and + reduces code duplication. + + * configure.ac: Don't build test/data/emacs-module/Makefile. + + * Makefile.in ($(test_module)): Inline compilation. + (clean): Also clean test module outputs. + +2017-06-13 Michael Albinus + + * lisp/net/tramp-sh.el (tramp-set-file-uid-gid): Do not handle locally on w32. + +2017-06-13 Michael Albinus + + Minor tweaks in Tramp manual + + * doc/misc/trampver.texi: Add prefixwithspace flag. + + * doc/misc/tramp.texi (Password handling): Harmonize example. + (File name completion): Use prefixwithspace flag. + (Frequently Asked Questions): Explain `tramp-histfile-override'. + +2017-06-13 Philipp Stephani + + Silence two Clang warnings by introducing additional local variables + + * lib/strftime.c (libc_hidden_def): + * lib-src/make-docfile.c (put_filename): Introduce local variables to + silence Clang warnings. + +2017-06-13 Noam Postavsky + + Fix wrong indentation after string literal (Bug#27306) + + * lisp/emacs-lisp/lisp-mode.el (lisp-indent-state) + (lisp-indent-calc-next): Remove `depth' field, use (car ppss) instead. + * test/lisp/emacs-lisp/lisp-mode-tests.el + (lisp-indent-region-after-string-literal): New test. + +2017-06-13 Philipp Stephani + + Fix version checks for emacs-module.h + + We don't need C11 or C++11 because stdbool.h is in C99, and for C++ we + don't need it at all. + +2017-06-13 Noam Postavsky + + Buttonize # part of printed functions (Bug#25226) + + * lisp/emacs-lisp/cl-print.el: Autoload `disassemble-1'. + (cl-print-compiled-button): New variable. + (help-byte-code): New button type, calls `disassemble' in its action. + (cl-print-object): Use it if `cl-print-compiled-button' is + non-nil. + +2017-06-12 Philipp Stephani + + Print module structure sizes when initializing test module + + * test/data/emacs-module/mod-test.c (emacs_module_init): Print + compile-time and runtime sizes of module structures to ease debugging + +2017-06-12 Glenn Morris + + Small portability fix for emacs-module.h (bug#27346) + + * src/emacs-module.h (EMACS_ATTRIBUTE_NONNULL) [!__has_attribute]: + Avoid 'error: missing binary operator before token "("'. + +2017-06-12 Glenn Morris + + Give a more informative failure in module assertion test + + * test/src/emacs-module-tests.el (module--test-assertions): + Rephrase final check to give a more informative failure. + +2017-06-12 Philipp Stephani + + Fix off-by-one error + + * test/data/emacs-module/mod-test.c (emacs_module_init): Fix + off-by-one error. + +2017-06-12 Glenn Morris + + Clean up after module assertion tests + + * test/src/emacs-module-tests.el (module--test-assertions): + Use a temporary directory to contain any core dumps. + +2017-06-12 Glenn Morris + + Small improvement for module assertion test + + * test/src/emacs-module-tests.el (module--test-assertions): + Don't rely on the precise form of an "Abort" message. + +2017-06-12 Glenn Morris + + Improve previous test/data/emacs-module/Makefile change + + * test/data/emacs-module/Makefile.in (clean): + Avoid doing unpleasant things if run in a build without modules. + +2017-06-12 Glenn Morris + + Small improvements for test/data/emacs-module/Makefile + + * test/data/emacs-module/Makefile.in (%.o): + Fix emacs-module dependency. + (SECONDARY): Stop make automatically deleting *.o. + (clean): New rule. + +2017-06-12 Glenn Morris + + * make-dist: Skip some more generated files in test/. + +2017-06-12 Alan Third + + Note how fullscreen differs on the NS port + + doc/lispref/frames.texi (Size Parameters): + doc/emacs/frames.texi (Tool Bars): Add a description of how macOS + hides the tool-bar and menu-bar in fullscreen. + +2017-06-12 Alan Third + + Add no-focus-on-map to NS build (bug#25408) + + * src/nsfns.m (ns_frame_parm_handlers): Add x_set_no_focus_on_map. + (x-create-frame): Check for no-focus-on-map. + * src/nsterm.h (x_set_no_focus_on_map): New function. + * src/nsterm.m (x_set_no_focus_on_map): New function. + (ns_raise_frame): Add parameter for specifying whether to focus the + frame. + (ns_frame_raise_lower): + (x_make_frame_visible): Handle new parameter for ns_raise_frame. + +2017-06-12 Paul Eggert + + _Noreturn not noreturn + + _Noreturn is more portable to non-C11 platforms. See: + https://www.gnu.org/software/gnulib/manual/html_node/stdnoreturn_002eh.html + * src/emacs-module.c: Use _Noreturn, not noreturn. No need to + include . Reindent to fit in 80 columns. + +2017-06-12 Glenn Morris + + Update make-dist for recent test/ changes + + * make-dist: No longer distribute test/data/emacs-module/Makefile. + +2017-06-12 Michael Albinus + + Handle port and domain in Tramp's password cache + + * doc/misc/tramp.texi (Password handling): Explain port and + domain handling in authinfo. + + * lisp/net/tramp.el (tramp-process-actions, tramp-clear-passwd): + * lisp/net/tramp-gvfs.el (tramp-gvfs-handler-askpassword): + * lisp/net/tramp-sh.el (tramp-do-copy-or-rename-file-out-of-band) + (tramp-maybe-open-connection): + * lisp/net/tramp-smb.el (tramp-smb-handle-copy-directory) + (tramp-smb-handle-file-acl, tramp-smb-handle-set-file-acl) + (tramp-smb-maybe-open-connection): Handle also domain and port. + +2017-06-12 Eli Zaretskii + + Avoid compilation warnings with pre-C99 libc + + * src/emacs-module.c (module_free_global_ref) + (module_assert_runtime, module_assert_env, value_to_lisp): Use 'pD' + instead of C99 't' format descriptor. + +2017-06-12 Philipp Stephani + + Flush all output streams before aborting + + Maybe the stdout buffer still contains something interesting that + should be flushed. + + * src/emacs-module.c (module_abort): Flush all output streams before + aborting. + +2017-06-12 Philipp Stephani + + Remove an assertion that doesn't test Emacs invariants + + * src/emacs-module.c (module_copy_string_contents): Remove an + assertion that doesn't test Emacs invariants. + +2017-06-12 Philipp Stephani + + Test module: add necessary version checks + + * test/data/emacs-module/mod-test.c (emacs_module_init): Add necessary + version checks. + +2017-06-12 Philipp Stephani + + Use additional CFLAGS from configure + +2017-06-12 Philipp Stephani + + Use Autoconf to generate the test module Makefile + + This makes it easier to pass compilation flags around. + + * configure.ac: Also build test module Makefile. + + * test/data/emacs-module/Makefile.in: New makefile template. + + * test/Makefile.in ($(test_module)): No longer necessary to pass + @MODULES_SUFFIX@ around. + + * .gitignore: Test module Makefile can now be ignored. + +2017-06-12 Philipp Stephani + + Also compile test module as C11 + + * test/data/emacs-module/Makefile (CFLAGS): Compile test module as C11 + +2017-06-12 Philipp Stephani + + Implement module assertions for users + + Add a new command-line option '-module-assertions' that users can + enable developing or debugging a module. If this option is present, + Emacs performs additional checks to verify that modules fulfill their + requirements. These checks are expensive and crash Emacs if modules + are invalid, so disable them by default. + + This is a command-line option instead of an ordinary variable because + changing it while Emacs is running would cause data structure + imbalances. + + * src/emacs.c (main): New command line option '-module-assertions'. + + * src/emacs-module.c (module_assert_main_thread) + (module_assert_runtime, module_assert_env, module_assert_value): + New functions to assert module requirements. + (syms_of_module): New uninterned variable 'module-runtimes'. + (init_module_assertions, in_main_thread, module_abort): New helper + functions. + (initialize_environment): Initialize value list. If assertions are + enabled, use a heap-allocated environment object. + (finalize_environment): Add assertion that environment list is never + empty. + (finalize_runtime_unwind): Pop module runtime object stack. + (value_to_lisp): Assert that the value is valid. + (lisp_to_value): Record new value if assertions are enabled. + (mark_modules): Mark allocated object list. + (MODULE_FUNCTION_BEGIN_NO_CATCH) + (module_non_local_exit_check, module_non_local_exit_clear) + (module_non_local_exit_get, module_non_local_exit_signal) + (module_non_local_exit_throw): Assert thread and environment. + (module_get_environment): Assert thread and runtime. + (module_make_function, module_funcall, module_intern) + (module_funcall, module_make_integer, module_make_float) + (module_make_string, module_make_user_ptr, module_vec_get) + (funcall_module, Fmodule_load): Adapt callers. + (module_make_global_ref): If assertions are enabled, use the global + environment to store global values. + (module_free_global_ref): Remove value from global value list. + + * test/Makefile.in (EMACSOPT): Enable module assertions when testing + modules. + + * test/data/emacs-module/mod-test.c (Fmod_test_invalid_store) + (Fmod_test_invalid_load): New functions to test module assertions. + (emacs_module_init): Bind the new functions. + + * test/src/emacs-module-tests.el (mod-test-emacs): New constant for + the Emacs binary file. + (mod-test-file): New constant for the test module file name. + (module--test-assertions): New unit test. + +2017-06-12 Philipp Stephani + + emacs-module: Use __attribute__((nonnull)) + + Annotate all parameters with __attribute__((nonnull)) that may not be + NULL. + +2017-06-12 Philipp Stephani + + Explicitly require C11 or C++11 in emacs-module.h + + We already implicitly require them by including stdbool.h. Just make + the error message a bit clearer, and remove an unnecessary version + comparison. + +2017-06-12 Philipp Stephani + + Add missing 'require' forms to prevent compiler warnings. + + * lisp/eshell/esh-ext.el (esh-arg, esh-proc): Add missing + requirements. + +2017-06-12 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-06-11 getopt-posix: port to glibc 2.25.90 + 2017-06-04 same-inode: port better to VMS 8.2 and later + * doc/misc/texinfo.tex, lib/getopt-pfx-core.h, lib/getopt-pfx-ext.h: + * m4/sys_types_h.m4: Copy from gnulib. + +2017-06-12 Paul Eggert + + Remove Lisp_Misc_Float + + * src/data.c (Ftype_of): Do not worry about Lisp_Misc_Float. + * src/lisp.h (Lisp_Misc_Float): Remove. This placeholder has been + unused for two decades; if we ever want to change floats to be a + misc type we can bring it back then. + +2017-06-12 Paul Eggert + + Make two symbols private to emacs-module.c + + * src/lisp.h (allocate_module_function, XSET_MODULE_FUNCTION): + Move from here ... + * src/emacs-module.c: ... to here. + +2017-06-12 Glenn Morris + + Merge from origin/emacs-25 + + da62c1532e4 (origin/emacs-25) Improve the documentation of filesets + +2017-06-12 Glenn Morris + + Merge from origin/emacs-25 + + e80f6a210b0 Describe problems with Microsoft Intellipoint + a73ec1edb07 More accurate documentation of the ':box' face attribute + +2017-06-12 Glenn Morris + + Merge from origin/emacs-25 + + eaa00584ceb Improve documentation of 'gnutls-verify-error' + 908498cc01b ; etc/PROBLEMS: Describe GTK-related crashes on elementar... + 741daec617e ; Describe the problem with ksh when resizing shell window + +2017-06-11 Michael Albinus + + Some further improvements for tramp-gvfs.el + + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-expand-file-name) + (tramp-gvfs-get-file-attributes) + (tramp-gvfs-maybe-open-connection): Handle davs? properly. + (tramp-gvfs-handler-askquestion): Improve `yes-or-no-p' prompt. + Show question also in batch mode. Cache result. + + * test/lisp/net/tramp-tests.el (tramp-test24-file-name-completion): + Support completion for host names and ports. + +2017-06-11 Simen Heggestøyl + + Fix highlighting of CSS selectors with double hyphens + + * lisp/textmodes/css-mode.el (css--font-lock-keywords): Fix + highlighting of selectors that contain double hyphens. They would be + mistaken for a variable. + +2017-06-11 Philipp Stephani + + Support threads in modules + + Rather than checking for the main thread, check for the current + thread. + + * emacs-module.c (check_thread): New function. + (MODULE_FUNCTION_BEGIN_NO_CATCH, module_get_environment) + (module_non_local_exit_check, module_non_local_exit_clear) + (module_non_local_exit_get, module_non_local_exit_signal) + (module_non_local_exit_throw, module_is_not_nil, module_eq): Use it. + +2017-06-11 Philipp Stephani + + Allow non-local exits in module initializers + + Previously signals, throws, and quits from module initialization + functions were ignored. These function aren't special, and better + errors can be reported using signals than with the initialization + return code, so allow non-local exits. + + * src/emacs-module.c (module_signal_or_throw): New helper function. + (Fmodule_load, funcall_module): Use it. + (Fmodule_load): Also allow quitting. + +2017-06-11 Noam Postavsky + + Let eshell/sudo handle absolute command names (Bug#27167) + + * lisp/eshell/esh-ext.el (eshell-find-interpreter): Don't change + absolute paths into relative ones. + +2017-06-10 Alan Third + + Don't wait for toolbar in NS native fullscreen + + * src/nsterm.m (EmacsView:updateFrameSize): Don't short-circuit the + function when in fullscreen. + +2017-06-10 Alexander Gramiak + + Fix the placement of GTK menus on multi-monitor systems + + menu_position_func did not properly use the current monitor's + resolution. Also see commit '2016-02-06 22:12:53 +0100'. + + * lisp/frame.el (frame-monitor-attribute, frame-monitor-geometry) + (frame-monitor-workarea): New functions. + + * src/xmenu.c (menu_position_func): Take into account the workarea of + the monitor that contains the mouse. (Bug#23568) + +2017-06-10 Eli Zaretskii + + Clarify documentation of 'face-spec-set' + + * lisp/faces.el (face-spec-set): Clarify the description of + SPEC-TYPE in the doc string. + + * doc/lispref/display.texi (Defining Faces): Clarify the + description of 'face-spec-set's SPEC-TYPE argument. (Bug#27246) + +2017-06-10 Michael Albinus + + Fix domain port and handling in tramp-gvfs.el + + * lisp/net/tramp-gvfs.el (tramp-gvfs-dbus-byte-array-to-string): + Return nil if BYTE-ARRAY is nil. + (tramp-gvfs-url-file-name, tramp-gvfs-handler-mounted-unmounted) + (tramp-gvfs-connection-mounted-p, tramp-gvfs-mount-spec): + Fix domain and port handling. + + * lisp/net/tramp.el (tramp-handle-file-name-case-insensitive-p): + Ignore errors. + +2017-06-10 Eli Zaretskii + + Improve documentation of 'face-spec-set-2' + + * lisp/faces.el (face-spec-recalc, face-spec-set-2): Rename 'spec' + to 'face-attrs'. + (face-spec-choose, face-spec-set-2): Doc fix. (Bug#27238) + +2017-06-10 Eli Zaretskii + + Fix handling of Python/Guile commands with arguments in gdb-mi.el + + * lisp/progmodes/gdb-mi.el (gdb-python-guile-commands-regexp): New + variable. + (gdb-control-commands-regexp): Use it. + (gdb-send): Don't increment gdb-control-level if the command + matches gdb-python-guile-commands-regexp and has non-empty + arguments. Reported by David Boles in + http://lists.gnu.org/archive/html/emacs-devel/2017-06/msg00009.html. + +2017-06-10 Eli Zaretskii + + Preserve point in Dired windows under 'dired-auto-revert-buffer' + + * lisp/dired.el (dired-find-file): When dired-auto-revert-buffer + is non-nil, bind switch-to-buffer-preserve-window-point to nil + while calling find-file. (Bug#27243) + +2017-06-09 Philipp Stephani + + Give test files a -tests.el suffix + + Rename a couple of test files that have the same name as the library + they test. This harmonizes the naming pattern and makes it possible + to have the tests directories in the load path. + +2017-06-09 Philipp Stephani + + Fix another compiler warning on macOS + + * src/image.c (x_query_frame_background_color): Don't define if we + have NextStep but no image support. + +2017-06-09 Philipp Stephani + + Add garbage collection support for module environments + + * src/emacs-module.c (mark_modules): New function. + (initialize_environment): Properly initialize Lisp objects. + * src/alloc.c (garbage_collect_1): Call it. + +2017-06-08 Glenn Morris + + Make autogen.sh report relevant environment variables + + * autogen.sh (check_version): + Indicate if using an environment variable. + +2017-06-08 Noam Postavsky + + Split variable macro env from function env + + * lisp/emacs-lisp/cl-macs.el (cl--sm-macroexpand): Remove. + (cl-symbol-macrolet): Instead of adding each binding directly into the + main environment with a special key format, put all symbol macro + bindings into a single entry in the main environment under + `:cl-symbol-macros'. + (cl--sm-macroexpand): Look up symbol bindings in the + `:cl-symbol-macros' entry of the environment. + +2017-06-07 Glenn Morris + + * make-dist: Directory modules/mod-test no longer exists. + +2017-06-07 Glenn Morris + + More authors.el updates + + * admin/authors.el (authors-ignored-files, authors-valid-file-names) + (authors-renamed-files-alist): Additions. + +2017-06-07 Glenn Morris + + * make-dist: Check a release has a ChangeLog with a release notice. + + * make-dist: Use existing ChangeLog if present. + +2017-06-07 Michael Albinus + + * lisp/net/tramp-sh.el (tramp-maybe-open-connection): Fix port handling. + +2017-06-07 Lars Ingebrigtsen + + (url-cookie-host-can-set-p): Protect against zero-length domains + + * lisp/url/url-cookie.el (url-cookie-host-can-set-p): Protect + against zero-length domains. + + Backtrace of a real-world site that triggers a bug: + + Debugger entered--Lisp error: (args-out-of-range "" 0) + url-cookie-host-can-set-p("www.washingtonpost.com" "") + url-cookie-handle-set-cookie("utm_term=0;Expires=Thursday, + 01-January-1970 00:00:00 GMT; path=/; domain=") + url-http-handle-cookies() + +2017-06-06 Glenn Morris + + More authors.el updates + + * admin/authors.el (authors-obsolete-files-regexps) + (authors-valid-file-names, authors-renamed-files-alist) + (authors-renamed-files-regexps): Additions. + +2017-06-06 Glenn Morris + + More small authors.el updates + + * admin/authors.el (authors-aliases): Fix recent addition. + (authors-obsolete-files-regexps, authors-no-scan-regexps) + (authors-ignored-files, authors-valid-file-names) + (authors-renamed-files-alist): Additions. + +2017-06-06 Glenn Morris + + Make authors.el report names that were ignored + + * admin/authors.el (authors-ignored-names): New. + (authors-canonical-author-name): Add file and position arguments. + Record ignored authors. + (authors-scan-change-log, authors-scan-el): + Pass file and position to authors-canonical-author-name. + (authors): Also print authors that were ignored. + +2017-06-06 Glenn Morris + + * admin/authors.el (authors-aliases): Additions. + +2017-06-06 Tino Calancha + + * test/lisp/subr-tests.el (subr-tests-bug22027): Add test. + +2017-06-06 Noam Postavsky + + * lisp/subr.el (read-passwd): Don't delete return value (Bug#22027). + +2017-06-06 Dmitry Gutov + + Enable ElDoc messages after the newline command + + * lisp/emacs-lisp/eldoc.el: + Add "newline" to the eldoc-add-command-completions call (bug#27228). + +2017-06-06 Dmitry Gutov + + Enable eldoc-mode explicitly inside read--expression + + * lisp/simple.el (read--expression): Call eldoc-mode (bug#27202). + +2017-06-06 Andy Moreton + + Fix check for package-unsigned-archives during retrieval + + * lisp/emacs-lisp/package.el (package--download-one-archive): + Fix check for package-unsigned-archives. + +2017-06-05 Noah Friedman + + Merge etc/emacs-buffer.gdb from emacs-25 to master. + +2017-06-05 Philipp Stephani + + Fix undefined behavior in mapbacktrace + + * src/eval.c (Fmapbacktrace): Don't assume that PDL is still valid. + +2017-06-05 Eli Zaretskii + + Fix emacs-module-tests on MS-Windows + + * src/print.c (print_vectorlike): Make sure module function's + address prints with a leading "0x". This fixes emacs-module-tests + on MS-Windows. Fix whitespace. + * src/dynlib.c (dynlib_addr): Remove unused variable. Update + commentary. + +2017-06-05 Philipp Stephani + + Use unwind protection to clean up data structures in modules + + Reuse existing functionality and simplify the code a bit. + + * src/emacs-module.c (Fmodule_load): Use unwind protection to clean up + runtime object. + (funcall_module): Use unwind protection to clean up environment + object. + (finalize_environment): Simplify signature. + (finalize_environment_unwind, finalize_runtime_unwind): New functions. + +2017-06-05 Michael Albinus + + Some minor tweaks in tramp-tests.el + + * test/lisp/net/tramp-tests.el (tramp-test05-expand-file-name-relative): + Let it pass for all gfvs based methods. + (tramp-test24-file-name-completion): Run method and host + completion for all syntaxes only when expensive tests are enabled. + Do not check host completion for gvfs based methods. + (tramp--test-gvfs-p): Add optional METHOD argument. + (tramp--test-afp-or-smb-p): Remove. + +2017-06-05 Michael Albinus + + Fix error in Tramp rsync method + + * lisp/net/tramp-sh.el (tramp-methods) : Add "-c" argument. + Otherwise, `tramp-test10-write-region' could fail. + +2017-06-05 Philipp Stephani + + Inline module_has_cleanup + + This constant is only used once, and we fail compilation anyway if + it's false. + + * src/emacs-module.c (MODULE_SETJMP_1): Inline __has_attribute. + +2017-06-05 Philipp Stephani + + Add missing dependency to test module source file + +2017-06-05 Paul Eggert + + Omit space that broke ‘make check’ + + * src/print.c (print_vectorlike): Omit stray space. + +2017-06-05 Paul Eggert + + Remove easserts etc. from emacs-module.c + + Most of these seem to run afoul of the comment "Do NOT use + 'eassert' for checking validity of user code in the module." + * src/emacs-module.c (MODULE_FUNCTION_BEGIN_NO_CATCH) + (module_non_local_exit_check, module_non_local_exit_clear) + (module_non_local_exit_get, module_non_local_exit_signal) + (module_non_local_exit_throw, module_make_string): + Remove unnecessary easserts that pointers are nonnull. + Hardware checks this for us nowadays, and the checks + just clutter up the code. + (module_extract_integer): Remove unnecessary verify that + a C signed integer is in the range INTMAX_MIN..INTMAX_MAX. + The C standard guarantees this. + (module_copy_string_contents): Remove unnecessary eassert + that Lisp strings are null-terminated. + (module_function_arity): Remove unnecessary easserts that + function arities are in range. + +2017-06-05 Paul Eggert + + Remove unnecessary checking in emacs-module.c + + * src/emacs-module.c (module_copy_string_contents): + Remove checking, as string lengths are always nonnegative and less + than STRING_BYTES_BOUND, and this is checked elsewhere. + (module_make_string): Check length against STRING_BYTES_BOUND, a + tighter bound than MOST_POSITIVE_FIXNUM. (funcall_module): Don't + assume that an out-of-range integer is nonnegative. + +2017-06-05 Paul Eggert + + SCHARS and STRING_BYTES are nonnegative + + Tell the compiler that SCHARS and STRING_BYTES are nonnegative, in + the hopes that this will optimize a bit better. Also, check this + at runtime if ENABLE_CHECKING. + * src/lisp.h (SCHARS, STRING_BYTES): + eassume that these functions return nonnegative values. + (STRING_SET_CHARS) [ENABLE_CHECKING]: + eassert that newsize is nonnegative. + +2017-06-05 Noam Postavsky + + * lisp/desktop.el (desktop-clear): Skip the daemon's frame (Bug#26912). + +2017-06-04 Philipp Stephani + + Remove an unused error symbol + + * src/emacs-module.c (syms_of_module): Remove unused error symbol + 'invalid-module-call'. + +2017-06-04 Philipp Stephani + + Support quitting in modules + + The idea is that modules should call env->should_quit from time to + time and return as quickly as possible if it returns true. + + * src/emacs-module.c (module_should_quit): New module function. + (initialize_environment): Use it. + (funcall_module): Process potential pending quit. + + * src/eval.c (maybe_quit): Add reference to module_should_quit. + +2017-06-04 Philipp Stephani + + Use more specific errors for module load failure + + * src/emacs-module.c (syms_of_module): Add more specific error + symbols. + (Fmodule_load): Use them. + +2017-06-04 Philipp Stephani + + Remove an unneeded assertion + + * src/emacs-module.c (module_copy_string_contents): Remove unneeded + assertion. If this assertion triggers, we raise an error anyway. + +2017-06-04 Philipp Stephani + + Guard against signed integer overflows + + * src/emacs-module.c (module_extract_integer) + (module_copy_string_contents, module_make_string): Guard against + signed integer overflows. + +2017-06-04 Philipp Stephani + + Add a couple more assertions to the module code + + These can help module authors debug crashes. + + * emacs-module.c (module_non_local_exit_check) + (module_non_local_exit_clear, module_non_local_exit_get) + (module_non_local_exit_signal, module_non_local_exit_throw) + (module_copy_string_contents, module_make_string) + (funcall_module, initialize_environment): Add assertions + +2017-06-04 Philipp Stephani + + Use ATTRIBUTE_MAY_ALIAS where alias violations are likely + + In particular, alias violations are likely for the return values of + dlsym(3), which get cast around arbitrarily. + + * src/emacs-module.c (Fmodule_load): Use ATTRIBUTE_MAY_ALIAS. + +2017-06-04 Philipp Stephani + + Simplify interface of dynlib_attr. + + Instead of returning bool, set the argument pointers to NULL if the + information is not available. + + * src/dynlib.c (dynlib_addr): Don't return bool. + +2017-06-04 Philipp Stephani + + Rationalize environment lifetime management functions + + * src/emacs-module.c (Fmodule_load, funcall_module): Adapt callers. + (finalize_environment): Add parameter for public part of the + environment, like 'initialize_environment'. Add assertions. + +2017-06-04 Philipp Stephani + + Rework printing of module functions + + Fix a FIXME in emacs-module.c. Put the printing into print.c, like + other types. + + * src/print.c (print_vectorlike): Add code to print module functions. + + * src/emacs-module.c (funcall_module): Stop calling + 'module_format_fun_env'. Now that module functions are first-class + objects, they can be added to signal data directly. + (module_handle_signal): Remove now-unused function + 'module_format_fun_env'. + + * test/src/emacs-module-tests.el (mod-test-sum-test): Adapt unit test. + + * src/eval.c (funcall_lambda): Adapt call to changed signature of + 'funcall_module'. + +2017-06-04 Philipp Stephani + + Define helper macro to reduce code duplication + + * src/emacs-module.c (MODULE_FUNCTION_BEGIN_NO_CATCH): New helper + macro. + (MODULE_FUNCTION_BEGIN, module_type_of, module_is_not_nil, module_eq): + Use it. + +2017-06-04 Philipp Stephani + + Remove two FIXMEs that can't be fixed + +2017-06-04 Eli Zaretskii + + Avoid slow startup in daemon mode when global-linum-mode is on + + * lisp/linum.el (linum-on): Don't turn on linum-mode in a + non-client frame of a daemon session. (Bug#27210) + +2017-06-04 Paul Eggert + + Fix eldoc bug with curved quote + + * lisp/progmodes/elisp-mode.el (elisp-get-fnsym-args-string): + Substitute quotes in documentation before returning it (Bug#27159). + +2017-06-04 Paul Eggert + + Tune ‘format’ after recent fix + + * doc/lispref/strings.texi (Formatting Strings): + * src/editfns.c (Fformat): Format field numbers no longer need + to be unique, reverting the previous doc change since that has + now been fixed. Also, document that %% should not have modifiers. + * src/editfns.c (styled_format): Improve performance. Remove + the need for the new prepass over the format string, by using + a typically-more-generous bound for the info array size. + Initialize the info array lazily. Move string inspection to + the same area to help caching. Avoid the need for a + converted_to_string bitfield by using EQ. Cache arg in a + local and avoid some potential aliasing issues to help the + compiler. Info array is now 0-origin, not 1-origin. + +2017-06-04 Nikolay Kudryavtsev + + Improve of file-local-name use in vc-git-checkin + + * lisp/vc/vc-git.el (vc-git-checkin): Use file-local-name only + when calling git commit. + +2017-06-03 Simen Heggestøyl + + Support a new CSS indentation style + + * lisp/textmodes/css-mode.el (css-smie-rules): Indent after property + immediately followed by a newline. + + * test/manual/indent/css-mode.css: Add test for the change above. + + * test/manual/indent/scss-mode.scss: Ditto. + +2017-06-03 Philipp Stephani + + Fix a bug when using format field numbers + + Previously styled_format overwrite the argument vector. This is no + longer possible because there might be more than one specification per + argument. Use the existing auxiliary info array instead. + + * src/editfns.c (styled_format): Record arguments in the info + structure instead of overwriting them. + * test/src/editfns-tests.el (format-with-field): Add unit test. + +2017-06-03 Paul Eggert + + Document uniqueness limitation of ‘format’ + + * doc/lispref/strings.texi (Formatting Strings): + * src/editfns.c (Fformat): + Document that field numbers should be unique within a format. + +2017-06-03 Glenn Morris + + Small rmailmm fix (bug#27203) + + * lisp/mail/rmailmm.el (rmail-mime-insert-bulk): + Fall back to HOME if no match in rmail-mime-attachment-dirs-alist. + +2017-06-03 Glenn Morris + + * admin/authors.el (authors-aliases): Addition. + +2017-06-03 Glenn Morris + + Add watch for password back to inferior python comint filter + + It was removed along with other items for speed (bug#16875), + but doesn't seem to have been causing an issue, and it's useful to + have it there (bug#27154). + * lisp/progmodes/python.el (inferior-python-mode): + Add comint-watch-for-password-prompt to comint-output-filter-functions. + +2017-06-03 Ryan (tiny change) + + Use completing-read-default in tmm-prompt + + tmm uses completing-read, but customizes its behavior so much + that any alternative completing-read-function will almost + certainly break it. For example, both ido-ubiquitous and ivy have + special code to deactivate themselves for tmm. + * lisp/tmm.el (tmm-prompt): Use completing-read-default instead of + completing-read. (Bug#27193) + +2017-06-02 Mats Lidell + + * etc/tutorials/TUTORIAL.sv: synced with TUTORIAL (Bug#20371) + +2017-06-02 Glenn Morris + + Fix with-todo-test + + * test/lisp/calendar/todo-mode-tests.el (with-todo-test): + HOME should be a directory, not a file. Delete it when finished. + +2017-06-02 Lele Gaifax (tiny change) + + Update TUTORIAL.it + + * etc/tutorials/TUTORIAL.it: Adjust to recent changes in TUTORIAL. + +2017-06-02 Eli Zaretskii + + Fix cursor position in Dired buffers after dired-sort-toggle + + * src/xdisp.c (display_and_set_cursor): Record cursor coordinates + even if the frame is marked as garbaged. (Bug#27187) + +2017-06-02 Eli Zaretskii + + Update TUTORIAL.he + + * etc/tutorials/TUTORIAL.he: Adjust to recent changes in TUTORIAL. + +2017-06-02 Noam Postavsky + + * etc/tutorials/TUTORIAL: Explain how to stop the tutorial (Bug#20371). + +2017-06-02 Paul Eggert + + Limit format fields to more POSIX-like spec + + * doc/lispref/strings.texi (Formatting Strings): + Don’t allow mixing numbered with unnumbered format specs. + * src/editfns.c (styled_format): Don’t bother checking for field 0, + since it doesn’t crash and the behavior is not specified. + * test/src/editfns-tests.el (format-with-field): Adjust tests to + match current doc. Add more tests for out-of-range fields. + +2017-06-02 Paul Eggert + + Improve performance by avoiding strtoumax + + This made (string-to-number "10") 20% faster on my old desktop, + an AMD Phenom II X4 910e running Fedora 25 x86-64. + * admin/merge-gnulib (GNULIB_MODULES): Remove strtoumax. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * lib/strtoul.c, lib/strtoull.c, lib/strtoumax.c, m4/strtoull.m4: + * m4/strtoumax.m4: Remove. + * src/editfns.c (str2num): New function. + (styled_format): Use it instead of strtoumax. Use ptrdiff_t + instead of uintmax_t. Check for integer overflow. + * src/lread.c (LEAD_INT, DOT_CHAR, TRAIL_INT, E_EXP): + Move to private scope and make them enums. + (string_to_number): Compute integer value directly during + first pass instead of revisiting it with strtoumax later. + +2017-06-02 Paul Eggert + + Minor improvements to format field numbers + + * src/editfns.c (styled_format): Allow field numbers in a %% spec. + No need for a special diagnostic for field numbers greater than + PTRDIFF_MAX. Reword diagnostic for field 0. + * test/src/editfns-tests.el (format-with-field): Adjust to match. + +2017-06-02 Philipp Stephani + + Implement field numbers in format strings + + A field number explicitly specifies the argument to be formatted. + This is especially important for potential localization work, since + grammars of various languages dictate different word orders. + + * src/editfns.c (Fformat): Update documentation. + (styled_format): Implement field numbers. + + * doc/lispref/strings.texi (Formatting Strings): Document field numbers. + + * lisp/emacs-lisp/bytecomp.el (byte-compile-format-warn): Adapt. + + * test/src/editfns-tests.el (format-with-field): New unit test. + +2017-06-01 Alexander Gramiak + + Limit scope of local overriding-terminal-local-map + + The function `binding' may call isearch-done, which globally sets + overriding-terminal-local-map to nil (Bug#23007). + * lisp/isearch.el (isearch-mouse-2): Don't bind + overriding-terminal-local-map around the call to `binding'. + +2017-06-01 Stephen Berman + + Correct and isolate the todo-mode test environment + + This avoids having to set todo-mode variables globally in the test + file and prevents any exisiting user todo-mode files from influencing + the tests. + + * test/lisp/calendar/todo-mode-tests.el: + (with-todo-test): New macro. + (todo-test-todo-quit01, todo-test-todo-quit02) + (todo-test-item-highlighting): Use it. + +2017-06-01 Alan Third + + Fix build errors on macOS 10.6 (bug#27059) + + * src/nsfns.m (compute_tip_xy): Don't use CGRectContainsPoint. + +2017-06-01 Eli Zaretskii + + Improve testing of octal and hex display of raw bytes + + * test/manual/redisplay-testsuite.el (test-redisplay-5-toggle) + (test-redisplay-5): Add a test with a large codepoint. + +2017-06-01 Vasilij Schneidermann + + Add customizable to display raw bytes as hex + + * src/xdisp.c (get_next_display_element): Dispatch used format string + for unprintables based on new display-raw-bytes-as-hex variable. + (display-raw-bytes-as-hex): New variable. (Bug#27122) + + * lisp/cus-start.el: Add defcustom form for display-raw-bytes-as-hex. + + * doc/emacs/display.texi: Document the new variable. + * etc/NEWS: Mention display-raw-bytes-as-hex. + + * test/manual/redisplay-testsuite.el (test-redisplay-5-toggle) + (test-redisplay-5): New tests. + (test-redisplay): Call test-redisplay-5. + +2017-06-01 Eli Zaretskii + + Revert "Add customizable to display raw bytes as hex" + + This reverts commit 7c9ac111c5e5d92e620b666893993d5dc562e483. + +2017-06-01 Eli Zaretskii + + Add customizable to display raw bytes as hex + + * src/xdisp.c (get_next_display_element): Dispatch used format string + for unprintables based on new display-raw-bytes-as-hex variable. + (display-raw-bytes-as-hex): New variable. (Bug#27122) + + * lisp/cus-start.el: Add defcustom form for display-raw-bytes-as-hex. + + * doc/emacs/display.texi: Document the new variable. + * etc/NEWS: Mention display-raw-bytes-as-hex. + + * test/manual/redisplay-testsuite.el (test-redisplay-5-toggle) + (test-redisplay-5): New tests. + (test-redisplay): Call test-redisplay-5. + +2017-06-01 Eli Zaretskii + + Fix linum under text-scaling when leuven-theme is used + + * etc/themes/leuven-theme.el (linum): Make the 'linum' face + inherit from 'default' and 'shadow', so that margins are enlarged + as expected under text-scaling. + +2017-06-01 Paul Eggert + + Free cwd when no longer needed + + * lib-src/emacsclient.c (main): Don’t dally when freeing cwd. + +2017-06-01 Anders Waldenborg (tiny change) + + Fix memory leak of cwd string in emacsclient (Bug#26628) + + * lib-src/emacsclient.c (main): emacsclient retrieves the current + working directory using get_current_dir_name which returns a newly + allocated string. Make sure this string is freed before exiting. + +2017-06-01 Glenn Morris + + Quieten compilation of some test files + + * test/lisp/dired-tests.el (dired-test-bug25609): Mark unused args. + * test/src/data-tests.el (binding-test-set-constant-t) + (binding-test-set-constant-nil, binding-test-set-constant-keyword) + (binding-test-set-constant-nil): Silence compiler. + * test/src/regex-tests.el (regex-tests-BOOST): Escape char literal. + +2017-06-01 Glenn Morris + + Use true names for invocation- and source-directory + + * src/emacs.c (init_cmdargs) : + * src/lread.c (init_lread) : Use true names. + +2017-06-01 Glenn Morris + + Avoid elisp-mode test failures when source dir has multiple names + + * test/lisp/progmodes/elisp-mode-tests.el (emacs-test-dir): + Use the true name of the directory. + +2017-06-01 Paul Eggert + + Fix bug with "%%" in error format + + * src/doprnt.c (doprnt): Format "%%" correctly. + Problem reported by Philipp Stephani in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00901.html + +2017-06-01 Paul Eggert + + * src/editfns.c (Fmessage): Improve doc string (Bug#23425#130). + +2017-06-01 Katsumi Yamaoka + + Revert mml-generate-mime-1 (bug#27141) + + * lisp/gnus/mml.el (mml-generate-mime-1): Reverted to emacs-25 version + with slight modernizations (bug#27141). + +2017-05-31 Michael Albinus + + Fix Bug#27108 + + * lisp/recentf.el (recentf-load-list): Bind `non-essential', + in order to avoid Tramp password requests during Emacs + startup. (Bug#27108) + +2017-05-31 Glenn Morris + + * test/Makefile.in (.SECONDARY): Stop make deleting .elc files. + +2017-05-31 Eli Zaretskii + + Document current-line hscrolling in ELisp manual + + * doc/lispref/windows.texi (Horizontal Scrolling): Document the + new mode of auto-hscrolling only the current line. + +2017-05-31 Eli Zaretskii + + Support lower bound on hscrolling when only current line scrolls + + * doc/emacs/display.texi (Horizontal Scrolling): Document the new + mode of auto-hscrolling only the current line. + + * src/xdisp.c (init_iterator): When hscrolling only the + current line, apply the window's min_hscroll here, so that + non-current lines will be hscrolled by that minimum. + Suggested by Stephen Berman . + (hscroll_window_tree): Account for window's min_hscroll when + deciding whether to recompute the hscroll. + (display_line): Subtract window's min_hscroll from x_incr, as that + was already accounted for in init_iterator. (Bug#27008) + +2017-05-31 Noam Postavsky + + cl-print: handle circular objects when `print-circle' is nil (Bug#27117) + + * lisp/emacs-lisp/cl-print.el (cl-print--currently-printing): New variable. + (cl-print-object): When `print-circle' is nil, bind it to a list of + objects that are currently printing to avoid printing the same object + endlessly. + * test/lisp/emacs-lisp/cl-print-tests.el (cl-print-circle): New test. + +2017-05-31 Noam Postavsky + + Further simplify test/Makefile, optionally load elc tests + + * test/Makefile.in: Use make's error ignoring feature instead of + suppressing test errors with shell. Compile test files in the main + make invocation instead of a recursive 'make' call. Optionally load + .elc test files if TEST_LOAD_EL is set to something other than 'yes'. + Remove obsolete commentary. + +2017-05-31 Eli Zaretskii + + Avoid inflooping in redisplay due to Spacemacs and linum-mode + + * src/xdisp.c (redisplay_internal): Limit the number of redisplay + retries when a frame becomes garbaged as result of redisplaying + it. (Bug#27115) + +2017-05-31 Tino Calancha + + * src/editfns.c (decode-time): Fix docstring. + +2017-05-31 Glenn Morris + + * admin/update_autogen: Remove bzr support. + +2017-05-31 Glenn Morris + + Avoid subr test failure when source dir has multiple names + + * test/lisp/subr-tests.el (subr-tests--this-file): + Use the true name of the file. The following test does a string + comparison of this value with that from method-files, which uses + load-history, which contains true names. + +2017-05-31 Dmitry Gutov + + Extract eldoc--supported-p + + * lisp/emacs-lisp/eldoc.el (eldoc--supported-p): New function. + (turn-on-eldoc-mode, eldoc-mode): Use it. + (http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00865.html) + +2017-05-30 Glenn Morris + + Make "make check" less verbose by default + + * test/Makefile.in (AM_DEFAULT_VERBOSITY, AM_V_ELC, am__v_ELC_) + (am__v_ELC_0, am__v_ELC_1, AM_V_GEN, am__v_GEN_, am__v_GEN_0) + (am__v_GEN_1, AM_V_at, am__v_at_, am__v_at_0, am__v_at_1): + New, copied from lisp/Makefile.in. + (%.elc, %.log): Simplify and quieten. + +2017-05-30 Alan Mackenzie + + Mode line "%q" construct: Just use one number when both would be the same. + + * src/xdisp (decode_mode_spec): recode the "%q" bit appropriately. + +2017-05-30 Alan Mackenzie + + Merge branch 'master' of /home/acm/emacs/emacs.git/master + +2017-05-30 Alan Mackenzie + + c-defun-name: Return fully qualified method names when wanted in C++, etc. + + * lisp/progmodes/cc-cmds.el (c-defun-name): Use + c-back-over-compound-identifier in place of c-backward-token-2 near the end + of the function. + +2017-05-30 Glenn Morris + + Reduce scope of recent test/Makefile HOME change + + * test/Makefile.in (%.log): Move setting of HOME here from top-level. + +2017-05-30 Paul Eggert + + Skip .#* temporaries when finding sources + + Without this patch, ‘make check’ can fail with the diagnostic + ‘invalid syntax in conditional’ if there is an Emacs temporary + file whose name starts with ‘.#’, because the ‘#’ is treated as + the start of a Make comment. + * lisp/Makefile.in (loaddefs, tagsfiles, check-defun-deps): + * test/Makefile.in (ELFILES): + Skip files starting with ‘.’, so that the .#* files do not cause + trouble. (We cannot easily skip just files starting with ‘.#’, + since ‘#’ starts a Make comment!) + +2017-05-30 Alan Mackenzie + + Merge branch 'master' of /home/acm/emacs/emacs.git/master + +2017-05-30 Alan Mackenzie + + Fix the mouse help/key map on the "%p" part of the mode line. + + * lisp/bindings.el (mode-line-percent-position): give it a + `risky-local-variable' property. + (mode-line-position): correct the quoting on the mode-line-percent-position + part of the variable, allowing the properties to be properly recognized. + +2017-05-30 Alan Mackenzie + + Fix the mouse help/key map on the "%p" part of the mode line. + + * lisp/bindings.el (mode-line-percent-position): give it a + `risky-local-variable' property. + (mode-line-position): correct the quoting on the mode-line-percent-position + part of the variable, allowing the properties to be properly recognized. + +2017-05-30 Paul Eggert + + Merge from gnulib + + * build-aux/config.guess: Copy from gnulib. + * lib/gnulib.mk.in: Regenerate. + +2017-05-30 Glenn Morris + + Stop make check interacting with HOME + + * test/Makefile.in (HOME): Export a non-existent value. + +2017-05-30 Paul Eggert + + Update .gitattributes to match sources better + + * .gitattributes: Remove nt/nmake.defs. Move dostorture.c, c.C, + algrthms.html. Use pattern for todo-mode. Improve patterns for + Ada, C, ObjC, shell. Add Pascal. Remove unused pattern *.ruby. + Add config.guess and config.sub as shell files. + +2017-05-30 Noam Postavsky + + Rename '--new-daemon' to 'fg-daemon' and '--old-daemon' to '--bg-daemon' + + * doc/emacs/cmdargs.texi (Initial Options): + * doc/lispref/os.texi (Startup Summary): + * etc/NEWS: + * etc/emacs.service: + * src/emacs.c (main): + * src/lisp.h: Rename '--new-daemon' to 'fg-daemon' and '--old-daemon' to + '--bg-daemon'. + +2017-05-30 Glenn Morris + + todo-mode: don't assume an ordering of tests + + * test/lisp/calendar/todo-mode-tests.el (todo-test-todo-quit02) + (todo-test-item-highlighting): Avoid prompting for input file. + +2017-05-30 Paul Eggert + + Improve .gdbinit Lisp value pretty-printing + + * src/.gdbinit (to_string): Use an unsigned representation for + Lisp values, as requested by Eli Zaretskii (Bug#27098). + Also, use "make_number(N)" for Lisp integers. + +2017-05-30 Dmitry Gutov + + Turn global-eldoc-mode into a globalized minor mode + + * lisp/emacs-lisp/eldoc.el (global-eldoc-mode): + Turn into globalized mode (bug#19853). + (turn-on-eldoc-mode): Make it into a wrapper instead of alias. + (eldoc-mode): Only show the message when called interactively. + +2017-05-29 Dmitry Gutov + + Use regexp matching instead of checking exit status + + * lisp/progmodes/xref.el (xref-collect-matches): + See if the output buffer contents look like Grep output + instead of checking exit status (bug#23451). + +2017-05-29 Stephen Berman + + Add initial tests for todo-mode.el + + *test/lisp/calendar/todo-mode-tests.el: + *test/lisp/calendar/todo-mode-resources/todo-test-1.toda: + *test/lisp/calendar/todo-mode-resources/todo-test-1.todo: New files. + + * .gitattributes: Ignore trailing whitespace in todo-mode test + data files, since it is part of the todo-mode file format. + +2017-05-29 Stephen Berman + + Make `todo-toggle-item-highlighting' work on multiline items (bug#27133) + + * lisp/calendar/todo-mode.el (todo-hl-line-range): New named function, + replacing an anonymous function for the sake of `describe-variable'. + (todo-modes-set-2): Use it as buffer-local value of hl-line-range-function + and remove boundp test of this variable, so its value is available on + invoking `todo-toggle-item-highlighting'. + +2017-05-29 Alan Third + + Fix build error on macOS 10.6 + + * src/nsfns.m (compute_tip_xy): Cast NSRect to CGRect and NSPoint to + CGPoint. + +2017-05-29 Jules Tamagnan (tiny change) + + Comply with pep 8 style guide for backslash in assignment (Bug#24809) + + * lisp/progmodes/python.el (python-indent--calculate-indentation): + Increase indent by `python-indent-offset' after + `:after-backslash-assignment-continuation'. + +2017-05-29 Wilfred Hughes + + Add suggestion to docstring + + * lisp/subr.el (interactive-p): Mention commandp, as this is often + what users are actually looking for. + +2017-05-29 Wilfred Hughes + + Ensure button-get works in any buffer + + * lisp/button.el (button-get): Previously we assumed that button-get + was called in the buffer containing the button. In other buffers, + button-get always returned nil. Fix this by passing the relevant + buffer from the marker. + +2017-05-29 Dmitry Gutov + + Signal error if find-grep returns a nonzero status + + * lisp/progmodes/xref.el (xref-collect-matches): Signal error + if find-grep returns a nonzero status (bug#23451). Remove the + comment: even if some output is present, a non-zero status + means something went wrong and it can't be relied upon. + +2017-05-29 Stephen Berman + + Make sure exiting todo-mode buffer buries it (bug#27121) + + This failed due to commit ea3ae33b from 2013-05-16, which prevented + quitting todo-mode buffer after visiting todo-archive buffer from + making the archive buffer current again. Avoid this now by simply + killing the archive buffer, since there's no need to keep it a live + buffer. Consequently, quitting a todo-mode buffer can now use + bury-buffer without an argument, which ensures that is will not + becomes current on quitting the buffer that replaced it in the window. + + * lisp/calendar/todo-mode.el (todo-quit): Kill todo-archive-mode + buffer instead of burying it. This now allows exiting the + todo-mode buffer by bury-buffer without an argument, so do that. + +2017-05-28 Michael Albinus + + Some tweaks, almost all for Tramp adb method + + * lisp/net/tramp-adb.el (tramp-adb-parse-device-names): + Use `make-tramp-file-name'. + (tramp-adb-get-device): Use `tramp-file-name-port-or-default'. + (tramp-adb-maybe-open-connection): Set "prompt" property. + (tramp-adb-wait-for-output): Use it. + + * lisp/net/tramp-cache.el (tramp-cache-print): Use `elt'. + (tramp-dump-connection-properties): Check also that there are + properties to be saved. Don't save "started" property of + "ftp" method. + + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-expand-file-name): + Use `make-tramp-file-name'. + + * lisp/net/tramp.el (tramp-remote-file-name-spec-regexp): + Host could be empty. + (tramp-file-name-port-or-default): New defun. + (tramp-dissect-file-name): Simplify `make-tramp-file-name' call. + (tramp-handle-file-name-case-insensitive-p): Use a progress reporter. + (tramp-call-process, tramp-call-process-region): + Use `make-tramp-file-name'. + + * test/lisp/net/tramp-tests.el (tramp-test03-file-name-defaults): + Revert change from 2017-05-24. + (tramp-test05-expand-file-name-relative): Let it also pass for + "adb" method. + +2017-05-28 Jürgen Hötzel + + Fix Tramp for Android 7 + + * tramp-adb.el (tramp-adb-ls-toolbox-regexp): + Username part of prompt is empty on Android 7. + (tramp-adb-ls-toolbox-regexp): + Ignore addition links column on Android 7. + (tramp-adb-get-ls-command): + Dont use --color=none when using toybox (Android 7). It's not + possible to disable coloring explicitly for toybox ls. + +2017-05-27 Svante Carl v. Erichsen (tiny change) + + Fix cl-indent for `loop' with :keywords (Bug#15543) + + * lisp/emacs-lisp/cl-indent.el (lisp-extended-loop-p): Allow for + ":keywords". + +2017-05-27 Paul Eggert + + Depromiscuify inotify with IN_MASK_ADD + + Use IN_MASK_ADD instead of using a no-longer-promiscuous-enough + mask. This simplifies the code and restores the ability to + use IN_ACCESS, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE, and IN_OPEN + in some cases (Bug#26973). + * src/inotify.c (INOTIFY_DEFAULT_MASK): Remove. + (Finotify_add_watch): Use IN_MASK_ADD instead. + +2017-05-27 Paul Eggert + + Restore inotify onlydir support + + There was no need to remove it in the 2017-03-26 inotify change, + as it is like IN_DONT_FOLLOW and does not affect other watchers + for the same file. + * src/inotify.c (symbol_to_inotifymask, Finotify_add_watch) + (syms_of_inotify): Bring back onlydir. + +2017-05-27 Paul Eggert + + Simplify computation of inotify mask + + * src/inotify.c (add_watch): Accept uint32_t imask instead + of Lisp_Object aspect. Caller changed. + (Finotify_add_watch): Use aspect_to_inotifymask earlier, to + simplify the code. + +2017-05-27 Eli Zaretskii + + Improve the documentation of filesets + + * doc/emacs/files.texi (Filesets): Fix the description of + fileset-init's effect on the menu bar. (Bug#27015) + +2017-05-27 Philipp Stephani + + Don't attempt to recover from undefined behavior in some cases + + These functions can only be run in batch mode and exit Emacs on + return, so nothing can be recovered. Disable unsafe recover + mechanisms so that we get real failures and good stack traces on + fatal signals. + + * lisp/emacs-lisp/bytecomp.el (batch-byte-compile) + (batch-byte-recompile-directory): + * lisp/emacs-lisp/ert.el (ert-run-tests-batch-and-exit) + (ert-summarize-tests-batch-and-exit): Don't attempt to recover + from undefined behavior. + +2017-05-27 Philipp Stephani + + Avoid another compiler warning on macOS + + When configured with --without-ns, HAVE_NS is not defined on macOS, + thus 'memory-limit' calls the deprecated sbrk(2) function. Avoid that + by using the pre-defined __APPLE__ preprocessor macro. + + * src/alloc.c (Fmemory_limit): Never use sbrk(2) on macOS. + +2017-05-27 Luke Yen-Xun Lee + + Fix ruler-mode text-scaling issues + + * lisp/ruler-mode.el (ruler-mode-text-scaled-width): New function + for computing scaled text width. + (ruler-mode-text-scaled-window-hscroll) + (ruler-mode-text-scaled-window-width): Compute text scaled + `window-width' value. + (ruler-mode-mouse-grab-any-column, ruler-mode-mouse-add-tab-stop) + (ruler-mode-ruler): Change `window-hscroll' into + `ruler-mode-text-scaled-window-hscroll', and change `window-width' + into `ruler-mode-text-scaled-window-width'. + +2017-05-27 Martin Rudalics + + Minor doc and doc-string fixes (Bug#27091) + + * src/window.c (Fset_window_scroll_bars): Fix doc-string. + + * doc/lispref/display.texi (Fringe Size/Pos, Scroll Bars) + (Display Margins): Mention that `set-window-buffer' may override + settings made by `set-window-fringes', `set-window-scroll-bars' + and `set-window-margins'. + * doc/lispref/windows.texi (Buffers and Windows): Fix doc of + `set-window-buffer'. + +2017-05-27 Eli Zaretskii + + Avoid args-out-of-range errors on fringe clicks after "C-h k" + + * src/keyboard.c (echo_truncate): Don't call Ftruncate if the echo + message is already shorter than NCHARS. (Bug#27040) + +2017-05-27 Eli Zaretskii + + Fix GUD "Stop" display when running pdb + + * lisp/progmodes/gud.el (gud-menu-map): Don't call gdb-show-stop-p + when GUD mode is 'pdb'. (Bug#27024) + +2017-05-27 Tak Kunihiro + + Support drag and drop of region by mouse (Bug#26725) + + * doc/emacs/frames.texi (Drag and Drop): Document support of drag + and drop region by mouse. + * lisp/mouse.el (mouse-drag-region): Call mouse-drag-and-drop-region + when start-event is on region. + (mouse-drag-and-drop-region): New function, moves the region by + (mouse-drag-and-drop-region): New defcustom. + * etc/NEWS: Mention mouse-drag-and-drop-region. + +2017-05-27 Noam Postavsky + + * lisp/emacs-lisp/eieio.el (defclass): Fix quote in warning message. + +2017-05-27 Alan Third + + Check if instancetype supported in ObjC + + * configure.ac: Add check for instancetype. + * src/nsterm.h [!NATIVE_OBJC_INSTANCETYPE]: Define instancetype. + +2017-05-26 Wilfred Hughes + + Mark keywordp as a safe, error-free function + + * lisp/emacs-lisp/byte-opt.el: Add keywordp to + side-effect-and-error-free-fns. + +2017-05-26 Paul Eggert + + * src/inotify.c: Add FIXME comments. + +2017-05-26 Andreas Politz + + Fix Bug#26973 + + * src/inotify.c (INOTIFY_DEFAULT_MASK): Removing ACCESS, OPEN + and CLOSE events on order do let other processes also reading + from their descriptors. (Bug#26973). + +2017-05-26 Michael Albinus + + Remove Emacs 23 compat code from Tramp + + * doc/misc/tramp.texi (Remote processes): Don't mention + Emacs 24 explicitely. + (Frequently Asked Questions): Remove Emacs 23 from + compatibility list. + + * lisp/net/tramp.el: + * lisp/net/tramp-adb.el: + * lisp/net/tramp-cache.el: + * lisp/net/tramp-gvfs.el: + * lisp/net/tramp-sh.el: + * lisp/net/tramp-smb.el: Replace compat function calls. + + * lisp/net/tramp-compat.el (remote-file-name-inhibit-cache) + (tramp-compat-condition-case-unless-debug) + (tramp-compat-copy-file, tramp-compat-copy-directory) + (tramp-compat-delete-file, tramp-compat-delete-directory) + (tramp-compat-process-live-p): Remove them. + + * lisp/net/trampver.el: Make version check fit for Emacs 24. + +2017-05-26 Katsumi Yamaoka + + Work for application/x-tar-gz and image/svg+xml + + ;; Try inlining the attachment in the article <87wp94dzj6.fsf@gmail.com> + ;; of bug#27078 in the Emacs bug list using Gnus. + + * lisp/gnus/mm-archive.el (mm-archive-decoders): + Add a decoder for application/x-tar-gz. + (mm-dissect-archive): Error out if a decoder is not found. + + * lisp/gnus/mm-decode.el (mm-get-image): Allow image/svg+xml. + +2017-05-26 Tino Calancha + + test-calc-23889: Skip test on 32-bit platforms + + This test fails on some 32-bit platforms as mentioned in + https://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00737.html + * test/lisp/calc/calc-tests.el (test-calc-23889): Skip when + the Lisp integer is not big enough. + +2017-05-25 Alan Third + + Fix NS tooltips showing in the wrong place (bug#27053) + + * src/nsfns.m (compute_tip_xy): Get current mouse position instead of + last recorded position. + +2017-05-25 Thomas Fitzsimmons + + lisp/net/soap-client.el: Bump version to 3.1.2 + + * lisp/net/soap-client.el: Bump version to 3.1.2. + +2017-05-25 Thomas Fitzsimmons + + Fix soap-inspect.el doc strings + + * lisp/net/soap-inspect.el (soap-inspect-xs-attribute): Fix doc + string. + (soap-inspect-xs-attribute-group): Likewise. + +2017-05-25 Thomas Fitzsimmons + + Fix two soap-client.el byte compilation warnings + + * lisp/net/soap-client.el (url-http-response-status): Add defvar. + (soap-fetch-xml-from-url): Remove special declaration of + url-http-response-status. + (soap-invoke-internal): Likewise. + +2017-05-25 Thomas Fitzsimmons + + lisp/net/soap-client.el: Require cl-lib version 0.6.1 + + * lisp/net/soap-client.el: Require cl-lib version 0.6.1. + +2017-05-25 Thomas Fitzsimmons + Stefan Monnier + + lisp/net/soap-client.el: Shorten some long lines + + * lisp/net/soap-client.el (soap-encode-xs-element): Remove + unnecessary progn. + (soap-xs-add-union): Wrap long line. + +2017-05-25 Alex Harsanyi + Stefan Monnier + + Remove cl dependency in soap-client.el and soap-inspect.el + + * lisp/net/soap-inspect.el: Replace cl library with cl-lib, case + with cl-case, destructuring-bind with cl-destructuring-bind and + loop with cl-loop. + + * lisp/net/soap-client.el: Replace cl library with cl-lib, + defstruct with cl-defstruct, assert with cl-assert, case with + cl-case, ecase with cl-ecase, loop with cl-loop and + destructuring-bind with cl-destructuring-bind. + +2017-05-25 Michael Albinus + + Switch Tramp to cl-lib + + * lisp/net/tramp-compat.el (cl-lib): Require it rather than cl. + + * lisp/net/tramp-ftp.el: Don't require cl. + + * lisp/net/tramp-gvfs.el: Don't require cl. + (tramp-gvfs-handler-mounted-unmounted) + (tramp-gvfs-connection-mounted-p): Use `cl-*' macros. + + * lisp/net/tramp-sh.el: Don't require cl. + (tramp-set-file-uid-gid): Use `shell-quote-argument'. + (tramp-sh-gvfs-monitor-dir-process-filter) + (tramp-sh-inotifywait-process-filter): Use `cl-*' macros. + + * lisp/net/tramp-smb.el: Don't require cl. + (tramp-smb-read-file-entry): Use `cl-*' macros. + + * lisp/net/tramp.el (cl-lib): Require it rather than cl. + (tramp-parse-file, tramp-parse-shostkeys-sknownhosts) + (tramp-parse-passwd, tramp-parse-etc-group) + (tramp-parse-putty): Use `cl-*' macros. + +2017-05-25 Paul Eggert + + * CONTRIBUTE: Suggest autogen.sh's 'all' operand. + +2017-05-25 Paul Eggert + + Port ATTRIBUTE_MAY_ALIAS to recent icc + + * src/conf_post.h (ATTRIBUTE_MAY_ALIAS) [__ICC]: + Define to empty. Otherwise, icc (ICC) 17.0.4 20170411 says + “warning #2621: attribute "__may_alias__" does not apply here” + for constructs like ‘struct sockaddr *sa = (whatever); + struct sockaddr_in __attribute__ ((__may_alias__)) *sin + = (struct sockaddr_in *) sa;’. + +2017-05-25 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-05-25 port to recent icc + * lib/intprops.h: Copy from gnulib. + +2017-05-24 Michael Albinus + + Fix Tramp for python.el + + * lisp/net/tramp.el (tramp-get-connection-process): Check, + that VEC is a `tramp-file-name' structure. + +2017-05-24 Alan Third + + Raise version of macOS we define instancetype for (bug#27059) + + * src/nsterm.m: Increase supported version number. + +2017-05-24 Alan Third + + Define new types on macOS 10.6 (bug#27041) + + * src/nsterm.h: Enable instancetype typedef for older macOS, and use + correct NSUInteger instead of int. + +2017-05-24 Glenn Morris + + Don't autoload new dns-mode command + + * lisp/textmodes/dns-mode.el (dns-mode-ipv6-to-nibbles): + Remove autoload cookie. + +2017-05-24 Stefan Monnier + + * src/fns.c (sxhash): Fix records hashing (bug#27057, bug#26639) + + (sxhash_vector): Make it work on pseudo vectors as well. + (sxhash): Treat records like vectors. + +2017-05-24 Michael Albinus + + Adapt tramp-tests.el according to new defstruct + + * test/lisp/net/tramp-tests.el (tramp-test03-file-name-defaults): + Fix test according to new defstruct. + (tramp-test29-environment-variables-and-port-numbers): + Expect it now as passed. Cleanup at the end. + +2017-05-24 Michael Albinus + + Introduce a defstruct `tramp-file-name' as central data structure. + + This solves also Bug#27009. + + * lisp/net/tramp.el (tramp-current-domain) + (tramp-current-port): New defvars. + (tramp-file-name): New defstruct. + (tramp-file-name-user-domain, tramp-file-name-host-port) + (tramp-file-name-equal-p): New defuns. + (tramp-file-name-p, tramp-file-name-method) + (tramp-file-name-user, tramp-file-name-host) + (tramp-file-name-localname, tramp-file-name-hop) + (tramp-file-name-real-user, tramp-file-name-domain) + (tramp-file-name-real-host, tramp-file-name-port): + Remove defuns. They are provided by the defstruct, or not + needed anymore. + (tramp-dissect-file-name, tramp-buffer-name) + (tramp-make-tramp-file-name, tramp-get-buffer) + (tramp-set-connection-local-variables) + (tramp-debug-buffer-name, tramp-message) + (tramp-error-with-buffer, with-parsed-tramp-file-name) + (tramp-completion-dissect-file-name1) + (tramp-handle-file-name-as-directory) + (tramp-handle-file-name-directory) + (tramp-handle-file-remote-p, tramp-handle-file-symlink-p) + (tramp-handle-find-backup-file-name) + (tramp-handle-insert-file-contents, tramp-process-actions) + (tramp-check-cached-permissions, tramp-local-host-p) + (tramp-get-remote-tmpdir, tramp-call-process) + (tramp-call-process-region, tramp-read-passwd) + (tramp-clear-passwd): + * lisp/net/tramp-adb.el (tramp-adb-parse-device-names) + (tramp-adb-handle-expand-file-name) + (tramp-adb-handle-file-truename, tramp-adb-handle-copy-file) + (tramp-adb-handle-process-file) + (tramp-adb-maybe-open-connection): + * lisp/net/tramp-cache.el (tramp-get-hash-table) + (tramp-get-file-property, tramp-set-file-property) + (tramp-flush-file-property, tramp-flush-directory-property) + (tramp-get-connection-property) + (tramp-set-connection-property, tramp-connection-property-p) + (tramp-flush-connection-property, tramp-cache-print) + (tramp-list-connections, tramp-dump-connection-properties) + (tramp-parse-connection-properties): + * lisp/net/tramp-cmds.el (tramp-cleanup-connection): + * lisp/net/tramp-ftp.el (tramp-ftp-file-name-handler): + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-expand-file-name) + (tramp-gvfs-url-file-name, tramp-gvfs-handler-askpassword) + (tramp-gvfs-handler-mounted-unmounted) + (tramp-gvfs-mount-spec, tramp-gvfs-get-remote-uid) + (tramp-gvfs-get-remote-gid) + (tramp-gvfs-maybe-open-connection): + * lisp/net/tramp-sh.el (tramp-sh-handle-file-truename) + (tramp-do-copy-or-rename-file-out-of-band) + (tramp-sh-handle-expand-file-name) + (tramp-sh-handle-start-file-process) + (tramp-sh-handle-process-file, tramp-compute-multi-hops) + (tramp-maybe-open-connection) + (tramp-make-copy-program-file-name, tramp-get-remote-path) + (tramp-get-inline-coding): + * lisp/net/tramp-smb.el (tramp-smb-handle-copy-directory) + (tramp-smb-handle-expand-file-name) + (tramp-smb-handle-file-acl, tramp-smb-handle-process-file) + (tramp-smb-handle-set-file-acl) + (tramp-smb-maybe-open-connection): Adapt according to defstruct. + +2017-05-24 Stephen Berman + + Fix and improve UI of scroll bar menu (bug#27047) + + In addition, since the Emacs manual writes "scroll bar", "tool + bar" and "menu bar", use this convention in the Show/Hide menues + and tooltips as well. + + * lisp/menu-bar.el (menu-bar-showhide-scroll-bar-menu): Make + pressing a radio button in the menu actually show that it was + pressed. Replace the two radio buttons to turn the horizontal + scroll bar on and off with a single check-box toggle and add a + separator between this and the vertical scroll bar radio + buttons. Use conventional spelling. + (menu-bar-horizontal-scroll-bar) + (menu-bar-no-horizontal-scroll-bar): Remove, since now unused. + (menu-bar-showhide-tool-bar-menu, menu-bar-showhide-menu) + (menu-bar-mode): Use conventional spelling. + +2017-05-24 Katsumi Yamaoka + + Remove string-as-unibyte + + * lisp/gnus/canlock.el (canlock-sha1): Remove useless variable. + (canlock-make-cancel-key): No need to use string-as-unibyte. + +2017-05-24 Tino Calancha + + Fix concatenation of "^" with diff-file-junk-re + + This regexp contains "\\|", thus a concatenation + of "^" with it just matches the beginning of line for the + first alternative in diff-file-junk-re. + * lisp/vc/ediff-ptch.el (ediff-map-patch-buffer): Concat "^" with + diff-file-junk-re wrapped in a shy group. + +2017-05-24 Glenn Morris + + Suppress intermittent test failure on hydra + + * test/lisp/emacs-lisp/eieio-tests/eieio-tests.el + (eieio-test-37-obsolete-name-in-constructor): Skip on hydra. + +2017-05-24 Peder O. Klingenberg + + New dns-mode command for IPv6 address conversion + + This converts IPv6 addresses to a format suitable for + reverse lookup zone files. (Bug#26820) + * lisp/textmodes/dns-mode.el (dns-mode-map, dns-mode-menu): + Add dns-mode-ipv6-to-nibbles. + (dns-mode-ipv6-to-nibbles, dns-mode-reverse-and-expand-ipv6): + New functions. + * test/lisp/dns-mode-tests.el: New file. + +2017-05-24 Noam Postavsky + + Protect *Backtrace* from being killed (Bug#26650) + + * lisp/emacs-lisp/debug.el (debugger-mode): Call `top-level' in + `kill-buffer-hook'. + +2017-05-24 Noam Postavsky + + Give a name to lisp-mode's adaptive-fill-function (Bug#22730) + + * lisp/emacs-lisp/lisp-mode.el (lisp-adaptive-fill): New function. + (lisp-mode-variables): Use it. + +2017-05-23 Philipp Stephani + + vc-hg.el: Silence byte compiler warning + + * lisp/vc/vc-hg.el (compilation-arguments): Forward-declare. + +2017-05-23 Paul Eggert + + Don't warn about missing brances on macOS + + On macOS, removing -Wmissing-braces is not enough; the warning has to + be disabled explicitly. + +2017-05-23 Wilfred Hughes + + Don't treat ' as a string delimiter in RPM spec files + + ' is commonly used as an apostrophe in the prose sections of spec + files, which was erroneously highlighted as strings. See for example + http://kmymoney2.sourceforge.net/phb/rpm-example.html + + * lisp/progmodes/sh-script.el (sh-mode-syntax-table): Treat ' as + punctuation in RPM spec files. + +2017-05-23 Stefan Monnier + + * lisp/emacs-lisp/cl-indent.el: Don't require CL. Use lexical-binding. + + (common-lisp-indent-function-1): Remove unused var `last-point`. + (lisp-indent-error-function): Move defvar before first use. + +2017-05-23 Stefan Monnier + + * lisp/international/rfc1843.el: Don't require CL. Use lexical-binding. + + * lisp/international/utf7.el: Don't require CL. Use lexical-binding. + + * lisp/net/shr.el: Use cl-lib instead of cl. + +2017-05-23 Stefan Monnier + + * test/src/fns-tests.el, test/src/data-tests.el: Don't use `cl` + + * test/src/data-tests.el (binding-test-manual, binding-test-setq-default) + (binding-test-makunbound, data-tests-varalias-watchers) + (data-tests-local-variable-watchers): Silence compiler warnings. + +2017-05-23 Stefan Monnier + + * lisp/vc/vc-hg.el (compilation-directory): Silence byte-compiler. + +2017-05-23 Alan Third + + Fix GNUstep build + + * src/nsterm.h [NS_IMPL_GNUSTEP]: Add typedefs for Cocoa-only types. + (NSWindowStyleMaskUtilityWindow): #define to NSUtilityWindowMask in + GNUstep and old versions of macOS. + * src/nsfns.m (ns-set-mouse-absolute-pixel-position): Function only + works in cocoa, not GNUstep. + +2017-05-23 Michael Albinus + + Add test for Bug#27009 in tramp-tests.el + + * lisp/net/tramp-sh.el (tramp-compute-multi-hops): + Check `tramp-file-name-real-host' for being a local host. + + * lisp/net/tramp.el (tramp-postfix-host-regexp): Fix docstring. + + * test/lisp/net/tramp-tests.el (tramp-test-temporary-file-directory): + Declare default host for mock method. + (tramp-test29-environment-variables-and-port-numbers): New test. + +2017-05-23 Glenn Morris + + Don't advertise s_client in tls.el docs + + * lisp/net/tls.el (tls-end-of-info, tls-success, tls-untrusted): + Don't mention s_client in docs. + + (cherry picked from commit 622c24a2b75a564b9861fc3ca7a7878741e8568d) + +2017-05-23 Rob Browning + + Remove s_client usage from tls.el + + * lisp/net/tls.el (tls-program, tls-checktrust): Remove s_client. + Ref http://bugs.debian.org/766397 + http://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00803.html + + + (cherry picked from commit 6e45de6bacc508db11b15b2c8ba86aad8c0570df) + +2017-05-22 Stefan Monnier + + * lisp/mail/rfc2047.el (rfc2047-decode-encoded-words): Set `words` to nil. + +2017-05-22 Sam Steingold + + Fix "g" in hg&git push&pull buffers + + lisp/vc/vc-git.el (vc-git--pushpull): Set locally + `compilation-directory' and `compilation-arguments'. + lisp/vc/vc-hg.el (vc-hg--pushpull): Likewise. + +2017-05-22 Eli Zaretskii + + Fix current-line hscrolling in buffers with header-line + + * src/xdisp.c (display_line): When testing the glyph row's + vertical position against the cursor position, account for header + line, if any. (Bug#27014) + +2017-05-22 Stefan Monnier + + * lisp/mail/rfc2047.el: Use cl-lib & lexical-binding, silence warning + + (rfc2047-decode-encoded-words): Use dolist. + (rfc2047-decode-string): Avoid string-to-multibyte. + (rfc2047-pad-base64): Use pcase. + +2017-05-21 Dima Kogan + + Make ff-find-other-file symmetric for C++ (Bug#20192) + + `cc-other-file-alist' has a mapping of file extensions to switch + between headers and sources, but the mappings weren't completely + symmetric. In particular .cpp would map to .hh, but .hh would NOT map + to .cpp. + + * lisp/find-file.el (cc-other-file-alist): Map ".hh" and ".h" to all + C++ extensions to make them symmetric with the C++ extensions that map + to them. This lets repeated invocations of `ff-find-other-file' + toggle between all pairs of sources/headers. + +2017-05-21 Philipp Stephani + + Fix definition of whitespace in JSON + + See + https://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00115.html. + + * lisp/json.el (json-skip-whitespace): Fix definition. + * test/lisp/json-tests.el (test-json-skip-whitespace): Adapt unit + test. + +2017-05-21 Philipp Stephani + + Improve module function terminology + + Module functions were previously called "function environments" when + the functions created by module_make_functions were lambdas. Now we + can adapt the terminology and rename "function environments" to + "module functions" everywhere. This also removes the name clash + between "function environments" and "module environments." + + * src/emacs-module.c (module_make_function): Adapt comment to reality; + stop using "function environment" terminology. + (funcall_module): Stop using "function environment" terminology. + +2017-05-21 Philipp Stephani + + Avoid uninitialized read + + * src/nsterm.m (ns_read_socket): Don't read uninitialized variable 'nevents'. + +2017-05-21 Philipp Stephani + + Fix call of registerServicesMenuSendTypes + + * src/nsterm.m (initFrameFromEmacs:): nil is not allowed for + returnTypes; pass an empty array instead. + +2017-05-21 Philipp Stephani + + Clean up code around 'ns-list-services' + + * src/nsfns.m (Fns_list_services): Remove unreachable code. In this + branch NS_IMPL_COCOA cannot be defined. + (interpret_services_menu): Define only if called to avoid compiler + warnings about unused static functions. + +2017-05-21 Philipp Stephani + + Remove unused automatic variables + + * nsterm.m (ns_read_socket): + * macfont.m (macfont_open): Remove unused automatic variables. + +2017-05-21 Philipp Stephani + + Nextstep: Replace deprecated enumerators + + * src/nsmenu.m (initWithContentRect:styleMask:backing:defer:): Replace + deprecated enumerator. + +2017-05-21 Philipp Stephani + + Nextstep: remove some deprecated method calls + + * src/nsterm.m (mouseDown:): + * src/nsmenu.m (runMenuAt:forFrame:keymaps:): Remove call to + deprecated method. The return value is always nil. + * src/macfont.m (mac_font_shape_1): Replace call to deprecated method. + +2017-05-21 Philipp Stephani + + Remove trailing semicolons in method definitions + + These semicolons are ignored and cause compiler warnings. + + * src/nsimage.m (setPixelAtX:Y:toRed:green:blue:alpha:): + * src/nsterm.m (init, updateFrameSize:): + (setFrame:): Remove trailing semicolon. + +2017-05-21 Philipp Stephani + + Remove calls to deprecated setUsesScreenFonts + + * src/macfont.m (mac_screen_font_get_metrics): Don't call setUsesScreenFonts. + (mac_font_shape_1): Remove screen_font_p parameter. + (mac_screen_font_shape): Remove screen_font_p argument. + +2017-05-21 Philipp Stephani + + Make a function static that isn't used outside this file + + * src/kqueue.c (kqueue_directory_listing): Make static. + +2017-05-21 Philipp Stephani + + Use NSCharacterCollection instead of CTCharacterCollection + + This should not cause behavior changes, but fixes a compiler warning + due to implicit conversions between the enums. + + * src/macfont.m (macfont_cache, macfont_lookup_cache) + (macfont_get_glyph_for_cid, macfont_get_uvs_table) + (macfont_variation_glyphs): Use NSCharacterCollection. + +2017-05-21 Philipp Stephani + + Remove unused function print_regions + +2017-05-21 Philipp Stephani + + Declare Nextstep unexec functions in lisp.h + + This removes compiler warnings about missing prototypes on macOS. + +2017-05-21 Philipp Stephani + + Nextstep: Use instancetype explicit return type + + This removes compiler warnings on macOS and improves type safety. + + * nsterm.m (initFrameFromEmacs:): + (menuDown:): + (toolbarClicked:): + (toggleToolbar:): + (setMiniwindowImage:): + (initFrame:window:): + (condemn, reprieve, setPosition:portion:whole:): + (repeatScroll:): + * nsmenu.m (initWithTitle:): + (initWithTitle:frame:): + (initForView:withIdentifier:): + (init, initWithContentRect:styleMask:backing:defer:): + (initFromContents:isQuestion:): + * nsimage.m (allocInitFromFile:): + (initFromXBM:width:height:fg:bg:): + (setXBMColor:): + (initForXPMWithDepth:width:height:): Use instancetype as return + type instead of implicit id. + +2017-05-21 Tino Calancha + + * lisp/emacs-lisp/package.el (package-delete): Delete readme file as well. + +2017-05-21 Alan Mackenzie + + Enhance mode-line percentage offset facility, with "%o" and "%q" + + "%o" will display the percentage "travel" of the window through the buffer. + "%q" will display a combination of the percentage offsets of the top and + bottom of the window. The new user option mode-line-percent-position will + facilitate selecting a setting for this part of the mode line. + + * lisp/bindings.el (mode-line-percent-position): New customizable user option. + (mode-line-position): Use mode-line-percent-position in place of "%p", etc. + + * src/xdisp.c (decode_mode_spec): Add handlers for "%o" and "%q". + + * doc/lispref/modes.texi (Mode Line Variables): Document + mode-line-percent-position. + (%-Constructs): Document %o and %q. + + * etc/NEWS: Add an entry for these new facilities. + +2017-05-21 Paul Eggert + + Work around macOS bug in create_process, too + + * src/process.c (create_process) [DARWIN_OS]: + Reset SIGCHLD after vfork here, too. + +2017-05-21 Paul Eggert + + Work around macOS bug with vforked child + + * src/callproc.c (call_process) [DARWIN_OS]: + Include workaround for apparent macOS bug. + +2017-05-21 Paul Eggert + + Pacify --enable-gcc-warnings without modules + + * src/print.c (print_vectorlike): New function, taken from + part of print_object. This one is indented properly, and + pacifies --enable-gcc-warnings by using a default case + instead of listing all the enum values, sometimes + incompletely. + (print_object): Use it. + +2017-05-21 Paul Eggert + + Remove DARWIN_OS_CASE_SENSITIVE_FIXME code + + It does not appear to be needed (Bug#24441). + * etc/PROBLEMS: Remove DARWIN_OS_CASE_SENSITIVE_FIXME stuff. + * src/fileio.c (file_name_case_insensitive_p): + Remove DARWIN_OS_CASE_SENSITIVE_FIXME code. + +2017-05-21 Paul Eggert + + Narrow DARWIN_OS_CASE_SENSITIVE_FIXME to 1 choice + + * etc/PROBLEMS: Document this (Bug#24441). + * src/fileio.c (file_name_case_insensitive_p): Prefer pathconf + with _PC_CASE_SENSITIVE, if it works, to + DARWIN_OS_CASE_SENSITIVE_FIXME code. + Support just one method for DARWIN_OS_CASE_SENSITIVE_FIXME, + which matches the Apple documentation more precisely. + +2017-05-21 Tom Tromey + + Fix mhtml-mode fontification bug + + Bug#26922 + * lisp/textmodes/mhtml-mode.el (mhtml-syntax-propertize): Call + sgml-syntax-propertize-inside if not in a submode. + * test/manual/indent/html-multi-4.html: New file. + +2017-05-21 Ryan (tiny change) + + Fix ido-enable-dot-prefix for empty choice (Bug#26997) + + * lisp/ido.el (ido-set-matches-1): Only check first character of + item if it's non-empty. + +2017-05-21 Ari Roponen + + * lisp/svg.el (svg-line): Fix x/y typo. (Bug#26953) + +2017-05-21 Glenn Morris + + Prevent loading vc-bzr writing to ~/.bzr.log + + * lisp/vc/vc-bzr.el (vc-bzr-status-switches): Disable bzr logging. + +2017-05-21 Glenn Morris + + Prevent running vc-tests writing to ~/.bzr.log + + * test/lisp/vc/vc-tests.el (vc-test--create-repo) + (vc-test--register, vc-test--working-revision) + (vc-test--checkout-model): Set temporary BZR_HOME, to disable logging. + +2017-05-21 Noam Postavsky + + Don't end non-hook variable with "-hook" (Bug#26623) + + * lisp/follow.el (follow-inside-post-command-hook-call): Renamed from + follow-inside-post-command-hook, update uses. + +2017-05-21 Charles A. Roelli + + Fix macOS mouse movement + + * lisp/frame.el (ns-set-mouse-absolute-pixel-position): New + function (Lisp). + (set-mouse-absolute-pixel-position): Change it to call + `ns-set-mouse-absolute-pixel-position' on macOS. + * src/nsfns.m (Fns_set_mouse_absolute_pixel_position): New + function. + * src/nsterm.h (NS_PARENT_WINDOW_TOP_POS): Use the primary + screen's height as a base for calculating global coordinates. + * src/nsterm.m (frame_set_mouse_pixel_position): Fix it in macOS. + * test/lisp/mouse-tests.el (bug26816-mouse-frame-movement): Test + movement of mouse relative to frame. + +2017-05-21 Alan Third + + Show tooltip on correct screen (bug#26905) + + * src/nsfns.m (compute_tip_xy): Find the correct screen for the + tooltip and constrain it to that screen. + +2017-05-21 Andreas Politz + + Don't save unrelated buffers before recompiling directory (Bug#25964) + + * lisp/emacs-lisp/bytecomp.el (byte-recompile-directory): Only save + buffers visiting lisp files under the directory being compiled. + +2017-05-20 Paul Eggert + + Minor fixes for arity ranges in emacs modules + + * src/emacs-module.c (module_make_function): + Check that arities fit into fixnums, for func-arity’s benefit. + (funcall_module): Avoid unnecessary conversion to EMACS_INT. + (module_function_arity): Allow arities greater than SHRT_MAX. + +2017-05-20 Philipp Stephani + + Reimplement module functions + + Instead of a lambda, create a new type containing all data required to + call the function, and support it in the evaluator. Because this type + now also needs to store the function documentation, it is too big for + Lisp_Misc; use a pseudovector instead. That also has the nice benefit + that we don't have to add special support to the garbage collector. + + Since the new type is user-visible, give it a predicate. + + Now we can easily support 'help-function-args' and 'func-arity'; add + unit tests for these. + + * src/lisp.h (allocate_module_function, MODULE_FUNCTIONP) + (XMODULE_FUNCTION): New pseudovector type 'module function'. + + * src/eval.c (FUNCTIONP): Also treat module functions as functions. + (funcall_lambda, Ffuncall, eval_sub): Add support for calling module + functions. + (Ffunc_arity): Add support for detecting the arity of module + functions. + + * src/emacs-module.c (module_make_function): Adapt to new structure. + Return module function object directly instead of wrapping it in a + lambda; remove FIXME. + (funcall_module): New function to call module functions. Replaces + `internal--module-call' and is called directly from eval.c. + (syms_of_module): Remove internal helper function, which is no longer + needed. + (module_function_arity): New helper function. + + * src/data.c (Ftype_of): Adapt to new implementation. + (Fmodule_function_p, syms_of_data): New user-visible function. Now + that module functions are first-class objects, they deserve a + predicate. Define it even if not compiled with --enable-modules so + that Lisp code doesn't have to check for the function's existence. + + * src/doc.c (Fdocumentation): Support module functions. + + * src/print.c (print_object): Adapt to new implementation. + + * src/alloc.c (mark_object): Specialized garbage collector support is + no longer needed. + + * lisp/help.el (help-function-arglist): Support module functions. + While there, simplify the arity calculation by using `func-arity', + which does the right thing for all kinds of functions. + + * test/data/emacs-module/mod-test.c: Amend docstring so we can test + the argument list. + + * test/src/emacs-module-tests.el (mod-test-sum-docstring): Adapt to + new docstring. + (mod-test-non-local-exit-signal-test): Because `internal--module-call' + is gone, the backtrace has changed and no longer leaks the + implementation. + (module--func-arity): New test for `func-arity'. + (module--help-function-arglist): New test for `help-function-arglist'. + +2017-05-20 Eli Zaretskii + + Avoid crashes in GC due to unescaped characters warning + + * src/lread.c (load_warn_unescaped_character_literals): Don't cons + Lisp objects from stack-based variables. (Bug#26961) + +2017-05-20 Charles A. Roelli + + New commands: find-library-other-window, find-library-other-frame + + * lisp/emacs-lisp/find-func.el (find-library-other-window) + (find-library-other-frame): New commands to complement the + existing 'find-library' command. (Bug#26712) + (read-library-name): New function to read a library name. + * etc/NEWS: Mention 'find-library-other-window' and + 'find-library-other-frame'. + +2017-05-20 Eli Zaretskii + + Fix automatic hscrolling of only the current line + + * src/xdisp.c (display_line): When hscrolling only the current + line, increment iterator's first_visible_x and last_visible_x + values to account for the hscroll. This propagates the hscroll + effect on the iterator geometry all the way down to the + subroutines called by display_line, and avoids scrolling bugs + under large hscroll values. (Bug#26994) + +2017-05-20 Paul Eggert + + Add handlerlist assertion to module code + + * src/emacs-module.c (module_reset_handlerlist): + Check handlerlist. Suggested by Philipp Stephani in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00521.html + +2017-05-20 Paul Eggert + + Port --enable-gcc-warnings to clang 3.9.1 + + * configure.ac (WERROR_CFLAGS): Omit -Wmissing-braces for Clang, + to shut off a false alarm. Problem reportd by Philipp Stephani in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00521.html + +2017-05-20 Noam Postavsky + + Limit integers printed as characters (Bug#16828) + + * lisp/simple.el (eval-expression-print-maximum-character): New + variable. + (eval-expression-print-format): Only display value as character if + it's less than or equal to `eval-expression-print-maximum-character'. + (eval-expression-get-print-arguments): Check + eval-expression-print-maximum-character, allow negative arg to + override it. + (eval-expression): + * lisp/progmodes/elisp-mode.el (elisp--eval-last-sexp): + (elisp--eval-last-sexp-print-value): Handle new variable. + * doc/emacs/building.texi (Lisp Eval): Document new variable and + behavior. + * etc/NEWS: Announce it. + * test/lisp/progmodes/elisp-mode-tests.el + (eval-last-sexp-print-format-small-int) + (eval-last-sexp-print-format-small-int-echo) + (eval-last-sexp-print-format-large-int) + (eval-last-sexp-print-format-large-int-echo): + * test/lisp/simple-tests.el (eval-expression-print-format-small-int) + (eval-expression-print-format-small-int-echo) + (eval-expression-print-format-large-int) + (eval-expression-print-format-large-int-echo): New tests. + +2017-05-20 Noam Postavsky + + Refactor lisp eval result printing + + * lisp/simple.el (eval-expression-print-format): Don't check + `standard-output' or `current-prefix-arg'. + (eval-expression-get-print-arguments): New function, centralizes + decision about how to print results of `eval-expression' and + `eval-last-sexp'. + (eval-expression): + * lisp/progmodes/elisp-mode.el (elisp--eval-last-sexp-print-value): + Use it. + +2017-05-19 Paul Eggert + + Check that signed right shift is arithmetic + + * src/data.c (ash_lsh_impl): Verify that signed right shift is + arithmetic; if we run across a compiler that uses a logical shift + we’ll need to complicate the code before removing this + compile-time check. Help the compiler do common subexpression + elimination better. + +2017-05-19 Paul Eggert + + Minor .gitignore fixes + + * .gitignore: modules/mod-test/Makefile was renamed to + test/data/emacs-module/Makefile. + Omit [0-9]*.core, subsumed by *.core. + test/indent/*.new was renamed to test/manual/indent/*.new. + Add *.swp, for Vim. + +2017-05-19 Stefan Monnier + + * lisp/emacs-lisp/package.el: Quote `package-desc' in docstrings + +2017-05-19 Eli Zaretskii + + Describe problems with Microsoft Intellipoint + + * etc/PROBLEMS: Describe problems with Microsoft Intellipoint and + mouse-2 events. For the details, see + http://lists.gnu.org/archive/html/help-emacs-windows/2017-05/msg00009.html. + +2017-05-19 Nick Helm (tiny change) + + Fix turning off whitespace-mode + + * lisp/whitespace.el (whitespace-display-char-on): Correct the way + the original buffer-display-table is saved and restored when + global-whitespace-mode is active. (Bug#26892) + + * test/lisp/whitespace-tests.el + (whitespace-tests-whitespace-mode-on): New function. + (whitespace-tests-display-tables): New test. + +2017-05-19 Michael Albinus + + Minor tweaks in tramp-tests.el + + * test/lisp/net/tramp-tests.el (tramp--test-afp-or-smb-p): New defun. + (tramp-test05-expand-file-name-relative): Use it. + (tramp-test38-unload): Run only in batch mode. + +2017-05-19 Michael Albinus + + Fix a problem with OpenSSH 7 in Tramp + + * lisp/net/tramp-sh.el (tramp-ssh-controlmaster-options): Set also + "ConnectTimeout" during test. Otherwise, OpenSSH 7 will hang. + +2017-05-19 Jean-Christophe Helary + + Improve documentation of 'split-string' + + * doc/lispref/strings.texi (Creating Strings): Rearrange text to + make it more readable. (Bug#26925) + +2017-05-19 Ruslan Bekenev + + Fix typos in doc strings + + * lisp/mail/rfc2231.el (rfc2231-encode-string): + * lisp/mail/rfc2047.el (rfc2047-encode-parameter): + * lisp/mail/rfc2045.el (rfc2045-encode-string): Fix typos in doc + strings. (Bug#26103) + +2017-05-19 Philipp Stephani + + Fix module tests on some systems + + If dladdr(3) isn't available or didn't work, the printed + representation of a module function will not include the file name, + but only the address. Make the tests pass in that case. + + * test/src/emacs-module-tests.el (module-function-object): Fix match for + module function printed representation + +2017-05-19 Jean-Christophe Helary + + Add an optional arguments to string-trim + + * lisp/emacs-lisp/subr-x.el (string-trim-left, string-trim-right) + (string-trim): Add optional args that serve as defaults per the + original behavior. (Bug#26908) + +2017-05-19 Stephen Berman + + Fix typo in last change to auto-hscroll-mode + + * lisp/cus-start.el (standard): Fix typo in value of auto-hscroll-mode. + +2017-05-19 Eli Zaretskii + + Support remote editing in emacsclient via Tramp + + * lib-src/emacsclient.c (main, decode_options) + (print_help_and_exit, longopts): New option '--tramp' / '-T' which + specifies how emacs should use tramp to find remote files. + + * doc/emacs/misc.texi (TCP Emacs server): New subsection describing + the various knobs to tune server.el for TCP opereation. + (emacsclient Options): Reference "TCP Emacs server" from description of + --server-file. Document the new '--tramp' / '-T' options. + * doc/emacs/emacs.texi (Top): Update the top-level menu. + + * etc/NEWS: Mention the new option. + +2017-05-19 Eli Zaretskii + + * lisp/replace.el (query-replace-regexp-eval): Doc fix. + +2017-05-19 Paul Eggert + + Attempt to work around macOS vfork bug + + Problem reported by YAMAMOTO Mitsuharu in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00342.html + This is related to the fix for Bug#26397. + * src/callproc.c (call_process_cleanup, call_process) [!MSDOS]: + Report internal error if wait_for_termination fails. + * src/sysdep.c (get_child_status): Return -1 if waitpid is + buggy, instead of aborting. + (wait_for_termination): Return bool success value. + All callers changed. + +2017-05-19 Eli Zaretskii + + Adjust defcustom form for 'auto-hscroll-mode' + + * lisp/cus-start.el (standard) : Adjust the + defcustom form. Suggested by Stephen Berman . + +2017-05-19 Paul Eggert + + Fix DARWIN_OS_CASE_SENSITIVE_FIXME==2 false alarm + + * src/fileio.c (file_name_case_insensitive_p): + Don’t compile the (DARWIN_OS_CASE_SENSITIVE_FIXME == 2) + code unless DARWIN_OS_CASE_SENSITIVE_FIXME is 2. + Problem reported by Philipp Stephani in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00495.html + +2017-05-19 Paul Eggert + + Port --enable-gcc-warnings to clang 3.9.1 + + * configure.ac (WERROR_CFLAGS): Omit -Wdouble-promotion if clang. + Problem reported by Philipp Stephani in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00495.html + +2017-05-19 Paul Eggert + + Clean up compiler warning in emacs-module.c + + * src/emacs-module.c (MODULE_SETJMP_1): Use the local var + instead of leaving it unused, to pacify picky compilers. + (module_reset_handlerlist): Now takes a dummy pointer to a struct + handler *, instead of a dummy pointer to an int. All uses changed. + +2017-05-19 Philipp Stephani + + Clean up some compiler warnings + + * src/sysdep.c (system_process_attributes) [DARWIN_OS]: + Remove unused locals. + +2017-05-18 Eli Zaretskii + + Support hscrolling only the current line + + * src/xdisp.c (hscrolling_current_line_p): New function. + (init_iterator): If auto-hscrolling just the current line, don't + increment the iterator's first_visible_x and last_visible_x + variables. + (hscroll_window_tree): Recompute window's hscroll when moving + vertically to another screen line. + (redisplay_window): If we are hscrolling only the current line, + disable the optimizations that rely on the current matrix being + up-to-date. + (display_line): Accept an additional argument CURSOR_VPOS, the + vertical position of the current screen line which might need + hscrolling; all callers changed. Compute first_visible_x and + last_visible_x specially when auto-hscrolling current line, by + repeating the calculation that is done in init_iterator in other + modes. + (syms_of_xdisp) : No longer boolean, it can now + accept a 3rd value 'current-line, to turn on the mode where + only the current line is hscrolled. + + * etc/NEWS: Mention the new auto-hscroll-mode value. + +2017-05-18 Eli Zaretskii + + Fix last change in line-move-finish + + * lisp/simple.el (line-move-finish): Fix last change. This corrects a + regression in C-n and C-p when lines are truncated, introduced by the + change in 2017-05-10. + +2017-05-18 Simen Heggestøyl + + Expand docstring for CSS mode + + * lisp/textmodes/css-mode.el (css-completion-at-point, css-mode): + Expand docstrings. + +2017-05-18 Tino Calancha + + Use the expression angle units while simplifying it + + Don't use the angle mode, use the angle units included + in the expression instead (Bug#23889). + * lisp/calc/calc-alg.el (calc-input-angle-units): New defun. + (math-simplify): Use it. + * lisp/calc/calc-forms.el (math-to-hms, math-from-hms): + Don't use calc-angle-mode if math-simplifying-units is non-nil. + * lisp/calc/calc-math.el (calcFunc-nroot, math-from-radians) + (math-to-radians-2, math-from-radians-2): Don't convert angle + to radians if math-simplifying-units is non-nil. + * test/lisp/calc/calc-tests.el (test-calc-23889): Add test. + +2017-05-18 Tino Calancha + + Revert "Ignore angle mode while simplifying units" + + This reverts commit 713e922243fb60d850f7b0ff83f3e2a3682f1832. + This commit causes Bug#25652. + +2017-05-17 Paul Eggert + + Avoid undefined behavior in struct sockaddr + + Problem noted by Philipp Stephani in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00391.html + * src/conf_post.h (ATTRIBUTE_MAY_ALIAS, DECLARE_POINTER_ALIAS): + New macros. + * src/process.c (conv_sockaddr_to_lisp, conv_lisp_to_sockaddr) + (connect_network_socket, network_interface_info) + (server_accept_connection): Use it when aliasing non-char objects. + +2017-05-17 Stefan Monnier + + * lisp/vc/smerge-mode.el (smerge-refine-regions): Work in multi-bufs + + Rename from smerge-refine-subst. Allow the `beg's to be markers. + Add autoload cookie. + (smerge--refine-forward): Rename from smerge-refine-forward. + (smerge--refine-chopup-region): Rename from smerge-refine-chopup-region. + Assume that its `beg` arg is a marker. + (smerge--refine-highlight-change): Rename from + smerge-refine-highlight-change. Remove `buf` arg. + (smerge-refine-subst): Redefine as an obsolete alias. + +2017-05-17 Paul Eggert + + Work around AddressSanitizer bug with vfork + + Problem reported by Jim Meyering in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00246.html + * src/conf_post.h (vfork) [ADDRESS_SANITIZER]: Define to fork. + Unfortunately with the AddressSanitizer in Fedora 25 x86-64, the + vforked child messes up the parent’s shadow memory. This is too + bad, as we’d rather have AddressSanitizer catch memory-access bugs + related to vfork. + +2017-05-17 Paul Eggert + + Catch IPv4/IPv6 issues at compile time + + * src/process.c (connect_network_socket): Use verify, + not eassert, so that any problems are caught at compile-time. + Avoid dodgy cast by using a local var of the correct type. + +2017-05-17 Paul Eggert + + Pacify --enable-gcc-warnings --with-x-toolkit=no + + * src/composite.c (autocmp_chars) [!HAVE_WINDOW_SYSTEM]: + Avoid unused local. + +2017-05-17 Glenn Morris + + * admin/update_autogen (commit): Pull before push. + +2017-05-17 Glenn Morris + + autoload-rubric no longer provides a feature by default + + * lisp/emacs-lisp/autoload.el (autoload-rubric): + Stop providing a feature unless explicitly requested. + (autoload-find-generated-file): Update autoload-rubric call. + +2017-05-17 Eli Zaretskii + + Remove redundant code in connect_network_socket + + * src/process.c (connect_network_socket) [HAVE_GETSOCKNAME]: + Remove redundant type-casting and variables. Don't call + 'getsockname' to find the port for AF_LOCAL sockets. + [AF_INET6]: Add an assertion to verify that the ports in the IPv4 + and IPv6 structures are at the same offset and have the same size. + +2017-05-16 Paul Eggert + + Fix minor timezone memory leak + + * src/editfns.c (wall_clock_tz): Remove; unused. + +2017-05-16 Paul Eggert + + Do not discard AddressSanitizer stderr + + * src/emacs.c (close_output_streams) [ADDRESS_SANITIZER]: + Do not close stderr. + +2017-05-16 Paul Eggert + + Simplify procname code to avoid GCC bug + + * src/process.c (server_accept_connection): Simplify and avoid + multiple calls and struct literals in the last case of a switch. + The old code ran afoul of GCC bug 80659, which caused an internal + compiler error. Problem reported by Jim Meyering in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00182.html + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80659 + +2017-05-16 Paul Eggert + + Pacify GCC 7 with --enable-gcc-warnings + + * src/regex.c (regex_compile): Swap labels, so that the + FALLTHROUGH immediately precedes the case label. + +2017-05-16 Paul Eggert + + Merge with gnulib, pacifying GCC 7 + + This incorporates: + 2017-05-16 manywarnings: update for GCC 7 + 2017-05-15 sys_select: Avoid "was expanded before it was required" + * configure.ac (nw): Suppress GCC 7’s new -Wduplicated-branches and + -Wformat-overflow=2 options, due to too many false alarms. + * doc/misc/texinfo.tex, lib/strftime.c, m4/manywarnings.m4: + Copy from gnulib. + * m4/gnulib-comp.m4: Regenerate. + * src/coding.c (decode_coding_iso_2022): + Fix bug uncovered by -Wimplicit-fallthrough. + * src/conf_post.h (FALLTHROUGH): New macro. + Use it to mark all switch cases that fall through. + * src/editfns.c (styled_format): Use !, not ~, on bool. + * src/gtkutil.c (xg_check_special_colors): + When using sprintf, don’t trust Gtk to output colors in [0, 1] range. + (xg_update_scrollbar_pos): Avoid use of possibly-uninitialized bool; + this bug was actually caught by Clang. + * src/search.c (boyer_moore): + Tell GCC that CHAR_BASE, if nonzero, must be a non-ASCII character. + * src/xterm.c (x_draw_glyphless_glyph_string_foreground): + Tell GCC that glyph->u.glyphless.ch must be a character. + +2017-05-16 Michael Albinus + + Make autoloading Tramp more robust + + * lisp/net/tramp.el (tramp-file-name-for-operation): + Use `default-directory' where appropriate. + (tramp-file-name-handler): Do not autoload. + (tramp-autoload-file-name-handler): Reintroduce function. + (tramp-register-autoload-file-name-handlers): Use it. + +2017-05-16 Michael Albinus + + Extend tramp-tests.el + + * test/lisp/net/tramp-tests.el (tramp-change-syntax): + Remove declaration, not needed anymore. + (tramp-test05-expand-file-name-relative): New test. + (tramp-test10-write-region): Extend test. + +2017-05-16 Michael Albinus + + * lisp/net/tramp.el: Avoid recursive load of Tramp. (Bug#26943) + +2017-05-16 Noam Postavsky + + Make `indent-line-to' respect field boundaries (Bug#26891) + + * lisp/indent.el (indent-line-to): Use `back-to-indentation' instead + of `backward-to-indentation'. + +2017-05-16 Noam Postavsky + + Make sure indent-sexp stops at end of sexp (Bug#26878) + + * lisp/emacs-lisp/lisp-mode.el (indent-sexp): Check endpos before + indenting. + * test/lisp/emacs-lisp/lisp-mode-tests.el (indent-sexp-stop): New + test. + +2017-05-16 Glenn Morris + + Stop some epg tests failing on rhel7 with gpg 2.0.22 (bug#23619) + + * test/lisp/epg-tests.el (with-epg-tests): + Also set GNUPGHOME in the environment of child processes. + This avoids problems if gpg does not pass --homedir to spawned agent. + +2017-05-16 Glenn Morris + + Add oldxmenu to system-configuration-features + + * configure.ac (HAVE_OLDXMENU): New. + (emacs_config_features): Add oldxmenu. + +2017-05-15 Ted Zlatanov + + * .gitlab-ci.yml: Adjust disclaimer as per RMS. + +2017-05-15 Eli Zaretskii + + Remove unneeded stuff from nt/inc/sys/time.h + + * nt/inc/sys/time.h (_TIMEVAL_DEFINED, struct timevat, timerisset) + (timercmp, timerclear): Don't define. Instead, include the system + header sys/time.h, and add only the interval timers stuff. This + avoids compiler warnings about 'gettimeofday's prototype, and also + avoids redefinition of macros from system headers. + +2017-05-15 Paul Eggert + + Fix address violation found by AddressSanitizer + + * src/process.c (connect_network_socket): + Use struct sockaddr_storage, not struct sockaddr_in, to store info + about a socket address. Problem reported by Philipp Stephani in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00314.html + This fix is based on a patch by Philipp in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00357.html + +2017-05-14 Gemini Lasswell + + Make edebug-step-in work on generic methods (Bug#22294) + + * lisp/emacs-lisp/edebug.el (edebug-match-cl-generic-method-args): + New function to implement the edebug-form-spec property of + the symbol cl-generic-method-args. + (edebug-instrument-function): If the function is a generic + function, find and instrument all of its methods. Return a list + instead of a single symbol. + (edebug-instrument-callee): Now returns a list. Update docstring. + (edebug-step-in): Handle the list returned by edebug-instrument-callee. + * lisp/emacs-lisp/cl-generic.el (cl-defmethod): Use name and + cl-generic-method-args in its Edebug spec. + * lisp/emacs-lisp/eieio-compat.el (defmethod): Use name and + cl-generic-method-args in its Edebug spec. + * lisp/subr.el (method-files): New function. + * test/lisp/subr-tests.el (subr-tests--method-files--finds-methods) + (subr-tests--method-files--nonexistent-methods): New tests. + +2017-05-14 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-05-14 same-inode: Adapt for windows-stat-inodes + 2017-05-14 windows-stat-inodes: New module + 2017-05-14 stat-time: Adapt for windows-stat-timespec + * lib/gnulib.mk.in: Regenerate. + * lib/stat-time.h, lib/sys_types.in.h, m4/sys_types_h.m4: + Copy from gnulib. + +2017-05-14 Eli Zaretskii + + Remove gettimeofday from w32 sources + + * lib-src/ntlib.c (gettimeofday): + * nt/inc/sys/time.h (gettimeofday, struct timezone): Remove unused + function 'gettimeofday' and all of its supporting code. + +2017-05-14 Eli Zaretskii + + Fix the MS-Windows build + + * nt/inc/sys/time.h (gettimeofday): + * src/w32.c (gettimeofday): Adjust signature to match Gnulib. + +2017-05-14 Eli Zaretskii + + More accurate documentation of the ':box' face attribute + + * doc/lispref/display.texi (Face Attributes): Fix the description + of negative width of the ':box' attribute. (Bug#26920) + +2017-05-14 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-05-13 largefile: Simplify + 2017-05-13 largefile: Improve and document + 2017-05-13 truncate: New module + 2017-05-13 windows-stat-timespec: New module + 2017-05-13 windows-stat-override: New module + 2017-05-11 getopt-posix: port to mingw + 2017-05-11 gettimeofday: Increase precision on mingw + 2017-05-10 time: Fix missing initialization of HAVE_TIMEZONE_T + 2017-05-10 Implement a way to opt out from MSVC support + 2017-05-09 tzset: Expand comment about TZ problem on native Windows + * build-aux/config.guess, lib/dup2.c, lib/fcntl.c, lib/fsync.c: + * lib/getdtablesize.c, lib/getopt.c, lib/gettimeofday.c: + * lib/mktime.c, lib/stat-time.h, lib/sys_stat.in.h, lib/unistd.in.h: + * lib/utimens.c, m4/gettimeofday.m4, m4/largefile.m4: + * m4/sys_stat_h.m4, m4/sys_time_h.m4, m4/time_h.m4, m4/time_rz.m4: + * m4/unistd_h.m4: Copy from gnulib. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + +2017-05-14 Marcin Borkowski + + Merge branch 'fix/bug-21072' + +2017-05-14 Ted Zlatanov + + * .gitlab-ci.yml: Add setup for GitLab CI builds. + +2017-05-13 Tak Kunihiro + + New minor mode 'pixel-scroll-mode' + + * lisp/pixel-scroll.el: New file. + + * etc/NEWS: Mention pixel-scroll-mode. + +2017-05-13 Philipp + + Make `old-style-backquotes' variable internal + + * src/lread.c (load_warn_old_style_backquotes, Fload, read1) + (syms_of_lread): Rename `old-style-backquotes' to + `lread--old-style-backquotes', and clarify that it's for internal + use only. + * lisp/emacs-lisp/bytecomp.el (byte-compile-from-buffer): Rename + variable. + * test/src/lread-tests.el (lread-tests--old-style-backquotes): Add + unit test. + * emacs-lisp/bytecomp-tests.el + (bytecomp-tests--old-style-backquotes): Add unit test. + +2017-05-13 Philipp Stephani + + Improve unescaped character literal warnings + + * src/lread.c (load_warn_unescaped_character_literals) + (syms_of_lread): + lisp/emacs-lisp/bytecomp.el (byte-compile-from-buffer): Improve + formatting of unescaped character literal warnings. + + * test/src/lread-tests.el (lread-tests--unescaped-char-literals): + test/lisp/emacs-lisp/bytecomp-tests.el + (bytecomp-tests--unescaped-char-literals): Adapt unit tests. + +2017-05-12 Alan Mackenzie + + Fontify C++ for loop variable as variable, even when followed by parentheses + + In the following: "for (auto *Friend : Class->friends()) {", "Friend" was + getting fontified as a function, due to insufficient checking of the tokens + between it and "()". + + * lisp/progmodes/cc-langs.el (c-:-op-cont-tokens, c-:-op-cont-regexp): New + lang-consts/vars. + + * lisp/progmodes/cc-engine.el (c-forward-declarator): After finding a putative + declarator's identifier, check for a ":" token inside a for's parentheses, and + abort the search for "(" if this is found. + +2017-05-12 Michael Albinus + + Make Tramp backward compatible + + * lisp/net/tramp-cmds.el (tramp-change-syntax): + Set tramp-autoload cookie. + + * lisp/net/tramp-compat.el: Run `tramp-change-syntax' at + startup, if necessary. + + * lisp/net/tramp.el (tramp-syntax): Use `tramp-compat-user-error'. + (tramp-register-autoload-file-name-handlers): Do not mark + `operations' for `tramp-file-name-handler'. + (tramp-register-file-name-handlers): Remove also + `tramp-autoload-file-name-handler' for backward compatibility. + (tramp-register-foreign-file-name-handler): Use `delete-dups'. + + * test/lisp/net/tramp-tests.el (tramp-change-syntax): Declare. + +2017-05-12 Noam Postavsky + + Modify `beginning-of-defun-comments' + + * lisp/emacs-lisp/lisp.el (beginning-of-defun-comments): Try not to stop + in the middle of a multiline comment. + +2017-05-12 Noam Postavsky + + Fix elisp-tests-with-temp-buffer compilation + + * test/lisp/emacs-lisp/lisp-tests.el (elisp-tests-with-temp-buffer): + Don't refer to the =!NAME= as "markers" since they produce variables + with just plain positions, not marker objects. Explicitly specify + that CONTENTS is evaluated at compile time. Don't re-evaluate + CONTENTS at runtime. Fix debug specification. Suppress warnings due + to BODY not using =!NAME= variables. + (elisp-test-point-position-regex): Rename from + `elisp-test-point-marker-regex'. + (mark-defun-test-buffer): Wrap in `eval-and-compile'. + +2017-05-12 Noam Postavsky + + * lisp/emacs-lisp/lisp.el (mark-defun): Simplify moving the point. + +2017-05-12 Marcin Borkowski + + Fix Bug#21072 and rework `mark-defun' + + * test/lisp/progmodes/elisp-mode-tests.el (mark-defun-test-buffer): + New variable + (mark-defun-no-arg-region-inactive) + (mark-defun-no-arg-region-active) + (mark-defun-arg-region-active) + (mark-defun-pos-arg-region-inactive) + (mark-defun-neg-arg-region-inactive, mark-defun-bob): Add tests for + the new `mark-defun'. + + * lisp/emacs-lisp/lisp.el (beginning-of-defun--in-emptyish-line-p): + New function. + (beginning-of-defun-comments): New function. + (mark-defun): Fix bug#21072, also rewrite large parts of `mark-defun' + to accept a numerical prefix argument. + +2017-05-12 Alfred M. Szmidt + + * lisp/mail/rmail.el (rmail-ignored-headers): Add 3 headers to ignore. + +2017-05-12 Eli Zaretskii + + Improve doc strings in net-utils.el + + * lisp/net/net-utils.el (ifconfig, iwconfig, netstat, arp) + (route, traceroute, nslookup, ftp, smbclient) + (smbclient-list-shares, finger, whois) + (network-connection-to-service, network-service-connection) + (network-connection-reconnect): Improve doc strings. + +2017-05-12 Andrew Robbins + + Extend DNS lookup commands to allow specifying the name server + + * lisp/net/net-utils.el (ffap-string-at-point): Removed due to + 'net-utils-machine-at-point' obviating this autoloaded + function (Bug#25426). + (dig-program-options): New customization variable. + (nslookup-host, dns-lookup-host, run-dig): Can now specify + optional name server argument interactively (by prefix arg) and + non-interactively. + + * etc/NEWS: Mention the extension of DNS lookup commands. + +2017-05-12 Glenn Morris + + Don't hard-code loaddefs files in lisp/Makefile + + * lisp/Makefile.in (loaddefs): New variable. + (AUTOGENEL): Use $loaddefs, and include directory. + (bootstrap-clean): Update for AUTOGENEL change. + +2017-05-11 Katsumi Yamaoka + + Kill modified buffers silently when quitting (bug#26862) + + * lisp/gnus/gnus-start.el (gnus-clear-system): Run do-auto-save to make + sure that latest drafts are saved, and kill modified buffers silently. + +2017-05-10 Perry E. Metzger + + Implement 1-based column numbering in mode line + + * src/xdisp.c (decode_mode_spec): Implement the %C construct. + + * lisp/bindings.el (column-number-indicator-zero-based): New + defcustom. + (mode-line-position): Use %C when + column-number-indicator-zero-based is nil. + + * src/xdisp.c (syms_of_xdisp) : + * src/buffer.c (syms_of_buffer) : + * doc/lispref/modes.texi (%-Constructs): + * doc/lispref/frames.texi (Frame Titles): Document the %C + construct. + + * doc/emacs/display.texi (Optional Mode Line): Document + 'column-number-indicator-zero-based'. + + * etc/NEWS: Mention 'column-number-indicator-zero-based' and the + %C construct. + +2017-05-10 Eli Zaretskii + + Ensure cursor's foreground color is in sync with 'default' face + + * src/w32term.c (x_set_cursor_gc): Don't reuse cursor GC if its + foreground color is different from the background of the glyph + string's face. (Bug#26851) + +2017-05-10 Eli Zaretskii + + Fix vertical cursor motion when columns are of unequal size + + * lisp/simple.el (line-move-finish): In line-move-visual mode, use + vertical-motion to move to the goal column, as the goal column + should in that case be interpreted in units of frame's canonical + character width. (Bug#26852) + +2017-05-10 Glenn Morris + + Fix finding test .el files + + * test/Makefile.in (ELFILES): Exclude the data/ directory. + * test/src/lread-tests.el (lread-test-bug26837): Revert previous. + +2017-05-10 Tino Calancha + + Tweak a recent test + + This test fails in my local machine because the data files + are compiled, and the test doesn't expect that. + * test/src/lread-tests.el (lread-test-bug26837): Match a suffix + ending with '.elc' when the data files are compiled. + +2017-05-10 Glenn Morris + + Put license information in each generated uni-*.el + + * admin/unidata/unidata-gen.el (unidata-gen-file): + Get Copyright line from copyright.html. + Put information in file header, not separate README. + (unidata-gen-charprop): Mention the source location. + * lisp/international/README: Remove file. + +2017-05-10 Noam Postavsky + + Fix lisp-indent-region and indent-sexp (Bug#26619) + + The new lisp-indent-region introduced in 2017-04-22 "Add new + `lisp-indent-region' that doesn't reparse the code." is broken because + it doesn't save the calculated indent amounts for already seen sexp + depths. Fix this by unifying the indent-sexp and lisp-indent-region + code. Furthermore, only preserve position 2 of the running parse + when the depth doesn't change. + * lisp/emacs-lisp/lisp-mode.el (lisp-ppss): Use an OLDSTATE that + corresponds with the start point when calling parse-partial-sexp. + (lisp-indent-state): New struct. + (lisp-indent-calc-next): New function, extracted from indent-sexp. + (indent-sexp, lisp-indent-region): Use it. + (lisp-indent-line): Take indentation, instead of parse state. + * test/lisp/emacs-lisp/lisp-mode-tests.el + (lisp-mode-tests--correctly-indented-sexp): New constant. + (lisp-indent-region, lisp-indent-region-defun-with-docstring): + (lisp-indent-region-open-paren, lisp-indent-region-in-sexp): New + tests. + +2017-05-10 Dmitry Gutov + + Simplify url-encode-url and add a test + + * lisp/url/url-util.el (url-encode-url): Simplify. + url-generic-parse-url copes with multibyte strings just fine + (https://debbugs.gnu.org/cgi/bugreport.cgi?bug=24117#185). + + * test/lisp/url/url-parse-tests.el + (url-generic-parse-url/multibyte-host-and-path): New test. + +2017-05-10 Glenn Morris + + More informative error when required feature missing + + * src/fns.c (Frequire): Include file name in missing feature error. + * doc/lispref/loading.texi (Named Features): Don't quote actual error. + +2017-05-10 Glenn Morris + + Put re-loaded file back at start of load-history (bug#26837) + + * src/lread.c (readevalloop): Fix the "whole buffer" check to + operate in the correct buffer. + (Feval_buffer): Move point back to the start after checking + for lexical binding. + * test/src/lread-tests.el (lread-test-bug26837): New test. + * test/data/somelib.el, test/data/somelib2.el: New test data files. + +2017-05-09 Eli Zaretskii + + Improve documentation of 'gnutls-verify-error' + + * lisp/net/gnutls.el (gnutls-verify-error): Improve and expand + doc string. (Bug#26845) + +2017-05-09 Glenn Morris + + Don't duplicate autoload code in package.el + + * lisp/emacs-lisp/autoload.el (autoload-rubric): Add a package option. + * lisp/emacs-lisp/package.el (autoload-rubric): Declare. + (package-autoload-ensure-default-file): Use autoload-rubric. + +2017-05-09 Michael Albinus + + * test/lisp/net/tramp-tests.el: Keep additional test. + +2017-05-09 Marcin Borkowski + + Add elisp-tests-with-temp-buffer, a new testing macro + + * test/lisp/emacs-lisp/lisp-tests.el + (elisp-test-point-marker-regex) New variable. + (elisp-tests-with-temp-buffer): New macro to help test functions + moving the point and/or mark. + +2017-05-09 Noam Postavsky + + Revert "Output number of characters added to file (Bug#354)" + + The extra message text turned out to be quite annoying in practice, + and is generally more trouble than it's worth. Also revert several + related changes. + + Partially revert "Handle `write-region' messages in Tramp properly" + Revert "New var write-region-verbose, default nil" + Revert "* src/fileio.c (write_region): Don't say "1 characters". (Bug#26796)" + Revert "Minor tuneup of write-region change" + Revert "Adjust write-region so file name is at the beginning again" + Revert "Fix handling of non-integer START param to write-region" + Revert "Output number of characters added to file (Bug#354)" + + * doc/emacs/files.texi (Misc File Ops): + * etc/NEWS: + * lisp/epa-file.el (epa-file-write-region): + * lisp/gnus/mm-util.el (mm-append-to-file): + * lisp/jka-compr.el (jka-compr-write-region): + * lisp/net/ange-ftp.el (ange-ftp-write-region): + * lisp/net/tramp-adb.el (tramp-adb-handle-write-region): + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-write-region): + * lisp/net/tramp-sh.el (tramp-sh-handle-write-region): + * lisp/net/tramp-smb.el (tramp-smb-handle-write-region): + * lisp/net/tramp.el (tramp-handle-write-region-message): + * src/fileio.c (write_region, syms_of_fileio): + * test/lisp/net/tramp-tests.el (tramp-test10-write-region): Remove + extra characters from file writing messages. + +2017-05-09 Noah Friedman + + (ybuffer-list): $alist must be ptr-unmasked at the end of the loop, + because $ptr is modified by ygetptr and we use $ptr immediately at the + beginning. + +2017-05-08 Ken Brown + + Skip a test from filenotify-tests.el on Cygwin + + * test/lisp/filenotify-tests.el (file-notify-test02-rm-watch): + Skip the last part of the test on Cygwin; it fails due to timing + issues. + (file-notify--test-read-event): Remove `sit-for' that was added + for Cygwin. + +2017-05-08 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-05-08 intprops: don’t depend on ‘verify’ + 2017-05-07 utimens: on native Windows, improve resolution if fd < 0 + 2017-05-07 utimens: Improve error code on native Windows + * lib/intprops.h, lib/utimens.c: Copy from gnulib. + +2017-05-08 Wilson Snyder + + Fix various verilog-mode.el issues. + + * lisp/progmodes/verilog-mode.el (verilog-read-decls): Fix SystemVerilog + 2012 import breaking AUTOINST. Reported by Johannes Schaefer. + (verilog-auto-wire-type, verilog-insert-definition): Fix AUTOWIRE using + logic in top-level non-SystemVerilog module, bug1142. Reported by Marcin K. + (verilog-define-abbrev-table) (verilog-mode-abbrev-table): Don't expand + abbrev inside comment/strings, bug1102. Reported by Slava Yuzhaninov. + (verilog-auto): Fix AUTORESET widths pulling from AUTOREGINPUT, + msg2143. Reported by Galen Seitz. + (verilog-modify-compile-command): Fix expansion of __FLAGS__ when + compile-command is globally set, bug1119. Reported by Galen Seitz. + +2017-05-08 Michael Albinus + + Handle `write-region' messages in Tramp properly + + * lisp/net/tramp.el (tramp-handle-write-region-message): New defsubst. + * lisp/net/tramp-adb.el (tramp-adb-handle-write-region): + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-write-region): + * lisp/net/tramp-sh.el (tramp-sh-handle-write-region): + * lisp/net/tramp-smb.el (tramp-smb-handle-write-region): Use it. + + * lisp/net/tramp.el (tramp-password-prompt-regexp) + (tramp-completion-mode-p): + * lisp/net/tramp-cmds.el (tramp-reporter-dump-variable) + (tramp-append-tramp-buffers): + * lisp/net/tramp-smb.el (tramp-smb-maybe-open-connection): + Use `bound-and-true-p'. + + * lisp/net/tramp-compat.el (tramp-compat-delete-file): + Don't check for `boundp' anymore. + + * test/lisp/net/tramp-tests.el (ert-x): Require it. + (tramp--test-messages): New defvar. + (tramp-test10-write-region): Extend test. + +2017-05-08 YAMAMOTO Mitsuharu + + Fix glyph string generation for multi-font compositions (Bug#26742) + + * src/xdisp.c (glyph_string_containing_background_width): New function. + (draw_glyphs): Use it to get correct background width. + (compute_overhangs_and_x): Don't change x in the middle of composite + characters. + +2017-05-07 Philipp Stephani + + Fix all unescaped character literals + +2017-05-07 Alan Mackenzie + + CC Mode internal cache: Handle a cache pos being inside a two-char construct. + + Cache c-state-semi-nonlit-pos-cache was failing when a cache position was, + e.g., between the two characters of an opening comment "/*", and additionally + there were an odd number of quote marks (apostrophes) in the comment. This + happened in .../src/xdisp.c in the Emacs master branch around 2017-05-02 at + buffer position 615001. + + * lisp/progmodes/cc-defs.el (c-emacs-features): Repurpose symbol + pps-extended-state to mean that there are at least 11 elements in the parser + state. + + * lisp/progmodes/cc-engine.el (c-cache-to-parse-ps-state) + (c-parse-ps-state-to-cache): Rewrite these to use enhanced cache element list + types which indicate potentially being inside two-char constructs. + (c-parse-ps-state-below): Rewrite to use the new versions of the above two + functions. + +2017-05-07 Glenn Morris + + Silence an mh-compat compiler warning + + * lisp/mh-e/mh-compat.el (mh-url-unreserved-chars): Always define. + +2017-05-07 Glenn Morris + + Evaluate mh-require when compiling + + * lisp/mh-e/mh-alias.el, lisp/mh-e/mh-folder.el: + * lisp/mh-e/mh-gnus.el, lisp/mh-e/mh-search.el: + Evaluate mh-require when compiling, as require is automatically. + * lisp/mh-e/mh-gnus.el: No longer disable byte-compilation. + +2017-05-07 Glenn Morris + + Remove obsolete method of changing byte-compile-dest-file + + * lisp/emacs-lisp/bytecomp.el (byte-compile-dest-file): + Define unconditionally. + +2017-05-07 Paul Eggert + + New var write-region-verbose, default nil + + By popular demand, write-region char counts are now off by default + (Bug#26796). + * src/fileio.c (write-region-verbose): New Lisp var. + (write_region): Output char count only if the var is non-nil. + * doc/emacs/files.texi (Misc File Ops), etc/NEWS: Document this. + +2017-05-07 Glenn Morris + + Write autoloads file atomically + + * lisp/emacs-lisp/autoload.el (autoload--save-buffer): + New function, to save buffer atomically. + (autoload-save-buffers, update-directory-autoloads): + Use autoload--save-buffer. + * lisp/Makefile.in ($(lisp)/loaddefs.el): + No longer write to a temp file by hand. + +2017-05-07 Glenn Morris + + Write autoloads file once only + + * lisp/emacs-lisp/autoload.el (autoload-find-generated-file): + Simplify. Don't bother about ensuring the output file exists. + (autoload-generated-file): Add doc. + (autoload-ensure-writable): Update doc. + (autoload-ensure-file-writeable): Handle non-existing file. + (autoload-ensure-default-file): Remove function. + +2017-05-07 Paul Eggert + + Port .gdbinit to GDB 7.11.1 + Python 2.7.12 + + * src/.gdbinit (Lisp_Object_Printer.to_string): + Explicitly convert integer val to 'int', so that + older GDBs do not complain about the conversion. + * src/lisp.h (Lisp_Object) [CHECK_LISP_OBJECT_TYPE]: + Give the struct a tag, so that older GDB pretty-printers have a + tag to hang their hat on. + +2017-05-06 Paul Eggert + + Pretty-print const Lisp_Objects in .gdbinit + + * src/.gdbinit (Emacs_Pretty_Printers.__call__): + Compare unqualified type to Lisp_Object, to do the right thing + when the expression has type ‘Lisp_Object const’. + Problem reported by Eli Zaretskii in: + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00138.html + +2017-05-06 Paul Eggert + + Pacify GCC setjmp/longjmp warning + + * src/eval.c (internal_lisp_condition_case): Do not modify local + var VAR, to pacify GCC’s setjmp/longjmp warning which in some + cases mistakenly diagnoses VAR possibly being modified between a + setjmp and a longjmp. + +2017-05-06 Philipp + + Fix bootstrap build of files.el + + * lisp/files.el (file-name-non-special): Don't use cl-letf. + +2017-05-06 Eli Zaretskii + + Fix last change for MS-Windows + + * test/src/emacs-module-tests.el (module-function-object): Port to + MS-Windows. + +2017-05-06 Philipp Stephani + + Introduce new misc type for module function + + This resolves a couple of FIXMEs in emacs-module.c. + + * src/lisp.h (MODULE_FUNCTIONP, XMODULE_FUNCTION): New functions. + + * src/alloc.c (make_module_function): New function. + (mark_object): GC support. + + * src/data.c (Ftype_of, syms_of_data): Handle module function type. + + * src/print.c (print_object): Print support for new type. + + * src/emacs-module.c (module_make_function, Finternal_module_call): + Use new module function type, remove FIXMEs. + (module_format_fun_env): Adapt and give it external linkage. + + * test/src/emacs-module-tests.el (module-function-object): Add unit + test. + +2017-05-06 Philipp Stephani + + Fix quoted files for 'verify-visited-file-modtime' + + Fixes Bug#25951. + + * lisp/files.el (file-name-non-special): Set the file name for the + correct buffer. + + * test/lisp/files-tests.el (files-tests--file-name-non-special--buffers): + Add unit test. + (files-tests--with-advice, files-tests--with-temp-file): New helper + macros. + +2017-05-06 Eli Zaretskii + + * src/fileio.c (write_region): Don't say "1 characters". (Bug#26796) + +2017-05-06 Eli Zaretskii + + Turn on GC_CHECK_MARKED_OBJECTS by default under ENABLE_CHECKING + + * src/alloc.c (GC_CHECK_MARKED_OBJECTS): Define to 1 by default of + ENABLE_CHECKING is defined. + (mark_object): Test for GC_CHECK_MARKED_OBJECTS being non-zero, + instead of being defined. + +2017-05-06 Tom Tromey + + Fix erc-join with channel password + + Bug#25349 + * lisp/erc/erc-join.el (erc-autojoin-after-ident): Switch order of + server names. + (erc-autojoin-channels, erc-autojoin-add, erc-autojoin-remove): + Likewise. + (erc-server-join-channel): Move to erc.el. + * lisp/erc/erc.el (erc-server-join-channel): Move from erc-join.el. + (erc-cmd-JOIN): Use erc-server-join-channel. + +2017-05-06 Tino Calancha + + Ensure the created temp file in a test is new + + * test/lisp/buff-menu-tests.el (buff-menu-24962): Use `make-temp-file' + to create the temp file. + +2017-05-06 Glenn Morris + + Decruftify dns-mode.el a little bit + + * lisp/textmodes/dns-mode.el (dns-mode-control-entities): + New constant. + (dns-mode-control-entity, dns-mode-bad-control-entity) + (dns-mode-type, dns-mode-class): New faces. + (dns-mode-control-entity-face, dns-mode-bad-control-entity-face) + (dns-mode-type-face, dns-mode-class): Make these variables use the + new faces, and mark as obsolete. + (dns-mode-font-lock-keywords): Use dns-mode-control-entities. + +2017-05-06 Paul Eggert + + Pretty-print Lisp_Object values in GDB + + * src/.gdbinit: Add a pretty-printer for Lisp_Object values. Now, + GDB displays them as "XIL(0xXXX)" rather than displaying them + as "..." when CHECK_LISP_OBJECT_TYPE is in effect and as "DDDDD" + otherwise. + +2017-05-05 Peder O. Klingenberg + + Tweak dns-mode font-lock + + * lisp/textmodes/dns-mode.el (dns-mode-font-lock-keywords): + Highlight $TTL as a control entity. (Bug#26780) + +2017-05-05 Glenn Morris + + Fontify the doc-string in some CL forms as such + + * lisp/emacs-lisp/lisp-mode.el (defconstant, defparameter): + Add the doc-string-elt property. (Bug#26778) + +2017-05-05 Glenn Morris + + * lisp/emacs-lisp/cl-lib.el (cl-mapcar): Remove recent autoload cookie. + +2017-05-05 Dmitry Gutov + + cl-defmethod: Make the edebug spec more technically correct + + * lisp/emacs-lisp/cl-generic.el (cl-defmethod): Denote the + edebug spec part for qualifiers as [&rest atom], per + http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00053.html. + +2017-05-05 Mike Kupfer + + Fix MH-E not to load cl at runtime (Bug#25552) + + * lisp/mh-e/mh-acros.el (defun-mh): Check at runtime, not + compile time, whether the target is bound. + * lisp/mh-e/mh-compat.el: Enable compilation. Pull in + mh-acros at compile time. + Authored-by: Glenn Morris , Noam Postavsky + + +2017-05-04 Jean-Christophe Helary + + Multiline support in NS "Open Selected File" service. + + * lisp/term/ns-win.el (ns-open-file-service): new function. Wraps the + original call in a (split-string) to create as many calls as there + are lines. + (ns-spi-service-call): Call `ns-open-file-service' instead of + `dnd-open-file'. + +2017-05-04 Göktuğ Kayaalp + + Require cl-lib at runtime in vc-hg + + * lisp/vc/vc-hg.el: Require cl-lib at runtime as well (bug#26609). + +2017-05-04 Tino Calancha + + Inherit incompatible/obsolete package faces from error + + Don't use the same face for installed packages as for incompatible + or obsolete ones. + * lisp/emacs-lisp/package.el (package-status-incompat): Inherit from error. + +2017-05-04 Michael Albinus + + Set process property `adjust-window-size-function' to `ignore' in Tramp + + * lisp/net/tramp-adb.el (tramp-adb-parse-device-names) + (tramp-adb-maybe-open-connection): + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-file-notify-add-watch): + * lisp/net/tramp-sh.el (tramp-do-copy-or-rename-file-out-of-band) + (tramp-maybe-open-connection): + * lisp/net/tramp-smb.el (tramp-smb-handle-copy-directory) + (tramp-smb-handle-file-acl, tramp-smb-handle-set-file-acl) + (tramp-smb-maybe-open-connection): Set process property + `adjust-window-size-function' to `ignore'. + +2017-05-04 Nicolas Petton + + * lisp/emacs-lisp/seq.el: Bump seq version. + +2017-05-04 Damien Cassou + + Add seq-set-equal-p to test for set equality + + * lisp/emacs-lisp/seq.el (seq-set-equal-p): Add function to compare + two lists as if they were sets. + + * test/lisp/emacs-lisp/seq-tests.el (test-seq-set-equal-p): Add test + for seq-set-equal-p. + +2017-05-04 Paul Eggert + + Spelling fixes + + * lisp/gnus/nndiary.el (nndiary-last-occurrence): + Rename from nndiary-last-occurence. + (nndiary-next-occurrence): + Rename from nndiary-next-occurence. All uses changed. + +2017-05-04 Paul Eggert + + Merge from pkg-config + + * m4/pkg.m4: Copy from pkg-config 0.29.1. + +2017-05-04 Tom Tromey + + Add color highlighting to css-mode + + Bug#25525 + * lisp/textmodes/css-mode.el (css--color-map): New constant. + (css-value-class-alist): Use css--color-map. + (css--number-regexp, css--percent-regexp) + (css--number-or-percent-regexp, css--angle-regexp): New constants. + (css--color-skip-blanks, css--rgb-color, css--hsl-color): New + functions. + (css--colors-regexp): New constant. + (css--hex-color, css--named-color, css--compute-color) + (css--contrasty-color, css--fontify-colors) + (css--fontify-region): New functions. + (css-mode): Set font-lock-fontify-region-function. + (css-mode-syntax-table): Set syntax on more characters. + (css-fontify-colors): New defcustom. + (scss-mode-syntax-table): Define syntax for ?$ and ?%. + * test/lisp/textmodes/css-mode-tests.el (css-test-property-values): + Update. + (css-test-rgb-parser, css-test-hsl-parser) + (css-test-named-color): New tests. + * etc/NEWS: Add entry. + +2017-05-03 Michael Albinus + + Fix Bug#26763 + + * lisp/files.el (delete-directory): Call file name handler + with `trash' argument. + + * lisp/net/ange-ftp.el (ange-ftp-delete-directory): + * lisp/net/tramp-sh.el (tramp-sh-handle-delete-directory): + Add TRASH arg. Implement it. (Bug#26763) + (tramp-get-remote-trash): Check for `delete-by-moving-to-trash'. + + * lisp/net/tramp-adb.el (tramp-adb-handle-delete-directory): + * lisp/net/tramp-smb.el (tramp-smb-handle-delete-directory): + Add _TRASH arg. + +2017-05-03 Paul Eggert + + Use ptrdiff_t, not int, for stack sizes + + * src/thread.c (invoke_thread_function): + * src/xterm.c (x_cr_export_frames): + Don’t assume SPECPDL_INDEX fits in ‘int’. + +2017-05-03 Paul Eggert + + Check list object type if --enable-gcc-warnings + + * configure.ac (--enable-check-lisp-object-type): + Default to "yes" if --enable-gcc-warnings is not "no". + * etc/NEWS: Mention this. + * src/eval.c (internal_lisp_condition_case): Fix some glitches + with 'volatile' uncovered by the above: in particular, 'clauses' + should be a pointer to volatile storage on the stack, and need not + be volatile itself. Use an int, not ptrdiff_t, to count clauses. + Don’t bother gathering binding count if VAR is nil. Use + more-specific local names to try to clarify what’s going on. + +2017-05-02 Glenn Morris + + Tweak auth-source-pass.el to avoid run-time subr-x + + * lisp/auth-source-pass.el (auth-source-pass--parse-data): + Avoid needing subr-x at run-time. + +2017-05-02 Charles A. Roelli + + Constrain non-child frames to screen area in OS X + + * src/nsterm.m (constrainFrameRect:toScreen:): Constrain non-child + frames in OS X, if they would otherwise go offscreen. + + (Bug#25818) + +2017-05-02 Michael Albinus + + Fix error in completion for separate Tramp syntax + + * lisp/net/tramp.el (tramp-completion-file-name-regexp-separate): + Tweak regexp. + + * test/lisp/net/tramp-tests.el (tramp-test24-file-name-completion): + Run method and host name completion for all syntaxes. + +2017-05-02 Eli Zaretskii + + Avoid compilation warnings + + * src/w32fns.c (Fx_file_dialog, w32_parse_and_hook_hot_key): + * src/w32term.c (x_draw_glyph_string): + * src/w32fns.c (compute_tip_xy): + * src/w32font.c (w32font_text_extents): + * src/w32menu.c (set_frame_menubar): + * src/search.c (Freplace_match): Avoid compiler warnings in + optimized builds. + +2017-05-02 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-05-02 utimens: port to Emacs + MS-Windows + * lib/utimens.c: Copy from gnulib. + +2017-05-02 Gemini Lasswell + + Fix Edebug specs for 'cl-defmethod' and 'defmethod' + + * lisp/emacs-lisp/cl-generic.el (cl-defmethod): Change Edebug spec + to make Edebug generate a new symbol for each method (Bug#24753) and + to support a string following :extra (Bug#23995). + * lisp/emacs-lisp/eieio-compat.el (defmethod): Change Edebug spec to + make Edebug generate a new symbol for each method (Bug#24753). + +2017-05-02 Eli Zaretskii + + Temporary fix for the MS_Windows build + + * nt/inc/ms-w32.h (WIN32_LEAN_AND_MEAN): Define to an empty value, + to be consistent with Gnulib's utimens.c. This is because utimens.c + unconditionally defines WIN32_LEAN_AND_MEAN to an empty value, so the + previous definition here conflicted with that. + +2017-05-02 Paul Eggert + + Port format-time-string to MS-Windows better + + * test/src/editfns-tests.el (format-time-string-with-zone): + Port test cases to MS-Windows. + +2017-05-02 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-05-01 New module 'localtime-buffer' + 2017-04-30 utimens: Add support for native Windows + * admin/merge-gnulib (AVOIDED_MODULES): Add tzset. + * configure.ac (tzset): No need for Emacs itself to check now. + * lib/gettimeofday.c, lib/time.in.h, lib/time_rz.c, lib/utimens.c: + * m4/gettimeofday.m4, m4/time_h.m4, m4/time_rz.m4: Copy from gnulib. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * lib/localtime-buffer.c, lib/localtime-buffer.h: + * m4/localtime-buffer.m4: New files, copied from gnulib. + * src/editfns.c (init_editfns): Assume tzset is callable. + +2017-05-01 Dmitry Gutov + + Speed up project-find-regexp for simple regexps + + * lisp/progmodes/xref.el (xref--regexp-syntax-dependent-p): + New function. + (xref--collect-matches): Use it. Don't try to enable the + appropriate major mode and file-local variables if the regexp + does not depend on the buffer's syntax (bug#26710). + (xref--collect-matches-1): Don't syntax-propertize in that + case either. + +2017-05-01 Philipp Stephani + + Warn about missing backslashes during load + + * src/lread.c (load_warn_unescaped_character_literals, Fload, read1) + (syms_of_lread): Warn if unescaped character literals are + found (Bug#20152). + * lisp/emacs-lisp/bytecomp.el (byte-compile-from-buffer): Check for + unescaped character literals during byte compilation. + * test/src/lread-tests.el (lread-tests--unescaped-char-literals): New + unit test. + (lread-tests--with-temp-file, lread-tests--last-message): Helper + functions for unit test. + * test/lisp/emacs-lisp/bytecomp-tests.el + (bytecomp-tests--unescaped-char-literals): New unit test. + * test/lisp/emacs-lisp/bytecomp-tests.el (bytecomp-tests--with-temp-file): + Helper macro for unit test. + +2017-05-01 Ken Brown + + * configure.ac: Suggest Mailutils on Cygwin. + +2017-05-01 Paul Eggert + + Don’t stress-test time zones near the Epoch + + * test/src/editfns-tests.el (format-time-string-with-zone) + (format-time-string-with-outlandish-zone): Don’t format + timestamps near the Epoch, as this runs into bugs on MS-Windows, + and we don’t want to worry about those bugs. + +2017-05-01 Glenn Morris + + Tweak vc-tests.el for bzr + + * test/lisp/vc/vc-tests.el (vc-test--working-revision): + Handle test environments where HOME does not exist. + +2017-05-01 Dmitry Gutov + + vc-git-state: Return `ignored' as appropriate with newer Git + + * lisp/vc/vc-git.el + (vc-git--program-version): New variable. + (vc-git--program-version): New function. + (vc-git-state): Use it to choose whether to add '--ignored' (bug#19343). + +2017-05-01 Dmitry Gutov + + vc-git-state: Bring back CentOS 6 compatibility + + * lisp/vc/vc-git.el (vc-git-state): + Bring back CentOS 6 compatibility (bug#19343). + +2017-05-01 Martin Rudalics + + Rewrite w32fns.c's `x_set_menu_bar_lines' + + * src/w32fns.c (x_set_menu_bar_lines): Redraw frame immediately + regardless of whether menu bar is added or removed. Clear + under internal border iff a W32 window exists. Store either 0 + or 1 as new parameter value. + (x_change_tool_bar_height): Use FRAME_W32_WINDOW instead of + FRAME_X_WINDOW. + +2017-05-01 Michael Albinus + + Fix filenotify-tests.el for cygwin + + * test/lisp/filenotify-tests.el (file-notify--test-read-event): + Add an additional `sit-for'. + (file-notify-test02-rm-watch): Add an additional + `file-notify--test-read-event' call. + +2017-05-01 Jonathan Ganc + + Speed up vc-git-status and make it more precise + + * lisp/vc/vc-git.el (vc-git-state) + (vc-git--git-status-to-vc-state): Update 'vc-git-state' to use + 'git status', so that 'vc-git-state' can now return 'ignored', + 'conflict', or 'unregistered' when appropriate. Discussed in + bug#26066. Fixes bug#19343. + +2017-05-01 Dmitry Gutov + + Fix Git revision navigation in currently removed directories + + * lisp/vc/vc-git.el (vc-git-next-revision): Use the repo root as + default-directory because FILE's parent directory might not exist + anymore (bug#26345). + +2017-04-30 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-04-30 strftime-fixes: New module + 2017-04-30 mktime: Work around TZ problem on native Windows + 2017-04-30 ctime, localtime: New modules + 2017-04-30 gettimeofday: Provide higher resolution on native Windows + 2017-04-29 utime-h: Modernize handling of 'struct utimbuf' + 2017-04-29 Make use of module 'utime-h' + 2017-04-30 Fix a few typos + * admin/merge-gnulib (AVOIDED_MODULES): Avoid utime-h, too. + * lib/gettimeofday.c, lib/mktime.c, lib/time.in.h, lib/utimens.c: + * m4/gettimeofday.m4, m4/include_next.m4, m4/mktime.m4: + * m4/strftime.m4, m4/time_h.m4, m4/timegm.m4, m4/utimens.m4: + Copy from gnulib. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + +2017-04-30 Eli Zaretskii + + Don't lose key bindings on mis-spelled text in flyspell-mode + + * lisp/textmodes/flyspell.el (flyspell-mouse-map): Bind mouse-2 + explicitly. + (make-flyspell-overlay): If the mis-spelled text already has a + 'keymap' property, make that keymap the parent of + flyspell-mouse-map, so as not to lose the parent's bindings. + (Bug#26672) + +2017-04-30 Martin Rudalics + + Fix `delete-frame' behavior including Bug#26682 + + * src/frame.c (other_frames): Accept two arguments now. Don't + care about minibuffer window. Don't care about visibility when + called from delete_frame with FORCE true (Bug#26682). + (delete_frame, Fmake_frame_invisible): Adjust other_frames + calls. + * src/w32term.c (w32_read_socket): Don't add a move frame event + for an invisible frame. + * lisp/frame.el (handle-delete-frame): Don't kill Emacs when + attempting to delete a surrogate minibuffer frame. + +2017-04-30 Paul Eggert + + Merge from gnulib + + This avoids incorporating the following, which I suspect are + more trouble for Emacs than they’re worth: + 2017-04-29 stat, fstat: fix time_t etc. on native Windows platforms + * admin/merge-gnulib (AVOIDED_MODULES): Avoid stat, too. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * lib/pathmax.h, lib/stat.c, m4/pathmax.m4, m4/stat.m4: Remove. + +2017-04-30 Paul Eggert + + Fix buffer overflow in make-docfile + + * lib-src/make-docfile.c (scan_c_stream): Check for buffer + overflow when reading an identifier. Use a static buffer for NAME + rather than a small dynamically-allocated buffer. + +2017-04-30 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-04-29 getopt: port to Solaris 10 with circa-1997 glibc getopt.h + * lib/getopt-pfx-ext.h: Copy from gnulib. + * lib/gnulib.mk.in: Regenerate. + +2017-04-30 Tino Calancha + + Fix dependency error during bootstrap + + * lisp/files.el: Require pcase and easy-mmode at compile time. + +2017-04-30 Mats Lidell + + * etc/tutorials/TUTORIAL.sv: synced with TUTORIAL + +2017-04-29 Philipp Stephani + + Reimplement auto-saving to visited files + + This reacts to confusing behavior of 'auto-save-visited-file-name', + cf. Bug#25478. + + * lisp/files.el (auto-save-visited-interval): New customization option. + (auto-save-visited-mode): New global minor mode. + (auto-save-visited-file-name): Make obsolete. + (auto-save--timer): New internal helper variable. + + * doc/emacs/files.texi (Auto Save Files): Document + 'auto-save-visited-mode' instead of obsolete + 'auto-save-visited-file-name'. + (Auto Save Control): Document customization option + 'auto-save-visited-interval'. + +2017-04-29 Paul Eggert + + Allow bypassing of some checks when merging + + * build-aux/git-hooks/pre-commit: Don't check merged-in changes. + +2017-04-29 Philipp Stephani + + Integrate module test with normal test suite + + * test/Makefile.in (ELFILES): Exclude module test if modules aren't + configured. + (EMACS_TEST_DIRECTORY): Expand test directory so that it's set + correctly even if Emacs changes the current directory. + ($(srcdir)/src/emacs-module-tests.log) + ($(test_module)): Proper dependency tracking for test module. + + * test/data/emacs-module/Makefile (ROOT): Adapt to new location. + Remove 'check' target and EMACS variable, which are no longer + necessary. + (SO): Change to include period. + + * test/src/emacs-module-tests.el (mod-test): Use EMACS_TEST_DIRECTORY + environment variable to reliably find test data. + + * configure.ac (HAVE_MODULES, MODULES_SUFFIX): Add necessary + substitutions. + +2017-04-28 Glenn Morris + + Broaden comint-password-prompt-regexp + + * lisp/comint.el (comint-password-prompt-regexp): + Broaden the regexp, for non-English locales. (Bug#26698) + +2017-04-28 Stefan Monnier + + * lisp/auth-source.el (auth-source-backend-parse): `return' -> cl-return. + +2017-04-28 Bartosz Duszel + + Don't pass the value of point to 'push-mark', as that's the default. + + * lisp/textmodes/bib-mode.el (mark-bib): + * lisp/simple.el (mark-whole-buffer, yank): + * lisp/ses.el (ses--advice-yank, ses-mark-row, ses-mark-column): + * lisp/progmodes/xscheme.el (xscheme-yank): + * lisp/progmodes/verilog-mode.el (verilog-mark-defun): + * lisp/progmodes/perl-mode.el (perl-mark-function): + * lisp/progmodes/pascal.el (pascal-mark-defun): + * lisp/progmodes/meta-mode.el (meta-mark-defun): + * lisp/progmodes/icon.el (mark-icon-function): + * lisp/progmodes/cc-cmds.el (c-mark-function): + * lisp/obsolete/vip.el (ex-goto): + * lisp/obsolete/vi.el (vi-put-before): + * lisp/mouse.el (mouse-yank-primary): + * lisp/menu-bar.el (menu-bar-select-yank): + * lisp/mail/sendmail.el (mail-yank-original): + * lisp/hexl.el (hexl-beginning-of-buffer, hexl-end-of-buffer): + * lisp/emulation/viper-cmd.el (viper-mark-beginning-of-buffer) + (viper-mark-end-of-buffer): + * lisp/cedet/semantic/senator.el (senator-mark-defun): + * lisp/allout.el (allout-mark-topic): Remove unnecessary argument + `(point)' from calls to `push-mark'. (Bug#25565) + +2017-04-28 Glenn Morris + + Merge from origin/emacs-25 + + 784602b1050 (origin/emacs-25) ; Add release notice + 3a34412caae (tag: emacs-25.2) Set Emacs version to 25.2 and update AU... + 56a4461a48d ; Move stray item from admin/notes/repo to CONTRIBUTE + 2b0d1118199 ; CONTRIBUTE: Remove stray header. + f2ab09ec60d Fix a typo in indexing the user manual + bc55a574235 * lisp/menu-bar.el (kill-this-buffer): Doc fix. (Bug#26466) + a6d50401b4b Document 'line-pixel-height' + 0c55cf43e61 * search.c (Fre_search_forward, Fre_search_backward): Imp... + c7ed57eaef4 Mention that processes start in default-directory (Bug#18... + 856ec9ffa1f * src/xdisp.c (vmessage, message): Clarify commentary. + 849a0aaa1c9 Belated fixes for admin.el's M-x make-manuals-dist + 84938d79698 default-directory: Remark that it must be a directory name + 3f0d047d2eb Delete confuse statement in manual + ee1bd94dd0c Improve packaging documentation + fb18bff91f0 Expand manual section on quitting windows + 9a737079645 Fix docstring of dabbrev-abbrev-char-regexp + afe8849bac1 * doc/misc/cl.texi (Iteration Clauses): Clarify example (... + ada79442c07 ;* doc/misc/info.texi (Choose menu subtopic): Improve ind... + d38fd9229c0 Narrow scope of modification hook renabling in org-src fo... + e0e9db4c84a ; Spelling fix + + # Conflicts: + # README + # etc/AUTHORS + # etc/HISTORY + # lisp/ldefs-boot.el + +2017-04-28 Glenn Morris + + * doc/misc/auth.texi: Commas don't work in node names. + + * test/lisp/auth-source-pass-tests.el: Fix loading of cl-lib. + +2017-04-28 Paul Eggert + + Test format-time-string with zone arg + + * test/src/editfns-tests.el (format-time-string-with-zone) + (format-time-string-with-outlandish-zone): New tests. + +2017-04-28 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-04-24 time_rz: fix heap buffer overflow vulnerability + 2017-04-23 stat-time: Update comments. + 2017-04-22 ftoastr: cite a newer paper + 2017-04-21 gettext-h: Avoid -Wundef warning. + * lib/ftoastr.c, lib/gettext.h, lib/stat-time.h, lib/time_rz.c: + * m4/getopt.m4: Copy from gnulib. + * m4/gnulib-comp.m4: Regenerate. + +2017-04-27 Damien Cassou + + auth-source-pass: Add documentation; fix tests and indentation. + + * doc/misc/auth.texi: Document new integration with Pass. Use @itemize + instead of @enumerate. + * lisp/auth-source-pass.el: Fix indentation. + (auth-source-pass--remove-directory-name): Remove. + * test/lisp/auth-source-pass-tests.el: Adjust test macros. + +2017-04-27 foudfou + + auth-source-pass: Enable finding entries by "host/username" + + * lisp/auth-source-pass.el: Enable finding entries by "host/username". + * test/lisp/auth-source-pass-tests.el: Adjust tests to check it. + +2017-04-27 Damien Cassou + + Integrate auth-source with password-store + + * lisp/auth-source-pass.el: auth-source backend for password-store. + * test/lisp/auth-source-pass-tests.el: Tests for auth-source-pass + behavior. + +2017-04-27 Damien Cassou + + * lisp/auth-source.el: Document parser functions. + +2017-04-27 Ted Zlatanov + + auth-source: factor out parsers and add tests + + * lisp/auth-source.el: Factor out the source parsers. Clean up comments. + * test/lisp/auth-source-tests.el: Add tests. + +2017-04-27 Martin Rudalics + + Fix doc and customization type of `window-combination-limit' (Bug#26673) + + * src/window.c (Vwindow_combination_limit): Fix doc-string. + * lisp/cus-start.el (window-combination-limit): Fix + customization type. + * doc/lispref/windows.texi (Recombining Windows): Fix + documentation of `window-combination-limit'. + +2017-04-27 Tino Calancha + + Drop face from hi-lock--unused-faces only when used + + * lisp/hi-lock.el (hi-lock-set-pattern): If REGEXP is already + highlighted, then push FACE into hi-lock--unused-faces (Bug#26666). + * test/lisp/hi-lock-tests.el (hi-lock-bug26666): Add test. + +2017-04-26 Alan Third + + Fix macOS version check (bug#26664) + + * src/nsterm.m (initFrameFromEmacs): Prevent window tabbing mode on + macOS versions 10.12+. + +2017-04-26 Glenn Morris + + Make charprop.el provide a feature + + * admin/unidata/unidata-gen.el (unidata-gen-charprop): + Provide a feature. + * lisp/loadup.el: Use the charprop feature. + +2017-04-26 Glenn Morris + + * lisp/loadup.el: Get charprop.el into etc/DOC again. + +2017-04-26 Stefan Monnier + + * lisp/ido.el (ido-everywhere): Use add-function. + +2017-04-26 Martin Rudalics + + Try to fix latest fix of w32_mouse_position + + * src/w32term.c (w32_mouse_position): Fix a bug introduced by + latest fix and try to make the affected code more rigorous. + +2017-04-26 Eli Zaretskii + + Avoid segfaults when 'find-font' is invoked for a TTY frame + + * src/font.c (font_pixel_size): Don't call GUI functions if F is a + text-mode frame. (Bug#26646) + +2017-04-26 Michael Albinus + + * lisp/net/tramp.el (tramp-set-connection-local-variables-for-buffer): + + New defun. + +2017-04-26 Glenn Morris + + * src/Makefile.in (leimdir): Remove variable, no longer used. + +2017-04-26 Glenn Morris + + Generate leim-list via lisp/Makefile, not src/Makefile + + * src/Makefile.in ($(leimdir)/leim-list.el): Remove rule. + (emacs$(EXEEXT)): Don't depend on leim-list. + * lisp/Makefile.in ($(lisp)/loaddefs.el): Depend on gen-lisp again. + +2017-04-25 Alan Third + + Fix define for GNUstep builds + + * src/nsterm.m (initFrameFromEmacs): Fix the ifdef so that GNUstep + doesn't see the code. + +2017-04-25 Glenn Morris + + Suppress intermittent test failure on hydra + + * test/lisp/emacs-lisp/eieio-tests/eieio-test-methodinvoke.el + (eieio-test-method-order-list-6): Skip on hydra. + +2017-04-25 Alan Third + + Fix some NS frame handling issues + + * src/nsterm.m (FRAME_DECORATED_FLAGS, FRAME_UNDECORATED_FLAGS): New + defines intended to make things tidier. + (x_set_undecorated): Use the new defines. + (windowWillResize): Don't use new macOS 12+ only feature. + (initFrameFromEmacs): Use the new defines, and disable automatic + window tabbing feature in macOS 12. + (x_set_undecorated, x_set_parent_frame, x_set_no_accept_focus, + x_set_z_group): Add NSTRACE notices. + +2017-04-25 Glenn Morris + + Avoid parallel race condition + + * lisp/Makefile.in ($(lisp)/loaddefs.el): Remove gen-lisp for now. + +2017-04-25 Glenn Morris + + Generate each unicode lisp file independently + + This is better for parallel builds, eg it eliminates race + conditions from having one process write multiple files. + * admin/unidata/Makefile.in (lparen, unifiles): New variables. + Parse unidata-gen.el, not charprop.el, to get the list of uni- files. + (all): Explicitly list the output lisp files. + (PHONY_EXTRAS): Remove. + (${unidir}/charprop.el): Change rule to just be for this file. + (${unifiles}): New rule to write each unicode lisp file. + (extraclean): Simplify. + * admin/unidata/unidata-gen.el (unidata-gen-charprop): + Quieten in batch mode. + (unidata-gen-files): Remove, no longer used. + * lisp/loadup.el: Update command-line parser. + +2017-04-25 Glenn Morris + + Further refactoring in unidata-gen.el + + * admin/unidata/unidata-gen.el (unidata-gen-charprop): + New function, split from unidata-gen-files. + (unidata-gen-files): Use unidata-gen-charprop. + +2017-04-25 Glenn Morris + + Allow unidata-gen-file to work independently + + * admin/unidata/unidata-gen.el (unidata-gen-file): + Make it work as a stand-alone function in batch mode. + (unidata-gen-files): Pass extra arguments to unidata-gen-file. + +2017-04-25 Glenn Morris + + Preparatory refactoring in unidata-gen.el + + * admin/unidata/unidata-gen.el (unidata-gen-file): + New function, split from unidata-gen-files. + (unidata-gen-files): Use unidata-gen-file. + +2017-04-25 Glenn Morris + + Write each generated character property lisp file only once + + * admin/unidata/unidata-gen.el (unidata-file-alist): + Rename from unidata-prop-alist. All users changed. + Use file name rather than property name as the key. + (unidata-prop-prop): New function. + (unidata-prop-index, unidata-prop-generator, unidata-prop-docstring) + (unidata-prop-describer, unidata-prop-default, unidata-prop-val-list): + Change to parse the argument rather than unidata-prop-alist. + (unidata-gen-table-character, unidata-gen-table) + (unidata-gen-table-symbol, unidata-gen-table-integer) + (unidata-gen-table-numeric, unidata-gen-table-word-list) + (unidata-gen-table-name, unidata-gen-table-decomposition) + (unidata-gen-table-special-casing): Pass index as an argument. + (unidata-check): Adapt to unidata-file-alist. + Pass index to generator functions. + (unidata-gen-files): Adapt to unidata-file-alist. + Write each output file once only. Overwrite rather than delete. + +2017-04-25 Andrew G Cohen + + Fix requesting sparse articles in gnus + + * lisp/gnus/gnus-art.el (gnus-request-article-this-buffer): Delete the + sparse article number from the list, not its id. + +2017-04-25 Glenn Morris + + Don't advertise s_client in tls.el docs + + * lisp/net/tls.el (tls-end-of-info, tls-success, tls-untrusted): + Don't mention s_client in docs. + +2017-04-25 Rob Browning + + Remove s_client usage from tls.el + + * lisp/net/tls.el (tls-program, tls-checktrust): Remove s_client. + Ref http://bugs.debian.org/766397 + http://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00803.html + +2017-04-25 Glenn Morris + + Further robustify cedet bootstrap to loaddefs not yet built + + * lisp/cedet/semantic/util.el (semantic-something-to-tag-table): + Avoid void-function error when bootstrapping and semantic/loaddefs.el + does not yet exist. + +2017-04-24 Alan Third + + Fix XBM colour rendering in NS port (bug#22060) + + src/nsimage.m (setXBMColor): Fix calculation of xbm_fg. + +2017-04-24 Vibhav Pant + + Add support for IRCv3 message tags. + + * erc-backend.el: + erc-response: Add `tags' element. + Add (erc-parse-tags). + (erc-parse-server-response): Use (erc-parse-tags) to parse message + tags (if any), and store them in `erc-resopnse' struct. + + * erc.el: (erc-display-message): Expose message tags with text + properties of the corresponding message line. + +2017-04-24 Lars Ingebrigtsen + + Add image sizing tests for an image that's narrow + + Needlessly refactor tests for clarity + +2017-04-23 Philipp Stephani + + Add missing remappings for Ido mode + + Among others, add a remapping for C-x 4 d, cf. Bug#26360. + + * lisp/ido.el (ido-mode): Remap missing commands. + (ido-file-internal, ido-visit-buffer): Add support for new + methods. + (ido-display-buffer-other-frame) + (ido-find-alternate-file-other-window, ido-dired-other-window) + (ido-dired-other-frame): New commands. + + * test/lisp/ido-tests.el (ido-tests--other-window-frame): Add unit + test for the bindings. + +2017-04-23 Martin Rudalics + + Let w32_mouse_position pick a child window only if it has a child frame + + * src/w32term.c (w32_mouse_position): When using a frame found + by ChildWindowFromPoint make sure it's a child frame (Bug#26615, + maybe). + +2017-04-23 Noam Postavsky + + Don't require bytecomp for running ert tests + + "Fix ert-tests when running compiled" 2016-12-06 accidentally + introduced a dependency on `bytecomp' into `ert'. As mentioned in + "Avoid ert test failures" 2017-04-18, the accidental dependency of ert + on bytecomp was masked by loading other libraries until recently. + + * lisp/emacs-lisp/ert.el (ert--expand-should-1): Only use + `byte-compile-macro-environment' if it's bound. + * test/src/eval-tests.el: Add defvar for dynamic variable + `byte-compile-debug'. + +2017-04-23 Andrew G Cohen + + Eliminate unneeded warp-to-article in gnus article referral + + * lisp/gnus/gnus-sum.el (gnus-summary-refer-thread): + (gnus-summary-refer-article): Remove gnus-warp-to article call. + +2017-04-23 Andrew G Cohen + + Allow limiting gnus summary buffers to a thread + + * lisp/gnus/gnus-sum.el (gnus-summary-limit-include-thread): Include + an optional argument to allow limiting the summary buffer to just the + thread-related articles. + (gnus-refer-thread-limit-to-thread): Introduce customizable variable + to control whether thread-referral adds the thread to the summary + buffer or limits to just the thread. + (gnus-summary-refer-thread): Use the new variable. + +2017-04-23 Andrew G Cohen + + Correct gnus-newsgroup-limits in gnus when including thread + + * lisp/gnus/gnus-sum.el (gnus-summary-limit-include-thread): Should + only add one list of thread-related articles to gnus-newsgroup-limits + rather than two. + +2017-04-23 Andrew G Cohen + + Improve gnus thread matching of similar subjects + + * lisp/gnus/gnus-sum.el (gnus-summary-limit-include-thread): + Use the more liberal gnus-general-simplify-subject regexp to + find thread articles with similar subjects. + +2017-04-22 Noam Postavsky + + Add new `lisp-indent-region' that doesn't reparse the code. + + Both `lisp-indent-region' and `lisp-indent-line' now use `syntax-ppss' + to get initial state, so they will no longer indent string literal + contents. + + * lisp/emacs-lisp/lisp-mode.el (lisp-ppss): New function, like + `syntax-ppss', but with a more dependable item 2. + (lisp-indent-region): New function, like `indent-region-line-by-line' + but additionally keep a running parse state to avoid reparsing the + code repeatedly. Use `lisp-ppss' to get initial state. + (lisp-indent-line): Take optional PARSE-STATE argument, pass it to + `calculate-lisp-indent', use `lisp-ppss' if not given. + (lisp-mode-variables): Set `indent-region-function' to + `lisp-indent-region'. + +2017-04-22 Noam Postavsky + + Remove ignored argument from lisp-indent-line + + * lisp/emacs-lisp/lisp-mode.el (lisp-indent-line): Remove WHOLE-EXP + argument, the behavior has long since been handled in + `indent-for-tab-command'. Also remove redundant `beg' and `shift-amt' + variables and use `indent-line-to'. + +2017-04-22 Noam Postavsky + + * lisp/emacs-lisp/lisp-mode.el (indent-sexp): Clean up marker. + +2017-04-22 Noam Postavsky + + Don't reparse the sexp in indent-sexp (Bug#25122) + + * lisp/emacs-lisp/lisp-mode.el (calculate-lisp-indent): Let + PARSE-START be a parse state that can be reused. + (indent-sexp): Pass the running parse state to calculate-lisp-indent + instead of the sexp beginning position. Saving the + CONTAINING-SEXP-START returned by `calculate-lisp-indent' is no longer + needed. Don't bother stopping if we don't descend below init-depth, + since we now alway scan the whole buffer (via syntax-ppss) anyway. + * test/lisp/emacs-lisp/lisp-mode-tests.el (indent-sexp): Add blank + line to test case. + +2017-04-22 Vibhav Pant + + Add cond test cases for singleton clauses. + + * test/lisp/emacs-lisp/bytecomp-tests.el: Add test cond forms where + the default clause is a single non-nil expression. + +2017-04-22 Vibhav Pant + + b-c--cond-jump-table-info: Use correct body for singleton clauses + + * lisp/emacs-lisp/bytecomp.el (byte-compile-cond-jump-table-info): + When a clause's body consists of a single constant expression, use + that expression as the body to be compiled. This fixes switch bytecode + evaluating to nil to such clauses. + +2017-04-22 Philipp Stephani + + ffap: Don't switch window unless needed + + When using ffap-other-window, don't change the window configuration + unless a new buffer has actually been created (Bug#25352). + + * lisp/ffap.el (ffap-other-frame): Don't change the window + configuration if no new buffer has been created. + * test/lisp/ffap-tests.el (ffap-other-window--bug-25352): Add unit + test. + +2017-04-22 Alan Mackenzie + + Fix fontification of C++ declaration with type FOO::FOO. + + * lisp/progmodes/cc-engine.el (c-find-decl-spots): Initialize + cfd-top-level properly. + (c-forward-decl-or-cast-1): On finding FOO::FOO, check it is followed by "(" + before deciding it is a constructor. + + * lisp/progmodes/cc-fonts.el (c-font-lock-complex-decl-prepare): Negate the + result of the c-bs-at-toplevel-p call passed to c-font-lock-declarators + (simple bug fix). + +2017-04-22 Philipp Stephani + + Fix usage of FRAME_Z_GROUP + + * src/nsterm.m (initFrameFromEmacs:): FRAME_Z_GROUP does not return a + Lisp object, cf. Bug#26597. + +2017-04-22 Alan Third + + Fix GNUstep build + + * src/nsfns.m (Fns_frame_z_list_order): Rewrite for GNUstep + compatibility. + * src/nsmenu.m (update_frame_tool_bar): Remove unused variable. + +2017-04-21 Alan Third + + Add no-accept-focus and frame-list-z-order to NS port + + * lisp/frame.el (frame-list-z-order): Add NS. + * src/nsfns.m: Add x_set_no_accept_focus to handler struct. + (Fx_create_frame): Handle no-accept-focus parameter. + (ns_window_is_ancestor): + (Fns_frame_list_z_order): New functions. + * src/nsterm.m (x_set_no_accept_focus): New function. + (initFrameFromEmacs): Use EmacsWindow instead of EmacsFSWindow for + non-fullscreen windows. + (EmacsWindow:canBecomeKeyWindow): New function. + +2017-04-21 Stefan Monnier + + Improve prefix handling for dash.el + + * lisp/emacs-lisp/autoload.el (autoload--make-defs-autoload): + Don't drop dash's "-" prefixes. + +2017-04-21 Stefan Monnier + + * lisp/emacs-lisp/cl-macs.el: Fix symbol-macrolet + + Revert 0d112c00ba0ec14bd3014efcd3430b9ddcfe1fc1 (to fix bug#26325) + and use a different fix for bug#26068. + (cl--symbol-macro-key): New function. + (cl--sm-macroexpand, cl-symbol-macrolet): Use it instead of `symbol-name`. + * test/lisp/emacs-lisp/cl-lib-tests.el (cl-lib-symbol-macrolet): + Failure is not expected any more. + +2017-04-21 Eli Zaretskii + + Avoid infinite loop in redisplay when header-line-format is invalid + + * src/xdisp.c (handle_invisible_prop): Avoid inflooping when the + string has an invalid %-construct in it and is displayed as part + of mode-line or header-line. (Bug#26586) + +2017-04-21 Lars Ingebrigtsen + + Add tests to check image scaling functionality + + This is in preparation to doing further work in this area to avoid + regressions. + + * test/data/image/blank-200x100.png: New file for testing + image scaling. + + * test/manual/image-size-tests.el: New file. + +2017-04-21 Lars Ingebrigtsen + + Allow svg-image to take all create-image PROPS + + * lisp/svg.el (svg-image): Allow passing in PROPS when + creating an image for convenience. + +2017-04-21 George D. Plymale II (tiny change) + + Treat non-erroring lisp call as successful eshell command (Bug#26161) + + This lets a compound command like 'cd .. && echo ok' print 'ok', + similar to how most other shells behave. + + * lisp/eshell/esh-cmd.el (eshell-exit-success-p): Only check if the + last exit code was zero, rather than first checking whether the last + command returned nil. + (eshell-exec-lisp): Set `eshell-last-command-status' to 1 on error. + +2017-04-21 Reuben Thomas + + Fix reading of tab settings in whitespace-mode + + lisp/whitespace.el (whitespace-indent-tabs-mode) + whitespace-tab-width): Remove these variables. The underlying + variables `indent-tabs-mode' and `tab-width' are already buffer-local + when needed, and whitespace-mode never changes them. + (whitespace-ensure-local-variables): Remove this function, which only + existed to set the above variables. + (whitespace-cleanup-region, whitespace-regexp) + (whitespace-indentation-regexp, whitespace-report-region) + (whitespace-turn-on, whitespace-color-on): Adjust these functions to + use `indent-tabs-mode' and `tab-width' directly, and not call + `whitespace-ensure-local-variables'. + +2017-04-20 Stefan Monnier + + * lisp/vc/vc-hg.el (vc-hg-state-fast): Fix compiler warning + + by simplifying ascii-test. + +2017-04-20 Vibhav Pant + + bytecomp: Don't inline functions that use byte-switch (Bug#26518) + + * lisp/emacs-lisp/bytecomp.el (byte-compile-unfold-bcf): Don't inline + FORM if the bytecode uses the byte-switch instruction. It is + impossible to guess the correct stack depth while inlining such + bytecode, resulting in faulty code. + +2017-04-20 Nicolas Petton + + Set Emacs version to 25.2 and update AUTHORS file + + * README: Set Emacs version to 25.2. + * etc/HISTORY: Add release log. + * lisp/ldefs-boot.el: + * etc/AUTHORS: + * ChangeLog.2: Update. + +2017-04-20 Noam Postavsky + + Don't register "def" as an autoload prefix (Bug#26412) + + * lisp/emacs-lisp/autoload.el (autoload--make-defs-autoload): Don't + accept "def" as a prefix. + +2017-04-20 Stefan Monnier + + Use substring completion for Info menus and index + + * lisp/info.el (Info-complete-menu-item): Add `category' metadata. + (Info-menu): Simplify now that we use the `default' arg of completing-read. + * lisp/minibuffer.el (completion-category-defaults): Use substring + completion for `info-menu`. + +2017-04-19 Glenn Morris + + Remove some explicit runtime loads of pcase + + Pcase is macros, so these should have used eval-when-compile. + Anyway, pcase entry points are autoloaded, so the compiler handles it. + * lisp/profiler.el, lisp/emacs-lisp/eieio-core.el: + * lisp/emacs-lisp/generator.el, lisp/emacs-lisp/subr-x.el: + * lisp/progmodes/xref.el: No need to require pcase. + +2017-04-19 Glenn Morris + + Stop cl-lib loading pcase at runtime + + The cause was an unexpanded pcase-defmacro in cl-loaddefs. + * lisp/emacs-lisp/autoload.el (make-autoload): + Treat pcase-defmacro like defmacro. + +2017-04-19 Alan Third + + Note frame documentation exceptions for NS builds + + * doc/lispref/frames.texi (Management Parameters, Child Frames): Note + NS differences. + +2017-04-19 Alan Third + + Fix bug introduced by my last commit + + * src/nsterm.m (ns_draw_fringe_bitmap): Revert key-mashing accident. + +2017-04-19 Alan Third + + Add new frame functionality to NS port + + * lisp/frame.el (frame-restack): Call ns-frame-restack. + * src/keyboard.c (kbd_buffer_get_event) [HAVE_NS]: Enable + MOVE_FRAME_EVENT handling. + * src/frame.h: + * src/frame.c: Enable 'z-group', 'undecorated' and 'parent' frame + definitions. + * src/nsfns.m: Add x_set_z_group, x_set_parent_frame and + x_set_undecorated (Cocoa only) to handler struct. + (Fx_create_frame): Handle 'z-group', 'parent-frame' and 'undecorated' + frame parameter. + (Fns_frame_restack): New function. + * src/nsmenu.m (free_frame_tool_bar, update_frame_tool_bar): + FRAME_TOOLBAR_HEIGHT is no longer a variable. + * src/nsterm.h (NS_PARENT_WINDOW_LEFT_POS, NS_PARENT_WINDOW_TOP_POS): + Add #defines to find the screen position of the parent frame. + (NS_TOP_POS): Remove defun. + (EmacsView): Remove redundant toolbar variables and add createToolbar + method. + (FRAME_NS_TITLEBAR_HEIGHT, FRAME_TOOLBAR_HEIGHT): Always calculate the + values instead of storing them in a variable. + * src/nsterm.m (x_set_offset, windowDidMove): Take parent frame + position into account when positioning frames. + (initFrameFromEmacs): Remove toolbar creation code and handle new + frame parameters. + (x_set_window_size): Remove toolbar height calculation. + (x_set_z_group): + (x_set_parent_frame): + (x_set_undecorated) [NS_IMPL_COCOA]: New function. + (x_destroy_window): Detach parent if child closes. + (updateFrameSize): Change NSTRACE message to reflect new reality and + no longer reset frame size. + (windowWillResize): Don’t change NS window name when the titlebar + is invisible. + (createToolbar): Move toolbar creation code into it’s own method. + (toggleFullScreen): FRAME_TOOLBAR_HEIGHT and FRAME_NS_TITLEBAR_HEIGHT + are no longer variables. + (windowDidMove): Fire MOVE_FRAME_EVENT Emacs event. + +2017-04-19 Glenn Morris + + Tweak bytecomp's loading of cl-extra + + * lisp/emacs-lisp/bytecomp.el: Don't force load of cl-extra in a + post-bootstrap emacs where cl-loaddefs does exist. + +2017-04-19 Glenn Morris + + Avoid unnecessary loading of subr-x at run-time + + * lisp/doc-view.el, lisp/filenotify.el, lisp/info-look.el: + * lisp/svg.el, lisp/emacs-lisp/byte-opt.el, lisp/net/shr.el: + * lisp/textmodes/sgml-mode.el, test/lisp/dom-tests.el: + No need to load subr-x at run-time. + * lisp/gnus/nnheader.el: No need to load subr-x. + +2017-04-18 michael schuldt (tiny change) + + Use iteration in math-factorial-iter + + * lisp/calc/calc-comb.el (math-factorial-iter): + Use iteration instead of recursion to avoid max-specpdl-size problem. + +2017-04-18 Glenn Morris + + * test/lisp/kmacro-tests.el: Require seq, for seq-concatenate. + +2017-04-18 Glenn Morris + + Avoid ert test failures + + * lisp/emacs-lisp/ert.el (ert--expand-should-1): + Avoid errors related to undefined byte-compile-macro-environment. + Somehow masked until very recently because loading seq (eg) + loads bytecomp. http://hydra.nixos.org/build/51730765 + +2017-04-18 Eli Zaretskii + + Fix a typo in indexing the user manual + + * doc/emacs/cmdargs.texi (General Variables): Fix a horrible typo. + +2017-04-18 Noam Postavsky + + Fix find-library-name for load-history entries with nil FILE-NAME (Bug#26355) + + * lisp/emacs-lisp/find-func.el (find-library--from-load-history): + Rename from find-library--from-load-path. Check for `load-history' + entries with nil FILE-NAMEs. Simplify by not double + checking for suffixes and making use of `locate-file'. + +2017-04-18 Alan Third + YAMAMOTO Mitsuharu + + Use vfork if possible on Darwin (bug#26397) + + + * src/conf_post.h (HAVE_WORKING_VFORK): Don't undef. + (vfork): Don't define. + * src/process.c (create_process) [DARWIN_OS]: Use fork if pty_flag is + set, otherwise vfork. + * src/callproc.c (call_process) [DARWIN_OS]: Use TIOCNOTTY to detach + the controlling terminal instead of setsid. + +2017-04-18 Fran Litterio + + Small erc-kill-channel fix (bug#23700) + + * lisp/erc/erc.el (erc-kill-channel): Handle null erc-default-target. + +2017-04-18 Glenn Morris + + ediff: use user-error rather than debug-ignored-errors + + * lisp/vc/ediff-diff.el (ediff-prepare-error-list): + * lisp/vc/ediff-help.el (ediff-help-for-quick-help): + * lisp/vc/ediff-init.el (ediff-barf-if-not-control-buffer) + (ediff-check-version): + * lisp/vc/ediff-merg.el (ediff-shrink-window-C): + * lisp/vc/ediff-mult.el (ediff-draw-dir-diffs, ediff-show-dir-diffs) + (ediff-append-custom-diff, ediff-meta-show-patch) + (ediff-filegroup-action, ediff-show-meta-buffer, ediff-show-registry) + (ediff-get-meta-info, ediff-patch-file-form-meta): + * lisp/vc/ediff-ptch.el (ediff-patch-file-internal): + * lisp/vc/ediff-util.el (ediff-toggle-autorefine) + (ediff--check-ancestor-exists, ediff-toggle-read-only) + (ediff-toggle-wide-display, ediff-toggle-multiframe) + (ediff-toggle-use-toolbar, ediff-toggle-show-clashes-only) + (ediff-next-difference, ediff-previous-difference) + (ediff-pop-diff, ediff-read-file-name, ediff-verify-file-buffer) + (ediff-save-buffer): + * lisp/vc/ediff-wind.el (ediff-make-wide-display): + * lisp/vc/ediff.el (ediff-find-file, ediff-buffers-internal) + (ediff-directories-internal, ediff-directory-revisions-internal) + (ediff-regions-wordwise, ediff-regions-linewise) + (ediff-load-version-control): Use user-error. + (debug-ignored-errors): No longer modify. + +2017-04-18 Glenn Morris + + mh-e: use user-error rather than debug-ignored-errors + + * lisp/mh-e/mh-alias.el (mh-alias-grab-from-field): + * lisp/mh-e/mh-utils.el (mh-get-msg-num): Use user-error. + (debug-ignored-errors): No longer modify. + +2017-04-18 Glenn Morris + + ispell.el: use user-error rather than debug-ignored-errors + + * lisp/textmodes/ispell.el (ispell-get-word): Use user-error. + (debug-ignored-errors): No longer modify. + +2017-04-17 Paul Eggert + + * src/xterm.c (x_fill_rectangle): Now static. + +2017-04-17 Paul Eggert + + Tighten recently-added UTF-8 check + + * src/coding.c (encode_coding_utf_8): Now extern. + * src/terminal.c (terminal_glyph_code) [HAVE_STRUCT_UNIPAIR_UNICODE]: + Check for UTF-8, not just for multibyte. + +2017-04-17 David Engster + + xml: Properly handle symbol-qnames for attribute parsing + + * lisp/xml.el (xml-parse-attlist): Do not strip 'symbol-qnames from + xml-ns argument (reverts aea67018) (Bug#26533). + (xml-maybe-do-ns): Properly handle default namespace by not + interning new symbol when 'special' flag is set. + + * tests/lisp/xml-tests.el (xml-parse-test--namespace-attribute-qnames) + (xml-parse-namespace-attribute-qnames): Add test for Bug#26533. + +2017-04-17 Paul Eggert + + * src/lisp.h (STRING_SET_CHARS): Simplify assertion. + +2017-04-17 Eli Zaretskii + + Fix assertion violations when displaying thread-related error + + * src/process.c (Faccept_process_output): Don't assume a thread's + name is always a string. + +2017-04-17 Paul Eggert + + dired ‘M’ should not complain about ‘.’ and ‘..’ + + * lisp/dired-aux.el (dired-do-redisplay): + Allow redisplay of ‘.’ and ‘..’ (Bug#26528). + +2017-04-17 Paul Eggert + + Remove unused coding enums + + * src/coding.h (enum coding_system_type, enum end_of_line_type): + Remove; unused. + +2017-04-17 Paul Eggert + + Work around bug with unibyte Linux consoles + + * src/terminal.c (terminal_glyph_code): Skip the UTF-8 stuff if + the terminal's coding system is unibyte (Bug#26396). + +2017-04-16 Teemu Likonen + + Fix org-agenda's command for calendar-lunar-phases + + Function org-agenda-phases-of-moon tries to call a non-existing + function calendar-phases-of-moon. The correct function is + calendar-lunar-phases. + +2017-04-16 Michael Albinus + + Tuning for `separate' Tramp syntax + + * lisp/net/tramp.el (tramp-method-regexp): Fix it for `separate' syntax. + (tramp-completion-file-name-regexp-separate): Simplify. + + * test/lisp/net/tramp-tests.el (tramp-test02-file-name-dissect-separate): + Extend test. + +2017-04-16 Alan Mackenzie + + Fix bug #26529: C-h k errors with a lambda function bound to a key. + + * lisp/help-fns.el (help-fns--signature, describe-function-1): Check + `function' is a symbol before trying to get property `reader-construct' from + it. + +2017-04-16 Simen Heggestøyl + + Fix highlighting of short selectors in CSS mode + + * lisp/textmodes/css-mode.el (css--font-lock-keywords): Highlight + selectors where the part before a colon is only one character long, + such as `a:hover'. + +2017-04-16 Eli Zaretskii + + Fix redisplay performance problems with some fonts + + * src/font.c (font_list_entities): Revert part of the changes + introduced on Apr 2, 2014 to fix bug#17125. It turns out having + zero_vector in the font-cache is an important indication that + cannot be removed. (Bug#21028) + +2017-04-16 Eli Zaretskii + + Add assertion to STRING_SET_CHARS + + * src/lisp.h (STRING_SET_CHARS): Add an assertion and commentary + to prevent incorrect usage. For details, see this discussion: + http://lists.gnu.org/archive/html/emacs-devel/2017-04/msg00412.html. + +2017-04-16 Eli Zaretskii + + Avoid compilation warnings on MS-Windows + + * src/w32term.c (w32_read_socket): Avoid compiler warnings about + parentheses around assignment. + * src/w32fns.c (w32_createwindow): Remove unused variable + dwStyle. Use "|=" where appropriate. + +2017-04-16 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-04-14 intprops: try to avoid tickling similar bugs + 2017-04-14 intprops: port to Oracle Studio 12.3 x86 + * doc/misc/texinfo.tex, lib/intprops.h: Copy from gnulib. + +2017-04-15 Martin Rudalics + + Fix bugs in `with-displayed-buffer-window' and `fit-window-to-buffer' + + * lisp/window.el (with-displayed-buffer-window): When a + 'window-height' action alist entry specifies a function, call + `temp-buffer-window-show' with a '(window-height . t)' dummy + entry so `window--try-to-split-window' will bind + `window-combination-limit' to t and that function does not + resize any other window but the one we split this one off + (Bug#25055, Bug#25179). + (fit-window-to-buffer): Call `window-max-delta' with NOUP t so + we steal space only from windows in the same combination. + Stealing space from other windows would not allow us to return + that space later when this window is deleted (Bug#25055, + Bug#25179). + +2017-04-15 Glenn Morris + + Avoid userlock queries hanging forever in batch mode + + * lisp/userlock.el (ask-user-about-lock) + (ask-user-about-supersession-threat): Abort in batch mode. + +2017-04-14 Martin Rudalics + + Fix segfault when calling frame_ancestor_p (Bug#26493) + + * src/xterm.c (handle_one_xevent): Check that hf was not reset + before calling frame_ancestor_p (Bug#26493). + +2017-04-14 Martin Rudalics + + A few additional copy-edits in documentation of frames + + * doc/lispref/frames.texi (Frame Layout) + (Implied Frame Resizing): Windows -> MS-Windows. + (Deleting Frames): Fix typo. + +2017-04-14 Glenn Morris + + Use user-error for some ert.el errors + + * lisp/emacs-lisp/ert.el (ert-read-test-name, ert-delete-all-tests) + (ert-results-find-test-at-point-other-window, ert-describe-test): + Use user-error. + +2017-04-14 Glenn Morris + + Use user-error for customize's "invalid face" error + + * lisp/cus-edit.el (customize-face): Use user-error. + (debug-ignored-errors): No more need to add "Invalid face". + +2017-04-14 Glenn Morris + + Remove duplicate lisp-eval-defun definition + + * lisp/emacs-lisp/lisp-mode.el (lisp-eval-defun): + Autoload rather than defining a stub. + +2017-04-14 Glenn Morris + + * lisp/Makefile.in (check-defun-dups): Ignore obsolete files. + +2017-04-14 Glenn Morris + + Create generated lisp files before main loaddefs.el + + This should improve reproducibility of lisp/loaddefs.el. + * lisp/Makefile.in (gen-lisp): New phony target. + ($(lisp)/loaddefs.el, compile-main): Depend on gen-lisp. + * src/Makefile.in ($(leimdir)/leim-list.el): Depend on all of ../leim. + * lisp/cedet/semantic.el (semantic-mode): + * lisp/cedet/semantic/fw.el (top-level): + * lisp/emacs-lisp/eieio-core.el (top-level): + Robustify to generated input files maybe not yet existing. + +2017-04-14 Paul Eggert + + Fix minor quoting issues in Makefile.in + + * Makefile.in (install-arch-dep, uninstall): + Quote EMACS and EMACS_NAME more consistently. + +2017-04-13 Glenn Morris + + * Makefile.in (install-etc): Use existing Makefile variables. + +2017-04-13 Eli Zaretskii + + Minor copyedits of recent changes in documentation + + * doc/lispref/frames.texi (Frame Layout, Frame Position) + (Frame Size, Frame Interaction Parameters, Input Focus) + (Raising and Lowering, Child Frames): Improve wording and indexing. + * doc/emacs/cmdargs.texi (Borders X): Improve indexing. + +2017-04-13 Glenn Morris + + Small src/Makefile simplification + + * src/Makefile.in ($(lispsource)/international/ucs-normalize.elc) + ($(lispsource)/term/ns-win.elc): Combine rules. + +2017-04-13 Simen Heggestøyl + + Add grid layout module to CSS property list + + * lisp/textmodes/css-mode.el (css-property-alist) + (css-value-class-alist): Add new properties and value classes from CSS + Grid Layout Module. + +2017-04-13 Martin Rudalics + + Describe recent frame and window changes in manuals + + * doc/emacs/emacs.texi (Top): + * doc/emacs/cmdargs.texi (Borders X): Clearly separate the terms + "outer border" (for the X border which can be set from within + Emacs) and "external border" (for the border which is added by + the window manager). + * doc/lispref/display.texi (Tooltips): Clarify slightly. + * doc/lispref/elisp.texi (Top): Update node and section names. + * doc/lispref/frames.texi (Frames): Describe difference between + top-level and child frames. + (Frame Layout): Describe outer border. Add more details about + how Emacs obtains the outer size and position of a frame and + about menu bar/tool bar wrapping. Add references to new frame + parameters. + (Size and Position): Remove subsection. + (Frame Position): New subsection excerpted from the earlier Size + and Position subsection. Clarify positioning concepts and + some of their shortcomings. Describe `move-frame-functions'. + (Frame Size): New subsection excerpted from the earlier Size + and Position subsection. Describe how to track frame size + changes and the new function `frame-size-changed-p'. + (Position Parameters): Describe child frame positioning. Warn + about negative offsets. Describe 'z-group' parameter. + (Size Parameters): Describe 'text-pixels' specification + facility and new 'min-width' and 'min-height' parameters. + (Layout Parameters): Clarify description of 'tool-bar-lines' and + 'menu-bar-lines' parameters. + (Frame Interaction Parameters): New subsubsection describing + 'parent-frame', 'delete-before', 'mouse-wheel-frame' and + 'no-other-frame' parameters. + (Management Parameters): Describe 'skip-taskbar', + 'no-focus-on-map', 'no-accept-focus', 'undecorated' and + 'override-redirect' parameters. + (Deleting Frames): Describe handling of 'delete-before' + parameter and child frames for `delete-frame' and + `delete-other-frames'. + (Finding All Frames): Describe `frame-list-z-order' and handling + of 'no-other-frame' parameter by `next-frame'. + (Minibuffers and Frames): Minor clarifications. + (Input Focus): Document `x-focus-frame'. Clarify descriptions + of `focus-in-hook', `focus-out-hook' and `focus-follows-mouse'. + (Visibility of Frames): Describe mapping and how the visibility + of a parent frame affects that of its child frames. + (Raising and Lowering): Describe restacking of frames and + z-groups. + (Child Frames): New section. + * doc/lispref/windows.texi (Selecting Windows): Describe + additional semantics of NORECORD argument of `select-window' and + how `buffer-list-update-hook' can emulate a "select window + hook". + (Mouse Window Auto-selection): New section. + +2017-04-13 Damien Cassou + + Fix imenu--sort-by-position for non-pairs parameters (bug#26457) + + * lisp/imenu.el (imenu--sort-by-position): Fix to accept lists beyond + pairs. + * test/lisp/imenu-tests.el: Add 2 tests for `imenu--sort-by-position`. + +2017-04-13 Eli Zaretskii + + Avoid unnecessary regeneration of the entire loaddefs.el + + * lisp/Makefile.in (autoloads .PHONY): Add commentary explaining + why $(lisp)/loaddefs.el is a dependency of '.PHONY'. + ($(lisp)/loaddefs.el): Copy an existing loaddefs.el to + loaddefs.tmp before running 'batch-update-autoloads' on it, to + avoid slow regeneration of the full contents. (Bug#26459) + Use 'move-if-change' instead of 'mv', to avoid producing a new + Emacs binary when not necessary. + +2017-04-13 Dmitry Gutov + + Handle indentation of nested ternary operators in JS + + * lisp/progmodes/js.el (js--looking-at-operator-p): + Handle nested ternary operators. + +2017-04-12 Eli Zaretskii + + Don't call 'kill-this-buffer' outside of menus + + * lisp/simple.el (kill-current-buffer): New function. + (completion-list-mode-map): Use it instead of kill-this-buffer. + * lisp/type-break.el (type-break-mode): + * lisp/term/ns-win.el (global-map): + * lisp/progmodes/gdb-mi.el (gdb-memory-mode-map) + (gdb-disassembly-mode-map, gdb-frames-mode-map) + (gdb-locals-mode-map, gdb-registers-mode-map): + * lisp/org/org-mhe.el (org-mhe-follow-link): + * lisp/net/secrets.el (secrets-mode-map): + * lisp/net/eudc.el (eudc-mode-map): + * lisp/net/eudc-hotlist.el (eudc-hotlist-mode-map): Use + kill-current-buffer instead of kill-this-buffer. (Bug#26466) + +2017-04-12 Eli Zaretskii + + * lisp/menu-bar.el (kill-this-buffer): Doc fix. (Bug#26466) + +2017-04-12 Martin Rudalics + + New internal-border face and args for select-window and x-focus-frame + + Add `internal-border' face and handle it whenever clearing the + internal border. If NORECORD equals the symbol + 'mark-for-redisplay', `select-window' will not record the window + but still mark it for redisplay. The new argument NOACTIVATE + for `x-focus-frame' tries to not activate FRAME when set. + + * lisp/faces.el (internal-border): New face. + * lisp/mwheel.el (mwheel-scroll): Select window to scroll with + `mark-for-redisplay'. + * lisp/scroll-bar.el (scroll-bar-drag) + (scroll-bar-horizontal-drag, scroll-bar-scroll-down) + (scroll-bar-scroll-up, scroll-bar-toolkit-scroll) + (scroll-bar-toolkit-horizontal-scroll): Select window to scroll + with `mark-for-redisplay'. + * lisp/window.el (handle-select-window): When + `focus-follows-mouse' is not 'auto-raise' try to not activate + FRAME. + * src/dispextern.h (face_id): Add INTERNAL_BORDER_FACE_ID. + * src/frame.c (Fx_focus_frame): New argument NOACTIVATE. + * src/frame.h (x_focus_frame): Update extern declaration. + * src/gtkutil.c (xg_clear_under_internal_border): Remove + function. + (xg_frame_resized, xg_frame_set_char_size): Call + x_clear_under_internal_border. + (xg_tool_bar_callback): Adapt x_focus_frame call. + * src/gtkutil.h (xg_clear_under_internal_border): Remove + declaration. + * src/nsfns.m (x_focus_frame): Add argument NOACTIVATE. + * src/w32fns.c (x_clear_under_internal_border): Fill border + with internal-border background if specified. + * src/w32term.h (x_clear_under_internal_border): Add extern + declaration. + * src/w32term.c (x_after_update_window_line): Fill border + with internal-border background if specified. + (w32_set_vertical_scroll_bar, w32_set_horizontal_scroll_bar) + (x_scroll_bar_clear, w32_read_socket): Call + x_clear_under_internal_border. + (x_focus_frame): New argument NOACTIVATE. + * src/window.c (select_window): Mark WINDOW for redisplay when + NORECORD equals 'mark-for-redisplay'. + (Fselect_window): Update doc-string. + (syms_of_window): Define Qmark_for_redisplay. + * src/xdisp.c (clear_garbaged_frames, echo_area_display) + (redisplay_internal): Call x_clear_under_internal_border. + * src/xfaces.c (lookup_basic_face): Handle `window-divider' + and `internal-border' faces. + (realize_basic_faces): Realize `internal-border' face. + (syms_of_xfaces): Define Qinternal_border. + * src/xfns.c (x_set_internal_border_width): Remove call for + xg_clear_under_internal_border. + (x_focus_frame): New argument NOACTIVATE. When non-nil try to not + activate frame. + * src/xterm.c (x_fill_rectangle): No more static. + (x_clear_under_internal_border, x_after_update_window_line): + Fill border with internal-border background if specified. + (xt_horizontal_action_hook): Rewrite. + (handle_one_xevent): Call x_clear_under_internal_border. + * src/xterm.h (x_fill_rectangle): Add extern declaration. + +2017-04-12 Paul Eggert + + Port recent frame changes to --enable-gcc-warnings + + * src/frame.c (next_frame, prev_frame): + Remove now-redundant assertions. + * src/frame.h (FOR_EACH_FRAME): Assume Vframe_list is nonempty. + +2017-04-12 Tak Kunihiro + + Scroll right and left using wheel-right and wheel-left. + + These changes also make use of touchpad and trackpad (Bug#26347). + + * doc/emacs/frames.texi (Mouse Commands): Document horizontal + scrolling using the mouse wheel. + + * lisp/mwheel.el (mwheel-scroll): Respond to wheel-right and wheel-left. + (mwheel-tilt-scroll-p, mwheel-flip-direction) + (mwheel-scroll-left-function, mwheel-scroll-right-function): New + defcustoms. + (mouse-wheel-left-event, mouse-wheel-right-event): New variables, + events that calls wheel-left/right. + + * etc/NEWS: Mention horizontal scrolling using the mouse wheel. + +2017-04-12 Eli Zaretskii + + * lisp/Makefile.in (autoloads-force): Fix usage of ".PHONY". + +2017-04-12 Martin Rudalics + + Add new frame parameters and associated functions + + Add new frame parameters `undecorated', `override-redirect', + `parent-frame', `skip-taskbar', `no-focus-on-map', + `no-accept-focus', `z-group', `delete-before', `no-other-frame', + `mouse-wheel-frame', `min-width', `min-height'. Add new + functions `frame-restack' and `frame-list-z-order'. + + * lisp/cus-start.el (focus-follows-mouse): Adapt customization + type. + * lisp/frame.el (handle-delete-frame): Handle child and + `delete-before' frames. + (other-frame): Stop looking for other frame after one round. + (frame-list-z-order, frame-restack): New functions. + (delete-other-frames): Handle child frames. + * lisp/frameset.el (frameset-persistent-filter-alist) + (frameset--record-relationships): Handle `delete-before', + `parent-frame' and `mouse-wheel-frame' parameters. Rename + latter from `frameset--record-minibuffer-relationships'. + (frameset--restore-frame): Handle ‘parent-frame’ parameter + specially. + (frameset-restore): Handle `delete-before', `parent-frame' and + `mouse-wheel-frame' parameters. + * lisp/mwheel.el (mwheel-scroll): Handle `mouse-wheel-frame' + parameter. + * lisp/window.el (window--min-size-ignore-p): Fix doc-string. + (mouse-autoselect-window-select, handle-select-window): Major + rewrite. Try to not ignore errors. Handle auto-selection of + child frames and different values of `focus-follows-mouse'. + * src/frame.c (frame_windows_min_size): Handle new `min-width' + and `min-height' frame parameters. + (make_frame): Initialize new frame structure members. + (do_switch_frame): Don't reset internal_last_event_frame for + descendant frames. + (Fframe_parent, frame_ancestor_p, Fframe_ancestor_p): New + functions. + (candidate_frame): Don't return `no-other-frame' frame. + (other_frames): New function replacing other_visible_frames. + (delete_frame): Rewrite. Handle child and `delete-before' frames. + (Fmake_frame_invisible): Call other_frames. + (store_frame_param): Check `delete-before' and `parent-frame' + parameters for circular dependencies. + (frame_parms, syms_of_frame): Add entries for and define new + frame parameters. + (focus_follows_mouse): New meaningful value `auto-raise'. + * src/frame.h (z_group): New enumeration type. + (frame): New slots parent_frame, undecorated, override_redirect, + skip_taskbar, no_focus_on_map, no_accept_focus, z_group. + (fset_parent_frame): New inlined function. + (FRAME_UNDECORATED, FRAME_OVERRIDE_REDIRECT) + (FRAME_PARENT_FRAME, FRAME_SKIP_TASKBAR, FRAME_NO_FOCUS_ON_MAP) + (FRAME_NO_ACCEPT_FOCUS, FRAME_Z_GROUP, FRAME_Z_GROUP_NONE) + (FRAME_Z_GROUP_ABOVE, FRAME_Z_GROUP_ABOVE_SUSPENDED) + (FRAME_Z_GROUP_BELOW): New macros. + (frame_ancestor_p): Add declaration. + * src/gtkutil.c (xg_create_frame_widgets): Handle + `undecorated' and `override-redirect' frame parameters. + (x_wm_set_size_hint): None for child frames. + (xg_set_undecorated, xg_frame_restack, xg_set_skip_taskbar) + (xg_set_no_focus_on_map, xg_set_no_accept_focus) + (xg_set_override_redirect): New functions. + (xg_update_scrollbar_pos, xg_update_horizontal_scrollbar_pos): + Don't let scrollbars obscure child frames. + * src/gtkutil.h: (xg_set_undecorated, xg_frame_restack) + (xg_set_skip_taskbar, xg_set_no_focus_on_map) + (xg_set_no_accept_focus, xg_set_override_redirect): Add extern + declarations. + * src/nsfns.m (ns_frame_parm_handlers): Add entries for new + frame parameters. + (Fx_create_frame): Install `min-width' and `min-height' frame + parameters. + * src/nsterm.m (mouseMoved:): Handle focus_follows_mouse change. + * src/w32fns.c (WS_EX_NOACTIVATE): Define if necessary. + (x_real_positions): Handle child frames. + (x_set_menu_bar_lines): Don't for child frames. + (x_set_undecorated, x_set_parent_frame, x_set_skip_taskbar) + (x_set_no_focus_on_map, x_set_no_accept_focus) + (x_set_z_group): New functions. + (w32_createvscrollbar, w32_createhscrollbar): Don't draw + scroll bars over child frames. + (w32_createwindow): Handle new frame parameters and child frames. + (w32_wnd_proc): Let mouse clicks into a child frame activate + the frame. Try to handle the `no-accept-focus' parameter. Do + SetFocus when our window is brought to top or becomes the + foreground window. + (w32_window): Don't initialize menu bar for child frames. + (Fx_create_frame): Handle new frame parameters. + (x_create_tip_frame): Set explicit_parent slot. + (w32_dialog_in_progress): New function. + (Fx_file_dialog): Handle `z-group-above' frames. + (w32_frame_list_z_order, Fw32_frame_list_z_order) + (w32_frame_restack, Fw32_frame_restack): New functions. + (w32_frame_parm_handlers): Add entries for new frame + parameters. + * src/w32font.c (Fx_select_font): Handle `z-group-above' + frames during font selection dialogue. + * src/w32term.c (construct_mouse_wheel): Construct mouse wheel + event from F's w32 window. + (w32_mouse_position): Handle child frames. + (w32_set_vertical_scroll_bar, w32_set_horizontal_scroll_bar): + Don't draw scroll bars over child frames. + (w32_read_socket): Always erase background of child frames. + When generating SELECT_WINDOW_EVENTs handle new value of + `focus-follows-mouse' and handle `no-accept-focus' parameter. + Handle `mouse-wheel-frame' parameter. + (x_calc_absolute_position, x_set_offset, x_set_window_size): + Handle child frames. + (x_make_frame_visible): Handle child frames specially. Handle + `no-focus-on-map' parameter. + * src/w32term.h (w32_dialog_in_progress): Add external + declaration. + * src/xdisp.c (x_consider_frame_title, prepare_menu_bars): Not + for child frames. + * src/xfns.c (Xm/MwmUtil.h): Include for WM hints. + (PropMotifWmHints, PROP_MOTIF_WM_HINTS_ELEMENTS): Define for + non-Motif, non-GTK case. + (x_real_pos_and_offsets): Handle child frames. + (x_set_undecorated, x_set_parent_frame) + (x_set_no_focus_on_map, x_set_no_accept_focus) + (x_set_override_redirect): New functions. + (x_set_menu_bar_lines): Not for child frames. + (x_window): Handle `undecorated' and `override_redirect' cases. + (Fx_create_frame): Handle new frame parameters. + (frame_geometry): Handle child frames and outer border. + (x_frame_list_z_order, Fx_frame_list_z_order) + (x_frame_restack, Fx_frame_restack): New functions. + (Fx_file_dialog, Fx_select_font): Set x_menu_set_in_use. + (x_frame_parm_handlers): Add entries for new frame parameters. + * src/xmenu.c (x_menu_set_in_use): Handle `z-group-above' + frames. + * src/xterm.c (x_set_frame_alpha): Don't set alpha of parent + for child frames. + (XTmouse_position): Handle child frames. + (x_scroll_bar_create, x_scroll_bar_expose): Don't let scroll + bars obscure child frames. + (handle_one_xevent): Handle child frame positions. If necessary + set `skip-taskbar' and reassign proper `z-group' when we are + mapped. When generating SELECT_WINDOW_EVENTs handle new value + of `focus-follows-mouse'. Handle `mouse-wheel-frame' parameter. + Let mouse clicks into a child frame activate the frame. + (x_calc_absolute_position, x_set_offset): Handle child frames + specially. + (x_set_skip_taskbar, x_set_z_group): New functions. + (x_make_frame_visible): Handle child frames. + (ATOM_REFS_INIT): Add entries for + Xatom_net_wm_state_skip_taskbar, Xatom_net_wm_state_above, + Xatom_net_wm_state_below. + * src/xterm.h (top-level): Declare Xatom_net_wm_state_above, + Xatom_net_wm_state_below and Xatom_net_wm_state_skip_taskbar. + (x_set_skip_taskbar, x_set_z_group): Add extern declarations. + +2017-04-11 Glenn Morris + + Update a package test for hydra + + * test/lisp/emacs-lisp/package-tests.el (with-package-test): + Also bind package-gnupghome-dir, see eg + http://hydra.nixos.org/build/51462182 . + +2017-04-11 Martin Rudalics + + Frame movement, focus and hook related changes + + New hook `move-frame-functions'. Run `focus-in-hook' + after switching to frame that gets focus. Don't run + XMoveWindow for GTK. + + * lisp/frame.el (handle-move-frame, frame-size-changed-p): New + functions. + + * src/frame.c (do_switch_frame): Simplify code. + (Fhandle_switch_frame): Switch frame before running + `handle-focus-in'. + (Vfocus_in_hook, Vfocus_out_hook): Clarify doc-strings. + (Vmove_frame_functions): New hook variable. + * src/keyboard.c (kbd_buffer_get_event): Handle + MOVE_FRAME_EVENT. Handle SELECT_WINDOW_EVENT separately. + (head_table): Add Qmove_frame entry. + (syms_of_keyboard): Add Qmove_frame. + (keys_of_keyboard): Define key for `move-frame'. + * src/termhooks.h (event_kind): Add MOVE_FRAME_EVENT. + * src/w32term.c (w32_read_socket): Create MOVE_FRAME_EVENT. + * src/window.c (run_window_size_change_functions): Record size of + FRAME's minibuffer window too. + * src/xterm.c (handle_one_xevent): Create MOVE_FRAME_EVENT. + (x_set_offset): For GTK call gtk_widget_move instead of + XMoveWindow. + +2017-04-11 Werner LEMBERG + + Avoid abort in ftfont.c due to faulty fonts + + * src/ftfont.c (ftfont_get_metrics): Try loading the font without + hinting, before aborting. (Bug#25945) + +2017-04-11 Eli Zaretskii + + Document 'line-pixel-height' + + * doc/lispref/display.texi (Size of Displayed Text): Document + line-pixel-height. Suggested by Tak Kunihiro + . (Bug#26379) + +2017-04-11 Jens Lechtenboerger + + Introduce customizable variable 'package-gnupghome-dir' + + * lisp/emacs-lisp/package.el (package-import-keyring) + (package--check-signature-content, package-check-signature): + Use new variable package-gnupghome-dir to control which GnuPG + homedir to use. + * doc/emacs/package.texi: Mention package-gnupghome-dir. + * etc/NEWS: Mention package-gnupghome-dir. + +2017-04-11 Martin Rudalics + + Set x_gtk_use_window_move by default for fixing bug#25851 and bug#25943 + + This activates a change that was installed a few weeks ago but whose + ChangeLog was inadvertently dropped during its commit. The proper + ChangeLog is included below as part of the present commit. + + * src/gtkutil.c (xg_set_geometry): When x_gtk_use_window_move + is set avoid calling x_gtk_parse_geometry (Bug#25851). + (x_wm_set_size_hint): When x_gtk_use_window_move is set, set + PPosition, USPosition and USSize flags if requested. + * src/xterm.c (x_set_offset): With GTK when + x_gtk_use_window_move is set, leave it entirely to + gtk_window_move to position the window and skip any + post-adjustments (Bug#25851 and Bug#25943). + (x_gtk_use_window_move): New variable. + +2017-04-10 Alan Mackenzie + + Fix a loop in C Mode caused by inadequate analysis of comments. + + After M-;, and the insertion of the opening "/*", the CC Mode after-change + function got confused, since the new comment opener matched the end of a + subsequent comment, but moving back over that comment did not come back to the + starting point. Fix this. + + * lisp/progmodes/cc-engine.el (c-end-of-macro): Add a limit parameter, wherer + point is left if no end-of-macro is found before it. + (c-forward-sws): Change the `safe-start' mechanism. Now `safe-start' is + non-nil except where we have an unclosed block comment at the end of a macro. + This enables us to populate the cache more fully, at the cost of some run + time. + +2017-04-10 Lars Brinkhoff + + Add PVSIZE function to return the size of a pseudovector. + + * src/lisp.h (PVSIZE): New function. + + * src/chartab.c (copy_char_table): + * src/data.c (Ftype_of, Finteractive_form, Faref, Faset): + * src/doc.c (Fdocumentation, store_function_docstring): + * src/eval.c (Fcommandp, funcall_lambda, lambda_arity, Ffetch_bytecode): + * src/fns.c (Flength, Fcopy_sequence): + * src/font.h (FONT_SPEC_P, FONT_ENTITY_P, FONT_OBJECT_P): + * src/lread.c (substitute_object_recurse): + * src/src/print.c (print_object): + Use it. + +2017-04-10 Michael Albinus + + Add Tramp tests + + * lisp/net/tramp.el (tramp-syntax): Adapt docstring. + + * test/lisp/net/tramp-tests.el + (tramp-test01-file-name-syntax-simplified) + (tramp-test01-file-name-syntax-separate) + (tramp-test02-file-name-dissect-simplified) + (tramp-test02-file-name-dissect-separate): New tests. + +2017-04-10 Martin Rudalics + + Make sure that `shell' makes BUFFER current + + * lisp/shell.el (shell): Restrict scope of recently added + `with-current-buffer' to make sure that BUFFER is current when + `shell' returns. + +2017-04-10 Jim Blandy + + Default to PCRE syntax when reading .hgignore + + * lisp/vc/vc-hg.el (vc-hg--slurp-hgignore-1): + Default to the PCRE syntax (bug#26249). + +2017-04-09 Michael Albinus + + Document Tramp changes + + * doc/misc/tramp.texi (Change file name syntax): New node. + + * etc/NEWS: Mention `tramp-change-syntax'. + + * lisp/net/tramp.el (tramp-file-name-regexp): Reinsert it. + External packages uses it. + (tramp-syntax): Set also `tramp-file-name-regexp'. + +2017-04-09 Paul Eggert + + Merge from gnulib (Bug#26398) + + This incorporates: + 2017-04-08 getopt: prefer - to _ in new file names + 2017-04-08 getopt: port recent getopt changes to macOS + * .gitignore: Add lib/getopt-cdefs.h. + * lib/getopt-cdefs.in.h: Rename from lib/getopt_cdefs.in.h. + * lib/getopt-core.h: Rename from lib/getopt_core.h. + * lib/getopt-ext.h: Rename from lib/getopt_ext.h. + * lib/getopt-pfx-core.h: Rename from lib/getopt_pfx_core.h. + * lib/getopt-pfx-ext.h: Rename from lib/getopt_pfx_ext.h. + * lib/getopt.in.h, lib/unistd.in.h, m4/getopt.m4: + Copy from Gnulib. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + +2017-04-09 Ken Raeburn + + Write updated loaddefs to a temporary file and rename into place. + + In a parallel build, byte compilation can be running at the same times + as loaddefs.el is being regenerated. However, in a CANNOT_DUMP build, + loaddefs.el is read at startup and must always be in a usable state. + + * lisp/Makefile.in ($(lisp)/loaddefs.el): Write generated output to + loaddefs.el.new and then rename it to loaddefs.el. + +2017-04-09 Glenn Morris + + In the manual, mention pops and imaps + + * doc/emacs/rmail.texi (Movemail, Remote Mailboxes): + Mention pops and imaps protocols. + +2017-04-09 Glenn Morris + + * doc/emacs/rmail.texi: Prefer @command to @code for movemail. + +2017-04-09 Sergey Poznyakoff + + Fix rmail handling of movemail protocols (bug#18278) + + * lisp/mail/rmail.el (rmail-remote-proto-p): New function. + (rmail-parse-url): Return protocol in second list element. + Only use passwords with remote mailboxes. + (rmail-insert-inbox-text): Handle non-simple local + mailboxes (maildir, MH, etc.). + +2017-04-09 Glenn Morris + + Fix typos in manual re movemail local mailboxes + + * doc/emacs/rmail.texi (Movemail, Other Mailbox Formats): + Fix examples of local mailbox urls. + +2017-04-08 Glenn Morris + + * lisp/gnus/nnmail.el (nnmail-crosspost-link-function): Simplify. + +2017-04-08 Glenn Morris + + Remove references to OS/2 in code, doc, and comments + + * lisp/gnus/nnheader.el (nnheader-read-timeout) + (nnheader-file-name-translation-alist): Remove OS/2 case, and simplify. + * lisp/emulation/viper-util.el (viper-color-defined-p): + * lisp/net/pop3.el (pop3-read-timeout): + * lisp/net/imap.el (imap-read-timeout): + * lisp/url/url-privacy.el (url-setup-privacy-info): Remove OS/2 case. + * lisp/emulation/viper-ex.el (viper-glob-function): + * lisp/vc/ediff-util.el (ediff-submit-report): Doc fix. + * lisp/cus-edit.el (custom-display): Remove "pm" (OS/2). + * doc/emacs/msdos-xtra.texi (MS-DOS): + * doc/misc/gnus.texi (Various Various): + * doc/misc/viper.texi (Rudimentary Changes): Remove mentions of OS/2. + +2017-04-08 Michael Albinus + + Tune Tramp syntax + + * lisp/net/tramp-cmds.el (tramp-change-syntax): + Use `tramp-syntax-values'. + + * lisp/net/tramp-compat.el (tramp-compat-tramp-syntax): New defsubst. + + * lisp/net/tramp.el (tramp-syntax): Rename possible values. + (tramp-syntax-values): New defun. + (tramp-prefix-format, tramp-method-regexp) + (tramp-postfix-method-format, tramp-prefix-ipv6-format) + (tramp-postfix-ipv6-format, tramp-postfix-host-format) + (tramp-completion-file-name-regexp): Use `tramp-compat-tramp-syntax' + and changed values. + (tramp-completion-file-name-regexp-default): Rename from + `tramp-completion-file-name-regexp-unified'. Adapt docstring. + (tramp-completion-file-name-regexp-simplified): Rename from + `tramp-completion-file-name-regexp-old-style'. Adapt docstring. + (tramp-initial-completion-file-name-regexp): + Use `tramp-completion-file-name-regexp-default'. + (tramp-run-real-handler): Do not autoload any longer. + +2017-04-08 Mark Oteiza + + Replace more nested ifs with cond + + This is a continuation of d526047 "Replace more nested ifs with cond". + * lisp/play/dunnet.el (dun-firstword, dun-firstwordl, dun-cat): Use + when and cond where appropriate. + +2017-04-08 Mark Oteiza + + Adjust the edebug spec of if-let* + + This was fixed in Bug#24748, but now looking more closely, using gate in + the spec seems correct. See (info "(elisp) Backtracking"). + * lisp/emacs-lisp/subr-x.el (if-let*): Use gate in edebug spec. + +2017-04-08 Mark Oteiza + + Replace some uses of cl-member-if with apply + + From the mhtml-mode series. Some of the uses of cl-lib are not + necessary. + * lisp/align.el: Don't require cl-lib. + (align-region): Use apply instead of cl-member-if. + * lisp/emulation/viper.el: Don't require cl-lib. + (viper-mode, this-major-mode-requires-vi-state): Use apply instead of + cl-member-if. + +2017-04-08 Philipp Stephani + + Validate SPEC of `dolist', cf. Bug#25477. + + * lisp/subr.el (dolist): Test type and length of SPEC. + * test/lisp/subr-tests.el (subr-tests--dolist--wrong-number-of-args): + Add unit test. + +2017-04-08 Philipp Stephani + + Add unit test for Bug#26378 + + * test/lisp/vc/ediff-diff-tests.el + (ediff-diff-tests--ediff-exec-process--nil): New unit test. + +2017-04-08 Lars Brinkhoff + + Fix circular read syntax for records. + + * lread.c (substitute_object_recurse): Work with records. + + * lread-tests.el (lread-record-1): New test. + +2017-04-08 Paul Eggert + + Deprecate copy-record in favor of copy-sequence + + Since copy-sequence seems to be needed anyway for records, have it + work on records, and remove copy-record as being superfluous. + * doc/lispref/records.texi (Records, Record Functions): + * lisp/emacs-lisp/cl-macs.el (cl-defstruct): + * lisp/emacs-lisp/eieio.el (make-instance, clone): + * test/src/alloc-tests.el (record-3): + Use copy-sequence, not copy-record, to copy records. + * doc/lispref/sequences.texi (Sequence Functions) + (Array Functions): Document that aref and copy-sequence + work on records. + * etc/NEWS: Omit copy-record. + * src/alloc.c (Fcopy_record): Remove. + * src/data.c (Faref): Document that arg can be a record. + * src/fns.c (Fcopy_sequence): Copy records, too. + +2017-04-08 Paul Eggert + + Fix dependency checking in src/Makefile.in + + * src/Makefile.in (AUTO_DEPEND, DEPDIR, DEPFLAGS): Move includes of + dependency files until after ALLOBJS is defined, since it uses ALLOBJS. + Otherwise, some dependencies will be missed. + +2017-04-08 Paul Eggert + + Minor tuneup of write-region change + + * src/fileio.c (write_region): Use SCHARS, not Flength, + on a value known to be a string. + +2017-04-08 Noam Postavsky + + Adjust write-region so file name is at the beginning again + + * lisp/epa-file.el (epa-file-write-region): + * lisp/gnus/mm-util.el (mm-append-to-file): + * lisp/jka-compr.el (jka-compr-write-region): + * lisp/net/ange-ftp.el (ange-ftp-write-region): + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-write-region): + * lisp/net/tramp-sh.el (tramp-sh-handle-write-region): + * src/fileio.c (write_region): Put file name at the beginning and move + number of characters to the end of the message. + +2017-04-08 Kaushal Modi + + Check that file argument is a string + + * lisp/vc/ediff-diff.el (ediff-exec-process): Check that the argument + passed to `file-local-copy' is a string (Bug#26378). Also fix + the existing comment for this function, and convert it to its + doc-string. + +2017-04-08 Noam Postavsky + + Fix handling of non-integer START param to write-region + + The previous patch for Bug#354 incorrectly assumed that START would + always be an integer. + + * lisp/epa-file.el (epa-file-write-region): + * lisp/jka-compr.el (jka-compr-write-region): + * lisp/net/ange-ftp.el (ange-ftp-write-region): + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-write-region): + * lisp/net/tramp-sh.el (tramp-sh-handle-write-region): + * src/fileio.c (write_region): Handle nil and string values of START. + +2017-04-07 Glenn Morris + + * lisp/textmodes/rst.el (rst-package-emacs-version-alist): Fixes. + +2017-04-07 Michael Albinus + + Add Tramp versions to `customize-package-emacs-version-alist' + + * lisp/net/trampver.el (customize-package-emacs-version-alist): + Add Tramp versions to `customize-package-emacs-version-alist'. + +2017-04-07 Tom Tromey + + * lisp/textmodes/rst.el (rst-toc-link-keymap): Move before first use. + +2017-04-07 Lars Brinkhoff + + * records.texi (Record Functions): fix typo. + +2017-04-07 Paul Eggert + + More casefiddle minor fixes + + * src/casefiddle.c (case_character_impl): Omit unnecessary casts. + (case_character_impl): Avoid reevaluation of CHAR_TABLE_REF. + (GREEK_CAPITAL_LETTER_SIGMA): Fix typo in my previous change. + +2017-04-07 Jeff Clough + + Output number of characters added to file (Bug#354) + + * fileio.c (write_region): + * epa-file.el (epa-file-write-region): + * jka-compr.el (jka-compr-write-region): + * ange-ftp.el (ange-ftp-write-region): + * tramp-gvfs.el (tramp-gvfs-handle-write-region): + * tramp-sh.el (tramp-sh-handle-write-region): + * mm-util.el (mm-append-to-file): Functions now output + characters written in addition to file name. + * files.texi: Added documentation to write-region and + append-to-file describing their output. + +2017-04-07 Noam Postavsky + + Fix ‘!NILP (Vpurify_flag)’ assertion failure during temacs bootstrap + + The recent changes to src/casefiddle.c cause build failure as seen + below: + + Starting program: /home/npostavs/src/emacs/emacs-bootstrapping/src/temacs + --batch --load loadup bootstrap + [Thread debugging using libthread_db enabled] + Using host libthread_db library "/usr/lib/libthread_db.so.1". + Loading loadup.el (source)... + Using load-path (/home/npostavs/src/emacs/emacs-bootstrapping/lisp + /home/npostavs/src/emacs/emacs-bootstrapping/lisp/emacs-lisp + /home/npostavs/src/emacs/emacs-bootstrapping/lisp/language + /home/npostavs/src/emacs/emacs-bootstrapping/lisp/international + /home/npostavs/src/emacs/emacs-bootstrapping/lisp/textmodes + /home/npostavs/src/emacs/emacs-bootstrapping/lisp/vc) + Loading emacs-lisp/byte-run (source)... + Loading emacs-lisp/backquote (source)... + Loading subr (source)... + Loading version (source)... + Loading widget (source)... + Loading custom (source)... + Loading emacs-lisp/map-ynp (source)... + Loading international/mule (source)... + Loading international/mule-conf (source)... + + lread.c:3914: Emacs fatal error: assertion failed: !NILP (Vpurify_flag) + + Breakpoint 1, terminate_due_to_signal at emacs.c:363 + 363 signal (sig, SIG_DFL); + (gdb) bt + #0 0x0000000000579826 in terminate_due_to_signal at emacs.c:363 + #1 0x000000000060ec33 in die at alloc.c:7352 + #2 0x000000000066db40 in intern_c_string_1 at lread.c:3914 + #3 0x0000000000576884 in intern_c_string at lisp.h:3790 + #4 0x00000000005dc84f in prepare_casing_context at casefiddle.c:69 + #5 0x00000000005dd37f in casify_object at casefiddle.c:311 + #6 0x00000000005dd47f in Fcapitalize at casefiddle.c:356 + #7 0x00000000006325ac in eval_sub at eval.c:2219 + #8 0x0000000000632368 in eval_sub at eval.c:2184 + #9 0x000000000063446c in apply_lambda at eval.c:2875 + #10 0x00000000006329af in eval_sub at eval.c:2294 + #11 0x000000000062d462 in Fprogn at eval.c:449 + #12 0x000000000062d4cf in prog_ignore at eval.c:461 + #13 0x000000000062f19c in Fwhile at eval.c:982 + #14 0x00000000006321f4 in eval_sub at eval.c:2172 + #15 0x000000000062d462 in Fprogn at eval.c:449 + #16 0x000000000062f0c4 in Flet at eval.c:963 + #17 0x00000000006321f4 in eval_sub at eval.c:2172 + #18 0x0000000000632963 in eval_sub at eval.c:2290 + #19 0x000000000062d462 in Fprogn at eval.c:449 + #20 0x000000000062f0c4 in Flet at eval.c:963 + #21 0x00000000006321f4 in eval_sub at eval.c:2172 + #22 0x0000000000668caa in readevalloop at lread.c:1927 + #23 0x0000000000667253 in Fload at lread.c:1332 + #24 0x0000000000632683 in eval_sub at eval.c:2233 + #25 0x0000000000668caa in readevalloop at lread.c:1927 + #26 0x0000000000667253 in Fload at lread.c:1332 + #27 0x0000000000632683 in eval_sub at eval.c:2233 + #28 0x0000000000631be5 in Feval at eval.c:2041 + #29 0x000000000057e1af in top_level_2 at keyboard.c:1121 + #30 0x000000000062ffc7 in internal_condition_case at eval.c:1324 + #31 0x000000000057e1f0 in top_level_1 at keyboard.c:1129 + #32 0x000000000062f51e in internal_catch at eval.c:1091 + #33 0x000000000057e0ea in command_loop at keyboard.c:1090 + #34 0x000000000057d6d5 in recursive_edit_1 at keyboard.c:697 + #35 0x000000000057d8b4 in Frecursive_edit at keyboard.c:768 + #36 0x000000000057b55b in main at emacs.c:1687 + + Lisp Backtrace: + "capitalize" (0xffffcf70) + "format" (0xffffd130) + "define-charset" (0xffffd370) + "while" (0xffffd560) + "let" (0xffffd7c0) + "dolist" (0xffffd910) + "let" (0xffffdb70) + "load" (0xffffdfe0) + "load" (0xffffe4a0) + + * src/casefiddle.c (syms_of_casefiddle): Declare four new symbols: + Qtitlecase, Qspecial_uppercase, Qspecial_lowercase and + Qspecial_titlecase. + (prepare_casing_context): Use aforementioned symbols. + +2017-04-07 Paul Eggert + + Merge from gnulib + + This merges some getopt fixes from Zack Weinberg, and affects only + non-GNUish platforms. It incorporates: + 2017-04-06 getopt-gnu: omit some duplicate code + 2017-04-06 getopt-posix: use angle-bracket include + 2017-04-06 getopt: annotate files with relationship to glibc + 2017-04-06 getopt: split up getopt.in.h and eliminate __need_getopt + 2017-04-06 getopt: better handling of ambiguous options + 2017-04-06 getopt: refactor long-option handling + 2017-04-06 getopt: tidy up _getopt_initialize a bit + 2017-04-06 getopt: merge from glibc: repetition reduction + 2017-04-06 getopt: clean up error reporting + 2017-04-06 getopt: fix fencepost error in ambiguous-W-option handling + 2017-04-06 getopt: clean up getopt.c and getopt1.c file headers + 2017-04-06 getopt: harmonize comments with glibc + 2017-04-06 getopt: remove USE_NONOPTION_FLAGS + 2017-04-06 getopt: tabify, in preparation for merge with glibc + 2017-04-06 md5, sha1, sha256, sha512: Add comments re correctness + * build-aux/config.sub, doc/misc/texinfo.tex, lib/getopt.c: + * lib/getopt.in.h, lib/getopt1.c, lib/getopt_int.h, lib/md5.c: + * lib/md5.h, lib/sha1.c, lib/sha1.h, lib/sha256.c, lib/sha256.h: + * lib/sha512.c, lib/sha512.h, lib/unistd.in.h, m4/getopt.m4: + Copy from gnulib. + * lib/getopt_cdefs.in.h, lib/getopt_core.h, lib/getopt_ext.h: + * lib/getopt_pfx_core.h, lib/getopt_pfx_ext.h: + New files, taken from gnulib. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: + Regenerate. + +2017-04-07 Hong Xu + + * search.c (Fre_search_forward, Fre_search_backward): Improve doc (Bug#25193). + +2017-04-07 Noam Postavsky + + Mention that processes start in default-directory (Bug#18515) + + * doc/lispref/processes.texi (Synchronous Processes): + (Asynchronous Processes): + * lisp/subr.el (start-process): + * src/callproc.c (call-process): Mention that the subprocess starts in + `default-directory' when local, suggest `start-file-process' and + `process-file' otherwise. + +2017-04-07 Noam Postavsky + + * src/xdisp.c (vmessage, message): Clarify commentary. + +2017-04-07 Paul Eggert + + Minor casefiddle.c cleanups + + * src/casefiddle.c: Redo recent changes to match GNU style, + and prefer C99-style decls within blocks. + (GREEK_CAPITAL_LETTER_SIGMA): Rename from CAPITAL_SIGMA, so that + we are merely using the Unicode name, and make it a constant + rather than a macro. All uses changed. + (SMALL_SIGMA): Remove; unused. + (GREEK_SMALL_LETTER_FINAL_SIGMA): Rename from SMALL_FINAL_SIGMA, + and make it a constant rather than a macro. All uses changed. + (do_casify_multibyte_string): Use ‘verify’ rather than an + unportable static_assertion local. + +2017-04-07 Paul Eggert + + * lisp/international/README: Update to match current list. + +2017-04-06 Paul Eggert + + Fix 'make clean' in lib subdirectory + + * lib/Makefile.in (clean): Remove *-t files. + (mostlyclean): Remove MOSTLYCLEANFILES that are not *-t files. + This removes files like lib/getopt.h that should be removed + even if this configuration did not need to build them. + (maintainer-clean): Remove TAGS here, not in distclean, + to be consistent with ../src/Makefile.in. + +2017-04-06 Michael Albinus + + Add new Tramp syntax + + * lisp/net/tramp-cmds.el (tramp-change-syntax): New defun. + + * lisp/net/tramp.el (tramp-syntax): Change default to `def'. + Add :set function. + (tramp-prefix-port-format): Simplify. + (tramp-file-name-regexp-separate): Remove. + (tramp-initial-file-name-regexp) + (tramp-completion-file-name-regexp-old-style) + (tramp-initial-completion-file-name-regexp): New defconst. + (tramp-prefix-format, tramp-prefix-regexp) + (tramp-method-regexp, tramp-postfix-method-format) + (tramp-postfix-method-regexp, tramp-prefix-ipv6-format) + (tramp-prefix-ipv6-regexp, tramp-postfix-ipv6-format) + (tramp-postfix-ipv6-regexp) + (tramp-postfix-host-format, tramp-postfix-host-regexp) + (tramp-remote-file-name-spec-regexp) + (tramp-file-name-structure, tramp-file-name-regexp) + (tramp-completion-file-name-regexp) + (tramp-rfn-eshadow-update-overlay-regexp): Change them to be defuns. + (tramp-tramp-file-p, tramp-find-method) + (tramp-dissect-file-name, tramp-make-tramp-file-name) + (tramp-completion-make-tramp-file-name) + (tramp-rfn-eshadow-update-overlay) + (tramp-register-autoload-file-name-handlers) + (tramp-register-file-name-handlers) + (tramp-unload-file-name-handlers) + (tramp-completion-handle-file-name-all-completions) + (tramp-completion-dissect-file-name, tramp-clear-passwd): + * lisp/net/tramp-ftp.el (tramp-ftp-file-name-handler): + * lisp/net/tramp-sh.el (tramp-sh-handle-vc-registered) + (tramp-compute-multi-hops): Use them. + +2017-04-06 Michal Nazarewicz + + Implement special sigma casing rule (bug#24603) + + In Greek, a sigma character has two lower case forms which depend on + their position in the word. Implement logic determining it. + + * src/casefiddle.c (struct casing_context, case_character_impl): Don’t + assume inword is true when flag is CASE_UP and false when flag is + CASE_DOWN. For final sigma detection we need this information tracked + reliably;. + (CAPITAL_SIGMA, SMALL_SIGMA, SMALL_FINAL_SIGMA): New macros defining + Unicode code point of different forms of sigma letter. + (case_character): Implement support for final sigma casing. + (do_casify_multibyte_string, do_casify_multibyte_region): Update after + changes to case_character. + + * test/src/casefiddle-tests.el (casefiddle-tests-casing): Add test + cases for final sigma. + +2017-04-06 Michal Nazarewicz + + Support casing characters which map into multiple code points (bug#24603) + + Implement unconditional special casing rules defined in Unicode standard. + + Among other things, they deal with cases when a single code point is + replaced by multiple ones because single character does not exist (e.g. + ‘fi’ ligature turning into ‘FL’) or is not commonly used (e.g. ß turning + into SS). + + * admin/unidata/SpecialCasing.txt: New data file pulled from Unicode + standard distribution. + * admin/unidata/README: Mention SpecialCasing.txt. + + * admin/unidata/unidata-get.el (unidata-gen-table-special-casing, + unidata-gen-table-special-casing--do-load): New functions generating + ‘special-uppercase’, ‘special-lowercase’ and ‘special-titlecase’ + character Unicode properties built from the SpecialCasing.txt Unicode + data file. + + * src/casefiddle.c (struct casing_str_buf): New structure for + representing short strings used to handle one-to-many character + mappings. + + (case_character_imlp): New function which can handle one-to-many + character mappings. + (case_character, case_single_character): Wrappers for the above + functions. The former may map one character to multiple (or no) + code points while the latter does what the former used to do (i.e. + handles one-to-one mappings only). + + (do_casify_natnum, do_casify_unibyte_string, + do_casify_unibyte_region): Use case_single_character. + (do_casify_multibyte_string, do_casify_multibyte_region): Support new + features of case_character. + * (do_casify_region): Updated to reflact do_casify_multibyte_string + changes. + + (casify_word): Handle situation when one character-length of a word + can change affecting where end of the word is. + + (upcase, capitalize, upcase-initials): Update documentation to mention + limitations when working on characters. + + * test/src/casefiddle-tests.el (casefiddle-tests-char-properties): + Add test cases for the newly introduced character properties. + (casefiddle-tests-casing): Update test cases which are now passing. + + * test/lisp/char-fold-tests.el (char-fold--ascii-upcase, + char-fold--ascii-downcase): New functions which behave like old ‘upcase’ + and ‘downcase’. + (char-fold--test-match-exactly): Use the new functions. This is needed + because otherwise fi and similar characters are turned into their multi- + -character representation. + + * doc/lispref/strings.texi: Describe issue with casing characters versus + strings. + * doc/lispref/nonascii.texi: Describe the new character properties. + +2017-04-06 Michal Nazarewicz + + Split up casify_region function (bug#24603) + + No functional changes at this time but splitting casify_region into + a function dealing with multibyte and another dealing with unibyte + buffers will make future code changes slightly easier. + + * src/casefiddle.c (casify_region): Move most of the code into two + new functions: + (do_casify_multibyte_region, do_casify_unibyte_region): new functions. + +2017-04-06 Michal Nazarewicz + + Add support for title-casing letters (bug#24603) + + * src/casefiddle.c (struct casing_context, prepare_casing_context): Add + titlecase_char_table member. It’s set to the ‘titlecase’ Unicode + property table if capitalisation has been requested. + (case_character): Make use of the titlecase_char_table to title-case + initial characters when capitalising. + + * test/src/casefiddle-tests.el (casefiddle-tests--characters, + casefiddle-tests-casing): Update test cases which are now passing. + +2017-04-06 Michal Nazarewicz + + Introduce case_character function + + Move single-character casing logic into a separate function so that + it is collected in a single place. This will make future changes to + the logic easier. This commit introduces no functionality changes. + + * src/casefiddle.c (struct casing_context, prepare_casing_context): New + sturcture for saving casing context and function to initialise it. + (case_character): New function which cases character base on provided + context. + (do_casify_integer, do_casify_multibyte_string, + do_casify_unibyte_string, casify_object, casify_region): Convert to + use casing_context and case_character. + +2017-04-06 Michal Nazarewicz + + Split casify_object into multiple functions + + casify_object had three major cases to cover and those were mostly + independent of each other. Move those branches to separate function + so it’s easier to comprehend each individual case. + + While at it, use somewhat more descriptive ch and cased variable names + rather than c and c1. + + This commit introduces no functional changes. + + * src/casefiddle.c (casify_object): Split into… + (do_casify_integer, do_casify_multibyte_string, + do_casify_unibyte_string): …new functions. + +2017-04-06 Lars Brinkhoff + + Update documentation for type semantics of records. + + * objects.texi (Record Type): improve description of what + `type-of' returns for records. + (Type Descriptors): new section. + * elisp.texi: reference it. + * records.texi (Records): reference it. Document behaviour when type + slot is a record. + + * alloc.c (Fmake_record, Frecord): mention type desciptors. + +2017-04-06 Stefan Monnier + + * lisp/help-fns.el (describe-symbol): `nil' is not an interesting default. + +2017-04-06 Tom Tromey + + require cl-lib to fix fallout from mhtml series + + * lisp/align.el, lisp/calc/calc-embed.el, lisp/cedet/semantic.el, + lisp/emulation/viper.el: Require cl-lib. + +2017-04-06 Ken Raeburn + + In CANNOT_DUMP builds, allow editing of files named "dump". + + * lisp/loadup.el: Perform the "dump" or "bootstrap" actions like + calling dump-emacs only if dump-emacs is defined; otherwise, don't + treat those command-line argument specially. + +2017-04-06 Ken Raeburn + + In CANNOT_DUMP builds, don't prepare for unexec. + + Having a command-line argument of "dump" or "bootstrap" would trigger + behavior like not installing signal handlers. In CANNOT_DUMP modes, + we should get signal handlers installed regardless of whatever funny + file names we decide to edit. + + src/emacs.c (main) [CANNOT_DUMP]: Don't enable the "dumping" + alterations to initialization that prepares the process for unexec. + +2017-04-06 Ken Raeburn + + Allow a CANNOT_DUMP build to use exec-path during bootstrap. + + During a bootstrap, loading rmail.el invokes movemail to determine its + flavor, but call-process doesn't work if exec-path is nil. + + * lisp/loadup.el: Only clear exec-path if dumping. + +2017-04-06 Ken Raeburn + + Fix CANNOT_DUMP build on Darwin/macOS. + + * src/conf_post.h (malloc, realloc, free) [DARWIN_OS && emacs && + CANNOT_DUMP]: Don't define as unexec_malloc, etc. + * src/emacs.c (main): Don't call unexec_init_emacs_zone. + +2017-04-05 Tom Tromey + + add two more mhtml tests + + * test/manual/indent/html-multi-2.html: New file. + * test/manual/indent/html-multi-3.html: New file. + +2017-04-05 Tom Tromey + + enable mhtml-mode by default + + * lisp/files.el (auto-mode-alist): Reference mhtml-mode, not + html-mode. + (magic-fallback-mode-alist): Likewise. + * lisp/net/eww.el (eww-view-source): Use mthml-mode. + +2017-04-05 Tom Tromey + + add mhtml-mode.el + + * etc/NEWS: Update. + * lisp/textmodes/mhtml-mode.el: New file. + * test/manual/indent/html-multi.html: New file. + * test/lisp/textmodes/mhtml-mode-tests.el: New file. + * doc/emacs/text.texi (HTML Mode): Mention mhtml-mode. + +2017-04-05 Tom Tromey + + change sgml-mode to help multi-html mode + + * lisp/textmodes/sgml-mode.el (sgml-syntax-propertize-rules): New + defconst. + (sgml-syntax-propertize): Use it. + (sgml--find-<>-backward): New function. + (sgml-parse-tag-backward): Use it. + +2017-04-05 Tom Tromey + + make js.el respect prog-first-column + + * lisp/progmodes/js.el (js--proper-indentation): Call prog-first-column. + +2017-04-05 Tom Tromey + + make smie.el respect prog-first-column + + * lisp/emacs-lisp/smie.el (smie-indent-bob): Call prog-first-column. + +2017-04-05 Tom Tromey + + change viper to use derived-mode-p + + * lisp/subr.el (provided-mode-derived-p): New function. + (derived-mode-p): Use it. + * lisp/emulation/viper.el (viper-mode): Use derived-mode-p. + (this-major-mode-requires-vi-state): Use provided-mode-derived-p. + (set-viper-state-in-major-mode): Use derived-mode-p. + +2017-04-05 Tom Tromey + + change align to use derived-mode-p + + * lisp/align.el (align-region): Use derived-mode-p. + +2017-04-05 Tom Tromey + + change org to use derived-mode-p + + * lisp/org/org-list.el (org-list-insert-radio-list): Use + derived-mode-p. + * lisp/org/org-table.el (orgtbl-setup, orgtbl-toggle-comment): Use + derived-mode-p. + +2017-04-05 Tom Tromey + + change semantic to use derived-mode-p + + * lisp/cedet/semantic.el (semantic-new-buffer-fcn): Use derived-mode-p. + +2017-04-05 Tom Tromey + + change calc to use derived-mode-p + + * lisp/calc/calc-embed.el (calc-embedded-find-modes) + (calc-embedded-make-info): Use derived-mode-p. + +2017-04-05 Tom Tromey + + change auto-insert to use derived-mode-p + + * lisp/autoinsert.el (auto-insert): Use derived-mode-p. + +2017-04-05 Paul Eggert + + * lisp/info.el (Info-search): Fix typo in April 1 change. + +2017-04-05 Paul Eggert + + Minor cleanups related to type-of + + * src/data.c (Frecordp): Rename from Frecordp_p, for consistency. + * src/data.c (syms_of_data): + * src/frame.c (syms_of_frame): Put all the primitive type names + together, under the "Types that type-of returns" comment. + +2017-04-05 Glenn Morris + + * doc/lispref/package.texi (Package Archives): Mention https. + +2017-04-05 Glenn Morris + + Advertise https for homepage of gnu.org packages + + * lisp/emacs-lisp/package.el (describe-package-1): + Use https, if supported, for the homepage of packages on gnu.org. + +2017-04-05 Glenn Morris + + Default to https for elpa.gnu.org if gnutls available + + * lisp/emacs-lisp/package.el (package-archives): + Default to https for elpa.gnu.org if gnutls is available. Ref: + http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00130.html + +2017-04-04 Paul Eggert + + Minor simplifications and doc for records + + * doc/lispref/records.texi (Records): Mention size limit. + * etc/NEWS: Mention records. + * src/alloc.c (allocate_pseudovector, allocate_record): + Prefer 'PSEUDOVECTOR_SIZE_MASK' to its definiens. + (allocate_record): Check arg range here, not in callers, as this + simplifies the code. Use allocate_vectorlike instead of + allocate_vector, to avoid duplicate runtime tests. + (Fmake_record, record): Don't mention PSEUDOVECTOR_SIZE_BITS in + the doc string, as it is not visible to the user. + (Fmake_record, record, Fcopy_record): + Prefer make_lisp_ptr to XSETVECTOR. + (record): Broaden memcpy to copy the type, too. + +2017-04-04 Eli Zaretskii + + Fix recent changes in record data type + + * src/alloc.c (Fmake_record, Frecord, Fcopy_record): Avoid + compiler warnings when 'ptrdiff_t' is narrower than 'long int'. + +2017-04-04 Philipp Stephani + + Make subprocess functions resolve the default directory + + `call-process' doesn't respect file name handlers in + `default-directory', so `file-name-non-special' has to resolve them + for `process-file', `start-file-process', and + `shell-command' (Bug#25949). + + * lisp/files.el (file-name-non-special): Also resolve default + directory for 'process-file', 'start-file-process', and + 'shell-command'. + * test/lisp/files-tests.el + (files-tests--file-name-non-special--subprocess): Add unit test. + +2017-04-04 Philipp Stephani + + Make ediff handle remote and quoted file names + + Quoted file names need to be unquoted before passed to + subprocesses (Bug#25950). + + * lisp/vc/ediff-diff.el (ediff-exec-process): Handle remote and quoted + file names. + * test/lisp/vc/ediff-diff-tests.el + (ediff-diff-tests--ediff-exec-process--quoted-file): Add unit test. + +2017-04-04 Stefan Monnier + + Backward compatibility with pre-existing struct instances. + + * lisp/emacs-lisp/cl-lib.el (cl--old-struct-type-of): New function. + (cl-old-struct-compat-mode): New minor mode. + + * lisp/emacs-lisp/cl-macs.el (cl-defstruct): Pass `record' to + cl-struct-define to signal use of record objects. + + * lisp/emacs-lisp/cl-preloaded.el (cl--struct-get-class, + cl-struct-define): Enable legacy defstruct compatibility. + + * test/lisp/emacs-lisp/cl-lib-tests.el (cl-lib-old-struct, + old-struct): New tests. + + * doc/lispref/elisp.texi, doc/lispref/records.texi: Document + `old-struct-compat'. + +2017-04-04 Lars Brinkhoff + + Make the URL library use records. + + * lisp/url/url.el, lisp/url/url-cache.el, lisp/url/url-dav.el, + lisp/url/url-expand.el, lisp/url/url-file.el, lisp/url/url-imap.el, + lisp/url/url-ldap.el: Use `url-p' instead of `vectorp'. + + * lisp/url/url-http.el (url-http): Check for type `url' instead of + `vector'. + +2017-04-04 Stefan Monnier + + Make EIEIO use records. + + * lisp/emacs-lisp/eieio-compat.el + (eieio--generic-static-object-generalizer): Adjust to new tags. + + * lisp/emacs-lisp/eieio-core.el: Use records, and place the class object + directly as tag. + (eieio--object-class): Adjust to new tag representation. + (eieio-object-p): Rewrite, and adapt to new `type-of' behavior. + (eieio-defclass-internal): Use `make-record'. + (eieio--generic-generalizer): Adjust generalizer code accordingly. + + * lisp/emacs-lisp/eieio.el (make-instance, clone): Use copy-record. + + * lisp/emacs-lisp/pcase.el (pcase-mutually-exclusive-predicates): + Add `recordp'. + + * doc/lispref/records.texi, doc/misc/eieio.texi: Update for records. + +2017-04-04 Lars Brinkhoff + + Make cl-defstruct use records. + + * lisp/emacs-lisp/cl-extra.el (cl--describe-class) + (cl--describe-class-slots): Use the new `type-of'. + + * lisp/emacs-lisp/cl-generic.el (cl--generic-struct-tag): Use type-of. + (cl--generic-struct-specializers): Adjust to new tag. + + * lisp/emacs-lisp/cl-macs.el (cl-defstruct): When type is nil, use records. + Use the type symbol as the tag. Use copy-record to copy structs. + (cl--defstruct-predicate): New function. + (cl--pcase-mutually-exclusive-p): Use it. + (cl-struct-sequence-type): Can now return `record'. + + * lisp/emacs-lisp/cl-preloaded.el (cl--make-slot-desc): Adjust ad-hoc + code to new format. + (cl--struct-register-child): Work with records. + (cl-struct-define): Don't touch the tag's symbol-value and + symbol-function slots when we use the type as tag. + + * lisp/emacs-lisp/cl-print.el (cl-print-object): Adjust to new tag. + + * test/lisp/emacs-lisp/cl-lib-tests.el (cl-lib-defstruct-record): + New test. + + * doc/lispref/records.texi, doc/misc/cl.texi: Update for records. + +2017-04-04 Lars Brinkhoff + + Add record objects with user-defined types. + + * src/alloc.c (allocate_record): New function. + (Fmake_record, Frecord, Fcopy_record): New functions. + (syms_of_alloc): defsubr them. + (purecopy): Work with records. + + * src/data.c (Ftype_of): Return slot 0 for record objects, or type + name if record's type holds class. + (Frecordp): New function. + (syms_of_data): defsubr it. Define `Qrecordp'. + (Faref, Faset): Work with records. + + * src/fns.c (Flength): Work with records. + + * src/lisp.h (prec_type): Add PVEC_RECORD. + (RECORDP, CHECK_RECORD, CHECK_RECORD_TYPE): New functions. + + * src/lread.c (read1): Add syntax for records. + + * src/print.c (PRINT_CIRCLE_CANDIDATE_P): Add RECORDP. + (print_object): Add syntax for records. + + * test/lisp/emacs-lisp/cl-print-tests.el (cl-print-tests-2): + New test. + + * test/src/alloc-tests.el (record-1, record-2, record-3): + New tests. + + * doc/lispref/elisp.texi, doc/lispref/objects.texi, + doc/lispref/records.texi: Add documentation for records. + +2017-04-04 Tino Calancha + + Fix a test in python-test.el + + Fix a test that breaks the test suite when it is run within a + virtual environment. + See following link for details: + https://lists.gnu.org/archive/html/emacs-devel/2017-03/msg00857.html + * test/lisp/progmodes/python-tests.el + (python-shell-calculate-process-environment-7): Bind + python-shell-virtualenv-root to VIRTUAL_ENV when this var is set; otherwise + bind it to '/env'. + +2017-04-04 Noam Postavsky + + Throw a `search-failed' derived error in Info search + + The original fix for Bug#6106 switched from signalling `search-failed' + to `user-error'. However, this breaks incremental searching over + multiple nodes because the isearch code doesn't expect a `user-error'. + + * src/search.c (syms_of_search): New error, `user-search-failed', + with `user-error' and `search-failed' as parents. + * doc/lispref/errors.texi (Standard Errors): Document it. + * etc/NEWS: Announce it. + * lisp/info.el (Info-search): Use it instead of `user-error' so that + isearch will handle failed searches correctly. + +2017-04-03 Michael Albinus + + Add Tramp test + + * doc/misc/tramp.texi (Remote processes): Fix typo. + + * lisp/shell.el (shell): Fix typo. + + * lisp/net/tramp.el (tramp-set-connection-local-variables): Simplify. + + * test/lisp/net/tramp-tests.el (tramp-test30-explicit-shell-file-name): + New test. + (tramp--test-special-characters, tramp--test-utf8): Adapt docstring. + (tramp-test31-vc-registered) + (tramp-test32-make-auto-save-file-name) + (tramp-test33-make-nearby-temp-file) + (tramp-test34-special-characters) + (tramp-test34-special-characters-with-stat) + (tramp-test34-special-characters-with-perl) + (tramp-test34-special-characters-with-ls, tramp-test35-utf8) + (tramp-test35-utf8-with-stat, tramp-test35-utf8-with-perl) + (tramp-test35-utf8-with-ls) + (tramp-test36-asynchronous-requests) + (tramp-test37-recursive-load, tramp-test38-unload): Rename. + +2017-04-03 Stefan Monnier + + * lisp/ses.el: Silence byte-compiler warnings. + + (ses-jump, ses-recalculate-cell, ses-define-local-printer): Silence + byte-compiler warnings. + +2017-04-02 Glenn Morris + + Belated fixes for admin.el's M-x make-manuals-dist + + * admin/admin.el (make-manuals-dist-output-variables): Additions. + (make-manuals-dist--1): Also copy docstyle.texi. + +2017-04-02 Paul Eggert + + Fix bugs in simplified test dependencies + + Problem reported by Glenn Morris in: + http://lists.gnu.org/archive/html/emacs-devel/2017-04/msg00017.html + * test/Makefile.in (LOGFILES, TESTS): Omit leading "./". + (TESTS): Omit unnecessary patsubst. + (test_template): Redo dependency heuristic, hopefully + correctly this time. It's the .log file that depends, + not the phony test target. Declare the phonies to be PHONY. + Resurrect the exception for the *-tests subdirectory. + Adjust to the fact that leading "./" is omitted now. + +2017-04-02 Wilfred Hughes + + Fix typo in docstring + + * lisp/help.el: Fix typo. + +2017-04-02 Michael Albinus + + Apply connecion-local variables for shells + + * doc/misc/tramp.texi (Remote processes): Show use of connection-local + variables. Don't mention Emacs 23 anymore. + (Frequently Asked Questions): Precise Emacs and MS Windows version. + + * lisp/files-x.el (connection-local-normalize-criteria): + Suppress nil properties. + (connection-local-set-profiles, with-connection-local-profiles): + Adapt docstring. + + * lisp/shell.el (shell): Apply connecion-local variables. + +2017-04-01 Evgeni Kolev (tiny change) + + Propertize only perl prototype chars `][$%&*;+@\' as punctuation + + This prevents variables in signatures such as `sub add ($a, $b)' from + being treated as punctuation. + * lisp/progmodes/perl-mode.el (perl-syntax-propertize-function): + Strictly match only prototype characters as punctuation. (Bug#26037) + +2017-04-01 Tom Tromey + + fix two js-mode syntax propertization bugs + + Bug#26070: + * lisp/progmodes/js.el (js--syntax-propertize-regexp-regexp): Add + zero-or-one to regular expression. + (js-syntax-propertize-regexp): Update. Propertize body of regexp + literal up to END. + * test/lisp/progmodes/js-tests.el (js-mode-propertize-bug-1) + (js-mode-propertize-bug-2): New tests. + +2017-04-01 Paul Eggert + + Simplify test dependency generation + + Generate default dependencies by using GNU extensions to ‘make’ + rather than via a hacky auxiliary program and script. + * .gitignore: Remove test/make-test-deps.mk. + * test/Makefile.in (ELFILES, LOGFILES, TESTS): + Use :=, not =, to avoid multiple redundant invocations of ‘find’. + (test_template): Infer dependency directly instead of via + make-test-deps.mk. + (check-doit): Prepend ‘@’ to avoid excessively long ‘make’ output. + (clean): No need to clean make-test-deps.mk. + (make-test-deps.mk): Remove rule. + * test/make-test-deps.emacs-lisp: Remove. + +2017-04-01 Stefan Monnier + + * test/lisp/emacs-lisp/cl-lib-tests.el: Improve symbol-macrolet tests + + (cl-lib-symbol-macrolet): Fix last test so it doesn't break the whole + test suite. + (cl-lib-symbol-macrolet-2): New test. + +2017-04-01 Tino Calancha + + Use only posix options in a ediff-ptch test + + * test/lisp/vc/ediff-ptch-tests.el (ediff-ptch-test-bug26084): + Use just "-b" patch option. Don't assume a particular suffix for + the backup files. + +2017-04-01 Jarno Malmari + + Initial implementation of HTTP Digest qop for url + + This also refactors digest authentication functions in url-auth.el. + + * lisp/url/url-auth.el (url-digest-auth, url-digest-auth-create-key): + (url-digest-auth-build-response, url-digest-auth-directory-id-assoc): + (url-digest-auth-name-value-string, url-digest-auth-source-creds): + (url-digest-cached-key, url-digest-cache-key, url-digest-find-creds): + (url-digest-find-new-key, url-digest-prompt-creds): Add new functions + to simplify code and aid in unit testing. + (url-digest-auth-build-response): Hook up new functionality, or fall + back to previous. + (url-digest-auth-make-request-digest-qop): + (url-digest-auth-make-cnonce, url-digest-auth-nonce-count): + (url-digest-auth-name-value-string): Add new helper functions. + * test/lisp/url/url-auth-tests.el (url-auth-test-colonjoin): + (url-auth-test-digest-ha1, url-auth-test-digest-ha2): + (url-auth-test-digest-request-digest): Add a few tests as now more + features are testable via intermediate functions. + (url-auth-test-challenges, url-auth-test-digest-request-digest): Test + the new implementation. Parts of these were accidentally already + merged in the past. + +2017-04-01 Tino Calancha + + Tweak ediff-ptch test in previous commit a bit more + + * test/lisp/vc/ediff-ptch-tests.el (ediff-ptch-test-bug26084): + Apply patches without requiring a shell. Add some comments. + +2017-03-31 Glenn Morris + + Tweak an ediff-ptch test + + * test/lisp/vc/ediff-ptch-tests.el (ediff-ptch-test-bug26084): + Add skip conditions. Avoid going through shell where not needed. + +2017-03-31 Michael Albinus + + * lisp/net/tramp-smb.el (tramp-smb-errors): + + Add "NT_STATUS_PASSWORD_MUST_CHANGE". + +2017-03-31 Stefan Monnier + + * test/lisp/emacs-lisp/cl-lib-tests.el (cl-lib-symbol-macrolet): New test. + +2017-03-31 Tino Calancha + + dired-mark-suffix: New command + + Now dired-mark-extension prepends '.' to extension when not present. + Add command dired-mark-suffix to preserve the previous + behaviour (Bug#25942). + * lisp/dired-x.el (dired-mark-suffix): New command; + mark files ending in a given suffix. + (dired--mark-suffix-interactive-spec): New defun. + (dired-mark-extension, dired-mark-suffix): Use it. + * doc/misc/dired-x.texi (Advanced Mark Commands): Update manual. + * test/lisp/dired-x-tests.el: New test suite; add test for these features. + +2017-03-31 Tino Calancha + + default-directory: Remark that it must be a directory name + + * src/buffer.c (default-directory): Update docstring (Bug#26272). + +2017-03-31 Tino Calancha + + Delete confuse statement in manual + + * doc/misc/cl.texi (For Clauses): Delete confuse statement + and its example (Bug#23550). + +2017-03-31 Paul Eggert + + Use find -delete if available + + This shortens the ‘make’ output and should avoid some + repetitive scanning of directories during a build. + * configure.ac (FIND_DELETE): New var. + * lisp/Makefile.in (compile-always, bootstrap-clean): + * test/Makefile.in (clean, bootstrap-clean): Use it. + * test/Makefile.in (ELCFILES, LOGSAVEFILES): Remove; no longer needed. + +2017-03-31 Mark Oteiza + + Remove gnus-boundp + + * lisp/gnus/gnus-start.el (gnus-display-time-event-handler): Use + bound-and-true-p. + * lisp/gnus/gnus-util (gnus-boundp): Remove. + +2017-03-31 Niels Möller (tiny change) + + Stop `fixup-whitespace' adding trailing whitespace (Bug#18783) + + * lisp/simple.el (fixup-whitespace): Insert no spaces if point is at + end of line after deleting horizontal whitespace. + +2017-03-31 Paul Eggert + + * src/inotify.c (add_watch): Add comment. + +2017-03-31 Andreas Politz + + Minor filenotify.el fixes + + * lisp/filenotify.el: Require subr-x. + (file-notify-callback): Use equal, not eq. + +2017-03-31 Noam Postavsky + + Improve packaging documentation + + * doc/lispref/package.texi (Packaging Basics): + * doc/lispref/tips.texi (Library Headers): Clarify some header + formats, relation between file headers and package + attributes (Bug#13281). + +2017-03-31 John Mastro + + Fix a small incompatibility in ibuffer + + Translate nil values from column functions to the empty string, so that + subsequent calls to string-width don't signal an error (Bug#26317). + * lisp/ibuffer.el (ibuffer-compile-format): If a column function returns + nil, treat it like the empty string. + +2017-03-30 Alan Mackenzie + + Fix C++ fontification problems 500 bytes after typing a space, and other bugs + + Also implement the "asymmetric space" rule for fontifying otherwise + ambiguous + declarations/expressions. + + * lisp/progmodes/cc-engine.el (c-before-change-check-<>-operators): Don't set + c-new-BEG or c-new-END when there is no need. + (c-forward-decl-or-cast-1): Add "CASE 17.5" to implement the "asymmetric + space" rule. + + * lisp/progmodes/cc-fonts.el (c-get-fontification-context): New function, + extracted from c-font-lock-declarations. Add to this function processing to + make `context' 'decl for lines contained within parens when these are also + declarations. + (c-font-lock-declarations): Call the newly extracted function above in place + of inline code. + + * lisp/progmodes/cc-mode.el (c-fl-decl-start): Set point before calling + c-literal-start. + + * lisp/progmodes/cc-vars.el (c-asymmetry-fontification-flag): New user option. + + * doc/misc/cc-mode.texi (Misc Font Locking): New node documenting the new + "asymmetric fontification" rule, including the variable + c-asymmetric-fontification-flag. + +2017-03-30 Paul Eggert + + Some inotify cleanup + + This catches some problems with integer overflow and races + that I noticed in inotify.c after reviewing the changes + installed to fix Bug#26126. + * src/fns.c, src/lisp.h (equal_no_quit): Now extern. + * src/inotify.c (aspect_to_inotifymask): + Check for cycles and for improper lists. + (make_lispy_mask, lispy_mask_match_p): Remove. + All callers changed to use INTEGER_TO_CONS and CONS_TO_INTEGER. + (inotifyevent_to_event, add_watch): + Don’t assume watch descriptors and cookies fit in fixnums. + (add_watch): Use assoc_no_quit, not Fassoc. + Avoid integer overflow in (very!) long-running processes where + the Emacs watch ID could overflow. Avoid some duplicate code. + (find_descriptor): New function. + (remove_descriptor): First arg is now the returned value from + find_descriptor, rather than the descriptor. This way, the + value can be removed without calling Fdelete, which might quit. + Wait until the end (when watch_list is consistent) before signaling + any errors. + (remove_watch, inotify_callback): + Use find_descriptor to avoid the need for Fdelete. + (inotify_callback): Use simpler tests for ioctl failure. + Free temporary buffer if signaled, and put it on the stack if small. + Use ssize_t to index through read results, to avoid a cast. + (valid_watch_descriptor): New function, with a tighter check. + (Finotify_rm_watch, Finotify_valid_p): Use it. + (Finotify_valid_p): Use assoc_no_quit and ass_no_quit instead + of Fassoc. Do not assume the first assoc succeeds. + * test/src/inotify-tests.el (inotify-valid-p-simple): + Add inotify-valid-p tests, some of which dump core without + the fixes noted above. + +2017-03-30 Michael Albinus + + * lisp/net/tramp-sh.el (tramp-get-remote-locale): Add "C.UTF-8" as candidate. + +2017-03-30 Stefan Monnier + + * lisp/cedet/semantic/wisent/wisent.el (wisent-automaton-p): Use obarrayp. + +2017-03-30 Paul Eggert + + Fix assoc_no_quit so that it does not quit + + The problem was that it called Fequal, which can quit. + * src/fns.c (enum equal_kind): + New enum, to be used in place of a boolean. + (equal_no_quit): New function. + (Fmemql, Feql): Use it to compare floats, as a minor tuneup. + (assoc_no_quit): Use it to avoid quitting, the main point here. + (internal_equal): Generalize bool to enum equal_kind arg, so that + there are now 3 possibilities instead of 2. Do not signal an + error if EQUAL_NO_QUIT. Put the arg before the depth, since depth + should be irrelevant if the arg is EQUAL_NO_QUIT. All callers + changed. + +2017-03-29 Alan Mackenzie + + Amend gitmerge to recognize the injunction "don't merge". + + * admin/gitmerge.el (gitmerge-skip-regexp): amend regexp to match "don't" as + well as "do not". + +2017-03-29 Simen Heggestøyl + + Add one more CSS pseudo-class + + * lisp/textmodes/css-mode.el (css-pseudo-class-ids): Add + `focus-within'. + +2017-03-29 Simen Heggestøyl + + Update list of CSS pseudo-classes + + * lisp/textmodes/css-mode.el (css-pseudo-class-ids): Update list of + pseudo-classes. + +2017-03-29 Noam Postavsky + + Adjust some search failure errors in info.el + + * lisp/info.el (Info-select-node): The search for beginning of node is + an internal detail, and is not normally expected to fail, so it should + not be a user error. + (Info-complete-menu-item): Failing to find a menu indicates the user + searched for a menu when there isn't one, so change to `use-error'. + +2017-03-28 Alan Mackenzie + + * lisp/progmodes/cc-defs.el (c-version): Restore c-version to 5.33 + +2017-03-28 Paul Eggert + + Don’t mishandle (format "%i" -1.0) + + * src/editfns.c (styled_format): Treat %i like %d when converting arg. + +2017-03-28 Noam Postavsky + + * lisp/emacs-lisp/ert.el (ert-run-tests): Make INTERACTIVE arg optional. + +2017-03-28 Michael Albinus + + * src/inotify.c (Finotify_add_watch): aspect can also be a symbol. + +2017-03-28 Noam Postavsky + + Don't add `search-failed' to ignored errors in info.el (Bug#6106) + + * lisp/info.el: Stop adding `search-failed' to `debug-ignored-errors'. + (Info-select-node, Info-search): Replace (signal 'search-failed ...) + with (user-error "Search failed: "...). + +2017-03-27 Paul Eggert + + Fix obsolete ‘test/automated’ references + + * Makefile.in (mostlyclean, clean, maybeclean_dirs, distclean) + (bootstrap-clean, maintainer-clean): + Clean ‘test’, not ‘test/automated’. Test for existence of + subdirectory only for ‘test’, not for directories that should + always exist. + * admin/MAINTAINERS, etc/TODO, lisp/emacs-lisp/bytecomp.el: + * lisp/emacs-lisp/seq.el, lisp/emacs-lisp/thunk.el: + * lisp/man.el (Man-parse-man-k): + * lisp/url/url-domsuf.el, make-dist: + * test/file-organization.org: + Fix obsolete references to test/automated. + +2017-03-27 Katsumi Yamaoka + + shr-image-fetched: Work for narrowed Gnus article + + See <8737e3msun.fsf@gmail.com> of bug#26231 in the bug-gnu-emacs list. + + * lisp/net/shr.el (shr-image-fetched): Work for narrowed article. + +2017-03-27 Michael Albinus + + * lisp/net/tramp.el (tramp-file-name-handler): Autoload it. + +2017-03-27 Eric Abrahamsen + + Expand manual section on quitting windows + + * doc/lispref/windows.texi (Quitting Windows): Provide more + information about the elements of the quit-restore window parameter, + and how they affect the behavior of quit-restore-window. + +2017-03-26 Philipp Stephani + + Add check for expected backtrace in module calls. + + * test.el (mod-test-non-local-exit-signal-test): Compare actual + backtrace to expected backtrace. + +2017-03-26 Eli Zaretskii + + Fix redisplay glitches due to recent change in redisplay_internal + + * src/xdisp.c (redisplay_internal): A better fix for bug#26097. + See http://lists.gnu.org/archive/html/emacs-devel/2017-03/msg00695.html + for the problems caused by the original fix. + +2017-03-26 Michael Albinus + + Fix Bug#26258 + + * lisp/net/tramp-adb.el (tramp-adb-file-name-handler-alist): + * lisp/net/tramp-gvfs.el (tramp-gvfs-file-name-handler-alist): + * lisp/net/tramp-sh.el (tramp-sh-file-name-handler-alist): + * lisp/net/tramp-smb.el (tramp-smb-file-name-handler-alist): + Autoload. Call `tramp-register-foreign-file-name-handler'. (Bug#26258) + + * lisp/net/tramp.el (tramp-autoload-file-name-handler): Remove. + (tramp-register-autoload-file-name-handlers) + (tramp-register-file-name-handlers): Do not handle + `tramp-autoload-file-name-handler' anymore. Mark `operations' + the handlers are responsible for. + (tramp-register-foreign-file-name-handler): New defun. + +2017-03-26 Noam Postavsky + + Fix docstring of dabbrev-abbrev-char-regexp + + * lisp/dabbrev.el (dabbrev-abbrev-char-regexp): Using a value of nil + is equivalent to "\\sw\\|\\s_", and has no special behavior. If the + previous character doesn't match, we search backwards for one that + does, not throw an error. Replace Lisp example with C based one to + make it clear that "symbol" means a sequence of word and symbol + constituent characters, not a Lisp symbol (Bug#358). + +2017-03-26 Johan Claesson (tiny change) + + * doc/misc/cl.texi (Iteration Clauses): Clarify example (Bug#19515). + +2017-03-26 Andreas Politz + + Minor fixes for inotify.c and filenotify.el + + * lisp/filenotify.el (file-notify--watch-absolute-filename): + Add docstring. + (file-notify-callback): Simplify. + + * src/inotify.c (Finotify_add_watch): Adapt docstring. + +2017-03-26 Andreas Politz + + Fix issues regarding inotify file-notification + + Remove special code handling the inotify back-end. + * lisp/filenotify.el (file-notify--watch): New struct + representing a file-watch. + (file-notify-descriptors): Use the new struct as hash-value. + (file-notify-handle-event): Check that event is a cons. + (file-notify--rm-descriptor, file-notify--event-watched-file) + (file-notify--event-file-name, file-notify--event-file1-name) + (file-notify-callback, file-notify-add-watch) + (file-notify-rm-watch, file-notify-valid-p): Use new struct. + Remove special code handling inotify descriptors. Remove code + handling multiple clients per descriptor. + (file-notify--descriptor): Remove unused function. + + Let inotify-add-watch return a unique descriptor on every + call, like every other back-end does (Bug#26126). Prevent + multiple clients from interfering with each other, when + watching a shared descriptor. + * src/inotify.c (watch_list): Extend the format by including a + id and the provided mask. + (INOTIFY_DEFAULT_MASK): Default mask used for all clients. + (make_watch_descriptor): Removed. + (make_lispy_mask, lispy_mask_match_p): New functions. + (inotifyevent_to_event): Match event against the mask provided + by the client. + (add_watch, remove_descriptor, remove_watch): New functions + for managing the watch_list. + (inotify_callback): Use the new functions. + (Finotify_add_watch, Finotify_rm_watch): Remove deprecated + flags from documentation. Add check for validity of provided + descriptor. Use the new functions. Use the default mask. + (INOTIFY_DEBUG): Add new debug conditional. + (inotify-watch-list, inotify-allocated-p): New debug functions. + (symbol_to_inotifymask, syms_of_inotify): Remove deprecated symbols. + + * test/lisp/filenotify-tests.el: + (file-notify-test02-rm-watch): Remove expected failure for inotify. + +2017-03-26 Paul Pogonyshev + + * lisp/emacs-lisp/pcase.el (pcase): Comment debug message (Bug#26177). + +2017-03-25 Jens Uwe Schmidt (tiny change) + + Stop edebug getting stuck on backquote (Bug#23651) + + * lisp/emacs-lisp/edebug.el (edebug-read-sexp): Move forward after + reading backquote or comma. + +2017-03-25 Eric Abrahamsen + + Expand manual section on quitting windows + + * doc/lispref/windows.texi (Quitting Windows): Provide more + information about the elements of the quit-restore window parameter, + and how they affect the behavior of quit-restore-window. + +2017-03-25 Eli Zaretskii + + Support in ispell.el multiple dictionaries loaded by Hunspell + + * lisp/textmodes/ispell.el (ispell-find-hunspell-dictionaries): + Support Hunspell configurations that load more than one dictionary + by default. Doc fix. (Bug#25830) + +2017-03-25 Michael Albinus + + Simplify Tramp autoloading. + + * lisp/net/tramp.el (tramp-completion-file-name-handler): + Simplify autoloading. Give it the `operations' property. + (tramp-completion-handle-expand-file-name): Remove. + +2017-03-25 Eli Zaretskii + + Fix a segfault due to failure to realize some faces + + * src/xdisp.c (redisplay_internal): If the frame becomes garbaged + while redisplaying its windows, redisplay all of its windows + again. (Bug#26097) + (init_iterator): When freeing all realized faces on all frames, + reset the 'face_change' flag of the frame whose window we are + about to iterate. + +2017-03-25 Philipp Stephani + + Use a named function for 'safe-local-variable + + This improves the help screen for `version-control' (Bug#25431). + + * lisp/files.el (version-control-safe-local-p): New function. + (version-control): Use it. + +2017-03-25 Eli Zaretskii + + ;* doc/misc/info.texi (Choose menu subtopic): Improve indexing. (Bug#26236) + +2017-03-25 Helmut Eller + + Make it easier to abort a series of tests with C-g + + * emacs-lisp/ert.el (ert-run-tests): Add "interactively" arg. If + interactively is true and a test was aborted then ask if the remaining + tests should be aborted too. + (ert-run-tests-batch, ert-run-tests-interactively): Pass in + interactively arg. + +2017-03-24 Paul Eggert + + Don’t require chown/chgrp for game installation + + Problem reported by Joseph Mingrone in: + http://lists.gnu.org/archive/html/emacs-devel/2017-03/msg00622.html + * lib-src/Makefile.in (exp_archlibdir): Don’t fail if chown or + chgrp fails with update-game-score and the game directory. + Instead, expect the installer to fix this up afterwards. + +2017-03-24 Stefan Monnier + + * lisp/emacs-lisp/lisp-mode.el: Don't highlight \( at BOL + + (elisp--font-lock-backslash): Extract from lisp-el-font-lock-keywords-2. + Don't highlight \ at BOL. Don't assume syntax-ppss preserves match-data. + +2017-03-23 Philipp Stephani + + Protect against an infloop in python-mode + + There appears to be an edge case caused by using `syntax-ppss' in a + narrowed buffer during JIT lock inside of Python triple-quote strings. + Unfortunately it is impossible to reproduce without manually + destroying the syntactic information in the Python buffer, but it has + been observed in practice. In that case it can happen that the syntax + caches get sufficiently out of whack so that there appear to be + overlapping strings in the buffer. As Python has no nested strings, + this situation is impossible and leads to an infloop in + `python-nav-end-of-statement'. Protect against this by checking + whether the search for the end of the current string makes progress. + + * python.el (python-nav-end-of-statement): Protect against infloop. + * progmodes/python-tests.el + (python-tests--python-nav-end-of-statement--infloop): Add unit test. + +2017-03-23 Michael Albinus + + * doc/lispref/os.texi (File Notifications): + + Strengthen the recommendation to use filenotify.el. + +2017-03-23 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-03-22 getopt: merge from glibc + * build-aux/config.sub, lib/getopt.c, lib/getopt.in.h: + * lib/getopt1.c, lib/getopt_int.h: Copy from gnulib. + * lib/gnulib.mk.in: Regenerate. + +2017-03-23 Michael Albinus + + Use lexical-bind in Tramp + + * lisp/net/tramp*.el: Add lexical-binding cookie. Move declarations up. + + * lisp/net/tramp-adb.el (tramp-adb-parse-device-names): Use `push' + rather than `add-to-list'. + (tramp-adb-get-device): Remove unused variable. + + * lisp/net/tramp-gvfs.el (tramp-gvfs-parse-device-names): Remove unused + variable. + + * lisp/net/tramp.el (auto-save-file-name-transforms): Declare. + (tramp-find-file-name-coding-system-alist): Use `push' rather + than `add-to-list'. + + * test/lisp/net/tramp-tests.el: Add lexical-binding cookie. + Require 'dired. Move declarations up. + (tramp-test32-make-nearby-temp-file): Wrap `make-nearby-temp-file' + and `temporary-file-directory' calls with `with-no-warnings'. + (tramp-test35-asynchronous-requests): Mark unused variable. + +2017-03-23 Kaushal Modi + Noam Postavsky + + Do not include comment start chars in ffap string + + * lisp/ffap.el (ffap-string-at-point): If the point is in a comment, + ensure that the returned string does not contain the comment start + characters (especially for major modes that have '//' as comment start + characters). Otherwise, in a major mode like c-mode, with `ido-mode' + enabled and `ido-use-filename-at-point' set to `guess', doing "C-x + C-f" on a "//foo" comment will initiate an attempt to access a path + "//foo" (Bug#24057). + +2017-03-23 Martin Rudalics + + c:/Temp/gtk-window-move/ChangeLog.txt + +2017-03-22 Michael Albinus + + Fix filenotify.el issue for kqueue + + * lisp/filenotify.el (file-notify-add-watch): Use directory + for remote file name handlers. + + * test/lisp/filenotify-tests.el (file-notify-test01-add-watch): + Create/delete temporary file only for "kqueue". + (file-notify-test02-rm-watch): Create/delete temporary files. + +2017-03-22 Michael Albinus + + Extend `file-notify-test02-rm-watch' + + * test/lisp/filenotify-tests.el (file-notify-test02-rm-watch): + Expect it failed for inotify. Divide tests into different + `unwind-protect' clauses. Check, that removing watch + descriptors out of order do not harm. (Bug#26126) + +2017-03-22 Noam Postavsky + + * test/lisp/emacs-lisp/lisp-mode-tests.el (indent-subsexp): Test for Bug#26187 + +2017-03-22 Graham Dobbins (tiny change) + + * lisp/emacs-lisp/lisp-mode.el (indent-sexp): Fix null endpos case + +2017-03-21 Paul Eggert + + Improve configure --with-pop etc. diagnostics + + * configure.ac: Improve diagnostics re --with-pop and + --with-mailutils (Bug#26102). + +2017-03-21 Eli Zaretskii + + Revert "Make --without-pop the default." + + This reverts commit 9319de675e395517f9a7b50cae1a3aad9cd0abc2. + +2017-03-21 Paul Eggert + + Don’t remove dependency files when configuring + + Problem reported by Tom Tromey in: + http://lists.gnu.org/archive/html/emacs-devel/2017-03/msg00533.html + * configure.ac: Don’t remove */*.o and */deps/* when + --enable-autodepend is in effect. + +2017-03-21 Paul Eggert + + Make --without-pop the default. + + Suggested by Angelo Graziosi in: + http://lists.gnu.org/archive/html/emacs-devel/2017-03/msg00431.html + * configure.ac: Change the default from --with-pop to + --without-pop. Adjust diagnostics to match. + +2017-03-21 Paul Eggert + + Streamline dependency-file generation + + * configure.ac (AUTODEPEND_PARENTS): New var. + mkdir the dependency directories here, to simplify ‘make’. + Remove dependency files just before outputting Makefiles, so that + they are preserved if ‘configure’ exits early due to some other problem. + * lib/Makefile.in, lwlib/Makefile.in, oldXMenu/Makefile.in: + * src/Makefile.in: Adjust deps strategies to be similar, as follows: + (MKDEPDIR): Remove. All uses removed. This cuts down on the + number of processes spun off by ‘make’. + (clean mostlyclean): Remove $(DEPDIR) contents, not $(DEPDIR) itself. + (distclean): Remove $(DEPDIR) itself. + * lwlib/Makefile.in (all): Move to front, so that depdir includes + do not alter default action. + +2017-03-21 Paul Eggert + + Port and simplify example sh script + + * doc/misc/org.texi (noweb-ref): Simplify shell script example and + don’t use ‘tail -1’, which is not portable. + +2017-03-21 Noam Postavsky + + Narrow scope of modification hook renabling in org-src fontification + + Modification hooks should be enabled while modifying text in the + org-src temp buffer, but in 2017-01-29 "Call modification hooks in + org-src fontify buffers" the hooks were enabled also for modifications + to the original org buffer. This causes fontification errors when + combined with certain packages, as reported in + http://lists.gnu.org/archive/html/emacs-orgmode/2017-03/msg00420.html. + + * lisp/org/org-src.el (org-src-font-lock-fontify-block): Reduce scope + of inhibit-modification-hooks let-binding. + +2017-03-21 Tino Calancha + + epatch: Save right backups in Git multipatches + + Multipatches on N Git files save wrong backups for + N-1 files; only the last one has a correct backup (Bug#26084). + * lisp/vc/diff-mode.el (diff-file-junk-re): Add 'Prereq: ' + * lisp/vc/ediff-ptch.el (ediff-map-patch-buffer): Use 'diff-file-junk-re'. + * test/lisp/vc/ediff-ptch-tests.el (ediff-ptch-test-bug25010): + Rename from ibuffer-test-bug25010. + (ediff-ptch-test-bug26084): New test. + +2017-03-21 Michael R. Mauger + + * lisp/progmodes/sql.el: Version 3.6 + + (sql-login-params): Added :must-match for completition of + `server' and `database' login parameters. + (sql-sqlite-login-params, sql-postgres-login-params): Set + :must-match to `confirm'. + (sql-get-login-ext): Use :must-match value to control + `read-file-name' or `completing-read'. + (sql-connect): Added optional BUF-NAME parameter; Reworked + connection variable processing; Pass buffer name to + `sql-product-interactive'. + (sql-product-interactive): Pass buffer name along. + (sql-comint): Add optional BUF-NAME and calculate reasonable default. + (sql-comint-oracle, sql-sybase-comint, sql-comint-informix) + (sql-comint-sqlite, sql-comint-mysql, sql-comint-solid) + (sql-comint-ingres, sql-comint-ms, sql-comint-postgres) + (sql-comint-interbase, sql-comint-db2, sql-comint-linter) + (sql-comint-vertica): Add optional BUF-NAME, pass to + `sql-comint'. + (sql-oracle--list-oracle-name): New function. + (sql-oracle-list-all): Use it. + (sql-oracle-completion-object): Enhanced. + +2017-03-20 Vincent Belaïche + + Solve ses-recalculate-cell updating only current line bug. + + * lisp/ses.el (ses-recalculate-cell): Add optional argument + ses--curcell to avoid overwriting ses--curcell when function is + called from ses-recalculate-all. Update docstring accordingly. + (ses-recalculate-all): Call ses-recalculate-cell with argument + ses--curcell to avoid its overwriting. + +2017-03-20 Paul Eggert + + Fix problem with out-of-date dependencies + + Problem reported by Robert Marshall in: + http://lists.gnu.org/archive/html/emacs-devel/2017-03/msg00501.html + Although this problem has been with us for a while, the recent + change from Automake to GNU Make exposed it again. + * configure.ac (AUTO_DEPEND): When autodepending, clean out any + leftover dependency and object files, since the previous sources' + dependencies may disagree with the current ones. Reconfiguring + typically needs to force a rebuild anyway. + +2017-03-20 Mark Oteiza + + Simpler filter implementation + + * lisp/play/dunnet.el (dun-endgame-question): Get or set + dun-endgame-questions one time only. Use dolist and an index to + prune the list. + +2017-03-20 Mark Oteiza + + * lisp/button.el (forward-button): Use user-error instead. + +2017-03-19 Paul Eggert + + Merge from gnulib + + This gets Emacs working again with HP-UX Itanium cc. + It incorporates: + 2017-03-19 stdalign: tweak version# and test for HP-UX IA64 + 2017-03-18 stdalign: restore previous behavior for HP-UX IA64 + 2017-03-17 stat-time, timespec: Support header files in C++ mode + 2017-03-17 stdalign: Make it work with HP-UX cc + 2017-03-17 flexmember: try to detect HP-UX 11.31 cc bug + 2017-03-16 stdint: Fix test compilation failure with HP-UX 11 cc. + 2017-03-14 gnulib-tool: don't produce tests with only snippets + 2017-03-14 limits-h: Make it work with HP-UX cc. + * etc/PROBLEMS: Remove now-obsolete entry for HP-UX 11.31. + * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. + * lib/limits.in.h, lib/stat-time.h, lib/stdalign.in.h: + * lib/stdint.in.h, lib/timespec.h, m4/flexmember.m4, m4/stdalign.m4: + Copy from gnulib. + +2017-03-19 Paul Eggert + + * ChangeLog.2: Merge from emacs-25. + +2017-03-19 Paul Eggert + + Fixups after merge from emacs-25 + + * etc/NEWS: Remove stray entry. + * etc/NEWS.25: Copy from Emacs emacs-25 etc/NEWS. + * lisp/textmodes/rst.el (rst-package-emacs-version-alist): + Make it nondecreasing. + +2017-03-19 Paul Eggert + + Merge from origin/emacs-25 + + d71e071 Improve documentation of interactive "r". + +2017-03-19 Paul Eggert + + Merge from origin/emacs-25 + + a094732 * etc/PROBLEMS: Say that HP-UX cc doesn't work. + 1925dd9 Fix duplicate wording in Emacs manual + 6de8429 * lisp/paren.el (show-paren--default, show-paren-function): A... + 2d671fd Fix wording in Emacs manual + a8766a2 Document how to customize input methods + 6eb8995 * lisp/net/eww.el (eww-reload): Doc fix. (Bug#25981) + aceac95 Fix warning message about native completion (Bug#25984) + a314c1f Clarify documentation of 'raise' and 'height' display specs + f366f6e Mention problems with GPaste in PROBLEMS + 6e788ef ; etc/PROBLEMS: Explain about the python+libedit problem (Bug... + 6406618 Fix doc strings in info.el + c1ed152 ; * src/keyboard.c (Fposn_at_point): Fix last change. + eed9677 Fix doc string of 'posn-at-point' + 0d5957e Documentation fix in elisp reference manual + +2017-03-19 Paul Eggert + + Merge from origin/emacs-25 + + ec4226d * lisp/woman.el (woman): Fix docstring prefix arg description. + 2b774fa Mention "editor" in Emacs man page header + ae60d0c Document problems with nerd-fonts + 2fdb5a9 ; Details about pinning Emacs to w32 task bar + 5c3105e * doc/lispref/modes.texi (Derived Modes): Make example more i... + 4c51ef4 Clarify what is the "cursor" + 8303c32 ; * etc/NEWS: Copyedits. + 3f7493e ; Fix a typo in comment + c54cf8d Improve commentary in lisp.h + 8b92f86 ; * admin/make-tarball.txt: Cross-reference admin/release-pro... + 0ba9932 Disable native completion for ipython (Bug#25067) + 38fc456 Fix a typo in ada-mode manual + 00e75ba ; * src/coding.c (Fencode_coding_region): Fix a typo in the d... + a541c21 Clarify documentation of 'bufferpos-to-filepos' and 'filepos-... + + # Conflicts: + # etc/NEWS + # etc/PROBLEMS + +2017-03-19 Paul Eggert + + Merge from origin/emacs-25 + + 02d9ad8 * admin/make-tarball.txt: Add documentation regarding the rel... + +2017-03-19 Paul Eggert + + Merge from origin/emacs-25 + + e1171de * CONTRIBUTE (Documenting your changes): Index new vars/comma... + +2017-03-19 Paul Eggert + + Merge from origin/emacs-25 + + ab0a60a ; * CONTRIBUTE (Generating ChangeLog entries): Drop duplicate... + 7e02a47 Index byte-compile-debug + 7c1e598 Document `byte-compile-debug' in the ELisp manual + 4d81eb4 Document variable `byte-compile-debug' + 72ef710 Fix call to debugger on assertion failure + ae8264c Call modification hooks in org-src fontify buffers + b3139da ; Fix last change in doc/lispref/strings.texi + c331f39 Improve documentation of 'format' conversions + 9f52f67 Remove stale functions from ert manual + c416b14 Fix a typo in Eshell manual + 06695a0 ; Fix a typo in ediff-merg.el + 954e9e9 Improve documentation of hooks related to saving buffers + 9fcab85 Improve documentation of auto-save-visited-file-name + 2236c53 fix typo in mailcap-mime-extensions + 85a3e4e Fix typos in flymake.el + a1ef10e More NEWS checking for admin.el's set-version + + # Conflicts: + # lisp/emacs-lisp/bytecomp.el + +2017-03-19 Paul Eggert + + Merge from origin/emacs-25 + + 5569e64 ; Spelling fixes + 24a5f57 * lisp/net/eww.el (eww-tag-meta): Handle single quoted URLs (... + 9b89896 * lisp/progmodes/sql.el (sql-product-alist): Doc tweak + 69b50f5 * lisp/progmodes/sql.el (sql-product-alist): Doc fix. (Bug#2... + 42eae54 Improve documentation of dabbrevs + b0ade0d Clarify that easy-menu-add is a nop (Bug#25382) + 3c69f2c * lisp/textmodes/rst.el (rst-package-emacs-version-alist): Fi... + + # Conflicts: + # lisp/textmodes/rst.el + +2017-03-19 Paul Eggert + + Merge from origin/emacs-25 + + 0e35405 Improve documentation of coding-systems + c2fd04c Improve definition of 'variable-pitch' face on MS-Windows + 16fb50d Fix an error message in python.el + a2a2073 Clarify major mode switching + fc38671 Add helpful comment to compile-command's docstring + ee65d85 Fix ':version' of 'select-enable-primary' + +2017-03-19 Paul Pogonyshev + + Fix bug in generator function with pcase (Bug#26068) + + * lisp/emacs-lisp/cl-macs.el (cl--sm-macroexpand): Remove some calls + to symbol-name. + +2017-03-19 Alan Mackenzie + + Fix chaotic indentation of C++ lambda. Enhance documentation thereof + + * lisp/progmodes/cc-engine.el (c-looking-at-inexpr-block): qualify an + invocation of c-on-identifier with a check we're not at the _end_ of an + identifier. + + * doc/misc/cc-mode.texi: (Tex title page): Remove @subtitlefont because the + perl versions of texi2dvi haven't implemented it. + (Syntactic Symbols): Note that `inlambda' is also used in C++ Mode, not just + in Pike Mode. + (Statement Block Symbols): Add a section illustrating a C++ lambda function. + (FAQ): Add a question about "excessive" indentation of the contents of a C++ + lambda function, and how to get rid of it. + +2017-03-19 Stefan Monnier + + Remove unused vars in cl-extra.el and tramp.el. + + * lisp/emacs-lisp/cl-extra.el (cl--print-table): Remove unused vars. + + * lisp/net/tramp.el (tramp-dissect-file-name): Remove unused `match'. + (outline-regexp, ls-lisp-use-insert-directory-program): Declare. + (tramp-find-foreign-file-name-handler): Mark unused arg, remove unused `v`. + +2017-03-19 Stefan Monnier + + Improve describe-symbol's layout of slots when describing types + + * lisp/emacs-lisp/cl-extra.el (cl--print-table): New function. + (cl--describe-class-slots): Use it. + +2017-03-18 Michael Albinus + + Fix Bug#26156 + + * lisp/net/tramp.el (tramp-completion-file-name-handler-alist): + : Remove handler. (Bug#26156) + +2017-03-18 Stefan Monnier + + * lisp/obarray.el (obarray-size): Avoid compiler warning. + +2017-03-18 Eli Zaretskii + + Fix last change in lib/Makefile.in + + * lib/Makefile.in (srcdir): Define, as including + $(srcdir)/../nt/gnulib-cfg.mk needs that. + +2017-03-18 Paul Eggert + + * configure.ac: Fix typo in diagnostic. + +2017-03-18 Paul Eggert + + Port out-of-source builds to windows-nt + + Problem reported by Angelo Graziosi in: + http://lists.gnu.org/archive/html/emacs-devel/2017-03/msg00431.html + * lib/Makefile.in: Prepend $(srcdir) to ../nt/gnulib-cfg.mk, + to handle out-of-source builds if windows-nt. + +2017-03-17 Eli Zaretskii + + MS-Windows followup for switch from Automake + + * nt/INSTALL: + * nt/INSTALL.W64: Remove references to Automake. (Bug#26100) + +2017-03-17 Eli Zaretskii + + Improve documentation of interactive "r". + + * doc/lispref/commands.texi (Interactive Codes): Mention that mark + must be set for "r" to work. + +2017-03-17 Paul Eggert + + Fixups for GNU Make switchover + + This fixes some minor problems introduced in the recent switch to GNU + Make, discovered by further testing. Without some of these changes + 'make -j' would sometimes have race conditions caused by missing + dependencies. (Bug#26100) + * .gitignore: Remove src/stamp-h.in, src/stamp-h1. + * Makefile.in ($(MAKEFILE_NAME)): Depend on configure, not + src/config.in, since the former's timestamp now represents + the latter's. + ($(srcdir)/configure): Use plain ./autogen.sh, for consistency + with other autogen.sh invocations. + ($(srcdir)/src/stamp-h.in): + Remove rule, as this file is no longer created. + * Makefile.in (top_distclean): + * src/Makefile.in (bootstrap-clean): + No need to remove stamp-h1, as that was an Automake byproduct + and Automake is no longer in use. + * lib/Makefile.in, src/Makefile.in: + (AUTOCONF_INPUTS, $(top_srcdir)/configure): Remove. + (../config.status, Makefile): Simplify by limiting dependencies + to files we care about and files in the repository, and by + using just one file to represent the timestamps on multiple + targets updated by the same rule. + * autogen.sh: Do not create or use src/stamp-h.in. + Instead, have 'find' test the two output files directly. + +2017-03-17 Paul Eggert + + Switch from Automake to GNU Make + + Emacs assumes GNU Make, and GNU Make has much of the functionality of + Automake built-in. The Emacs build process uses Automake primarily + because Emacs uses some Gnulib code and Gnulib formerly required + Automake. Now that Gnulib no longer requires Automake, Emacs can + stop using Automake and this should simplify Emacs maintenance + in the future (Bug#26100). Although this patch may look long, most of + it is generated automatically: the changes to build-aux/config.guess, + build-aux/config.sub, build-aux/install-sh, and lib/gnulib.mk.in are + all done by admin/merge-gnulib. + * .gitignore: Remove build-aux/ar-lib, build-aux/compile, + build-aux/config.guess, build-aux/config.sub, build-aux/depcomp, + build-aux/install-sh, build-aux/missing, and lib/Makefile.in, + as they are no longer built by autogen.sh. + Add lib/gnulib.mk, as it is now built by 'configure'. + Remove nt/gnulib.mk, as it is no longer built by 'make'. + * INSTALL.REPO, README, admin/make-tarball.txt: + Remove mention of Automake. + * Makefile.in (AUTOCONF, AUTOMAKE, AUTOHEADER, ACLOCAL, lib) + (AUTOCONF_INPUTS, ACLOCAL_PATH, ACLOCAL_INPUTS) + ($(srcdir)/aclocal.m4, AUTOMAKE_INPUTS) + ($(srcdir)/lib/Makefile.in, $(srcdir)/nt/gnulib.mk, am--refresh): + Remove. + ($(MAKEFILE_NAME)): Depend on lib/gnulib.mk.in. + ($(srcdir)/configure, $(srcdir)/src/stamp-h.in) + ($(srcdir)/src/config.in): + Use autogen.sh instead of doing it by hand. + * admin/merge-gnulib (AVOIDED_MODULES, avoided_flags)): + New vars, to simplify processing of avoided modules. + (GNULIB_TOOL_FLAGS): Move --avoid flags into AVOIDED_MODULES. + Add --gnu-make, and change makefile name to gnulib.mk.in. + Copy config.guess, config.sub, and install-sh too, since + Automake no longer does that for us. + * admin/notes/copyright: + * admin/update_autogen (genfiles): + Update list of files. + Remove hack for nt/gnulib.mk, a file that is no longer needed. + * autogen.sh (progs): Remove Automake. + (automake_min): Remove. + Build aclocal.m4 so that autoreconf need not use aclocal. + * build-aux/config.guess, build-aux/config.sub: + * build-aux/install-sh: + New files, copied from Gnulib. These are now updated by + admin/merge-gnulib instead by autogen.sh. + * configure.ac (AC_PROG_MAKE_SET, ACLOCAL_PATH, AM_CONDITIONAL): + Remove. + (AM_INIT_AUTOMAKE, AM_SILENT_RULES): Remove call. + (AC_PROG_CC_C_O): Call this instead of AM_PROG_CC_C_O. + (BUILDING_FOR_WINDOWSNT, HYBRID_MALLOC_LIB): Remove; no longer needed. + (--disable-silent-rules): New option, since Automake no longer + does this for us. + (AM_V, AM_DEFAULT_V): Set unconditionally, and do not bother + with AM_SUBST_NOTMAKE. + (AC_PROG_INSTALL): Add call. + (MAKEINFO): Do not bother with the 'missing' program. + (MAKEINFO, SYSTEM_TYPE): AC_SUBST. + (AC_CONFIG_FILES): Add Makefile, lib/gnulib.mk. + (SUBDIR_MAKEFILES): Remove duplication. + * lib/Makefile.am: Remove, replacing with: + * lib/Makefile.in: New file, with the old Makefile.am contents + and with the following changes: + (AUTOMAKE_OPTIONS, BUILT_SOURCES, CLEANFILES, EXTRA_DIST) + (MOSTLYCLEANDIRS, MOSTLYCLEANFILES, noinst_LIBRARIES, SUFFIXES) + (AM_CFLAGS, DEFAULT_INCLUDES, libegnu_a_SOURCES, libegnu_a_LIBADD) + (EXTRA_libegnu_a_SOURCES, libegnu_a_SHORTNAME, libegnu_a_CPPFLAGS): + Remove. + (VPATH, abs_top_builddir, top_builddir, top_srcdir, all, AM_V_AR) + (AM_V_CC, AM_V_GEN, AM_V_at, DEPDIR, DEPFLAGS, MKDEPDIR, SYSTEM_TYPE) + (libgnu.a, libegnu.a, ETAGS, $(ETAGS), tags, TAGS, clean) + (mostlyclean, distclean, bootstrap-clean, maintainer-clean): + New macros and rules, since Automake no longer does them. + Include ../nt/gnulib-cfg.mk if SYSTEM_TYPE is windows-nt, + instead of including ../nt/gnulib.mk if BUILDING_FOR_WINDOWS_NT. + Include dependency files if AUTO_DEPEND. + (ALL_CFLAGS, AUTOCONF_INPUTS, libgnu_a_OBJECTS, libegnu_a_OBJECTS): + New macros. + (bootstrap-clean): Depend on distclean, not maintainer-clean, + and remove gnulib.mk. + (AUTOCONF_INPUTS, $(top_srcdir)/configure, ../config.status, Makefile): + New macros and rules, copied from ../Makefile.in. + ($(libegnu_a_OBJECTS), $(libgnu_a_OBJECTS)): Depend on BUILT_SOURCES. + (.c.o, e-%.o): New generic rules. + * lib/gnulib.mk: Remove. + * lib/gnulib.mk.in: New file, which is built by autogen.sh + and contains much of what used to be in lib/gnulib.mk. + * m4/gnulib-common.m4: Copy from gnulib. + * make-dist: Do not distribute build-aux/compile, build-aux/depcomp, + build-aux/missing, build-aux/ar-lib, lib/Makefile.am, nt/gnulib.mk, + nt/gnulib-modules-to-delete.cfg. Distribute lib/Makefile.in, + lib/gnulib.mk.in, and nt/gnulib-cfg.mk instead. + * nt/Makefile.in (AM_V_GEN, am__v_GEN_, am__v_GEN_0) + (am__v_GEN_1, ${srcdir}/gnulib.mk): Remove. + * nt/gnulib-cfg.mk: New file, which supersedes ... + * nt/gnulib-modules-to-delete.cfg: ... this file, which is removed. + * src/Makefile.in (ACLOCAL_INPUTS): Remove. + (AUTOCONF_INPUTS): Merge ACLOCAL_INPUTS into it. + ($(top_srcdir)/configure, ../config.status, config.in Makefile): + Defer to parent Makefile. + +2017-03-17 Paul Eggert + + Don't suggest Mailutils on MS-Windows + + * configure.ac: Don't suggest GNU Mailutils on MS-Windows, as it + hasn't been ported. + +2017-03-17 Thien-Thi Nguyen + + Fix bug: Range-check integer ‘alpha’ frame parm value + + Typo introduced 2013-04-01, "Prefer < to > + in range checks such as 0 <= i && i < N". + + * src/frame.c (x_set_alpha): Use ‘ialpha’, not ‘alpha’. + +2017-03-17 Thien-Thi Nguyen + + Fix bug: Range-check integer ‘alpha’ frame parm value + + Typo introduced 2013-04-01, "Prefer < to > + in range checks such as 0 <= i && i < N". + + * src/frame.c (x_set_alpha): Use ‘ialpha’, not ‘alpha’. + +2017-03-17 Michael Albinus + + Fix Bug#26127 + + * lisp/filenotify.el (file-notify--rm-descriptor): Check, that + there is a function which could be called. (Bug#26127) + + * test/lisp/filenotify-tests.el (file-notify--test-cleanup): + Clear also `file-notify-descriptors'. + (file-notify--test-make-temp-name): Move up. + (file-notify-test02-rm-watch): New test. + (file-notify-test03-events, file-notify-test04-autorevert) + (file-notify-test05-file-validity) + (file-notify-test06-dir-validity) + (file-notify-test07-many-events, file-notify-test08-backup) + (file-notify-test09-watched-file-in-watched-dir) + (file-notify-test10-sufficient-resources): Rename. + +2017-03-17 Paul Eggert + + * etc/PROBLEMS: Say that HP-UX cc doesn't work. + +2017-03-17 Paul Eggert + + Emacs 'movemail' is now a configure-time option + + The new configure option --with-mailutils lets the builder say + that Emacs should assume that GNU Mailutils is installed, instead + of continuing to build and install its own limited and insecure + substitute for 'movemail'. + * INSTALL, etc/NEWS, etc/PROBLEMS: Mention --with-mailutils. + * configure.ac: Add --with-mailutils option. + (with_mailutils): New variable. + Do not bother configuring 'movemail' when not building it. + Warn about issues relating to --with-mailutils. + * doc/emacs/rmail.texi (Movemail): Mention --with-mailutils. + (Movemail, Remote Mailboxes): Document port numbers in + POP and IMAP URLs. + * lib-src/Makefile.in (with_mailutils): New macro. + (UTILITIES): Use it. + +2017-03-16 Stefan Monnier + + Add obarray-size and fix tests accordingly. Use obarrayp in cedet. + + * lisp/obarray.el (obarray-size): New function. + + * lisp/cedet/semantic/lex-spp.el (semantic-lex-spp-symbol) + (semantic-lex-spp-save-table, semantic-lex-spp-macros): + * lisp/cedet/semantic/bovine/c.el (semantic-c-describe-environment): + Use obarrayp. + + * test/lisp/obarray-tests.el (obarray-make-default-test) + (obarray-make-with-size-test): Use it. + +2017-03-16 Michael Albinus + + Document remote file name syntax change + + * doc/emacs/files.texi (Remote Files, Quoted File Names): + * doc/misc/org.texi (dir): Change examples to use a method. + + * doc/misc/tramp.texi (Top) [trampf]: Remove macro. Add + `Testing' menu entry. + (History): Fix typos. Mention syntax change. + (Configuration, Default Host, File name Syntax) + (File name completion, Frequently Asked Questions): + Change examples to use a method. + (External methods, Default Host, Multi-hops, Remote processes): + Fix typos. + (Default Method): Mention pseudo method "-". + (External packages): Rewrite intention of `non-essential'. + + * etc/NEWS: Mark recent Tramp entries as documented. + +2017-03-16 Stefan Monnier + + (semantic-lex-type-invalid): Fix nested backquote. + + * lisp/cedet/semantic/lex.el: Use lexical-binding. + (semantic-lex-type-invalid): Fix nested backquote. + (semantic-lex-map-symbols, semantic-lex-type-symbol) + (semantic-lex-keyword-symbol): Use obarrayp. + +2017-03-15 Michael Albinus + + * lisp/ido.el (ido-read-internal, ido-complete): Do not bind `non-essential'. + +2017-03-15 Mark Oteiza + + Write a named function + + * lisp/comint.el (comint-nonblank-p): New function. + (comint-input-filter): Use it. + +2017-03-15 Mark Oteiza + + Replace more nested ifs with cond + + This is a continuation of 0db5ba4 "Replace nested ifs with cond". + * lisp/play/dunnet.el (dun-special-object, dun-inven, dun-drop): + (dun-drop-check, dun-swim, dun-break): Use when and cond where + appropriate. + (dun-examine): Fix indentation. + (dun-doverb): Use when. + (dun-read-line): Refactor. + +2017-03-15 Noam Postavsky + + Recomplexify ‘delete-trailing-whitespace’ by treating \n as whitespace again + + Mostly reverts "Simplify ‘delete-trailing-whitespace’ by not treating + \n as whitespace" from 2016-07-04. Setting \n to non-whitespace + causes the regex engine to backtrack a lot when searching for + "\\s-+$" (Bug#26079). + + * lisp/simple.el (delete-trailing-whitespace): Don't change newline + syntax, search for "\\s-$" and then skip backward over trailing + whitespace. + +2017-03-14 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-03-14 snippets: move unadjusted snippet sources to lib + 2017-03-14 gnulib-tool: fix typo in comment output + 2017-03-14 snippets: work around GNU Make 3.82 VPATH + 2017-03-13 gnulib-tool: minor --gnu-make fixups + 2017-03-12 gnulib-tool: new option --gnu-make + * .gitignore: Remove lib/arg-nonnull.h, lib/c++defs.h, + lib/warn-on-use.h. Change exception from + build-aux/snippet/_Noreturn.h to lib/_Noreturn.h. + * admin/authors.el (authors-renamed-files-regexps): + * admin/notes/copyright, make-dist: + The snippet files moved from build-aux/snippet to lib. + * lib/_Noreturn.h: Rename from build-aux/snippet/_Noreturn.h. + * lib/arg-nonnull.h: Rename from build-aux/snippet/arg-nonnull.h. + * lib/c++defs.h: Rename from build-aux/snippet/c++defs.h. + * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. + * lib/warn-on-use.h: Rename from build-aux/snippet/warn-on-use.h. + +2017-03-14 Eli Zaretskii + + Fix duplicate wording in Emacs manual + + * doc/emacs/programs.texi (Which Function): Delete duplicate + wording. (Bug#26098) + +2017-03-14 Michael Albinus + + Reenable lost Tramp test case + + * test/lisp/net/tramp-tests.el (tramp-test24-file-name-completion): + Reenable lost test case. + +2017-03-14 Alan Third + + Revert "Remove NSEvent loop from ns_select (bug#25265)" + + This reverts commit 3bd2e9e975ed29daaf03ca7559e4664aade0674f. + +2017-03-14 Alan Third + + Revert "Add missing timeout value in ns_select" + + This reverts commit a65236214d9202fb69a6ba5169d4ac1a4bcb0b0d. + +2017-03-14 Alan Third + + Remove old macOS compatibility code + + * src/nsimage.m, src/nsmenu.m, src/nsterm.m: Remove code only for + macOS versions below 10.6 as they are not supported in Emacs 25+. + +2017-03-14 Michael Albinus + + Tune `tramp-completion-file-name-regexp-unified' + + * lisp/net/tramp.el (tramp-completion-file-name-regexp-unified): + Extend this regexp to match also "/". + +2017-03-14 Tino Calancha + + Show ancestor buffer in 3way merges + + Add an option ediff-show-ancestor', to control if the ancestor buffer + must be shown in 3way merges (Bug#25493); set it non-nil by default. + Add a toggle to change this option interactively; the original + value of the option is restored on exit. + + Update the window setup so that the ancestor buffer is + shown in 3way merges when ediff-show-ancestor is non-nil. + + Any operation on ediff windows must take in account the + ancestor window as well, when this is shown. + + * lisp/vc/ediff-init.el (ediff-show-ancestor): New option. + (ediff--show-ancestor-orig): New defvar. + * lisp/vc/ediff-wind.el (ediff-window-Ancestor): New defvar. + (ediff-setup-windows-plain-merge, ediff-setup-windows-multiframe-merge): + Display ancestor buffer if ediff-show-ancestor is non-nil. + (ediff-keep-window-config): Expect ancestor window in + ediff-window-config-saved. + (ediff-window-alist): Add entry for the ancestor window. + * lisp/vc/ediff-util.el (ediff-setup-control-buffer): + ediff-window-config-saved contains ancestor window. + (ediff-show-ancestor): Delete this command. + (ediff-setup-keymap): Bind ediff-toggle-show-ancestor to '/' for merge jobs. + (ediff-update-diffs): Compute new diffs using ancestor buffer in 3way merges; + don't cheat it to think that is performing a comparison, that trick is not + necessary anymore: simply call 'ediff-setup-diff-regions-function' + with file-A, file-B and the file ancestor. + (ediff-recenter): Update doc string. Consider the ancestor buffer. + (ediff--check-ancestor-exists): New defun. + (ediff-toggle-show-ancestor): New command; toggle ediff-show-ancestor. + (ediff--restore-options-on-exit): Restore ediff-show-ancestor on exit. + (ediff-scroll-vertically, ediff-scroll-horizontally) + (ediff-operate-on-windows): Consider the ancestor as well. + * lisp/vc/ediff-help.el (ediff-long-help-message-merge): + List ediff-toggle-show-ancestor. + * doc/misc/ediff.texi (Introduction, Quick Help Commands): Update manual. + +2017-03-14 Tino Calancha + + diff-mode: Improve default faces for buffer ancestor + + * lisp/vc/ediff-init.el (ediff-current-diff-Ancestor) + (ediff-fine-diff-Ancestor): Use defaults consistent with + faces for 'ediff-buffer-A' and 'ediff-buffer-B'. + +2017-03-14 Hong Xu + + * lisp/paren.el (show-paren--default, show-paren-function): Add docstring. + +2017-03-13 Paul Eggert + + Fix make-dist typo + + * make-dist: Fix typo introduced in the Bug#25895 fix. + +2017-03-13 Eli Zaretskii + + Fix wording in Emacs manual + + * doc/emacs/text.texi (Paragraphs): Fix a garbled sentence. + (Bug#26086) + +2017-03-13 Michael Albinus + + etc/NEWS: Remote file names require a method. + +2017-03-13 Michael Albinus + + Require method in remote file name syntax + + * lisp/minibuffer.el (completion--nth-completion): + Do not bind `non-essential'. + + * lisp/net/tramp-adb.el (tramp-adb-maybe-open-connection): + * lisp/net/tramp-gvfs.el (tramp-gvfs-maybe-open-connection): + * lisp/net/tramp-sh.el (tramp-maybe-open-connection): + * lisp/net/tramp-smb.el (tramp-smb-maybe-open-connection): Do not call + `tramp-check-proper-method-and-host'. + + * lisp/net/tramp-sh.el (tramp-ssh-controlmaster-options): Better traces. + (tramp-maybe-open-connection): Do not use argument for + ´tramp-completion-mode-p'. + + * lisp/net/tramp.el (tramp-default-method-marker): New defconst. + (tramp-prefix-format, tramp-postfix-method-format) + (tramp-prefix-ipv6-format, tramp-postfix-ipv6-format) + (tramp-prefix-port-format, tramp-postfix-host-format) + (tramp-file-name-regexp, tramp-completion-file-name-regexp): + Use `eq' instead of `eqal'. + (tramp-method-regexp, tramp-domain-regexp) + (tramp-remote-file-name-spec-regexp) + (tramp-file-name-regexp-unified) + (tramp-completion-file-name-regexp-unified) + (tramp-completion-file-name-regexp-separate): Adapt regexp. + (tramp-completion-file-name-handler-alist) + (tramp-run-real-handler): Autoload them. + (tramp-find-method): Handle `tramp-default-method-marker'. + (tramp-check-proper-method-and-host) + (tramp-completion-run-real-handler): Remove them. + (tramp-error-with-buffer, tramp-connectable-p): Do not use + argument for ´tramp-completion-mode-p'. + (tramp-find-foreign-file-name-handler): Remove COMPLETION + argument. Do not apply heuristic for completion. + (tramp-file-name-handler): Do not modify `non-essential'. + (tramp-completion-file-name-handler): Change implementation. + (tramp-autoload-file-name-handler) + (tramp-completion-handle-file-name-all-completions): + Call `tramp-run-real-handler'. + (tramp-completion-mode-p): Do not autoload. Remove argument. + Do not apply heuristic for completion. + (tramp-completion-dissect-file-name): Simplify implementation. + (tramp-handle-file-name-as-directory): Call `tramp-connectable-p'. + + * test/lisp/net/tramp-tests.el (tramp-test01-file-name-syntax) + (tramp-test02-file-name-dissect) + (tramp-test03-file-name-defaults) + (tramp-test06-directory-file-name): Adapt to the new syntax. + (tramp-test11-copy-file, tramp-test12-rename-file) + (tramp--test-check-files): Deactivate temporarily tests with + quoted file names. + (tramp-test16-directory-files, tramp-test17-insert-directory): + Adapt tests. + (tramp-test24-file-name-completion): Do not check for + completion mode. + (tramp-test31-make-auto-save-file-name): Deactivate temporarily + two tests. + +2017-03-13 Eli Zaretskii + + Fix bidi paragraph direction when inserting text at newline + + * src/insdel.c (invalidate_buffer_caches): Invalidate the bidi + paragraph cache when inserting immediately after a newline. + (Bug#26083) + +2017-03-13 Tino Calancha + + * test/lisp/emacs-lisp/cl-print-tests.el (cl-print-tests-1): Fix regexp. + +2017-03-13 Stefan Monnier + + * lisp/emacs-lisp/cl-print.el (cl-print-compiled): New variable + + (cl-print-object) : Print the docstring and + interactive form. Obey cl-print-compiled. + +2017-03-13 Noam Postavsky + + Fix indent-sexp when called from inside a string (Bug#21343) + + * lisp/emacs-lisp/lisp-mode.el (indent-sexp): Get initial syntax parse + state from `syntax-ppss'. + +2017-03-13 Noam Postavsky + + * lisp/emacs-lisp/lisp-mode.el (indent-sexp): Simplify. + + * test/lisp/emacs-lisp/lisp-mode-tests.el (indent-sexp): + (indent-subsexp, indent-sexp-in-string): New tests. + +2017-03-12 Stefan Monnier + + Use switch on pseudovector types; plus cleanups along the way + + * src/lisp.h (PSEUDOVECTOR_TYPE): New function, extracted from mark_object. + (PSEUDOVECTOR_TYPEP): Change type of `code'. + + * src/alloc.c (sweep_vectors): Remove out-of-date assertion. + (mark_object): Use PSEUDOVECTOR_TYPE. + + * src/data.c (Ftype_of): Use switch on pvec type. + + * src/print.c (print_object): Use switch on pvec type. + + * lisp/emacs-lisp/cl-generic.el (cl--generic-typeof-types): + Add recently added types. + +2017-03-12 Paul Eggert + + Install update-game-score only on request + + Most distributions do not install update-game-score properly + due to setuid/setgid complications, so install it only when + the installer specifies a user or group (Bug#25895). + * .gitattributes: Remove lib-src/update-game-score.exe.manifest. + * Makefile.in (gameuser, gamegroup, use_gamedir, PATH_GAME): + New vars. + (epaths-force): Use PATH_GAME. + (uninstall): Remove snake-scores and tetris-scores only if shared. + * configure.ac: Default --with-gameuser to 'no'. + (UPDATE_MANIFEST): Remove. + * etc/NEWS: Mention this. + * lib-src/Makefile.in (UPDATE_MANIFEST): Remove. + (use_gamedir): New macro. + (UTILITIES): Remove update-game-score unless use_gamedir. + (SCRIPTS): Remove $(UPDATE_MANIFEST). + ($(DESTDIR)${archlibdir}): Install game directory program and data + only if use_gamedir. + * lib-src/update-game-score.exe.manifest: Remove, as + update-game-score is no longer installed on MS-Windows. + * lisp/play/gamegrid.el (gamegrid-add-score-with-update-game-score): + Use auxiliary program only if setuid or setgid. + * make-dist: Do not distribute update-game-score.exe.manifest. + * src/callproc.c (init_callproc): + Set Vshared_game_score_directory based on PATH_GAME, not DOS_NT. + (syms_of_callproc): Remove unnecessary initialization of + Vshared_game_score_directory. + +2017-03-12 Simen Heggestøyl + + Add `touch-action' to list of CSS properties + + * lisp/textmodes/css-mode.el (css-property-alist): Add `touch-action' + property. + +2017-03-12 Eli Zaretskii + + Teach etags to process ENUM_BF correctly + + * lib-src/etags.c (sym_type): New enumeration value st_C_enum_bf. + (hash): Regenerated values for asso_values[] array. + (in_word_set): Update values of TOTAL_KEYWORDS and + MAX_HASH_VALUE. Add "ENUM_BF" to the wordlist[] array. + (in_enum_bf): New file-global variable. + (consider_token): Skip ENUM_BF if not in a macro definition. + (C_entries): Reset the in_enum_bf flag when past its closing + parenthesis. + + * test/manual/etags/ETAGS.good_1: + * test/manual/etags/ETAGS.good_2: + * test/manual/etags/ETAGS.good_3: + * test/manual/etags/ETAGS.good_4: + * test/manual/etags/ETAGS.good_5: + * test/manual/etags/ETAGS.good_6: + * test/manual/etags/CTAGS.good: Adapt to changes in etags. + +2017-03-12 Michael Albinus + + Use path/to/file instead of path/to.file in tramp.texi + + * doc/misc/tramp.texi (Configuration, File name Syntax): + Use path/to/file instead of path/to.file. + +2017-03-12 Paul Eggert + + Remove some stray gnulib files + + * admin/merge-gnulib: rm m4/gnulib-tool.m4 too. + (GNULIB_MODULES): Remove unsetenv, as it is not needed and + the --avoid=unsetenv option avoided most of it anyway. + * lib/unsetenv.c, m4/gnulib-tool.m4, m4/setenv.m4: Remove. + * lib/gnulib.mk: Regenerate. + +2017-03-12 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-03-11 gnulib-common.m4: avoid aclocal.m4 bloat + * doc/misc/texinfo.tex, m4/gnulib-common.m4: Copy from gnulib. + +2017-03-12 Glenn Morris + + Remove trivial duplication in epg-config + + * lisp/epg-config.el (epg-config--program-alist): + Use epg-gpg-minimum-version. + +2017-03-12 Glenn Morris + + Small epg-find-configuration improvement + + * lisp/epg-config.el (epg-find-configuration): + Handle epg-gpg-program customized but not saved. (Bug#25947) + +2017-03-11 Stefan Monnier + + Improve last change + + * lisp/emacs-lisp/lisp-mode.el (lisp-el-font-lock-keywords-2): + Use ppss to check escaping and add help-echo. + +2017-03-11 Stefan Monnier + + Highlight useless backslashes in Elisp strings + + * lisp/emacs-lisp/lisp-mode.el (lisp-el-font-lock-keywords-2): + Put warning face on backslashes that have no effect. + +2017-03-11 Eli Zaretskii + + Document how to customize input methods + + * doc/emacs/mule.texi (Input Methods): Document how to customize + input methods. + +2017-03-11 Eli Zaretskii + + * lisp/net/eww.el (eww-reload): Doc fix. (Bug#25981) + +2017-03-11 Eli Zaretskii + + Fix generation of nt/gnulib.mk on macOS + + * nt/Makefile.in (${srcdir}/gnulib.mk): Don't use the -f- option + to Sed, as that is not portable with non-GNU Sed variants. + (Bug#26043) + +2017-03-11 Eli Zaretskii + + Avoid aborts/assertion violations due to 'vim-empty-lines-mode' + + * src/xdisp.c (handle_single_display_spec): If position to be + restored after processing the display property comes from an + overlay, protect against that overlay's end point being outside of + the narrowed region. + Reported by Filipe Silva in + http://lists.gnu.org/archive/html/emacs-devel/2017-03/msg00176.html. + +2017-03-10 Glenn Morris + + Small improvement for epa-display-error (bug#24553) + + * lisp/epa.el (epa-display-error): Report the actual program in use. + +2017-03-10 Paul Eggert + + Tweak X toolkit code to pacify modern GCC + + * lwlib/lwlib-Xaw.c, lwlib/lwlib-Xm.c, lwlib/lwlib.c: + Don’t include , since this code now calls emacs_abort + rather than abort. + * lwlib/lwlib-Xaw.c (make_dialog, xaw_generic_callback) + (wm_delete_window): + * lwlib/lwlib-Xm.c (make_menu_in_widget, do_call): + * lwlib/lwlib.c (instantiate_widget_instance, lw_make_widget): + * lwlib/xlwmenu.c (abort_gracefully, draw_separator) + (separator_height, XlwMenuInitialize): + Use emacs_abort, not abort. Without this change, some calls + to ‘abort’ were invalid, as stdlib.h was not always included. + * src/widget.c (resources, emacsFrameClassRec): + * src/xfns.c (x_window) [USE_X_TOOLKIT]: + * src/xmenu.c (create_and_show_popup_menu) [USE_X_TOOLKIT]: + * src/xterm.c (emacs_options) [USE_X_TOOLKIT}: + (x_term_init) [USE_X_TOOLKIT]: + Cast string constants to char * to pacify --enable-gcc-warnings. + +2017-03-10 Michael Albinus + + * doc/misc/tramp.texi (Android shell setup): Require adb program + +2017-03-10 Michael Albinus + + Adapt tramp-tests.el + + * test/lisp/net/tramp-tests.el (tramp-test06-directory-file-name) + (tramp-test24-file-name-completion): Call + `tramp-completion-mode-p' with argument. + +2017-03-10 Thien-Thi Nguyen + + [doc] Replace bindat example: s/fortune cookie/rfc868 payload/ + + * doc/lispref/processes.texi (Bindat Examples): + Mention two examples in intro blurb; rewrite first example. + +2017-03-10 Paul Eggert + + Simplify checks for xdg-open and xdg-email + + browse-url's xdg-open detection was too picky on some GNU/Linux + desktops; see Bug#25778. Simplify the code by assuming xdg-open works + if it is executable, as nowadays this is more likely to be correct than + trying to use heuristics from a few years ago. Don't test for nohup: it + is ineffective nowadays, as xdg-open's child uses the default action for + SIGHUP even if xdg-open's invoker ignores SIGHUP. While we're at it, + allow for Wayland here, as "emacs -nw" might be running in a non-X + Wayland terminal. + * lisp/mail/emacsbug.el (report-emacs-bug-can-use-xdg-email): + * lisp/net/browse-url.el (browse-url-can-use-xdg-open): + Simplify to a test for DISPLAY and whether the helper program is + executable. Allow WAYLAND_DISPLAY as an option. + +2017-03-09 Vibhav Pant + + Byte compile cond clauses without any bodies correctly. + + * lisp/emacs-lisp/bytecomp.el (byte-compile-cond-jump-table): When a + cond clause has no body, push t on to the stack. + +2017-03-09 Michael Albinus + + Fix bug#23006 + + * lisp/minibuffer.el (completion--nth-completion): + Let-bind `non-essential'. + + * lisp/net/tramp.el (tramp-completion-mode): Fix docstring. + (tramp-completion-mode-p): Optional parameter VEC. Replace + check for `last-input-event' by analysing VEC argument. + (tramp-error-with-buffer, tramp-file-name-handler) + (tramp-connectable-p, tramp-handle-file-name-as-directory): + * lisp/net/tramp-sh.el (tramp-maybe-open-connection): Use it. + +2017-03-09 Vibhav Pant + + etc/NEWS: Add entry for new `switch' bytecode. + +2017-03-08 Paul Eggert + + * src/data.c (arithcompare): Add comments. + +2017-03-08 Glenn Morris + + Update a cl-print test + + * test/lisp/emacs-lisp/cl-print-tests.el (cl-print-tests-1): + Update for recent change in cl-print-object function output. + +2017-03-08 Sam Steingold + + Replace change-log-date-face -> change-log-date + + This fixes c430f7e23fc2c22f251ace4254e37dea1452dfc3. + +2017-03-08 Michael Albinus + + Fix bug#26011 + + * lisp/net/tramp-sh.el (tramp-do-copy-or-rename-file-via-buffer): + Check, whether file is too large. (Bug#26011) + +2017-03-08 Andreas Schwab + + * data.c (minmax_driver): Use CHECK_NUMBER_OR_FLOAT_COERCE_MARKER. + (Fmax, Fmin): Restore documentation. + + * data.c (cons_to_unsigned, cons_to_signed, Fstring_to_number): Reorder + comparisons that are written backward. + +2017-03-08 Thien-Thi Nguyen + + [doc elisp] Add some index entries for "old" advice mechanism + + * doc/lispref/functions.texi (Porting old advice): + Add one @cindex and two @findex entries. + +2017-03-08 Paul Eggert + + * etc/NEWS: Adjust to match previous patch. + +2017-03-08 Paul Eggert + + min and max should not return markers + + Problem reported by Glenn Morris in: + http://lists.gnu.org/archive/html/emacs-devel/2017-03/msg00147.html + * src/data.c (minmax_driver): Convert any marker result to an + integer, since some callers assume this. + * test/src/data-tests.el (data-tests-max, data-tests-min): + Test for this. + +2017-03-08 Stefan Monnier + + * lisp/emacs-lisp/cl-print.el (cl-print-object): Use #f(..) for functions. + +2017-03-08 Alan Third + + Add missing timeout value in ns_select + + * src/nsterm.m (ns_select): Set timeout to distant future when relying + on fd_handler's timeout. + +2017-03-07 Glenn Morris + + * admin/update_autogen: Ensure nt/gnulib.mk exists, for autoreconf. + +2017-03-07 Paul Eggert + + Remove isnan hack for Solaris 10 gcc 3.4.3 + + This seems to have been a false alarm (Bug#26018). + * src/data.c (isnan): + * src/floatfns.c (isfinite, isnan): + Use standard implementation if available. + +2017-03-07 Eli Zaretskii + + Support browsing URLs with embedded spaces on MS-Windows + + * lisp/net/browse-url.el (browse-url-default-windows-browser): + Unhex %XX hex-encoded characters, as w32-shell-execute doesn't + support that in file:// URLs. (Bug#26014) + +2017-03-07 Paul Eggert + + Define copysign on all platforms + + * configure.ac (copysign): Remove test. + * src/floatfns.c (signbit): New macro, if not already defined. + (Fcopysign): Use it instead of copysign. + (Fcopysign, syms_of_floatfns): Define the function on all platforms. + +2017-03-07 Phillip Lord + + Revert "Replace ldefs-boot with a much smaller file" + + This reverts commit c27b645956a11fab1dd8fa189254d525390958f5. + + This commit has been reverted because the new mechanism was too + sensitive to changes in the lisp source, generation of new ldefs-boot + files was platform specific and resulted in warnings about undefined + variables. + + See also 11436e2890d. + +2017-03-07 Phillip Lord + + Revert "Record autoloads till emacs dump" + + This reverts commit 72c668a9042ac6475eadedfee5c87fb1e6b2d753. + + This commit has been reverted because the new mechanism was too + sensitive to changes in the lisp source, generation of new ldefs-boot + files was platform specific and resulted in warnings about undefined + variables. + + See also 11436e2890d. + +2017-03-07 Phillip Lord + + Revert "Remove unused ldefs-boot.el" + + This reverts commit ef8c9f8fc922b615aca91b47820d1f1900fddc96. + + This commit has been reverted because the new mechanism was too + sensitive to changes in the lisp source, generation of new ldefs-boot + files was platform specific and resulted in warnings about undefined + variables. + + See also 11436e2890d. + +2017-03-07 Phillip Lord + + Revert "Remove conditional includes from bootstrap" + + This reverts commit 1b946305182312faa7fcd838caf55dcb07b2ab04. + + This commit has been reverted because the new mechanism was too + sensitive to changes in the lisp source, generation of new ldefs-boot + files was platform specific and resulted in warnings about undefined + variables. + + See also 11436e2890d. + +2017-03-07 Phillip Lord + + Revert "Speed generation of ldefs-boot-auto" + + This reverts commit 7b5e1c8238ef961fd3305b1dce053b9bced684ba. + + This commit has been reverted because the new mechanism was too + sensitive to changes in the lisp source, generation of new ldefs-boot + files was platform specific and resulted in warnings about undefined + variables. + + See also 11436e2890d. + +2017-03-07 Phillip Lord + + Revert "Fix minor problems with loaddefs autogeneration" + + This reverts commit f2bd2c1e6476acc71e71f6cb2a1c56c5edd900ba. + + This commit has been reverted because the new mechanism was too + sensitive to changes in the lisp source, generation of new ldefs-boot + files was platform specific and resulted in warnings about undefined + variables. + +2017-03-07 Noam Postavsky + + Set default when asking for send-mail-function (Bug#25874). + + * lisp/mail/sendmail.el (sendmail-query-user-about-smtp): Pass first + option as default for `completing-read'. + +2017-03-07 Paul Eggert + + min and max now return one of their arguments + + * doc/lispref/numbers.texi (Comparison of Numbers): + * etc/NEWS: Document this. + * src/data.c (Amax, Amin): Remove constants. All uses removed. + (minmax_driver): New function. + (Fmax, Fmin): Use it instead of arith_driver. + * test/src/data-tests.el (data-tests-max, data-tests-min): New tests. + +2017-03-06 Alan Third + + Remove NSEvent loop from ns_select (bug#25265) + + * src/nsterm.m (ns_select): Remove event processing loop and replace + with simple test for a new event. + +2017-03-06 Eli Zaretskii + + A better fix for bug#25845 + + * src/xdisp.c (font_for_underline_metrics): New function. + * src/dispextern.h: Add its prototype. + * src/xterm.c (x_draw_glyph_string): + * src/w32term.c (x_draw_glyph_string): + * src/nsterm.m (ns_draw_text_decoration): Call it. This avoids + having identical code 3 times in 3 different files. + +2017-03-06 Noam Postavsky + + Fix warning message about native completion (Bug#25984) + + * lisp/progmodes/python.el (python-shell-completion-native-turn-on-maybe): + The relevant variable is `python-shell-completion-native-enable'. + +2017-03-06 Tom Tromey + + Fix typos in EIEIO manual + + * doc/misc/eieio.texi (Slot Options, Class Options): Fix typos. + +2017-03-05 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-03-04 dtotimespec: simplify + * lib/dtotimespec.c: Copy from gnulib. + +2017-03-05 Paul Eggert + + ffloor etc. now accept only floats + + * etc/NEWS: Say why. + * src/floatfns.c (Ffceiling, Fffloor, Ffround, Fftruncate): + Require arg to be float. + * test/src/floatfns-tests.el (fround-fixnum): Check this. + +2017-03-05 Eli Zaretskii + + Fix display of cursor on underlined text + + * src/nsterm.m (ns_draw_text_decoration): + * src/xterm.c (x_draw_glyph_string): + * src/w32term.c (x_draw_glyph_string): Compute the position and + thickness of the underline by looking for the first glyph of the + run of underlined glyphs that includes the glyph string we are + drawing. (Bug#25845) + +2017-03-05 Mark Oteiza + + Add more CL concept index items, print Concept Index + + * doc/misc/cl.texi: Print concept index. + (Generalized Variables, Variable Bindings): + (Dynamic Bindings, Function Bindings, Macro Bindings, Conditionals): + (Blocks and Exits, Iteration, Multiple Values): Add concept index + items. + +2017-03-05 Mark Oteiza + + Add 'loop facility' to the CL concept index + + * doc/misc/cl.texi (Loop Facility): Add "loop facility" as a concept + index item. + +2017-03-05 martin rudalics + + In `window--display-buffer' fix behavior reported in Bug#25946 + + * lisp/window.el (window--display-buffer): Set the dedicated + status of the window used and clear its history of previous + buffers also for the case that the window already shows the + buffer to be displayed. (Bug#25946) + +2017-03-05 Paul Eggert + + Compare and round more carefully + + * etc/NEWS: Document this. + * src/data.c (store_symval_forwarding): + * src/sound.c (parse_sound): + Do not botch NaN comparison. + * src/data.c (cons_to_unsigned, cons_to_signed): + Signal an error if a floating-point arg is not integral. + * src/data.c (cons_to_unsigned, cons_to_signed): + * src/fileio.c (file_offset): + Use simpler overflow check. + * src/dbusbind.c (xd_extract_signed, xd_extract_unsigned): + Avoid rounding error in overflow check. + (Fcar_less_than_car): Use arithcompare directly. + * test/src/charset-tests.el: New file. + +2017-03-05 Paul Eggert + + Fewer rounding errors with (format "%f" fixnum) + + * etc/NEWS: Document this. + * src/editfns.c (styled_format): When formatting integers via a + floating-point format, use long double instead of double + conversion, if long double’s extra precision might help. + +2017-03-05 Paul Eggert + + * src/floatfns.c (Fftruncate): Simplify via emacs_trunc. + + * src/editfns.c (styled_format): Omit unnecessary code for "%0d" etc. + +2017-03-04 Eli Zaretskii + + Clarify documentation of 'raise' and 'height' display specs + + * doc/lispref/display.texi (Other Display Specs): Clarify the + effect of 'height' display spec on the following 'raise'. + (Bug#25824) + +2017-03-04 Eli Zaretskii + + Fix header shown by Info 'L' command + + * lisp/info.el (Info-history-find-node): A better heading for the + list of visited nodes. (Bug#25876) + +2017-03-04 K. Handa + + Add a section about incorrect Bengali rendering. + +2017-03-04 Eli Zaretskii + + Fix minor problems with loaddefs autogeneration + + * admin/ldefs-clean.el (ldefs-clean): Bind coding-system-for-read + and coding-system-for-write, to produce a UTF-8 file with Unix + EOLs on MS-Windows. + + * lisp/ldefs-boot-manual.el (image-type): Add autoload cookie. + +2017-03-04 David Bremner (tiny change) + + Fix issues with dedicated windows in shr.el + + * lisp/net/shr.el (shr-pixel-buffer-width, shr-render-td-1): Make + the window not dedicated, to avoid errors if it was, before + setting its buffer temporarily. (Bug#25828) + +2017-03-04 Eli Zaretskii + + Mention problems with GPaste in PROBLEMS + + * etc/PROBLEMS (GPaste): Mention the problem in yanking caused by + GPaste, and its solution. (Bug#25902) + +2017-03-04 Glenn Morris + + Avoid duplicate gud menu items with gdb-mi + + * lisp/progmodes/gud.el (gud-menu-map): Avoid duplicate "Run" + entries in gdbmi mode. (Bug#23923) + +2017-03-03 Paul Eggert + + * src/editfns.c (styled_format): Omit unnecessary code. + +2017-03-03 Paul Eggert + + logb now works correctly on large integers + + * admin/merge-gnulib (GNULIB_MODULES): Add count-leading-zeros. + * etc/NEWS: Document the change. + * lib/count-leading-zeros.c, lib/count-leading-zeros.h: + * m4/count-leading-zeros.m4: New files, copied from Gnulib. + * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. + * src/floatfns.c: Include count-leading-zeros.h. + (Flogb): Do not convert fixnum to float before taking the log, + as the rounding error can cause the answer to be off by 1. + * src/lisp.h (EMACS_UINT_WIDTH): New constant. + * test/src/floatfns-tests.el (logb-extreme-fixnum): New test. + +2017-03-03 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-02-25 maintainer-makefile: Fix AC_PROG_SED with autoconf cache. + 2017-02-24 ftoastr: port to -Wdouble-promotion + * lib/ftoastr.c, m4/gnulib-common.m4: Copy from gnulib. + +2017-03-03 Eli Zaretskii + + Avoid duplicating characters recorded in macros + + * src/keyboard.c (record_char): Don't store in macro definitions + characters that came from executing a macro. (Bug#25860) + +2017-03-03 Eli Zaretskii + + Fix color component calculations in color.el + + * lisp/color.el (color-name-to-rgb): Use 16 bits per color component. + (color-rgb-to-hex): Accept an optional argument + DIGITS-PER-COMPONENT, defaulting to 4, and format the hexadecimal + notation either for 8 or 16 bits per component. (Bug#25890) + * lisp/net/shr-color.el (shr-color->hexadecimal): Call + color-rgb-to-hex with the optional argument of 2, to match color + processing on the Web. + +2017-03-03 Tino Calancha + + Use lexical binding in benchmark.el + + * lisp/emacs-lisp/benchmark.el: Enable lexical binding. + (benchmark-elapse): Use 'declare'. + * test/lisp/emacs-lisp/benchmark-tests.el: Add test suite. + +2017-03-03 Noam Postavsky + + Switch pp.el to lexical binding + + Additionally, do some minor code cleanup. + + * lisp/emacs-lisp/pp.el: Set lexical-binding. + (pp-buffer): Use skip-syntax-forward. + (pp-eval-expression): Use push. + (pp-last-sexp): Use with-syntax-table. + * test/lisp/emacs-lisp/pp-tests.el: New tests. + +2017-03-03 Chunyang Xu (tiny change) + + Fix completing-read call in reb-change-syntax + + * lisp/emacs-lisp/re-builder.el (reb-change-syntax): Use 'default' arg + of completing-read. + +2017-03-03 Rolf Ade (tiny change) + + sql-mode w/ sqlite: In-memory database + + Enable the usage of an in-memory database. Prior to this, sql-mode w/ + sqlite could only be used with file databases. + * list/progmodes/sql.el (sql-get-login-ext): Don't expand an empty + file name provided by the user, but call sub-process sqlite with that, + in which case it uses an in-memory database. + +2017-03-03 Allen Li + + Stop abbrev-prefix-mark from adding extra newline (Bug#25767) + + `abbrev--before-point' does not adjust `pos' to account for when it + deletes the "-" left by abbrev-prefix-mark. Therefore, when + `abbrev-before-point' goes to restore point, it moves point one + character too far forward. + + * lisp/abbrev.el (abbrev--before-point): Adjust pos when deleting "-". + +2017-03-03 Tino Calancha + + * lisp/subr.el (apply-partially): Move to 'Basic Lisp functions' section. + +2017-03-02 Paul Eggert + + Restore XFLOATINT but with restricted args + + Turn instances of extract_float into XFLOAT_DATA when possible, + and to a resurrected XFLOATINT when the arg is a number. + The resurrected XFLOATINT is more like XFLOAT and XINT in + that is valid only if its arg is a number. This clarifies + the ways in which floats can be extracted at the C level. + * src/editfns.c (styled_format): + * src/floatfns.c (extract_float, Fexpt): + Use XFLOATINT rather than open-coding it. + * src/fns.c (internal_equal): + * src/image.c (imagemagick_load_image): + * src/xdisp.c (resize_mini_window): + Prefer XFLOAT_DATA to extract_float on values known to be floats. + * src/frame.c (x_set_screen_gamma): + * src/frame.h (NUMVAL): + * src/image.c (x_edge_detection, compute_image_size): + * src/lread.c (read_filtered_event): + * src/window.c (Fset_window_vscroll): + * src/xdisp.c (handle_single_display_spec, try_scrolling) + (redisplay_window, calc_pixel_width_or_height, x_produce_glyphs) + (on_hot_spot_p): + Prefer XFLOATINT to extract_float on values known to be numbers. + * src/lisp.h (XFLOATINT): Bring back this function, except + it now assumes its argument is a number. + +2017-03-02 Glenn Morris + + Ert commands to error if no test at point (bug#25931) + + * lisp/emacs-lisp/ert.el (ert-results-mode-menu): + Deactivate some items if no test at point. + (ert--results-test-at-point-no-redefinition): + Add option to signal an error rather than return nil. + (ert-results-pop-to-backtrace-for-test-at-point) + (ert-results-pop-to-messages-for-test-at-point) + (ert-results-pop-to-should-forms-for-test-at-point) + (ert-results-describe-test-at-point): Error if no test at point. + +2017-03-02 Paul Eggert + + Remove XFLOATINT + + * src/lisp.h (XFLOATINT): Remove this alias for extract_float. + All callers changed to use extract_float. + * src/frame.h (NUMVAL): Now an inline function, not a macro. + +2017-03-02 Paul Eggert + + Fix rounding errors in <, =, etc. + + * etc/NEWS: Document this. + * src/bytecode.c (exec_byte_code): + * src/data.c (arithcompare): + Do not lose information when comparing floats to integers. + * test/src/data-tests.el (data-tests-=, data-tests-<) + (data-tests->, data-tests-<=, data-tests->=): + Test this. + +2017-03-02 Eli Zaretskii + + Fix display of mouse-highlight produced by overlapping overlays + + * src/xfaces.c (face_at_buffer_position): If called to find the + mouse-face, only consider the highest-priority source for that + face, and ignore the rest. Previously, all the mouse-face + definitions at POS were merged in that case. + * src/xdisp.c (note_mouse_highlight): Record the overlay that + specifies mouse-face _after_ clearing the info about the previous + overlay, so as not to clear the information about the just-recorded + overlay. (Bug#25906) + +2017-03-02 Eli Zaretskii + + Fix display of strike-through text in variable-height lines + + * src/nsterm.m (ns_draw_text_decoration): + * src/xterm.c (x_draw_glyph_string): + * src/w32term.c (x_draw_glyph_string): Fix calculation of the + strike-through y-coordinate for a glyph row which is taller than + the strike-through text. (Bug#25907) + +2017-03-02 Martin Rudalics + + Don't call x_net_wm_state for scroll bar windows (Bug#24963, Bug#25887) + + * src/xterm.c (handle_one_xevent): For ConfigureNotify events + don't call x_net_wm_state when the window is a scroll bar window. + (Bug#24963, Bug#25887) + +2017-03-02 Katsumi Yamaoka + + gnus-summary-select-article-buffer: Don't re-render existing article + + * lisp/gnus/gnus-sum.el (gnus-summary-select-article-buffer): + Don't re-render existing article. + +2017-03-02 Katsumi Yamaoka + + Don't add debbugs address to message body (bug#25896) + + * lisp/gnus/gnus-group.el (gnus-read-ephemeral-bug-group): + Don't add debbugs address to message body (bug#25896), and + don't add it to message header either if it already exists. + +2017-03-02 Stefan Monnier + + * lisp/cedet/semantic/db-global.el: Make dynbind use explicit + + (semanticdb--ih): Declare. + (semanticdb-enable-gnu-global-databases): Use it instead of `ih'. + (semanticdb-enable-gnu-global-in-buffer, semanticdb-get-database-tables) + (semanticdb-find-tags-for-completion-method): Silence compiler warning. + +2017-03-02 Stefan Monnier + + * lisp/help-fns.el (describe-variable): Use cl-print for the value + + Use `pp-buffer' rather than `pp' so as to avoid calling prin1 twice. + +2017-03-02 Glenn Morris + + * test/lisp/net/puny.el: New file. + +2017-03-02 Glenn Morris + + Small puny.el fix + + * lisp/net/puny.el (puny-decode-string-internal): + Handle strings with no ascii parts. (Bug#23688) + +2017-03-02 Glenn Morris + + Small recover-this-file improvement + + * lisp/files.el (recover-this-file): Explicit error if not + visiting a file. (Bug#23671) + +2017-03-01 Glenn Morris + + Fix for coding-system completion (bug#23670) + + * lisp/international/mule.el (read-buffer-file-coding-system): + Ensure that completion-pcm--delim-wild-regex is enclosed in parens, + so that completion-pcm--pattern->regex can append "*?". + +2017-03-01 Paul Eggert + + Fix rounding error in ‘ceiling’ etc. + + Without this fix, (ceiling most-negative-fixnum -1.0) returns + most-negative-fixnum instead of correctly signaling range-error, + and similarly for floor, round, and truncate. + * configure.ac (trunc): Add a check, since Gnulib’s doc says + ‘trunc’ is missing from MSVC 9. The Gnulib doc says ‘trunc’ is + also missing from some other older operating systems like Solaris + 9 which I know we don’t care about any more, so MSVC is the only + reason to worry about ‘trunc’ here. + * src/editfns.c (styled_format): Formatting a float with %c is now an + error. The old code did not work in general, because FIXNUM_OVERFLOW_P + had rounding errors. Besides, the "if (FLOATP (...))" was in there + only as a result of my misunderstanding old code that I introduced + 2011. Although %d etc. is sometimes used on floats that represent + huge UIDs or PIDs etc. that do not fit in fixnums, this cannot + happen with characters. + * src/floatfns.c (rounding_driver): Rework to do the right thing + when the intermediate result equals 2.305843009213694e+18, i.e., + is exactly 1 greater than MOST_POSITIVE_FIXNUM on a 64-bit host. + Simplify so that only one section of code checks for overflow, + rather than two. + (double_identity): Remove. All uses changed to ... + (emacs_trunc): ... this new function. Add replacement for + platforms that lack ‘trunc’. + * src/lisp.h (FIXNUM_OVERFLOW_P, make_fixnum_or_float): + Make it clear that the arg cannot be floating point. + * test/src/editfns-tests.el (format-c-float): New test. + * test/src/floatfns-tests.el: New file, to test for this bug. + +2017-03-01 Glenn Morris + + Small help--loaded-p fix + + * lisp/help-fns.el (help--loaded-p): Handle entry in load-history + with nil file name. (Bug#25847) + +2017-03-01 Leo Liu + + * src/fns.c (Fbuffer_hash): Doc fix. + +2017-03-01 Eric Abrahamsen + + Don't use mapconcat with chars in gnus registry marks (Bug#25839) + + * lisp/gnus/gnus-registry.el (gnus-registry-article-marks-to-chars): + Instead, use a plain concat, which will create a string out of a list + of characters. + +2017-03-01 Noam Postavsky + + Fix epg-tests with dummy-pinentry program (Bug#23619) + + * test/data/epg/dummy-pinentry: New file. + * test/lisp/epg-tests.el (with-epg-tests): Add it to gpg-agent.conf + when a passphrase is required. Add debug declaration. Set + GPG_AGENT_INFO non-destructively. + +2017-02-28 Phillip Lord + + Speed generation of ldefs-boot-auto + + Previously, generation of ldefs-boot-auto required at least one full + bootstrap and, in extreme cases, two. Now, from build system, it + requires the same time as taken to dump Emacs. + + * Makefile.in: Remove all calls, pass to src. + * admin/ldefs-clean.el: Update for changed messages. + * lisp/Makefile.in (compile-first-delete): Add. + * lisp/ldefs-boot-auto.el: Update. + * src/Makefile.in (generate-ldefs-boot): Add. + +2017-02-28 Phillip Lord + + Add error handling to magic-mode-alist + + * lisp/files.el (set-auto-mode): Add explicit error handling in two + places. + +2017-02-28 Phillip Lord + + Remove conditional includes from bootstrap + + Previously, bootstrap-emacs includes optional functionality, depending + on the platform which is not needed for bootstrap function. As a + result, bootstrap-emacs contains different functions in different + circumstances. If ldefs-boot-auto.el is generated, then loaded + functions will not be added to ldefs-boot-auto.el, although they may be + required during some builds. With this change, bootstrap-emacs should + always behave the same way and, therefore, require the same autoloads. + + * lisp/loadup.el: No longer load optional includes during bootstrap + dumping. + * lisp/ldefs-boot-auto.el: Regenerate. + * lisp/ldefs-boot-manual.el: Add two autoloads. + +2017-02-28 Phillip Lord + + Do not use find-file non-interactively + + * lisp/international/titdic-cnv (miscdic-convert): Use + insert-file-contents in place of find-file. + +2017-02-28 Stefan Monnier + + * src/xdisp.c (overlay_arrows_changed_p): Fix return value and doc + + (update_overlay_arrows): Skip non-markers. + +2017-02-28 Ken Brown + + Try to avoid hang when logging out of MS-Windows + + * src/w32term.c (x_update_window_begin, x_update_window_end) + (my_show_window, my_set_window_pos, my_set_focus) + (my_set_foreground_window, my_destroy_window) + (my_bring_window_to_top, x_iconify_frame): Replace calls to + SendMessage by calls to SendMessageTimeout with a 6-second + timeout. (Bug#25875) + +2017-02-28 Stefan Monnier + + * lisp/textmodes/reftex-toc.el (reftex-re-enlarge): Demote errors. + +2017-02-28 Stefan Monnier + + * doc/misc/eieio.texi: Update to account for the cl-generic facilities + + (Quick Start, Class Options, Generics): Adjust names for cl-generic. + (Methods): Document cl-defmethod. + Explain in more detail the order in which the various + methods are executed. Document the conditions under which a method + is redefined. Remove reference to `eieio-generic-call-arglst`. + Don't document the precise return value of cl-next-method-p. + (Static Methods): Adjust to use `subclass` specializer. + (Method Invocation): Use cl-call-next-method and drop mention of :primary. + (Signal Handling, Signals): Adjust names and args for cl-generic; add + cl-no-primary-method. + (CLOS compatibility, Wish List): Adjust to new featureset. + +2017-02-28 Stefan Monnier + + * lisp/cedet/mode-local.el (define-mode-local-override): Declare doctring. + + * lisp/nxml/nxml-mode.el (nxml-mode): Use new sgml-syntax-propertize. + +2017-02-28 Stefan Monnier + + * lisp/textmodes/sgml-mode.el: syntax-propertize + + (sgml-syntax-propertize-function): Mark . + (sgml-syntax-propertize-inside): New fun. + +2017-02-28 Stefan Monnier + + * lisp/textmodes/css-mode.el (css-completion-at-point): Auto-insert + + ": ;" after completing a property. + +2017-02-28 Tino Calancha + + Show Ibuffer and jump to line listing current buffer + + * lisp/ibuffer.el (ibuffer-jump): New command (Bug#25577). + +2017-02-27 Juri Linkov + + * lisp/vc/add-log.el (change-log-next-buffer): Check if file exists + + before adding it to the list of files. + +2017-02-27 Juri Linkov + + Put text properties on query-replace separator string instead of "\0" + + * lisp/replace.el (query-replace--split-string): + Split at a substring instead of just character. + (query-replace-read-from): Put text properties on the + separator string instead of "\0". (Bug#25482) + +2017-02-27 Juri Linkov + + Add file name and its extension to suggestions in dired-mark-files-regexp + + * lisp/dired.el (dired-mark-files-regexp): Add file name + and its extension to the list of suggested defaults. (Bug#25578) + +2017-02-27 Chunyang Xu (tiny change) + + Prompt default extension in dired-mark-extension + + * lisp/dired-x.el (dired-mark-extension): Prompt default extension + based on extension of file at point. (Bug#25578) + +2017-02-27 Tino Calancha + + Prevent for consing in cl-mapc and cl-mapl + + * lisp/emacs-lisp/cl-extra.el (cl--mapcar-many): Add optional arg ACC; + If non-nil, accumulate values in the result (Bug#25826). + (cl-mapc): Do computations inside function instead of call cl-map. + (cl-mapl): Do computations inside function instead of call cl-maplist. + * lisp/emacs-lisp/cl-lib.el (mapcar): Add autoload cookie. + Call cl--mapcar-many with non-nil 3rd argument. + * test/lisp/emacs-lisp/cl-extra-tests.el (cl-extra-test-map) + (cl-extra-test-mapc, cl-extra-test-mapcar, cl-extra-test-mapl) + (cl-extra-test-maplist): New tests. + +2017-02-27 Tino Calancha + + Choose the right target dir on dired operations + + Prevent from changing the input target dir + when dired-dwim-target is non-nil (Bug#25609). + * lisp/dired-aux.el (dired-do-create-files): + If dired-dwim-target is non-nil, then bind 'default' to nil. + * test/lisp/dired-tests.el (dired-test-bug25609): Add test. + +2017-02-27 Stefan Monnier + + * src/xdisp.c (overlay_arrows_changed_p): Fix last change. + +2017-02-27 Noam Postavsky + + Don't record eshell/clear "command" in history (Bug#25838) + + `eshell/clear' is implemented by sending a series of blank lines, + which is not a useful thing to have in the history. + + * lisp/eshell/em-hist.el (eshell-input-filter-default): Use + `string-blank-p' which does check for newlines (even though newlines + have comment-end syntax, not whitespace syntax class). + * lisp/eshell/esh-mode.el (eshell/clear): Remove + `eshell-add-to-history' from `eshell-input-filter-functions' while + sending the blank lines. This change is needed to solve the bug if + the user customizes `eshell-input-filter' to something that doesn't + filter newlines. + +2017-02-26 Paul Eggert + + Remove a few unused C functions + + * src/eval.c (let_shadows_global_binding_p): + * src/print.c (write_string): + * src/systhread.c (sys_mutex_destroy, sys_thread_equal): + Remove. + * src/print.c (write_string): Rename from write_string_1. + All uses changed. + +2017-02-26 Eli Zaretskii + + Avoid segfault in overlay_arrows_changed_p + + * src/xdisp.c (overlay_arrows_changed_p): Fix recent change + to avoid a segfault. + +2017-02-26 Noam Postavsky + + Don't call package--ensure-init-file if initialized during startup + + * lisp/emacs-lisp/package.el (package-initialize): Check + `after-init-time' rather than `load-file-name' to decide if + `package--ensure-init-file' should be called. Depending on + `load-file-name' will fail if the user calls `pacakge-initialize' in + file which is loaded from the init file (Bug#24643, Bug#25819). + +2017-02-26 Eli Zaretskii + + Fix display of before- and after-strings at invisible text + + * src/xdisp.c (next_overlay_string): Don't raise the + ignore_overlay_strings_at_pos_p flag if the iterator is already + set to continue at a buffer position different from the one + where the overlay strings we just processed were loaded. (Bug#25856) + +2017-02-26 Michael Albinus + + Work on `tramp-completion-mode-p' + + * etc/NEWS: Say that `tramp-completion-mode' is obsolete. + + * lisp/net/tramp.el (tramp-completion-mode): Make it obsolete. + (tramp-completion-mode-p): Reintroduce the check for 'tab. + +2017-02-25 Tom Tromey + + Use font-lock-doc-face in js-mode + + Bug#25858: + * lisp/progmodes/js.el (js-font-lock-syntactic-face-function): New + defun. + (js-mode): Use it. + * test/lisp/progmodes/js-tests.el (js-mode-doc-comment-face): New + test. + +2017-02-25 Noam Postavsky + + Don't use IP 0.0.0.0 for package test server (Bug#22582) + + * test/lisp/emacs-lisp/package-resources/package-test-server.py: Set + 'server_address' when port number is given on the command line. Print + IP and port number as a URL, and flush it after printing. + * test/lisp/emacs-lisp/package-tests.el: + (package-test-update-archives-async): Grab the whole URL from server + output. + +2017-02-25 Tom Tromey + + Add more branch support to vc-dir + + Bug#25859: + * lisp/vc/vc-dir.el (vc-dir-mode-map) Add "B" bindings. + * lisp/vc/vc.el (vc-revision-history): New defvar. + (vc-read-revision): Use vc-revision-history. + (vc-print-branch-log): New function. + * doc/emacs/maintaining.texi (VC Directory Commands): Document new + bindings. + * etc/NEWS: Mention new vc-dir bindings. + +2017-02-25 Alan Mackenzie + + Allow for the :: operator in C++ "enum class" declarations. + + * lisp/progmodes/cc-engine.el (c-backward-typed-enum-colon): Check for + "::". + +2017-02-25 Michael Albinus + + Fix bug#25854 + + * lisp/net/tramp-sh.el (tramp-do-file-attributes-with-ls): + Simplify error handling for huge inodes. + (tramp-convert-file-attributes): Handle very huge inodes. (Bug#25854) + +2017-02-25 Eli Zaretskii + + Avoid leaving garbage on screen when using 'raise' display property + + * src/xdisp.c (display_line): Reset voffset value of the iterator + when it hits ZV, to avoid "inheriting" it to glyph rows past ZV, + which then leaves stuff on screen that needs to be cleared by + redisplay. (Bug#25855) + +2017-02-25 Eli Zaretskii + + Fix doc strings in info.el + + * lisp/info.el (Info-selection-hook, Info-mode-hook) + (Info-edit-mode-hook): Doc fixes. (Bug#25794) + +2017-02-25 Eli Zaretskii + + Fix doc string of 'posn-at-point' + + * src/keyboard.c (Fposn_at_point): Clarify the doc string. + (Bug#25796) + +2017-02-25 Peder O. Klingenberg + + New option -u / --suppress-output to emacsclient + + * lib-src/emacsclient.c (print_help_and_exit, longopts) + (decode_options, main): Implement new option --suppress-output / -u to + suppress printing of eval-results. + * doc/emacs/misc.texi (emacsclient Options): Document the new + "--suppress-output/-u" options. + * etc/NEWS: Mention the new options. + +2017-02-25 Noam Postavsky + + Fix scrolling with partial line corner case (Bug#25792) + + Also fix up the scrolling tests so that they don't make so many + assumptions about the current window configuration. + + * src/xdisp.c (try_window): Take partial line height into account when + comparing cursor position against scroll margin. + + * test/manual/scroll-tests.el (scroll-tests-with-buffer-window): Add + HEIGHT argument, to allow setting up window with exact height and + partial line. + (scroll-tests-display-buffer-with-height): New display-buffer action + function. + (scroll-tests-scroll-margin-over-max): + (scroll-tests--scroll-margin-whole-window): Pass HEIGHT to + `scroll-tests--scroll-margin-whole-window'. + (scroll-tests-conservative-show-trailing-whitespace): New test. + (scroll-tests-scroll-margin-negative): Fix line counting. + (scroll-tests--point-in-middle-of-window-p): Set window height + properly. + +2017-02-25 Tom Tromey + + Fix indentation error in js.el + + * lisp/progmodes/js.el (js--indent-in-array-comp): Wrap forward-sexp + call in condition-case. + * test/lisp/progmodes/js-tests.el (js-mode-indentation-error): New + test. + +2017-02-24 Tom Tromey + + add "async" and "await" keywords + + * lisp/progmodes/js.el (js--keyword-re): Add async, await. + +2017-02-24 Stefan Monnier + + Use cl-print for Edebug and EIEIO + + * lisp/emacs-lisp/edebug.el (edebug-prin1-to-string): Use cl-print. + (edebug-prin1, edebug-print): Remove. + + * lisp/emacs-lisp/eieio.el (object-print): Declare obsolete. + (cl-print-object): Add a method for EIEIO objects. + (eieio-edebug-prin1-to-string): Delete. + (edebug-prin1-to-string): Don't advise any more. + + * lisp/emacs-lisp/eieio-datadebug.el (data-debug-insert-object-button): + Replace `object-print' -> `cl-prin1-to-string'. + +2017-02-24 Stefan Monnier + + Fix left over uses of `call-next-method' + + * lisp/cedet/semantic/db-global.el (object-print): + * lisp/cedet/semantic/db.el (object-print): Use `cl-call-next-method'. + +2017-02-24 Stefan Monnier + + Minor redisplay optimisations + + * src/frame.c (Ficonify_frame): No need to redisplay everything. + + * src/xdisp.c (overlay_arrows_changed_p): Add `set_redisplay' argument. + (redisplay_internal): Use it to avoid redisplaying everything. + (try_window_id): Use it keep the same behavior as before. + +2017-02-24 Stefan Monnier + + * lisp/emacs-lisp/cl-print.el: New file + + * lisp/emacs-lisp/nadvice.el (advice--where): New function. + (advice--make-docstring): Use it. + + * src/print.c (print_number_index): Don't declare here any more. + (Fprint_preprocess): New function. + + * test/lisp/emacs-lisp/cl-print-tests.el: New file. + +2017-02-24 Peder O. Klingenberg + + Make calc's least common multiple positive (bug#25255) + + * lisp/calc/calc-comb.el (calcFunc-lcm): Return absolute value. + * doc/misc/calc.texi (Combinatorial Functions): Update for the above. + +2017-02-24 Tino Calancha + + Documentation fix in elisp reference manual + + * doc/lispref/macros.texi (Defining Macros): Drop redundant mention + on 'declare' forms (Bug#25846). + +2017-02-24 Gemini Lasswell + + Support read syntax for circular objects in Edebug (Bug#23660) + + * lisp/emacs-lisp/edebug.el (edebug-read-special): New name + for edebug-read-function. Handle the read syntax for circular + objects. + (edebug-read-objects): New variable. + (edebug-read-and-maybe-wrap-form1): Reset edebug-read-objects. + + * src/lread.c (Fsubstitute_object_in_subtree): Make + substitute_object_in_subtree into a Lisp primitive. + +2017-02-24 Lixin Chin (tiny change) + + Add Conference to the list of valid bibtex entry types + + * lisp/textmodes/bibtex.el (bibtex-BibTeX-entry-alist): + Add Conference as a duplicate of InProceedings. (Bug#25143) + +2017-02-23 Glenn Morris + + * lisp/comint.el (comint-password-prompt-regexp): Add SUDO. (Bug#24817) + +2017-02-23 Glenn Morris + + Small dunnet score file improvements + + * lisp/play/dunnet.el (dun-log-file): Switch to per-user default. + (dun-do-logfile): Handle non-existing score file. + +2017-02-23 Glenn Morris + + * lisp/play/dunnet.el (dun-help): Doc fix. + +2017-02-23 Mark Oteiza + + Declare dun-line and dun-line-list + + Previously, there were free variables 'line' and 'line-list'. + * lisp/play/dunnet.el (dun-line, dun-line-list): New variables. + (dun-press, dun-vparse, dun-parse2, dun-unix-parse, dun-batch-parse): + (dun-batch-parse2, dun-batch-loop, dun-batch-dos-interface): + (dun-batch-unix-interface): Use them. + +2017-02-23 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-02-16 xbinary-io: rename from xsetmode + 2017-02-15 xsetmode: new module + * lib-src/etags.c (main): + * lib-src/hexl.c (main): + * src/emacs.c (main) [MSDOS]: + Prefer set_binary_mode to the obsolescent SET_BINARY. + * lib/binary-io.c, lib/binary-io.h: Copy from gnulib. + +2017-02-23 Paul Eggert + + hexl: handle large files and I/O errors + + * lib-src/hexl.c: Include inttypes.h, for PRIxMAX etc. + Do not include ctype.h, as the code no longer uses isdigit. + (DEFAULT_GROUPING, un_flag, iso_flag, group_by): Now local to ‘main’. + (DEFAULT_BASE, endian): Remove; was not really used. + (usage): Remove; now done by ‘main’, as that’s simpler. + (progname): Now static. + (output_error, hexchar): New functions. + (main): Use them. Simplify. Remove "-oct", "-big-endian", and + "-little-endian" options, as they did not work and were not used. + Use SET_BINARY only on stdin, and fopen with "rb" otherwise. + Use SET_BINARY only once on stdout. + Do not assume file offsets fit in ‘long’. + If an I/O error occurs, report it and exit with nonzero status. + +2017-02-23 Eli Zaretskii + + Avoid quitting inside a critical section on MS-Windows + + * src/w32uniscribe.c (uniscribe_list_family): + * src/w32font.c (w32font_list_family, w32font_text_extents) + (w32font_list_internal, w32font_match_internal) + (list_all_matching_fonts): Prevent quitting while these functions + cons lists of fonts, to avoid leaving the critical section taken + by the main thread, which will then cause any other thread + attempting to enter the critical section to hang. (Bug#25279) + +2017-02-22 Dmitry Gutov + + Use revision-completion-table in vc-retrieve-tag + + * lisp/vc/vc.el (vc-retrieve-tag): Use the + revision-completion-table command for completion (bug#25710). + +2017-02-22 Stefan Monnier + + * lisp/emacs-lisp/cl-generic.el (cl--generic-typeof-types): Add `atom' + + remove entries whose car can't be returned by type-of. + (cl--generic-all-builtin-types): New var. + (cl-generic-generalizers): Use it to avoid requiring + extra entries in cl--generic-typeof-types. + +2017-02-22 Noam Postavsky + + Find macro binding for symbol-bound macros too (Bug#6848) + + There are 2 ways to bind a macro: with global-set-key or + kmacro-bind-to-key. The former binds a key to a symbol, while the + latter binds to a lambda. In 2010-03-03 "Fix keyboard macro key + lookup (Bug#5481)", `insert-kbd-macro' was fixed to detect the lambda + case, but broke the symbol case. + + * lisp/macros.el (insert-kbd-macro): Also check for bindings of + MACRONAME. + +2017-02-22 Stefan Monnier + + * lisp/emacs-lisp/cl-generic.el (cl-defgeneric): Fix last change + +2017-02-22 Juri Linkov + + * lisp/isearch.el (lazy-highlight-max-at-a-time): Doc fix (bug#21092). + +2017-02-22 Juri Linkov + + * lisp/isearch.el (isearch-lazy-highlight): New choice ‘all-windows’. + + (isearch-lazy-highlight-update): Check it to decide whether to apply + overlays only on the selected window. + + * lisp/follow.el (follow-mode): Set isearch-lazy-highlight to ‘all-windows’. + (Bug#17453, bug#21092) + +2017-02-22 Paul Eggert + + Minor weak hash table performance tweaks + + * src/fns.c (make_hash_table): Omit unnecessary assignment to + h->next_weak when the hash table is not weak. + (copy_hash_table): Put the copy next to the original in the + weak_hash_tables list, as this should have better locality + when scanning the weak hash tables. + +2017-02-22 Paul Eggert + + Use float instead of Lisp_Object for rehash_size + + * src/alloc.c (purecopy_hash_table): + * src/fns.c (maybe_resize_hash_table, Fmake_hash_table): + (Fhash_table_rehash_size): + * src/lisp.h (struct Lisp_Hash_Table.rehash_size): + The rehash_size member of struct Lisp_Hash_Table is now a + float, not a Lisp_Object. + * src/alloc.c (purecopy_hash_table): Assign members in order. + * src/fns.c (make_hash_table): Use EMACS_INT for size and + float for rehash_size, instead of Lisp_Object for both. + All callers changed. + * src/lisp.h (DEFAULT_REHASH_SIZE): Now float, not double, + and 1 smaller. + * src/print.c (print_object): Simplify by calling + Fhash_table_rehash_size and Fhash_table_rehash_threshold. + Avoid unnecessary NILP. + +2017-02-22 Paul Eggert + + Use ptrdiff_t instead of Lisp_Object for collision + + * src/alloc.c (purecopy_hash_table): Assign, don’t purecopy. + * src/fns.c (set_hash_next_slot, set_hash_index_slot): Hash index + arg is now ptrdiff_t index (or -1 if empty), not Lisp_Object + integer (or Qnil if empty). All callers changed. + (larger_vecalloc): New static function. + (larger_vector): Use it. + (HASH_NEXT, HASH_INDEX): Move here from lisp.h. Return ptrdiff_t + index (or -1) not Lisp_Object integer (or Qnil). All callers changed. + * src/fns.c (make_hash_table, maybe_resize_hash_table, hash_lookup) + (hash_put, hash_remove_from_table, hash_clear, sweep_weak_table): + * src/profiler.c (evict_lower_half, record_backtrace): + -1, not nil, is now the convention for end of collision list. + * src/fns.c (maybe_resize_hash_table): Avoid double-initialization + of the free list. Reallocate H->next last, in case other + reallocations exhaust memory. + * src/lisp.h (struct Lisp_Hash_Table): ‘next_free’ is now + ptrdiff_t, not Lisp_Object. Adjust commentary for ‘next’ and + ‘index’, which no longer contain nil. + (HASH_NEXT, HASH_INDEX): Move to src/fns.c. + +2017-02-22 Paul Eggert + + Hash table threshold is now float, not double + + Change default from 0.8 to 0.8125 so it fits in float without + rounding glitches. + * doc/lispref/hash.texi (Creating Hash): + * doc/lispref/objects.texi (Hash Table Type): + * etc/NEWS: + Document change. + * src/fns.c (make_hash_table, maybe_resize_hash_table) + (Fmake_hash_table): Threshold is now float, not double. + Be consistent about how this is rounded. + * src/lisp.h (struct Lisp_Hash_Table.rehash_threshold): + Change back to float, now that the other code rounds consistently. + (DEFAULT_REHASH_THRESHOLD): Now float 0.8125 instead of double 0.8. + +2017-02-22 Juri Linkov + + Avoid flicker in lazy-highlight by doing all updates without redisplay. + + * lisp/isearch.el (lazy-highlight-max-at-a-time): + Change default value from 20 to nil to not trigger redisplay + between updating iterations. + (lazy-highlight-cleanup): New arg ‘procrastinate’ to not remove + overlays when non-nil. + (isearch-lazy-highlight-new-loop): Call lazy-highlight-cleanup + with non-nil second arg when the search string is not empty. + Run timer with isearch-lazy-highlight-start instead of + isearch-lazy-highlight-update. + (isearch-lazy-highlight-start): New function. (Bug#25751) + +2017-02-21 Stefan Monnier + + * lisp/emacs-lisp/autoload.el (make-autoload): Support cl-defgeneric + + * lisp/emacs-lisp/cl-generic.el (cl-defgeneric): Tweak for autoloading. + +2017-02-21 Stefan Monnier + + * lisp/emacs-lisp/cl-preloaded.el (cl-struct-define): Fix duplication + + which resulted in incomplete list of parents in one copy of the + cl-structure-class class. + +2017-02-21 Glenn Morris + + Tweak recent custom-reevaluate-setting change + + * lisp/custom.el (custom-reevaluate-setting): + Tweak previous change to avoid font-lock init issues. + +2017-02-21 Glenn Morris + + Ensure delayed-init custom variables get marked special + + * lisp/custom.el (custom-reevaluate-setting): + If the variable has never been set, defvar it. (Bug#25770) + +2017-02-21 Mark Oteiza + + Turn on lexical-binding in dunnet.el + + * lisp/play/dunnet.el: Turn on lexical-binding. Re-instate lexical + byte compile warnings. + +2017-02-21 Mark Oteiza + + Make dunnet insertion functions n-ary + + * lisp/play/dunnet.el (dun-mprinc, dun-mprincl, dun-minsert): + (dun-minsertl, dun-batch-mprinc, dun-batch-mprincl): Change to accept + any number of arguments. + (dun-parse, dun-describe-room, dun-quit, dun-inven, dun-shake): + (dun-take, dun-go, dun-move, dun-press, dun-score): + (dun-compile-save-out, dun-do-logfile): Collect arguments from + multiple insertion calls into less calls with more args. + +2017-02-21 Katsumi Yamaoka + + message-goto-body-1: Fix regexp so as not to match multi-line + + * lisp/gnus/message.el (message-goto-body-1): + Fix regexp so as not to match multi-line. + +2017-02-20 Noam Postavsky + + Simplify cl-get using `plist-member' + + * lisp/emacs-lisp/cl-extra.el (cl-get, cl-getf, cl--set-getf): Use + `plist-member' instead of explicit loop. + * test/lisp/emacs-lisp/cl-extra-tests.el: New tests. + +2017-02-20 Paul Eggert + + Verify xwidget USE_LSB_TAG assumption + + * src/xwidget.c (Fxwidget_webkit_execute_script): + Add verification. Problem reported by Andreas Schwab (Bug#25816#8). + +2017-02-20 Stefan Monnier + + * src/insdel.c (make_gap): Improve comment. + +2017-02-20 Mark Oteiza + + Do not use switch-to-buffer for working in a temp buffer + + * lisp/play/dunnet.el (dunnet): Use pop-to-buffer-same-window instead, + cf. Bug#22244. + (dun-load-d, dun-eval, dun-save-game, dun-do-logfile): Use + with-temp-buffer instead. + +2017-02-20 Mark Oteiza + + Make dun-room-shorts a defconst + + * lisp/play/dunnet.el (dun-room-shorts): Make defconst and collect + initial value into the declaration. + (dun-space-to-hyphen): Remove. + +2017-02-20 Paul Eggert + + Port xwidget to -DCHECK_LISP_OBJECT_TYPE + + * src/xwidget.c (webkit_javascript_finished_cb) + (Fxwidget_webkit_execute_script): Don't assume Lisp_Object is an + integer. This fix is just a hack; I’ll file a bug report about + the underlying problem. + +2017-02-20 Katsumi Yamaoka + + mm-decode.el: Simplify regexp used to search html meta tag + + * lisp/gnus/mm-decode.el (mm-add-meta-html-tag, mm-shr): + Simplify regexp used to search html meta tag. + +2017-02-20 Katsumi Yamaoka + + mm-shr: Ignore coding-system `ascii' + + * lisp/gnus/mm-decode.el (mm-shr): Ignore coding-system `ascii'. + +2017-02-20 Tom Tromey + + vc-log-outgoing fixes for git; add binding to vc-dir + + * lisp/vc/vc-dir.el (vc-dir-mode-map): Bind "O" to vc-log-outgoing. + * lisp/vc/vc-git.el (vc-git-log-outgoing, vc-git-log-incoming): Use + async execution. + (vc-git-log-view-mode): Also truncate lines for log-outgoing and + log-incoming. + * lisp/vc/vc.el (vc-log-incoming, vc-log-outgoing): Don't pass nil + as remote-location argument. + +2017-02-20 Tom Tromey + + Remove stale comments from vc-git and vc-hg + + * lisp/vc/vc-git.el (vc-git-retrieve-tag): Remove comment. + * lisp/vc/vc-hg.el (vc-hg-retrieve-tag): Remove comment. + +2017-02-20 Mark Oteiza + + Remove member clone + + * lisp/play/dunnet.el (dun-answer): Use member instead. + (dun-members): Remove. + +2017-02-20 Mark Oteiza + + Prefix global var + + * lisp/play/dunnet.el (room): Rename to dun-room. + (dun-messages, dunnet, dun-describe-room, dun-drop, dun-move): + (dun-restore, dun-do-logfile, dun-batch-loop): Use new name. + +2017-02-20 Mark Oteiza + + Replace nested ifs with cond + + * lisp/play/dunnet.el (dun-messages, dun-describe-room, dun-examine): + (dun-eat, dun-put-objs, dun-turn, dun-press, dun-ls, dun-cd): Use when + and cond where appropriate. + (dun-sauna-heat): Accept sauna level as an argument. Use cond. + (dun-take): Use null and dun-mprincl. + (dun-inven-weight, dun-load-d): Reformat. + (dun-remove-obj-from-inven, dun-remove-obj-from-room): Nix setq to nil. + +2017-02-19 Paul Eggert + + Fix glitches in recent hash table changes + + * src/fns.c (Fmake_hash_table): Simplify the machine code slightly + by using 0 rather than -1. + * src/lisp.h (struct Lisp_Hash_Table.pure): Now bool rather + than a bitfield, for speed (the bitfield did not save space). + (struct Lisp_Hash_Table.rehash_threshold): Now double rather than + float, since the float caused unwanted rounding errors, e.g., + (hash-table-rehash-threshold (make-hash-table)) yielded + 0.800000011920929 instead of the correct 0.8. + +2017-02-19 Stefan Monnier + + * src/insdel.c (make_gap): Increase enough to avoid O(N^2) behavior. + +2017-02-19 Eli Zaretskii + + Avoid aborts during loadup + + * src/emacs-module.c (syms_of_module): + * src/image.c (xpm_make_color_table_h): Update calls to + make_hash_table to adjust to a recent change in fns.c. + * src/fns.c (make_hash_table): + * src/lisp.h (make_hash_table): 4th arg is now of type double. + +2017-02-19 Michael Albinus + + Rework connection local variables + + For connection local variables interface, `class' is renamed + to `profile'. All arguments `criteria' are a plist now. + + * doc/lispref/variables.texi (Connection Local Variables): + Rewrite. + + * lisp/files-x.el (connection-local-profile-alist): Rename + from `connection-local-class-alist'. Adapt docstring. + (connection-local-criteria-alist): Adapt docstring. + (connection-local-normalize-criteria): New defun. + (connection-local-get-profiles): Rename from + `connection-local-get-classes'. Rewrite. + (connection-local-set-profiles): Rename from + `connection-local-set-classes'. Rewrite. + (connection-local-get-profile-variables): Rename from + `connection-local-get-class-variables'. Rewrite. + (connection-local-set-profile-variables): Rename from + `connection-local-set-class-variables'. Rewrite. + (hack-connection-local-variables) + (hack-connection-local-variables-apply)): Rewrite. + (with-connection-local-profiles): Rename from + `ith-connection-local-classes'. Rewrite. + + * lisp/net/tramp.el (tramp-set-connection-local-variables): + Compute criteria. + + * lisp/net/tramp-cmds.el (tramp-bug): + Use `connection-local-profile-alist'. + + * test/lisp/files-x-tests.el (files-x-test--variables1) + (files-x-test--variables2, files-x-test--variables3) + (files-x-test--variables4, files-x-test--criteria1) + (files-x-test--criteria2): Make them a defconst. + (files-x-test--application) + (files-x-test--another-application, files-x-test--protocol) + (files-x-test--user, files-x-test--machine): New defconst. + (files-x-test--criteria): New defvar. + (files-x-test--criteria3): Remove. + (files-x-test-connection-local-set-profile-variables): + Rename from `files-x-test-connection-local-set-class-variables'. + Rewrite. + (files-x-test-connection-local-set-profiles): Rename from + `files-x-test-connection-local-set-classes'. Rewrite. + (files-x-test-hack-connection-local-variables-apply) Rewrite. + (files-x-test-with-connection-local-profiles): Rename from + `files-x-test-with-connection-local-classes'. Rewrite. + +2017-02-19 Mark Oteiza + + Set up combination and random item location + + * lisp/play/dunnet.el (dun-combination): Make defconst. + (tloc, tcomb): Remove. Replace with a top-level form. + +2017-02-19 Mark Oteiza + + Replace movement variables with an alist and accessor + + * lisp/play/dunnet.el (north, south, east, west, northeast, southeast): + (northwest, southwest, up, down, in, out): Remove. + (dun-movement-alist): New constant. + (dun-movement): New function. + (dun-n, dun-s, dun-e, dun-w, dun-ne, dun-se, dun-nw, dun-sw, dun-up): + (dun-down, dun-in, dun-out): Use a symbol for indicating movement. + (dun-move, dun-special-move): Translate movement symbol to an + enumeration. + +2017-02-19 Mark Oteiza + + Change top-level setq forms to defvar or defconst + + Also collect some code onto fewer lines and reindent. + * lisp/play/dunnet.el (dun-visited, dun-current-room, dun-exitf): + (dun-badcd, dun-computer, dun-floppy, dun-key-level, dun-hole): + (dun-correct-answer, dun-lastdir, dun-numsaves, dun-jar, dun-dead): + (room, dun-numcmds, dun-wizard, dun-endgame-question, dun-logged-in): + (dungeon-mode, dun-unix-verbs, dun-dos-verbs, dun-batch-mode): + (dun-cdpath, dun-cdroom, dun-uncompressed, dun-ethernet): + (dun-restricted, dun-ftptype, dun-endgame, dun-rooms): + (dun-light-rooms, dun-verblist, dun-inbus, dun-nomail, dun-ignore): + (dun-mode, dun-sauna-level, north, south, east, west, northeast): + (southeast, northwest, southwest, up, down, in, out, dungeon-map): + (dun-objnames, obj-special, dun-room-objects, dun-room-silents): + (dun-inventory, dun-objects, dun-object-lbs, dun-object-pts): + (dun-objfiles, dun-perm-objects, dun-physobj-desc, dun-permobj-desc): + (dun-diggables, dun-room-shorts, dun-endgame-questions): Change + declaration to use defvar or defconst. + (dun-doverb, dun-vparse, dun-vparse2, dun-batch-parse): + (dun-batch-parse2): Omit the dun- prefix from arguments dun-ignore + dun-verblist. Those are now constants and the byte compiler doesn't + allow defconsts in lambda lists. + +2017-02-19 Mark Oteiza + + Move all dunnet globals up to the top + + * lisp/play/dunnet.el: Adjust comments to reflect moved forms. + (dun-visited, dun-current-room, dun-exitf): + (dun-badcd, dun-computer, dun-floppy, dun-key-level, dun-hole): + (dun-correct-answer, dun-lastdir, dun-numsaves, dun-jar, dun-dead): + (room, dun-numcmds, dun-wizard, dun-endgame-question, dun-logged-in): + (dungeon-mode, dun-unix-verbs, dun-dos-verbs, dun-batch-mode): + (dun-cdpath, dun-cdroom, dun-uncompressed, dun-ethernet): + (dun-restricted, dun-ftptype, dun-endgame, dun-rooms): + (dun-light-rooms, dun-verblist, dun-inbus, dun-nomail, dun-ignore): + (dun-mode, dun-sauna-level, north, south, east, west, northeast): + (southeast, northwest, southwest, up, down, in, out, dungeon-map): + (dun-objnames, obj-special, dun-room-objects, dun-room-silents): + (dun-inventory, dun-objects, dun-object-lbs, dun-object-pts): + (dun-objfiles, dun-perm-objects, dun-physobj-desc, dun-permobj-desc): + (dun-diggables, dun-room-shorts, dun-endgame-questions): Move to the + top of the file, before any uses. + +2017-02-19 Michael Albinus + + Fix bug#25788 + + * lisp/net/tramp.el (tramp-autoload-file-name-handler): + Do not load tramp.el just for "/". (Bug#25788) + +2017-02-19 YAMAMOTO Mitsuharu + + Fix fringe bitmap initialization on MS-Windows + + * src/fringe.c (init_fringe_bitmap) [HAVE_NTGUI]: Fix initialization + of fb->bits. (Bug#25673) + +2017-02-19 Stefan Monnier + + Change type of `rehash_threshold' and `pure' fields in hash-tables + + * src/lisp.h (struct Lisp_Hash_Table): Change type of + `rehash_threshold' and `pure' fields and move them after `count'. + * src/fns.c (make_hash_table): Change type of `rehash_threshold' and `pure'. + (Fmake_hash_table, Fhash_table_rehash_threshold): + * src/category.c (hash_get_category_set): + * src/xterm.c (syms_of_xterm): + * src/profiler.c (make_log): + * src/print.c (print_object): + * src/alloc.c (purecopy_hash_table, purecopy): Adjust accordingly. + +2017-02-19 Paul Eggert + + Use 'char *FOO' instead of 'char* FOO' + +2017-02-19 Mark Oteiza + + More json.el changes + + * lisp/json.el (json-read-keyword, json-read-number, json-read-object): + (json-read-array): Just use = for char comparison. + +2017-02-18 Noam Postavsky + + * lisp/woman.el (woman): Fix docstring prefix arg description. + +2017-02-18 Alan Mackenzie + + Fix edebug-spec on c-lang-defvar. + + This allows c-lang-defvars with the symbol 'dont-doc in the place of the + optional documentation to be instrumented for edebug. + + lisp/progmodes/cc-langs.el (top-level): Amend the edebug-spec for + c-lang-defvar. + (c-opt-identifier-concat-key, c-decl-prefix-or-start-re): remove redundant + 'dont-doc. + +2017-02-18 Lars Ingebrigtsen + + Lists used as plists now have to be an even length + + * lisp/net/eww.el (eww-size-text-inputs): `eww-form' isn't a plist. + (eww-process-text-input): Not here, either. + +2017-02-18 Michael Albinus + + Unset `non-essential' in Tramp when not needed anymore + + * doc/misc/trampver.texi: + * lisp/net/trampver.el: Change version to "2.3.2-pre". + + * lisp/net/tramp-sh.el (tramp-maybe-open-connection): + Use `tramp-completion-mode-p'. + + * lisp/net/tramp.el (tramp-file-name-handler): Unset `non-essential' + when file name doesn't match `tramp-completion-file-name-regexp'. + +2017-02-18 Eli Zaretskii + + Automatically regenerate emacs.1 and *.rc files + + * Makefile.in (CONFIG_STATUS_FILES_IN): New variable, lists + non-Makefile files produced by config.status. + ($(MAKEFILE_NAME)): Depend on $(CONFIG_STATUS_FILES_IN), so that + their targets are regenerated when the source changes. + +2017-02-18 Alan Mackenzie + + Set the syntax table in AWK Mode. + + This is a partial reversion of CC Mode commit on 2016-05-09 17:49:45 +0000. + It fixes bug #25722. + + lisp/progmodes/cc-mode.el (awk-mode): Explicitly set the syntax table. + +2017-02-18 Eli Zaretskii + + Mention "editor" in Emacs man page header + + * doc/man/emacs.1.in: Mention "editor" in the header line. + (Bug#25771, Bug#25779) + +2017-02-18 Göktuğ Kayaalp + + Fix Turkish language environment setup + + * lisp/language/european.el ("Turkish"): Fix a typo in Turkish + language setup. (Bug#25763) + +2017-02-18 Rami Ylimäki + + Support 24-bit direct colors on text terminals + + * src/term.c (init_tty): Use 24-bit terminal colors if corresponding + foreground and background functions are present in terminal type + definition. + * src/tparam.h: Define prototype for tigetstr. + + * lisp/term/tty-colors.el (tty-color-define): Convert color palette + index to pixel value on 16.7M color terminals. + (tty-color-24bit): New function to convert color palette index to + pixel value on 16.7M color terminals. + (tty-color-desc): Don't approximate colors on 16.7M color terminals. + * lisp/term/xterm.el (xterm-register-default-colors): Define all named + TTY colors on 16.7M color terminals. + + * doc/misc/efaq.texi (Colors on a TTY): Add instructions on how to + enable direct color TTY mode. + * etc/NEWS: Mention direct color TTY mode and point to FAQ. + +2017-02-18 Rami Ylimäki + + Remove unused TN_max_pairs field + + * src/termchar.h (tty_display_info): Remove TN_max_pairs field, + describing maximum number of terminal background/foreground color pairs. + * src/term.c (tty_default_color_capabilities, tty_setup_colors) + (init_tty): Remove references to TN_max_pairs. + +2017-02-18 Eli Zaretskii + + Improve documentation of query-replace-from-to-separator + + * doc/emacs/search.texi (Query Replace): Document the meaning of + the nil value of query-replace-from-to-separator. (Bug#25482) + +2017-02-18 Eli Zaretskii + + Document problems with nerd-fonts + + * etc/PROBLEMS (fonts): Describe the potential problems with + nerd-fonts that cause slow display. (Bug#25697) + +2017-02-18 Eli Zaretskii + + Improve commentary for a recent change in keyboard.c + + * src/keyboard.c (Fset__this_command_keys): Add a comment about + the magic 248 value. (Bug#25612) + +2017-02-18 Eli Zaretskii + + Avoid infloop in rect.el + + * lisp/rect.el (rectangle--*-char): Avoid inflooping when called + with argument N whose absolute value is greater than 1. (Bug#25773) + +2017-02-18 Eli Zaretskii + + Remove annoying warnings about let-binding + + * src/data.c (Fmake_variable_buffer_local, Fmake_local_variable): + Remove warnings about making symbols local while let-bound. + (Bug#25561) + +2017-02-18 Hong Xu + + Avoid errors when flyspell-generic-check-word-predicate is a lambda. + + * flyspell.el (flyspell-auto-correct-word, flyspell-word): Apply + functionp instead of fboundp on + flyspell-generic-check-word-predicate (Bug#25765). + +2017-02-18 Glenn Morris + + Remove the build number from emacs-version variable + + It's a largely internal detail that can confuse users. (Bug#25590) + * lisp/version.el (emacs-build-number): New constant. + (emacs-version): Use emacs-build-number. + * lisp/loadup.el (top-level): When dumping, increment + emacs-build-number rather than emacs-version. + * src/emacs.c (emacs-version): Doc fix. + * doc/lispref/intro.texi (Version Info): Update emacs-version details. + Mention emacs-build-number. + * lisp/gnus/gnus-util.el (gnus-emacs-version): + * lisp/mail/emacsbug.el (report-emacs-bug): + * admin/admin.el (set-version): Update for emacs-version change. + +2017-02-18 Glenn Morris + + Ensure that user-mail-address always has a value + + * lisp/startup.el (user-mail-address): Initialize in the normal way. + (command-line): Reset user-mail-address if needed using + standard custom machinery. + * lisp/mail/feedmail.el (feedmail-fiddle-from): + * lisp/mail/rmail.el (rmail-unknown-mail-followup-to): + * lisp/mail/rmailsum.el (rmail-header-summary): + Simplify now that user-mail-address is always set. + +2017-02-18 Mark Oteiza + + Turn on lexical-binding in ruby-mode + + * lisp/progmodes/ruby-mode.el: Turn on lexical-binding. + (ruby-font-lock-syntax-table): Use make-syntax-table. + (ruby-mode): 'define-derived-mode' writes the keys for us. + +2017-02-18 Mark Oteiza + + Turn on lexical-binding in elint.el + + * lisp/emacs-lisp/elint.el: Quote entry point commands in commentary. + (elint-running, elint-current-pos): Move these dynamic vars to toward + the top of the file. + (elint-check-quote-form): Ignore unused argument. + (elint-check-conditional-form): Remove unused binding. + +2017-02-18 Gemini Lasswell + + * lisp/emacs-lisp/subr-x.el (if-let*): Fix Edebug spec (Bug#24748) + +2017-02-18 Mark Oteiza + + Enable erc-accidental-paste-threshold-seconds by default + + * lisp/erc/erc.el (erc-accidental-paste-threshold-seconds): Set + default to 0.2 (Bug#25709). + +2017-02-17 Michal Nazarewicz + + Fix build failure caused by ‘Generate upcase and downcase tables from Unicode’ + + The [5ec3a584: Generate upcase and downcase tables from Unicode data] + commit broke bootstrap from a truly clean tree (e.g. a fresh clone or + one created with ‘make extraclean’), see + . + + The failure was caused by characters.el trying to read Unicode + property tables which aren’t available so early in the build process. + + Wrap the part that requires Unicode property tables in a condition + checking if those are available. If they aren’t they case and syntax + tables won’t be fully set but later on, the characters.el file will be + evaluated again and this time with Unicode properties available so + final Emacs ends up with the exact same case and syntax tables. + +2017-02-17 Katsumi Yamaoka + + mm-add-meta-html-tag: Improve regexp + + * lisp/gnus/mm-decode.el (mm-add-meta-html-tag): + Improve regexp to search html meta tag. + +2017-02-17 Katsumi Yamaoka + + mm-shr: Prefer charset specified in html meta tag + + * lisp/gnus/mm-decode.el (mm-shr): Prefer charset specified in html + meta tag than mail-parse-charset in the case there is no charset spec + in MIME header. + +2017-02-17 Glenn Morris + + Stop duplicating some custom-types in message.el + + * lisp/gnus/message.el (user-mail-address, user-full-name): + No need to re-specify custom-type. + +2017-02-17 Glenn Morris + + Whitespace trivia in dunnet.el + + * lisp/play/dunnet.el (dun-special-object, dun-put-objs) + (dun-rlogin-endgame): Whitespace trivia. + +2017-02-17 Glenn Morris + + Explicit error on changing case of negative integers + + * src/casefiddle.c (casify_object): Reject negative integers: + Emacs characters are positive integers. (Bug#25684) + +2017-02-17 Dmitry Gutov + + Fix buffers update in vc-retrieve-tag + + * lisp/vc/vc.el (vc-retrieve-tag): When the granularity is + `repository', use the repository root and pass it to + vc-resynch-buffer (bug#25714). + +2017-02-16 Paul Eggert + + * src/buffer.h: Fix indenting. + +2017-02-16 Paul Eggert + + Add sanity checks for Bswitch hash tables + + * src/bytecode.c (exec_byte_code) [BYTE_CODE_SAFE]: + Check that operand is a hash table and hashes to ints. + +2017-02-16 Paul Eggert + + * src/keyboard.c (read_key_sequence): Fix integer-overflow glitch. + +2017-02-16 Vibhav Pant + + bytecomp.el: Avoid unnecessary calculation for jump table addresses. + + * lisp/emacs-lisp/bytecomp.el (byte-compile-lapcode): Don't do + redundant operations while calculating the correct jump addresses + from TAGs in jump tables. + +2017-02-16 Mark Oteiza + + Minor changes in json.el + + * lisp/json.el (json-advance): Simpler docstring. + (json-read-escaped-char): Use xdigit subform in rx expression. + (json-read-string): Just use = for char comparison. + +2017-02-16 Mark Oteiza + + Don't expand body inside a let-binding when there are no bindings + + * lisp/emacs-lisp/pcase.el (pcase-codegen): Only let-bind if VARS + is non-nil. + +2017-02-16 Glenn Morris + + Handle user-mail-address being the empty string + + * lisp/mail/feedmail.el (feedmail-fiddle-from): + * lisp/mail/rmail.el (rmail-unknown-mail-followup-to): + * lisp/mail/rmailsum.el (rmail-header-summary): + Belated update for 2002-09-29 startup.el change, 680ebfa, where + the value of user-mail-address during initialization was changed + from nil to the empty string. + +2017-02-15 Michael Albinus + + Continue to fix bug#25607 + + * lisp/ido.el (ido-complete): Let-bind `non-essential' to nil. + (ido-file-name-all-completions-1): Do not bind `non-essential'. + + * lisp/net/tramp.el: (tramp-completion-file-name-handler): + Improve autoloaded version. + (tramp-completion-file-name-handler): Remove old compat code. + Check only for `tramp-completion-mode-p'. + (tramp-completion-mode-p): Autoload. Do not check any longer + for `last-input-event'. + (tramp-completion-handle-expand-file-name): Simplify. (Bug#25607) + +2017-02-15 Michal Nazarewicz + + casing: don’t assume letters are *either* upper- or lower-case (bug#24603) + + A compatibility digraph characters, such as Dž, are neither upper- nor + lower-case. At the moment however, those are reported as upper-case¹ + despite the fact that they change when upper-cased. + + Stop checking if a character is upper-case before trying to up-case it + so that title-case characters are handled correctly. This fixes one of + the issues mentioned in bug#24603. + + ¹ Because they change when converted to lower-case. Notice an asymmetry + in that for a character to be considered lower-case it must not be + upper-case (plus the usual condition of changing when upper-cased). + + * src/buffer.h (upcase1): Delete. + (upcase): Change to upcase character unconditionally just like downcase + does it. This is what upcase1 was. + + * src/casefiddle.c (casify_object, casify_region): Use upcase instead + of upcase1 and don’t check !uppercasep(x) before calling upcase. + + * src/keyboard.c (read_key_sequence): Don’t check if uppercase(x), just + downcase(x) and see if it changed. + + * test/src/casefiddle-tests.el (casefiddle-tests--characters, + casefiddle-tests-casing): Update test cases which are now passing. + +2017-02-15 Michal Nazarewicz + + Generate upcase and downcase tables from Unicode data (bug#24603) + + Use Unicode data to generate case tables instead of mostly repeating + them in lisp code. Do that in a way which maps ‘Dz’ (and similar) + digraph to ‘dz’ when down- and ‘DZ’ when upcasing. + + https://debbugs.gnu.org/cgi/bugreport.cgi?msg=89;bug=24603 lists all + changes to syntax table and case tables introduced by this commit. + + * lisp/international/characters.el: Remove case-pairs defined with + explicit Lisp code and instead use Unicode character properties. + + * test/src/casefiddle-tests.el (casefiddle-tests--characters, + casefiddle-tests-casing): Update test cases which are now working + as they should. + +2017-02-15 Michal Nazarewicz + + Add tests for casefiddle.c (bug#24603) + + Fixes cases marked FIXME upcoming in followup commits. + + * test/src/casefiddle-tests.el (casefiddle-tests-char-properties, + casefiddle-tests-case-table, casefiddle-tests-casing-character, + casefiddle-tests-casing, casefiddle-tests-casing-byte8, + casefiddle-tests-casing-byte8-with-changes): New tests. + (casefiddle-tests--test-casing): New helper function for runnig + some of the tests. + +2017-02-15 Michal Nazarewicz + + oldXMenu: add missing #include + + Some of the files in oldXMenu use functions from string.h without + including that header which results in compile warnings: + + ChgPane.c:46:5: warning: implicit declaration of function ‘strlen’ + ChgPane.c:46:20: warning: incompatible implicit declaration of + built-in function ‘strlen’ + ChgSel.c:62:2: warning: implicit declaration of function ‘strlen’ + ChgSel.c:62:17: warning: incompatible implicit declaration of built-in + function ‘strlen’ + Create.c:220:5: warning: implicit declaration of function ‘strcmp’ + InsPane.c:65:5: warning: implicit declaration of function ‘strlen’ + InsPane.c:65:20: warning: incompatible implicit declaration of + built-in function ‘strlen’ + InsSel.c:68:5: warning: implicit declaration of function ‘strlen’ + InsSel.c:68:20: warning: incompatible implicit declaration of built-in + function ‘strlen’ + InsSel.c:75:5: warning: implicit declaration of function ‘strcmp’ + + Add the necessary ‘#include ’. + + oldXMenu/ChgPane.c, oldXMenu/ChgSel.c, oldXMenu/Create.c, oldXMenu/InsPane.c, + oldXMenu/InsSel.c: add missing #include + +2017-02-15 Paul Eggert + + Fixup recent rmail patch + + * lisp/mail/rmail.el (rmail-epa-decrypt): Remove unused local. + +2017-02-15 Richard Stallman + + Rmail fix + + * lisp/mail/rmail.el (rmail-epa-decrypt-1): Include the just-decrypted text + as element 4 of the value. + (rmail-epa-decrypt): Take the text to insert from that element. + +2017-02-15 Vibhav Pant + + bytecomp-tests.el: Store all test forms in one constant. + + * test/lisp/emacs-lisp/bytecomp-tests.el: Store all test expressions + in a single constant (byte-opt-testsuite-arith-data), add new forms + which generate lapcode with adjacent/redundant tags. + +2017-02-15 Glenn Morris + + Small lispref edit + + * doc/lispref/os.texi (User Identification): + Remove extraneous detail about user-mail-address. + +2017-02-15 Katsumi Yamaoka + + Document fill-separate-heterogeneous-words-with-space (bug#25685) + + * doc/lispref/text.texi (Filling): + Document fill-separate-heterogeneous-words-with-space (bug#25685). + +2017-02-15 Noam Postavsky + + Test comment-multi-line = nil auto fill case too + + * test/lisp/progmodes/js-tests.el (js-mode-auto-fill): Test with + `comment-multi-line' both nil and non-nil. + * lisp/newcomment.el (comment-multi-line): Mark safe if it's a + boolean. + * etc/NEWS: Mention that `js-mode' now sets `comment-multi-line'. + +2017-02-15 Katsumi Yamaoka + + Don't delete leading and trailing space from CJK word (bug#25685) + + * lisp/textmodes/fill.el (fill-delete-newlines): + Don't delete leading and trailing space from CJK word. + (fill-separate-heterogeneous-words-with-space): + New user option that controls it (bug#25685). + +2017-02-15 Juri Linkov + + ‘M-s w RET word C-s’ repeats incremental search. + + * lisp/isearch.el (isearch-new-nonincremental): New variable. + (with-isearch-suspended): Bind isearch-new-nonincremental to + isearch-nonincremental, and restore it afterwards. + (isearch-forward-exit-minibuffer, isearch-reverse-exit-minibuffer): + Set isearch-new-nonincremental to nil. (Bug#25562) + +2017-02-14 Tom Tromey + + Make vc-git detect conflict state for vc-dir + + * lisp/vc/vc-git.el (vc-git-dir-status-state): New struct. + (vc-git-dir-status-update-file): New function. + (vc-git-after-dir-status-stage, vc-git-dir-status-goto-stage): Use + vc-git-dir-status-state; add 'ls-files-conflict state. + (vc-git-dir-status-files): Create a vc-git-dir-status-state. + +2017-02-14 Vibhav Pant + + byte-opt: Replace merged tags in jump tables too. (bug#25716) + + * lisp/emacs-lisp/byte-opt.el (byte-optimize-lapcode): While merging + adjacent tags, make sure that the old tag is replaced in all jump + tables, if any. This fixes the bytecode VM jumping to the wrong + address in compiled cond forms where the body of a clause was a loop + of any sort. + +2017-02-14 Glenn Morris + + Remove overly broad element from default mail-dont-reply-to-names + + * lisp/mail/mail-utils.el (mail-dont-reply-to): + Do not include just "user@" in mail-dont-reply-to-names, and simplify. + Ref: lists.gnu.org/archive/html/help-gnu-emacs/2017-02/msg00049.html + * lisp/gnus/message.el (message-dont-reply-to-names): Doc fix. + * doc/misc/message.texi (Wide Reply): Tiny fix re dont-reply-to-names. + +2017-02-14 Juri Linkov + + * etc/NEWS: Mention query-replace-from-to-separator. (Bug#25482) + +2017-02-13 Arash Esbati + + Match all characters in optional argument of \documentclass + + * lisp/textmodes/reftex.el (reftex-TeX-master-file): Match all + characters in optional argument containing name of the main file. + +2017-02-13 Vibhav Pant + + Merge branch 'master' into feature/byte-switch + +2017-02-13 Katsumi Yamaoka + + Fix non-ASCII text encoding (bug#25658) + + * lisp/gnus/mm-bodies.el (mm-encode-body): + Fix non-ASCII text encoding (bug#25658). + +2017-02-13 Vibhav Pant + + test/lisp/emacs-lisp/bytecomp-tests.el: Add more tests for switch. + +2017-02-13 Stefan Monnier + + * doc/lispref/modes.texi (Derived Modes): Make example more idiomatic + +2017-02-13 Glenn Morris + + Fix recent bootstrap issue by moving string-to-list + + * lisp/international/mule-util.el (string-to-list, string-to-vector): + Move from here... + * lisp/subr.el (string-to-list, string-to-vector): ...to here. + The implementation is trivial and at least string-to-list + has ended up being needed early during bootstrap. + +2017-02-13 Glenn Morris + + Doc fixes related to mail-host-address + + * lisp/startup.el (mail-host-address): Doc fix. + * doc/lispref/os.texi (System Environment): + Remove extraneous details of mail-host-address. + +2017-02-13 Glenn Morris + + Simplify time-stamp mail host usage + + * lisp/time-stamp.el (time-stamp-mail-host-name): Remove function. + (time-stamp-string-preprocess): Handle "h" (mail host) directly. + +2017-02-13 Glenn Morris + + Doc fix for vhdl-mode re mail-host-address + + * lisp/progmodes/vhdl-mode.el (vhdl-file-header): Doc fix. + (mail-host-address): Do not add to vhdl-related custom group, + since vhdl-template-replace-header-keywords doesn't use it. + +2017-02-13 Mark Oteiza + + Substitute leading $HOME/ in xdg-user-dirs + + * lisp/xdg.el (xdg--substitute-home-env): New function. + (xdg--user-dirs-parse-line): Use it. + (xdg-user-dir): Expand ~/ in xdg-user-dirs values. + +2017-02-13 Mark Oteiza + + * lisp/buff-menu.el: Turn on lexical-binding. + +2017-02-13 Juri Linkov + + * lisp/replace.el (query-replace-from-to-separator): Move propertize + + and char-displayable-p test to query-replace-read-from. + Add choice nil to disable this feature. + (query-replace-read-from): Don't reevaluate custom setting. + Use char-displayable-p to test the first non-whitespace character + in query-replace-from-to-separator, use " -> " when fails. + Add prompt for the case when separator is nil but + query-replace-defaults is non-nil. + Remove unused test for regexp-flag. + Thanks to Thierry Volpiatto + +2017-02-13 Karl Fogel + + Convert more uses of `looking-at' to `following-char' + + This follows up to Mark Oteiza's commit of 12 Feb 2017, 14:46:03 UTC + (commit 91478f46238a) with more of the same. + + * lisp/bookmark.el (bookmark-send-edited-annotation): + (bookmark-bmenu-execute-deletions): Replace instances of looking-at + with char comparisons using following-char. + +2017-02-12 Paul Eggert + + Fix typos in tests for lax-plist-get etc. + + Problem reported by Eli Zaretskii (Bug#25606#62). + * test/src/fns-tests.el (test-cycle-lax-plist-get) + (test-cycle-plist-put, test-cycle-lax-plist-put): + Fix tests to match behavior. + +2017-02-12 Michael Albinus + + Fix bug#25607 + + * lisp/net/tramp.el (tramp-completion-file-name-handler): + Improve autoloaded version. + (tramp-autoload-file-name-handler): Avoid recursive load. + (tramp-completion-handle-expand-file-name): Handle empty NAME. + (Bug#25607) + +2017-02-12 Mark Oteiza + + Remove server-buffer-clients string from minor-mode-alist + + * lisp/server.el: Don't put an element for server-buffer-clients into + minor-mode-alist. (Bug#20201) + +2017-02-12 Mark Oteiza + + Nix some useless uses of looking-at, looking-back + + * lisp/allout.el (allout-kill-topic): + (allout-next-topic-pending-encryption): + * lisp/bookmark.el (bookmark-kill-line): + * lisp/cus-edit.el (custom-save-variables, custom-save-faces): + * lisp/cus-theme.el (custom-theme-write-variables): + (custom-theme-write-faces): + * lisp/emacs-lisp/autoload.el (autoload-generate-file-autoloads): + * lisp/emacs-lisp/bytecomp.el (byte-compile-from-buffer): + * lisp/emacs-lisp/checkdoc.el (checkdoc-interactive-loop): + (checkdoc-interactive-ispell-loop): + (checkdoc-message-interactive-ispell-loop, checkdoc-this-string-valid): + (checkdoc-this-string-valid-engine): + * lisp/emacs-lisp/elint.el (elint-get-top-forms): + * lisp/emulation/viper-cmd.el (viper-backward-indent): + * lisp/image-dired.el (image-dired-delete-char): + * lisp/simple.el (kill-visual-line): Replace instances of looking-at, + looking-back with char comparisons using following-char, preceding-char. + +2017-02-12 Eli Zaretskii + + Clarify what is the "cursor" + + * doc/lispref/windows.texi (Window Point): Clarify the notion of + "cursor". + +2017-02-11 Tom Tromey + + Recognize JS regexp literals more correctly + + Bug#25529 + * lisp/progmodes/js.el (js--syntax-propertize-regexp-regexp): New + constant. + (js-syntax-propertize-regexp): Use it. Remove "end" argument. + (js--syntax-propertize-regexp-syntax-table): Remove. + (js-syntax-propertize): Update. + * test/lisp/progmodes/js-tests.el (js-mode-regexp-syntax-bug-25529): + New test. + +2017-02-11 Vibhav Pant + + src/bytecode.c (exec_byte_code): Make hash_code a Lisp_Object. + + This avoids using XUINT every time while comparing it with + HASH_HASH (h, i), replacing it with EQ. + +2017-02-11 Vibhav Pant + + src/bytecode.c (exec_byte_code): Remove unnecessary (e)assert. + +2017-02-11 Tom Tromey + + Fix bug in css--mdn-find-symbol + + * lisp/textmodes/css-mode.el (css--mdn-find-symbol): Skip whitespace + before skipping word characters. + test/lisp/textmodes/css-mode-tests.el (css-mdn-symbol-guessing): Add + regression test. + +2017-02-11 Vibhav Pant + + src/bytecode.c: Add optional sanity check for jump tables. + + * src/bytecode.c (exec_byte_code): When sanity checks are enabled, + check that the jump table's size is equal to it's count. + +2017-02-11 Vibhav Pant + + Merge branch 'master' into feature/byte-switch + + * src/bytecode.c: Refactor to follow GNU coding standards + +2017-02-11 Eli Zaretskii + + Fix handling of XBM images on MS-Windows + + * src/image.c (xbm_load) [HAVE_NTGUI]: Fix calculation of + 'nbytes' when inverting XBM data bits. (Bug#25661) + +2017-02-11 Eli Zaretskii + + Fix handling of PBM data + + * src/image.c (pbm_load): Handle PBM data with no blanks between + individual pixel values correctly. (Bug#25660) + +2017-02-10 Noam Postavsky + + Fix warnings in debug tracing code + + * src/xdisp.c (dump_glyph, dump_glyph_string): + * src/xfaces.c (dump_realized_face): Cast arguments or adjust format + specifiers to match signedness. + +2017-02-10 Sam Steingold + + Extract grep-find-ignored-directories processing from rgrep-default-command + + (rgrep-find-ignored-directories): Extract from `rgrep-default-command'. + Some Emacs packages use `grep-find-ignored-directories' to ignore some + directories, so will use this function instead of custom code. + (rgrep-default-command): Use `rgrep-find-ignored-directories'. + +2017-02-10 Vibhav Pant + + src/bytecode.c: Avoid comparing values unnecessarily in Bswitch + + * src/bytecode.c: (exec_byte_code) While linear searching the jump + table, compare the value's hash table first to avoid calling + h->test.cmpfn every time. + +2017-02-10 Paul Eggert + + Fix a few integer-overflow glitches + + * src/composite.c (composition_compute_stop_pos, composition_reseat_it): + * src/dispextern.h (struct composition_it.rule_idx): + * src/keyboard.c (Fset__this_command_keys): + * src/xwidget.c (webkit_js_to_lisp): + Don’t assume object sizes fit in ‘int’. + * src/xwidget.c (Fxwidget_resize): + Don’t assume Emacs integers fit in ‘int’. + +2017-02-10 Eli Zaretskii + + Fix a bug with displaying an image after a TAB + + * src/xdisp.c (display_line): Handle TAB at end of screen line + specially only when we are displaying characters. (Bug#25662) + +2017-02-10 Eli Zaretskii + + Improve commentary in lisp.h + + * src/lisp.h: Explain in the comment why enlarging a Lisp_Misc + object is discouraged. + +2017-02-10 Paul Eggert + + Move cyclic tests to fns-tests.el + + * test/src/fns-tests.el (cyc1, cyc2, dot1, dot2): New functions. + (test-cycle-length, test-cycle-safe-length, test-cycle-member) + (test-cycle-memq, test-cycle-memql, test-cycle-assq) + (test-cycle-assoc, test-cycle-rassq, test-cycle-rassoc) + (test-cycle-delq, test-cycle-delete, test-cycle-reverse) + (test-cycle-plist-get, test-cycle-lax-plist-get) + (test-cycle-plist-member, test-cycle-plist-put) + (test-cycle-lax-plist-put, test-cycle-equal, test-cycle-nconc): + New tests. + * test/manual/cyclic-tests.el: File deleted. + +2017-02-10 Gemini Lasswell + + Fix instrumenting code with propertized strings in Edebug + + * lisp/emacs-lisp/edebug.el (edebug-read-function): Allow + 'read' to decide what is and isn't a syntax error. (Bug#25068) + +2017-02-10 Vladimir Panteleev + + Improve fontification in bat-mode + + * lisp/progmodes/bat-mode.el (bat-font-lock-keywords): Match + word and symbol constituents when looking for variable names + to fontify; also, correct the syntax table and mark the equal + sign (=) character as punctuation. Improve fontification + accuracy of iteration/positional variables. + (bat-mode): Set comment-start-skip. (Bug#25541) + + * test/lisp/progmodes/bat-mode-tests.el: New file, tests for + bat-mode.el. + +2017-02-10 Eli Zaretskii + + Restore special setting of this-command-keys by M-x + + It was lost when execute-extended-command was reimplemented in Lisp. + + * src/keyboard.c (Fset__this_command_keys): New function. + (syms_of_keyboard): Defsubr it. + + * lisp/simple.el (execute-extended-command): Set this-command-keys + as novice.el expects. (Bug#25612) + +2017-02-09 Juri Linkov + + * lisp/isearch.el (isearch-search-fun-default): Set isearch-adjusted + + to t to display "Pending" in the search prompt for lax + word/symbol search (bug#25562). Don't use lax for lazy-highlighting + when 'bound' is non-nil. + (word-search-regexp, isearch-symbol-regexp): Don't depend on lax + at the beginning of regexp (bug#22589). + + * lisp/info.el (Info-isearch-search): + Use isearch--lax-regexp-function-p. + + * doc/emacs/search.texi (Word Search, Symbol Search): + Mention "Pending" prompt for lax word/symbol search. + +2017-02-09 Vibhav Pant + + src/bytecode.c (exec_byte_code): Remove unneeded assert. + + bytecode.c (exec_byte_code): Use h->count instead of HASH_TABLE_SIZE + +2017-02-09 Vibhav Pant + + bytecode.c (exec_byte_code): don't check hash code in linear search. + + * src/bytecode.c (exec_byte_code): Don't check that the hash code is + not nil when linear scanning the jump table. Hash tables for are + declared with :size as the exact number of cases, so each entry i + should have a hash code. When BYTE_CODE_SAFE, do it as a sanity + check. + +2017-02-09 Tino Calancha + + Ibuffer: Update mode documentation + + * lisp/ibuffer.el (ibuffer-mode): List newest commands in mode documentation. + +2017-02-09 Steven Allen (tiny change) + + Fix environment variable for xdg-data-dirs + + * lisp/xdg.el (xdg-data-dirs): Use XDG_DATA_DIRS, not XDG_CONFIG_DIRS + +2017-02-09 Tino Calancha + + Ibuffer: Erase output buffer before shell commands + + * lisp/ibuf-macs.el (define-ibuffer-op): Add keyword arguments + BEFORE and AFTER; they are forms to run before/after the operation. + * lisp/ibuf-ext.el (ibuffer--maybe-erase-shell-cmd-output): + New defun; if shell-command-dont-erase-buffer is nil, then + erase shell command output buffer. + (ibuffer-do-shell-command-pipe, ibuffer-do-shell-command-file): Use it. + +2017-02-09 Tino Calancha + + Ibuffer: Don't truncate shell command output + + * lisp/ibuf-ext.el (ibuffer-do-shell-command-pipe) + (ibuffer-do-shell-command-pipe-replace) + Use 'call-shell-region' (Bug#22679). + (ibuffer-do-shell-command-file): Use call-process-shell-command. + If FILE, the file that the buffer object is visiting, + exists and the buffer is up-to-date, then use + FILE instead of creating a temporary file (Bug#22679). + +2017-02-09 Vibhav Pant + + Improve byte-switch execution. + + * lisp/emacs-lisp/byte-opt.el, + lisp/emacs-lisp/bytecomp.el (byte-decompile-bytecode-1), + (byte-compile-lapcode): Calculate the actual jump address while + compiling, store it in the jump table. + + * src/bytecode.c: Jump to the looked up value directly, do a linear + search when the number of elements is <= 5. + +2017-02-09 Noam Postavsky + + Make sure eshell pipelines don't drop data + + * lisp/eshell/esh-proc.el (eshell-sentinel): If called while still + handling output of the process, make sure to close the pipes only later, + so that the next process in the pipeline recieves EOF only after getting + all its input (Bug#25549). + +2017-02-09 Katsumi Yamaoka + + Make mm-shr use mail-parse-charset by default + + * lisp/gnus/mm-decode.el (mm-shr): Use mail-parse-charset by default. + This helps an html message with no charset spec to be decoded. + +2017-02-08 Stephen Berman + + describe-char: unambiguous name for inserting ASCII 7 + + * lisp/descr-text.el (describe-char): Make the input + suggestion for inserting ASCII character 7 by name use the + unambiguous name "BELL (BEL)" (bug#25641). + +2017-02-08 Michael Albinus + + Modify suppressing `vc-refresh-state' in filenotify-tests.el + + * test/lisp/filenotify-tests.el (file-notify-test03-autorevert): + Use an advice rather than an alias for suppressing `vc-refresh-state'. + +2017-02-08 Noam Postavsky + + Disable native completion for ipython (Bug#25067) + + * lisp/progmodes/python.el: + (python-shell-completion-native-disabled-interpreters): Add "ipython". + +2017-02-07 Michael Albinus + + Suppress undesired error messages in filenotify-tests.el + + * test/lisp/filenotify-tests.el (file-notify-test03-autorevert): + Suppress `vc-refresh-state', it produces undesired error messages. + +2017-02-07 Eli Zaretskii + + Fix a typo in ada-mode manual + + * doc/misc/ada-mode.texi (Project file variables): Add a missing + right bracket. Reported by Jean-Christophe Helary + . + +2017-02-07 Lars Ingebrigtsen + + Ensure that Gnus bugs show up in the Emacs tracker + + * lisp/gnus/gnus.el (gnus-bug-package): Include Emacs in the + package spec. + +2017-02-07 Lars Ingebrigtsen + + Revert "Don't tag Gnus bugs with "gnus"" + + This reverts commit b6fa58072304c2a24f1fe8a0e06a4739a7f8211b. + + The debbugs syntax requires a package name + +2017-02-07 Vibhav Pant + + Add tests for checking byte-switch code. + + * test/lisp/emacs-lisp/bytecomp-tests.el (bytecomp-cond): New test, + test byte-switch bytecode. + +2017-02-07 Mark Oteiza + + Add xdg library + + * etc/NEWS: Mention new library. + * lisp/xdg.el: New file. + +2017-02-07 Paul Eggert + + Do not trick info/dir’s timestamp + + * Makefile.in (${srcdir}/info/dir): When making this file, do not + do anything special about its timestamp. Previously this rule + used move-if-change, which meant that this file’s timestamp could + end up being older than the files it depends on, and this caused + ‘make --question info’ to fail, which caused ‘make-dist’ to fail + now that ‘make-dist’ invokes ‘make --question info’. + +2017-02-07 Paul Eggert + + Make FOR_EACH_TAIL more like other FOR_EACH macros + + See comments by Stefan Monnier in: + http://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00181.html + and by Eli Zaretskii in: + http://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00207.html + * src/fns.c (internal_equal): Do not bypass check for depth + overflow when tail-recursing via a dotted list tail or an overlay + plist, to avoid a rare infloop. + * src/lisp.h (FOR_EACH_TAIL, FOR_EACH_TAIL_SAFE): Take TAIL as an + arg, and update it at each iteration, rather than have callers + access it.tail. All callers changed. + (FOR_EACH_TAIL): Do not check for dotted lists, as this is now + the caller’s responsibility. All callers changed. + (FOR_EACH_TAIL_CONS): Remove. All callers changed. + (struct for_each_tail_internal.tail): Remove; no longer needed. + (FOR_EACH_TAIL_INTERNAL): Remove dotted arg, and set the tail + arg each time through the loop. All callers changed. + +2017-02-05 Paul Eggert + + Port to clang 3.8.0 + + It does not allow a for-loop's control var to be an anonymous struct. + * src/lisp.h (struct for_each_tail_internal): New type. + (FOR_EACH_TAIL_INTERNAL): Use it. + +2017-02-05 Paul Eggert + + Add cyclic-list tests + + * test/manual/cycle-tests.el: New file (Bug#25606). + +2017-02-05 Paul Eggert + + FOR_EACH_TAIL now checks for quit + + As per Eli Zaretskii (Bug#25606#20). Although these calls to + maybe_quit are unnecessary in practice, Eli was not convinced + that the calls are unnecessary. + * src/lisp.h (FOR_EACH_TAIL, FOR_EACH_TAIL_CONS): + Call maybe_quit every so often. + (FOR_EACH_TAIL_INTERNAL): New arg CHECK_QUIT. All callers changed. + +2017-02-05 Paul Eggert + + Signal list cycles in ‘length’ etc. + + Use macros like FOR_EACH_TAIL instead of maybe_quit to + catch list cycles automatically instead of relying on the + user becoming impatient and typing C-g (Bug#25606). + * src/fns.c (Flength, Fmember, Fmemq, Fmemql, Fassq, Fassoc, Frassq) + (Frassoc, Fdelete, Freverse): + Use FOR_EACH_TAIL instead of maybe_quit. + (Fnreverse): Use simple EQ to check for circular list instead + of rarely_quit, as this suffices in this unusual case. + (Fplist_put, Flax_plist_put, Flax_plist_put): + Use FOR_EACH_TAIL_CONS instead of maybe_quit. + (internal_equal): Use FOR_EACH_TAIL_CONS to check lists, instead + of by-hand tail recursion that did not catch cycles. + * src/fns.c (Fsafe_length, Fplist_get): + * src/xdisp.c (display_mode_element): + Use FOR_EACH_TAIL_SAFE instead of by-hand Floyd’s algorithm. + * src/lisp.h (QUIT_COUNT_HEURISTIC): Remove; no longer needed. + (rarely_quit): Simply count toward USHRT_MAX + 1, since the + fancier versions are no longer needed. + (FOR_EACH_TAIL_CONS, FOR_EACH_TAIL_SAFE) + (FOR_EACH_TAIL_INTERNAL): New macros, the last with definiens + mostly taken from FOR_EACH_TAIL. + (FOR_EACH_TAIL): Rewrite in terms of FOR_EACH_TAIL_INTERNAL. + +2017-02-05 Paul Eggert + + Simplify use of FOR_EACH_TAIL + + * src/data.c (circular_list): New function. + * src/lisp.h (FOR_EACH_TAIL): Use Brent’s algorithm and C99 for-loop + decl, to eliminate the need for the args TAIL, TORTOISE and N, and + to speed things up a bit on typical hosts with optimization. + All uses changed (Bug#25605). + +2017-02-05 Simen Heggestøyl + + * lisp/textmodes/css-mode.el: Require subr-x at compile time + +2017-02-05 Eli Zaretskii + + Clarify documentation of 'bufferpos-to-filepos' and 'filepos-to-bufferpos' + + * doc/lispref/nonascii.texi (Text Representations): Clarify that + 'exact' value of QUALITY argument to 'bufferpos-to-filepos' and + 'filepos-to-bufferpos' can lead to expensive and slow processing. + + * lisp/international/mule-util.el (filepos-to-bufferpos) + (bufferpos-to-filepos): Doc fix. (Bug#25626) + +2017-02-05 Vibhav Pant + + Merge remote-tracking branch 'origin/master' into feature/byte-switch + +2017-02-05 Vibhav Pant + + bytecomp.el: Use macroexp-const-p instead of bc-cond-valid-obj2-p. + + * lisp/emacs-lisp/bytecomp.el (byte-compile-cond-vars): Use + (macroexp-cons-p) instead of (byte-compile-cond-valid-obj2-p) to + make sure that obj1/obj2 can be compared with `eq'. + +2017-02-05 Vibhav Pant + + * byte-opt.el (byte-decompile-bytecode-1): Use eq instead of =. + +2017-02-05 Vibhav Pant + + bytecomp.el: Inline lapcode containing `byte-switch' correctly. + + * lisp/emacs-lisp/bytecomp.el (byte-compile-inline-lapcode): + Restore value of byte-compile-depth after emitting a jump to a tag + in a jump table, or default/done tags. + Set the depth of final tags for byte-switch to nil after emitting + any jumps to them. + +2017-02-05 Vibhav Pant + + byte-opt.el: Replace jump tables while decompiling correctly. + + * lisp/emacs-lisp/byte-opt.el (byte-decompile-bytecode-1): + Don't make a copy of the constant vector, as it isn't used with + the decompiled lapcode. + Make sure that the correct lapcode pair/list is being modified while + replacing the jump table. + +2017-02-05 Vibhav Pant + + bytecomp.el: Don't store non-keyword symbols in jump-tables. + + * lisp/emacs-lisp/bytecomp.el (byte-compile-cond-valid-obj2-p) return + nil when OBJ is a non-keyword symbol (i.e a variable), as the jump + table can only be used when comparing variables with constant values. + +2017-02-04 Tom Tromey + + typo fix + + (css--colon-inside-selector-p): Fix typo in docstring. + +2017-02-04 Tom Tromey + + Set comment-multi-line in js-mode + + Bug#6806: + * lisp/progmodes/js.el (js-mode): Set comment-multi-line to t. + * test/lisp/progmodes/js-tests.el (js-mode-auto-fill): New test. + +2017-02-04 Simen Heggestøyl + + * test/manual/indent/scss-mode.scss: Fix indentation + +2017-02-04 Simen Heggestøyl + + Fix indentation of multiline CSS property values + + * lisp/textmodes/css-mode.el (css-smie-grammar): Give colons belonging + to properties higher precedence. + (css--colon-inside-selector-p, css--colon-inside-funcall): New + functions for helping SMIE during tokenization. + (css-smie--forward-token, css-smie--backward-token): Distinguish + colons belonging to properties from other colons. + + * test/manual/indent/css-mode.css: Add tests for the changes above. + + * test/manual/indent/scss-mode.scss: Ditto. + +2017-02-04 Gemini Lasswell + + Add tests for lisp/kmacro.el + + * test/lisp/kmacro-tests.el: New file. (Bug#24939) + +2017-02-04 Eli Zaretskii + + Fix autorevert-tests on MS-Windows + + * test/lisp/autorevert-tests.el + (auto-revert-test02-auto-revert-deleted-file): Don't check that + auto-revert-use-notify was reset to nil on w32. + +2017-02-04 Gemini Lasswell + + New macro 'ert-with-message-capture' + + * lisp/emacs-lisp/ert-x.el (ert-with-message-capture): New macro. + (Bug#25158) + + * test/lisp/autorevert-tests.el (auto-revert--wait-for-revert) + (auto-revert-test00-auto-revert-mode) + (auto-revert-test01-auto-revert-several-files) + (auto-revert-test02-auto-revert-deleted-file) + (auto-revert-test03-auto-revert-tail-mode) + (auto-revert-test04-auto-revert-mode-dired): + * test/lisp/filenotify-tests.el (file-notify-test03-autorevert): Use + ert-with-message-capture. + +2017-02-04 Gemini Lasswell + + Avoid invalid read syntax errors due to 'ert-with-test-buffer' + + * lisp/emacs-lisp/ert-x.el (ert-with-test-buffer): Fix the + 'declare' form. (Bug#24722) + +2017-02-04 Eli Zaretskii + + Fix a syntax error when evaluating pcase.el under Edebug + + * lisp/emacs-lisp/pcase.el (pcase-MACRO): Replace def-edebug-spec + with an explicit 'put' form. Suggested by Gemini Lasswell + . (Bug#24717) + +2017-02-04 Gemini Lasswell + Eli Zaretskii + + Change edebug-max-depth from defconst to defcustom + + * lisp/emacs-lisp/edebug.el (edebug-max-depth): Add defcustom. + (Bug#24713) + + * etc/NEWS: Mention edebug-max-depth. + + * doc/lispref/edebug.texi (Checking Whether to Stop): Mention + edebug-max-depth and index it. Add cross-references for + max-lisp-eval-depth and max-specpdl-size. + +2017-02-04 Eli Zaretskii + + Support options with embedded whitespace in 'dired-listing-switches' + + * lisp/dired.el (dired-listing-switches): Document how to quote + options with embedded whitespace. + + * lisp/files.el (insert-directory): Use split-string-and-unquote + to support dired-listing-switches that specify command-line + options with embedded spaces. (Bug#25485) + +2017-02-04 Gemini Lasswell + Noam Postavsky + + Add tests for lisp/emacs-lisp/testcover.el + + * test/lisp/emacs-lisp/testcover-tests.el: New file. + * test/lisp/emacs-lisp/testcover-resources/testcases.el: New file. + +2017-02-04 Eli Zaretskii + + Document 'save-some-buffers-default-predicate' + + * doc/lispref/files.texi (Saving Buffers): + * doc/emacs/files.texi (Save Commands): Document + save-some-buffers-default-predicate. + +2017-02-04 Richard Stallman + + New defcustom 'save-some-buffers-default-predicate' + + * lisp/files.el (save-some-buffers-default-predicate): New defcustom. + (save-some-buffers): Use it when PRED is nil or omitted. + +2017-02-04 Mark Oteiza + + Rename to if-let* and when-let* + + Make the existing if-let and when-let aliases. + * lisp/emacs-lisp/subr-x.el (if-let*, when-let*): New macros. Rewrite + docstrings, incorporating that from let* and the existing if-let. + (if-let, when-let, and-let*): Alias them. + +2017-02-03 Vibhav Pant + + Revert "Use maphash instead of cl-loop." + + This reverts commit bfa88520136dd6b187ba101e6db5a5f8f0d5e874. + +2017-02-03 Nicolas Petton + + Bump Emacs version to 25.2 RC1 + + * README: + * configure.ac: + * msdos/sed2v2.inp: + * nt/README.W32: Bump Emacs version. + * lisp/ldefs-boot.el: Update. + +2017-02-03 Nicolas Petton + + * admin/make-tarball.txt: Add documentation regarding the release banner. + +2017-02-03 Tino Calancha + + * CONTRIBUTE (Documenting your changes): Index new vars/commands in manual. + +2017-02-03 Paul Eggert + + Re-port alloc.c to Solaris sparc and simplify + + alloc.c had bitrotted a bit, and used an undefined symbol + stack_base when Emacs was built on Solaris sparc, leading to + compilation failures. Also, code related to __builtin_unwind_init + was unnecessarily duplicated. Fix the bitrot and remove some + duplication. + * src/alloc.c: Remove uses of GC_SAVE_REGISTERS_ON_STACK, since it + is never defined. + (test_setjmp) [!HAVE___BUILTIN_UNWIND_INIT && GC_SETJMP_WORKS]: + Define a no-op dummy, to simplify use. + (test_setjmp) [!GC_SETJMP_WORKS]: Test setjmp_tested_p here rather + than in the caller, to simplify use. + (stacktop_sentry): New type. + (__builtin_unwind_init) [!HAVE___BUILTIN_UNWIND_INIT]: New macro. + (SET_STACK_TOP_ADDRESS): New macro, containing code that was duplicated. + (flush_stack_call_func, Fgarbage_collect): Use it. + (init_alloc): Omit unnecessary initialization. + After dumping, Emacs need not re-test setjmp. + +2017-02-03 Noam Postavsky + + Add tests for scrolling + + * test/manual/scroll-tests.el: New tests for scroll-margin behavior. + +2017-02-03 Noam Postavsky + + Fix scrolling with partial lines + + * src/xdisp.c (partial_line_height): New function. + (try_scrolling): + * src/window.c (window_scroll_pixel_based): Use it for calculating the + pixel scroll margin correctly in a window with partial lines. + +2017-02-03 Noam Postavsky + + Make limit on scroll-margin variable + + * src/xdisp.c (maximum-scroll-margin): New variable. + * lisp/cus-start.el: Make it customizable. + * etc/NEWS: Mention it. + * doc/emacs/display.texi (Auto Scrolling): + * doc/lispref/windows.texi (Textual Scrolling): Document it. + * src/window.c (window_scroll_pixel_based): Use it instead of hardcoding + division by 4 (Bug #5718). + +2017-02-03 Noam Postavsky + + Don't count mode line for scroll-margin limit + + * src/window.c (window_scroll_margin): Use window_box_height to avoid + counting header line, scrollbars for scroll-margin limit (Bug #5718). + +2017-02-03 Noam Postavsky + + Refactor uses of scroll_margin to a function + + Its effective range needs to be clamped between 0 and (window height / + 4), so it's better to have this constraint in a single place. + + * src/window.c (window_scroll_margin): New function. + (window_scroll_pixel_based, window_scroll_line_based): + (Frecenter, Fmove_to_window_line): + * src/xdisp.c (try_scrolling, try_cursor_movement): + (redisplay_window, try_window, try_window_id): Use it. + +2017-02-03 Dmitry Gutov + + (xref-collect-matches): Use '-E' together with '-e' + + * lisp/progmodes/xref.el (xref-collect-matches): Use '-E' + together with '-e', as suggested by Noam Postavsky + (http://lists.gnu.org/archive/html/emacs-devel/2017-01/msg00780.html). + +2017-02-03 Paul Eggert + + Pacify Oracle Studio 12.5 + + * src/emacs.c (main): Do not silently convert char * to bool. + +2017-02-02 Paul Eggert + + Fix lisp.h underparenthesization + + * src/lisp.h (STACK_CONS, AUTO_STRING_WITH_LEN): + Parenthesize compound literals that are function call args. + Although this does not fix any bugs, it is the proper style for + macro parenthesization as it means this code will continue to + work even if make_lisp_ptr is changed to a macro. + +2017-02-02 Stefan Monnier + + * lisp/doc-view.el (doc-view-mode): Don't require a final newline + + (doc-view-revert-buffer): Silence overflow warnings. + +2017-02-02 Paul Eggert + + Merge from gnulib + + 2017-01-30 Port to PGI 16.10 x86-64 + 2017-01-20 time_rz: fix comment typo + 2017-01-14 strftime: %z is -00 if unknown + This incorporates: + * doc/misc/texinfo.tex, lib/c-ctype.h, lib/strftime.c: + * lib/time-internal.h, lib/verify.h: + Copy from gnulib. + +2017-02-02 Tino Calancha + + Check if there are hunks before kill or refine a hunk + + * lisp/vc/diff-mode.el (diff--some-hunks-p): New predicate. + (diff-hunk-kill, diff-file-kill, diff-refine-hunk): Use it (Bug#25571). + +2017-02-02 Tino Calancha + + Ignore error after kill last file or hunk + + * lisp/vc/diff-mode.el (diff-hunk-kill): Go to beginning of hunk before kill. + Ignore error after kill last hunk (Bug#25570). + (diff-file-kill): Idem. + +2017-02-02 Tino Calancha + + Show current line highlighted in *Occur* buffer + + * lisp/replace.el (list-matching-lines-current-line-face) + (list-matching-lines-jump-to-current-line): New user options. + (occur--orig-line, occur--orig-line-str): New variables. + (occur, occur-engine): Use them. + (occur--final-pos): New variable. + (occur-1): Use it. + (occur-engine): Idem. + Show the current line with 'list-matching-lines-current-line-face'. + Set point on the first matching line after the current one. + * etc/NEWS: Add entry for the new option. + +2017-02-02 Tino Calancha + + Allow occur command to operate on the region + + See discussion in: + https://lists.gnu.org/archive/html/emacs-devel/2016-12/msg01084.html + * lisp/replace.el (occur--region-start, occur--region-end) + (occur--matches-threshold): New variables. + (occur-engine): Use them. + (occur): Idem. + Add optional arg REGION; if non-nil occur applies in that region. + * doc/lispintro/emacs-lisp-intro.texi (Keybindings): Update manual + * doc/emacs/search.texi (Other Repeating Search): Idem. + +2017-02-02 Mark Oteiza + + Treat list-buffers-directory as a string + + Another step in the long history of list-buffers-directory. A thread + branch discussing the meaning/use of the variable starts here + https://lists.gnu.org/archive/html/emacs-devel/2009-09/msg00684.html + Also see (info "(elisp) Buffer File Name"). + * lisp/buff-menu.el: Relocate special case code into info.el. Nix + Info-* defvars. + (Buffer-menu--pretty-file-name): Remove special case. Use + bound-and-true-p. + (Buffer-menu-info-node-description): Remove. + * lisp/ibuffer.el (ibuffer-buffer-file-name): Treat + list-buffers-directory as a string. + * lisp/info.el (Info-node-description): New function. + (Info-select-node): Use it. + +2017-02-02 Mark Oteiza + + Turn on lexical-binding in parse-time.el + + * lisp/calendar/parse-time.el: Turn on lexical-binding. + (parse-time-iso8601-regexp, parse-iso8601-time-string): Remove unused + bindings. + +2017-02-02 Mark Oteiza + + Prevent creating thumbnails of all gif frames + + With the previous defaults, doing image-dired on a directory with an + animated foo.gif would cause creation of foo.thumb-N.gif for each of + N frames in foo.gif. By default image-dired looks for foo.thumb.gif, so + there additionally is no usable thumbnail after all the needless effort. + image-dired never handled animation, regardless. + * lisp/image-dired.el: Mention limitation. + (image-dired-cmd-create-thumbnail-options): + (image-dired-cmd-create-temp-image-options): + (image-dired-cmd-create-standard-thumbnail-options): Append [0] to + filename to indicate only converting the 0th frame. + (image-dired-display-image-mode): Don't show a cursor. + +2017-02-02 Paul Eggert + + Fix quitting bug when buffers are frozen + + Problem noted by Eli Zaretskii in: + http://lists.gnu.org/archive/html/emacs-devel/2017-01/msg00721.html + This patch also fixes some other issues in that report. + * src/lisp.h (incr_rarely_quit): Remove. + All callers changed to use rarely_quit directly. + * src/search.c (freeze_buffer_relocation) + (thaw_buffer_relocation): New functions. + (looking_at_1, fast_looking_at, search_buffer): + Use them to fix bug when quitting when buffers are frozen. + * src/sysdep.c (emacs_intr_read): Rename from emacs_nointr_read. + All uses changed. + +2017-02-02 Paul Eggert + + Revamp quitting and fix infloops + + This fixes some infinite loops that cannot be quitted out of, + e.g., (defun foo () (nth most-positive-fixnum '#1=(1 . #1#))) + when byte-compiled and when run under X. See: + http://lists.gnu.org/archive/html/emacs-devel/2017-01/msg00577.html + This also attempts to keep the performance improvements I recently + added, as much as possible under the constraint that the infloops + must be caught. In some cases this fixes infloop bugs recently + introduced when I removed immediate_quit. + * src/alloc.c (Fmake_list): + Use rarely_quit, not maybe_quit, for speed in the usual case. + * src/bytecode.c (exec_byte_code): + * src/editfns.c (Fcompare_buffer_substrings): + * src/fns.c (Fnthcdr): + * src/syntax.c (scan_words, skip_chars, skip_syntaxes) + (Fbackward_prefix_chars): + Use rarely_quit so that users can C-g out of long loops. + * src/callproc.c (call_process_cleanup, call_process): + * src/fileio.c (read_non_regular, Finsert_file_contents): + * src/indent.c (compute_motion): + * src/syntax.c (scan_words, Fforward_comment): + Remove now-unnecessary maybe_quit calls. + * src/callproc.c (call_process): + * src/doc.c (get_doc_string, Fsnarf_documentation): + * src/fileio.c (Fcopy_file, read_non_regular, Finsert_file_contents): + * src/lread.c (safe_to_load_version): + * src/sysdep.c (system_process_attributes) [GNU_LINUX]: + Use emacs_read_quit instead of emacs_read in places where + C-g handling is safe. + * src/eval.c (maybe_quit): Move comment here from lisp.h. + * src/fileio.c (Fcopy_file, e_write): + Use emacs_write_quit instead of emacs_write_sig in places where + C-g handling is safe. + * src/filelock.c (create_lock_file): Use emacs_write, not + plain write, as emacs_write no longer has a problem. + (read_lock_data): Use emacs_read, not read, as emacs_read + no longer has a problem. + * src/fns.c (rarely_quit): Move to lisp.h and rename to + incr_rarely_quit. All uses changed.. + * src/fns.c (Fmemq, Fmemql, Fassq, Frassq, Fplist_put, Fplist_member): + * src/indent.c (compute_motion): + * src/syntax.c (find_defun_start, back_comment, forw_comment) + (Fforward_comment, scan_lists, scan_sexps_forward): + Use incr_rarely_quit so that users can C-g out of long loops. + * src/fns.c (Fnconc): Move incr_rarely_quit call to within + inner loop, so that it catches C-g there too. + * src/keyboard.c (tty_read_avail_input): Remove commented-out + and now-obsolete code dealing with interrupts. + * src/lisp.h (rarely_quit, incr_rarely_quit): New functions, + the latter moved here from fns.c and renamed from rarely_quit. + (emacs_read_quit, emacs_write_quit): New decls. + * src/search.c (find_newline, search_buffer, find_newline1): + Add maybe_quit to catch C-g. + * src/sysdep.c (get_child_status): Always invoke maybe_quit + if interruptible, so that the caller need not bother. + (emacs_nointr_read, emacs_read_quit, emacs_write_quit): + New functions. + (emacs_read): Rewrite in terms of emacs_nointr_read. + Do not handle C-g or signals; that is now for emacs_read_quit. + (emacs_full_write): Replace PROCESS_SIGNALS two-way arg + with INTERRUPTIBLE three-way arg. All uses changed. + +2017-02-02 Paul Eggert + + Remove immediate_quit. + + The old code that sets and clears immediate_quit was + ineffective except when Emacs is running in terminal mode, and + has problematic race conditions anyway, so remove it. This + will introduce some hangs when Emacs runs in terminal mode, + and these hangs should be fixed in followup patches. + * src/keyboard.c (immediate_quit): Remove. All uses removed. + +2017-02-01 Alan Mackenzie + + Allow C++ nested brace-list-entries to be better indented. + + This fixes bug #24431. The key change of this bug fix is correctly analyzing + nested brace lists when the opening element stands on the same line as both + its introductory brace and an enclosing parameter list parenthesis. + + * list/progmodes/cc-align.el (c-lineup-under-anchor): New line-up function. + + * list/progmodes/cc-engine.el (c-looking-at-or-maybe-in-bracelist): Accept the + presence of exactly an identifier between an open parenthesis and an open + brace as evidence of the brace starting a brace list. + (c-looking-at-statement-block): New function, extracted from + c-looking-at-inexpr-block. Enhance it to analyze inner blocks recursively + when needed. + (c-looking-at-inexpr-block): Extract new function (see above) and call it. + (c-add-stmt-syntax): Enhance, with new &optional parameter, to supply the + prime syntactic symbol with a fixed anchor point. When this is used, restrict + all added syntactic symbols to those having an anchor point on the same line. + Add, in addition to the current additional symbols, c-brace-list-entry when + needed; use c-looking-at-statement-block to determine the latter. + (c-guess-basic-syntax, CASE 9D): Use c-add-stmt-syntax rather than just + c-add-syntax, to assemble the syntactic context of a 'brace-list-entry, thus + getting, possibly, several accompanying syntactic entries. + + * lisp/progmodes/cc-styles.el (c-style-alist, "gnu" style): New entry for + 'brace-list-intro, namely c-lineup-arglist-intro-after-paren. + + * lisp/progmodes/cc-vars.el (c-offsets-alist): Change the factory default + offset for 'brace-list-entry from 0 to c-lineup-under-anchor. + + * doc/misc/cc-mode.texi (Syntactic Symbols): Amend the definition of + brace-list-intro. + (Brace List Symbols): Amend the example to show the new analysis of brace + lists when the first element comes on the same line as the opening brace. + (Misc Line-Up): Document the new line-up function c-lineup-under-anchor. + +2017-02-01 Lars Ingebrigtsen + + Revert "DOn't use string-as-unibyte in Gnus" + + This reverts commit d1c931009004aef847105b7bac6b6ffafd985b82. + + Not all the cases where we had string-as-unibyte were characters, + so this needs to be considered more thoroughly before being redone. + +2017-02-01 Vibhav Pant + + Use maphash instead of cl-loop. + + * lisp/emacs-lisp/bytecomp.el: (byte-compile-lapcode) Use maphash + instead of cl-loop + +2017-02-01 Michael Albinus + + Fix a subtle problem in Tramp with timers + + * lisp/net/tramp.el (tramp-accept-process-output): Change argument + list. Make it work when called inside a timer. See + . + +2017-01-31 Eli Zaretskii + + Index byte-compile-debug + + * doc/lispref/compile.texi (Compilation Functions): Index + byte-compile-debug. + +2017-01-31 Philipp Stephani + + Document `byte-compile-debug' in the ELisp manual + + * doc/lispref/compile.texi: Document variable `byte-compile-debug'. + +2017-01-31 Ted Zlatanov + + read-multiple-choice: explain dialog popups more + + * lisp/emacs-lisp/subr-x.el (read-multiple-choice): Explain + when a graphical popup is used and how it can be avoided. + +2017-01-31 Ted Zlatanov + + auth-source-user-and-password: add forgotten user parameter + + * lisp/auth-source.el (auth-source-user-and-password): Use + accidentally unused "user" parameter. + Reported by Oscar Najera . + +2017-01-31 Simen Heggestøyl + + Fix typo in a NEWS entry for CSS mode + +2017-01-31 Philipp Stephani + + Document variable `byte-compile-debug' + + * lisp/emacs-lisp/bytecomp.el (byte-compile-debug): Document variable. + +2017-01-31 Lars Ingebrigtsen + + DOn't use string-as-unibyte in Gnus + + * lisp/gnus/nnmail.el (nnmail-parse-active): Don't use + string-as-unibyte. + (nnmail-insert-xref): Ditto. + + * lisp/gnus/canlock.el (canlock-make-cancel-key): Ditto. + + * lisp/gnus/gnus-art.el (gnus-article-browse-html-parts): Ditto. + + * lisp/gnus/gnus-srvr.el (gnus-browse-foreign-server): Ditto. + (gnus-browse-foreign-server): Ditto. + (gnus-browse-foreign-server): Ditto. + + * lisp/gnus/gnus-start.el + (gnus-update-active-hashtb-from-killed): Ditto. + (gnus-read-newsrc-el-file): Ditto. + + * lisp/gnus/mml.el (mml-generate-mime-1): Ditto. + + * lisp/gnus/nnir.el (nnir-get-active): Ditto. + (nnir-get-active): Ditto. + +2017-01-31 Juri Linkov + + Allow C-s C-w to yank ' to the search ring in the Gnus article buffer + + * lisp/gnus/gnus-art.el (gnus-article-mode-syntax-table): Make + M-. in article buffers work for `foo' strings, and still allow + C-s C-w to yank ' to the search ring (bug#22248). + +2017-01-31 Paul Eggert + + * src/alloc.c, src/lisp.h: Fix minor glitches in recent changes. + +2017-01-31 Tino Calancha + + * test/lisp/vc/diff-mode-tests.el: Require diff-mode. + +2017-01-31 Dima Kogan + + New test for diff-mode handling trailing -- + + test/lisp/vc/diff-mode-tests.el: New test file + +2017-01-31 Dima Kogan + + Handle patch terminators produced by git and bzr patch export + + Patch by Juri Linkov posted in the #9597 bug report + + * lisp/vc/diff-mode.el (diff-sanity-check-hunk): Find and ignore + terminator (Bug #9597, #5302) + +2017-01-31 Dima Kogan + + Revert two accidental commits + + This reverts commit f3c77d11af65f3b319b1784b4c3cf08c51aa7997. + This reverts commit 3c941b900007c9e79c00af0f21d88154f6d8af1a. + +2017-01-31 Dima Kogan + + stash + +2017-01-31 Dima Kogan + + comint-get-old-input-default: behavior follows docstring + + lisp/comint.el (comint-get-old-input-default): Modify behavior to follow + docstring: if `comint-use-prompt-regexp' is nil, then return the CURRENT LINE, + if point is on an output field. + +2017-01-31 Noam Postavsky + + Fix call to debugger on assertion failure + + * lisp/emacs-lisp/cl-preloaded.el (cl--assertion-failed): The first + argument must be `error', and the second is a list of arguments for + `signal'. + +2017-01-30 Tom Tromey + + css-mode documentation lookup feature + + * etc/NEWS: Mention new feature. + * lisp/textmodes/css-mode.el (css-mode-map): New defvar. + (css--mdn-lookup-history): New defvar. + (css-lookup-url-format): New defcustom. + (css--mdn-property-regexp, css--mdn-completion-list): New defconsts. + (css--mdn-after-render, css--mdn-find-symbol, css-lookup-symbol): New + defuns. + * test/lisp/textmodes/css-mode-tests.el (css-mdn-symbol-guessing): New + test. + +2017-01-30 Glenn Morris + + edt-mapper: just loading a library should not run code + + * lisp/emulation/edt-mapper.el (edt-mapper): New function, + containing code previously at top-level. + * lisp/emulation/edt.el (edt-load-keys): After loading edt-mapper, + run edt-mapper function. + +2017-01-30 Glenn Morris + + mh-compat.el: remove duplicate definition + + * lisp/mh-e/mh-compat.el (mh-make-obsolete-variable): + Remove duplicate definition. + +2017-01-30 Paul Eggert + + Add delq list arg check + + * src/fns.c (Fdelq): Check that list is a proper list. + This is more compatible with what ‘delete’ does. + +2017-01-30 Stefan Monnier + + * lisp/indent.el (indent-region-line-by-line): New function. + + Extracted from indent-region. + (indent-region, indent-region-function): Use it. + +2017-01-30 Stefan Monnier + + * lisp/subr.el (string-make-unibyte, string-make-multibyte): Obsolete. + +2017-01-30 Eli Zaretskii + + More fixes to prevent crashes on C-g + + * src/fns.c (Fassq, Frassq, Fplist_put): Reset immediate_quit + before returning, to avoid crashes in quit. (Bug#25566) + +2017-01-30 Eli Zaretskii + + Avoid crashes on C-g in TTY sessions + + * src/keyboard.c (handle_interrupt): Don't quit if + waiting_for_input is set, as doing that is "unsafe": it will + abort. (Bug#25566) + +2017-01-30 Vibhav Pant + + * lisp/emacs-lisp/bytecomp.el: Create jump tables with :purecopy t + + Merge remote-tracking branch 'origin/master' into feature/byte-switch + +2017-01-30 Vibhav Pant + + Fix hash tables not being purified correctly. + + * src/alloc.c + (purecopy_hash_table) New function, makes a copy of the given hash + table in pure storage. + Add new struct `pinned_object' and `pinned_objects' linked list for + pinning objects. + (Fpurecopy) Allow purifying hash tables + (purecopy) Pin hash tables that are either weak or not declared with + `:purecopy t`, use purecopy_hash_table otherwise. + (marked_pinned_objects) New function, marks all objects in pinned_objects. + (garbage_collect_1) Use it. Mark all pinned objects before sweeping. + * src/lisp.h Add new field `pure' to struct `Lisp_Hash_Table'. + * src/fns.c: Add `purecopy' parameter to hash tables. + (Fmake_hash_table): Check for a `:purecopy PURECOPY' argument, pass it + to make_hash_table. + (make_hash_table): Add `pure' parameter, set h->pure to it. + (Fclrhash, Fremhash, Fputhash): Enforce that the table is impure with + CHECK_IMPURE. + * src/lread.c: (read1) Parse for `purecopy' parameter while reading + hash tables. + * src/print.c: (print_object) add the `purecopy' parameter while + printing hash tables. + * src/category.c, src/emacs-module.c, src/image.c, src/profiler.c, + src/xterm.c: Use new (make_hash_table). + +2017-01-29 Dmitry Gutov + + Escape dash in xref rgrep regexp + + * lisp/progmodes/xref.el (xref-collect-matches): Escape dash + in REGEXP if it's the first character. + +2017-01-29 Dmitry Gutov + + Say JavaScript, not Javascript + + * lisp/progmodes/js.el (js-mode-map, js-syntax-propertize) + (js-js-error, js-eval, js-set-js-context) + (js--get-js-context): + Refer to the language consistently as JavaScript. + +2017-01-29 Juanma Barranquero + + lisp/*.el: Fix some warnings + + * lisp/battery.el (dbus-get-property): + * lisp/dired-aux.el (format-spec): Declare function. + + * lisp/net/zeroconf.el (zeroconf-list-service-names) + (zeroconf-list-service-types, zeroconf-list-services): + Mark unused lexical arg. + + * lisp/progmodes/hideshow.el (hs-hide-block-at-point): + * lisp/progmodes/sql.el (sql-end-of-statement): + Pass LIMIT to 'looking-back'. + +2017-01-29 Noam Postavsky + + Don't warn about obsolete defgenerics when defining them + + * lisp/emacs-lisp/cl-generic.el (cl-defgeneric): The declaration code + should run after the definition code (Bug#25556). + +2017-01-29 Noam Postavsky + + Call modification hooks in org-src fontify buffers + + * lisp/org/org-src.el (org-src-font-lock-fontify-block): Let-bind + `inhibit-modification-hooks' to nil, since this function can be called + from jit-lock-function which binds that variable to t (Bug#25132). + +2017-01-29 Tino Calancha + + Fix Bug#25524 + + * lisp/vc/diff-mode.el (diff-beginning-of-hunk): + Return position at the beginning off the hunk. + (diff-file-junk-re): Add SVN keywords. + +2017-01-28 Stephen Berman + + hl-line.el: Don't try to operate on a killed buffer + + * lisp/hl-line.el (hl-line-maybe-unhighlight): Examine only + live buffers (bug#25522). + +2017-01-28 Mark Oteiza + + Use access-file in EWW to check before downloading a file + + * lisp/net/eww.el (eww-download): Check accessibility of + eww-download-directory to prevent starting a download that will fail + to write. + * src/fileio.c (Faccess_file): Clarify the use of string argument in + the docstring. + +2017-01-28 Yuri D'Elia + + Subject: Check Bcc after the Messag hook has run + + * lisp/gnus/message.el (message-send): If the hook modifies + the message (mml tags or headers), we should check bcc on the + final message, not on the original. + +2017-01-28 Eli Zaretskii + + Improve documentation of 'format' conversions + + * src/editfns.c (Fformat): More accurate description of %g and + effects of the various flags on it. More accurate description of + integer conversions. + + * doc/lispref/strings.texi (Formatting Strings): More accurate + description of %g and effects of the various flags on it. More + accurate description of integer conversions. (Bug#25557) + +2017-01-28 Juanma Barranquero + + test/*.el: Avoid byte-compiler warnings + + * test/lisp/abbrev-tests.el (abbrev-table-p-test): Remove unused 'let*'. + + * test/lisp/faces-tests.el (faces--test): New customization group. + (faces--test1, faces--test2): Use it. + + * test/lisp/ffap-tests.el (ffap-tests-25243): + Call 'mark-whole-buffer' interactively. + + * test/lisp/ibuffer-tests.el (ibuffer-filter-groups, ibuffer-filtering-alist) + (ibuffer-filtering-qualifiers, ibuffer-save-with-custom) + (ibuffer-saved-filter-groups, ibuffer-saved-filters): Defvar. + (ibuffer-format-qualifier, ibuffer-unary-operand): Declare. + + * test/lisp/minibuffer-tests.el (completion-test1): + Mark unused lexical arguments. + + * test/lisp/simple-tests.el (simple-test--dummy-buffer): Wrap result in + 'with-no-warnings' to avoid them when the macro is invoked for effect. + + * test/lisp/emacs-lisp/cl-seq-tests.el (cl-seq-count-test): + Mark unused lexical arguments. + + * test/lisp/emacs-lisp/let-alist-tests.el (let-alist-surface-test): + Mark unused lexical arguments. + (let-alist-cons): Remove unused let binding. + + * test/lisp/net/dbus-tests.el (dbus-debug): Defvar. + (dbus-get-unique-name): Declare. + + * test/lisp/progmodes/python-tests.el (python-bob-infloop-avoid): + Call 'font-lock-fontify-buffer' interactively. + + * test/lisp/textmodes/tildify-tests.el (tildify-space-undo-test--test): + Mark unused lexical argument. + +2017-01-27 Eli Zaretskii + + Restore a test that was removed by a recent commit + + * src/fileio.c (Ffile_accessible_directory_p): Don't overwrite the + errno value unless it's necessary. (Bug#25419) + +2017-01-27 Mark Oteiza + + Fix a couple eww customization types + + * lisp/new/eww.el (eww-download-directory, eww-bookmarks-directory): + Change customization type to "directory". + +2017-01-27 Philipp Stephani + + Don't require a shell when loading htmlfontify + + * lisp/htmlfontify.el (hfy-which-etags): Don't call a shell for + detecting the etags version (Bug#25468). + * test/lisp/htmlfontify-tests.el (htmlfontify-bug25468): Add unit + test. + +2017-01-27 Paul Eggert + + Slightly tune file-accessible-directory-p fix + + * src/fileio.c (Ffile_accessible_directory_p): + Remove unnecessary test (Bug#25419). + +2017-01-27 Arash Esbati + + Add \citetitle to biblatex cite format + + * lisp/textmodes/reftex-vars.el (reftex-cite-format-builtin): Add + \citetitle[*] to `reftex-cite-format' and bind them to keys i/I + per user request + http://lists.gnu.org/archive/html/auctex/2017-01/msg00049.html. + +2017-01-27 Lars Ingebrigtsen + + Fix charsets and encodings from non-file MIME parts + + * lisp/gnus/mml.el (mml-generate-mime-1): Get the charsets and + encoding right for parts that do not originate from files. + +2017-01-27 Eli Zaretskii + + Fix 'describe-variable' for longish variable values + + * lisp/help-fns.el (describe-variable): Don't accidentally remove + the last character of a variable's value. (Bug#25545) + +2017-01-27 Vladimir Panteleev (tiny change) + + Remove stale functions from ert manual + + * doc/misc/ert.texi (Useful Techniques when Writing Tests): + Replace ert--mismatch references with its cl-lib replacement, + cl-mismatch. + +2017-01-27 Eli Zaretskii + + Fix a typo in Eshell manual + + * doc/misc/eshell.texi (History): Fix a typo. Reported by Mak + Kolybabi . + +2017-01-27 Eli Zaretskii + + Ensure last line is at window bottom in shell buffers + + * lisp/shell.el (shell-mode): Use setq-local. Set + scroll-conservatively to 101 locally. See the discussion at + http://lists.gnu.org/archive/html/emacs-devel/2016-12/msg00736.html + for the reasons. + +2017-01-27 Michael Hoffman (tiny change) + + Support Bash Ctrl-Z indication of directory name in term.el + + * term.el (term-emulate-terminal): Do not display ?\032 escape + codes even when 'handled-ansi-message' is non-nil. (Bug#11919) + +2017-01-27 Eli Zaretskii + + Don't report zero errno for inaccessible directory + + * src/fileio.c (Ffile_accessible_directory_p): Report EACCES when + a file handler reports a failure. (Bug#25419) + +2017-01-27 Eli Zaretskii + + Fix filenotify-tests on MS-Windows + + * test/lisp/filenotify-tests.el (file-notify-test04-file-validity) + (file-notify-test05-dir-validity) + (file-notify-test06-many-events) + (file-notify-test08-watched-file-in-watched-dir): Manually remove + the watch descriptor before calling file-notify--test-cleanup-p. + (Bug#25539) + +2017-01-27 Hong Xu + + python-mode: Fix detection for opening blocks. + + * python.el (python-info-dedenter-opening-block-positions): There + can't be any back-indented lines between an opening block and the + current line. + + * python-tests.el (python-indent-electric-colon-4): Add an indent + test case where there is one-more indented previous opening block. + +2017-01-27 Lars Ingebrigtsen + + Fix Message check for bogus domain names + + * lisp/gnus/message.el (message-make-fqdn): Fix check for + bogus system names (bug#24570). + +2017-01-27 Øyvind Stegard (tiny change) + + Subject: Restore correct Gnus newsgroup name after sending message + + * lisp/gnus/gnus-msg.el (gnus-msg-mail): Set the value of + gnus-newsgroup-name in the correct buffer (bug#24329). + +2017-01-27 Lars Ingebrigtsen + + Mention the new Gnus sorting command + +2017-01-27 Lars Ingebrigtsen + + Don't try to find charsets of non-text MIME parts + + * lisp/gnus/mml.el (mml-generate-mime-1): It seems nonsensical + to try to determine the charset of non-text message parts, so + skip that (bug#24190). This will also remove messages like + "bunzip2ing /tmp/acsb.cpio.bz2...done" while sending messages + if you include such files. + +2017-01-27 Lars Ingebrigtsen + + Add new command gnus-article-sort-by-marks + + * doc/misc/gnus.texi (Summary Sorting): Mention + gnus-summary-sort-by-marks. + + * lisp/gnus/gnus-sum.el (gnus-article-sort-by-marks): New + function (bug#23393). + (gnus-thread-sort-by-marks): Ditto. + (gnus-summary-sort-by-mark): New command suggested by Dan Jacobson. + (gnus-summary-mode-map): Add keystroke. + (gnus-summary-make-menu-bar): Add to menu. + +2017-01-26 Katsumi Yamaoka + + Make `C-h b' work correctly in Gnus article buffer (bug#18257) + + * lisp/gnus/gnus-art.el (gnus-article-describe-bindings): + Ignore summary commands that aren't bound to + gnus-article-read-summary-keys keys (bug#18257). + +2017-01-26 Lars Ingebrigtsen + + Fix crossposting in non-primary groups + + * lisp/gnus/message.el + (message-cross-post-followup-to-header): Gnus server prefixes + shouldn't be included in the group names (bug#21661). + (message-cross-post-followup-to): Ditto. + +2017-01-26 Lars Ingebrigtsen + + Gnus doc clarification + + * doc/misc/gnus.texi (Unavailable Servers): Explicitly say + that "unreachable" is the same as disabling it (bug#21630). + +2017-01-26 Lars Ingebrigtsen + + Remove dead code from rfc2047 + + * lisp/mail/rfc2047.el (rfc2047-fold-field): Remove dead code. + + It's been disabled since 2005, when I made the change with the + following comment. + + (rfc2047-encode-message-header): Disabled header folding -- not + all headers can be folded, and this should be done by the message + composition mode. Probably. I think. + +2017-01-26 Lars Ingebrigtsen + + Fill too long mail headers + + * lisp/gnus/message.el (message--fold-long-headers): New + function to fold too-long headers (bug#21608). + (message-send-mail): Use it to fill headers longer than 998 + characters (which is the protocol limit). + +2017-01-26 Lars Ingebrigtsen + + Make nndoc more resilient against corrupted files + + * lisp/gnus/nndoc.el (nndoc-possibly-change-buffer): Don't bug + out on invalid files, like invalid .gz files (bug#21538). + This may hinder Gnus from starting up. + +2017-01-26 Lars Ingebrigtsen + + Respect buffer-local message-fcc-handler-function + + * lisp/gnus/message.el (message-do-fcc): Copy the local + variables from the Message buffer so that local settings of + `message-fcc-handler-function' etc are respected (bug#21174). + +2017-01-26 Lars Ingebrigtsen + + (message-do-fcc): Modernise the code slightly. + + * lisp/gnus/message.el (message-do-fcc): Modernise the code slightly. + +2017-01-26 Lars Ingebrigtsen + + Avoid a regexp overflow in message-goto-body + + * lisp/gnus/message.el (message-goto-body-1): Avoid using a + complicated backtracking regexp, because they may overflow on + large headers (bug#21160). + +2017-01-26 Lars Ingebrigtsen + + Refactor message-goto-body + + * lisp/gnus/message.el (message-goto-body-1): Refactor out for reuse. + +2017-01-26 Lars Ingebrigtsen + + Fix typo in last checkin + + * lisp/gnus/nnimap.el (nnimap-shell-program): Document + nnimap-shell-program (bug#20651). + +2017-01-26 Lars Ingebrigtsen + + Document nnimap-shell-program + + * lisp/gnus/nnimap.el (nnimap-shell-program): Document + nnimap-shell-program (bug#20651). + +2017-01-26 Lars Ingebrigtsen + + Document :shell-command in `make-network-process' + + * doc/lispref/processes.texi (Network): Document :shell-command. + + * lisp/net/network-stream.el (open-network-stream): Document + the :shell-command parameter (bug#20651). + +2017-01-26 Lars Ingebrigtsen + + Gnus doc clarification + + * lisp/gnus/gnus-sum.el (gnus-summary-save-article): Mention + the gnus-prompt-before-saving variable (bug#20500). + +2017-01-26 Lars Ingebrigtsen + + Fix the previous mml patch better + + * lisp/gnus/mml.el (mml-minibuffer-read-file): Fix the + previous patch in a better way (bug#20480). + +2017-01-26 Lars Ingebrigtsen + + Give a slight better error message in mml-minibuffer-read-file + + * lisp/gnus/mml.el (mml-minibuffer-read-file): Give a slightly + better error message when the user enters nothing (bug#20480). + +2017-01-26 Vibhav Pant + + * lisp/emacs-lisp/bytecomp.el:(bc-cond-jump-table-info)add docstring + +2017-01-26 Lars Ingebrigtsen + + Make eww buffers prettier in the buffer listing + + * lisp/net/eww.el (eww-render): Put the currently visited URL + into the buffer listing (bug#23738). + (eww-render): Ditto. + +2017-01-26 Lars Ingebrigtsen + + Allow mml-attach-file to prompt less + + * lisp/gnus/mml.el (mml-attach-file): If given a prefix, don't + prompt for type/description/disposition, but use defaults + (bug#19202). + +2017-01-26 Lars Ingebrigtsen + + Don't allow message-newline-and-reformat to be run outside the body + + * lisp/gnus/message.el (message-newline-and-reformat): Error + out if run outside the body of a message (bug#18820). + +2017-01-26 Vibhav Pant + + * lisp/emacs-lisp/bytecomp.el: Use correct function to push nil + + * lisp/emacs-lisp/bytecomp.el: (byte-compile-cond-jump-table) Use + byte-compile-constant instead of byte-compile-form to push nil. + +2017-01-26 Vibhav Pant + + * lisp/emacs-lisp/disass.el: Fix spacing while showing jump tables + +2017-01-26 Paul Eggert + + Replace QUIT with maybe_quit + + There’s no longer need to have QUIT stand for a slug of C statements. + Use the more-obvious function-call syntax instead. + Also, use true and false when setting immediate_quit. + These changes should not affect the generated machine code. + * src/lisp.h (QUIT): Remove. All uses replaced by maybe_quit. + +2017-01-26 Paul Eggert + + A quicker check for quit + + On some microbenchmarks this lets Emacs run 60% faster on my + platform (AMD Phenom II X4 910e, Fedora 25 x86-64). + * src/atimer.c: Include keyboard.h, for pending_signals. + * src/editfns.c (Fcompare_buffer_substrings): + * src/fns.c (Fnthcdr, Fmemq, Fmemql, Fassq, Frassq, Fplist_put) + (Fnconc, Fplist_member): + Set and clear immediate_quit before and after loop instead of + executing QUIT each time through the loop. This is OK for loops + that affect only locals. + * src/eval.c (process_quit_flag): Now static. + (maybe_quit): New function, containing QUIT’s old body. + * src/fns.c (rarely_quit): New function. + (Fmember, Fassoc, Frassoc, Fdelete, Fnreverse, Freverse) + (Flax_plist_get, Flax_plist_put, internal_equal, Fnconc): + Use it instead of QUIT, for + speed in tight loops that might modify non-locals. + * src/keyboard.h (pending_signals, process_pending_signals): + These belong to keyboard.c, so move them here ... + * src/lisp.h: ... from here. + (QUIT): Redefine in terms of the new maybe_quit function, which + contains this macro’s old definiens. This works well with branch + prediction on processors with return stack buffers, e.g., x86 + other than the original Pentium. + +2017-01-26 Paul Eggert + + Simplify make-list implementation + + * src/alloc.c (Fmake_list): Don’t unroll loop, as the complexity + is not worth it these days. + +2017-01-26 Mark Oteiza + + Make use of cl-loop destructuring + + * lisp/progmodes/js.el (js--get-tabs): Replace extraneous bits with + destructuring. + (with-js): Add declare forms. + +2017-01-25 Lars Ingebrigtsen + + Revert "Bind C-c keys in the article buffer" + + This reverts commit 6b4195f2ace1f6328c5a833fde40f39babef4fa6. + + The commit somehow lead to problems in other parts of Emacs. + +2017-01-25 Lars Ingebrigtsen + + Document how to quote MML tags + + * doc/misc/emacs-mime.texi (MML Definition): Mention how to + quote MML tags (bug#18881). + +2017-01-25 Lars Ingebrigtsen + + Make address parsing more robust + + * lisp/mail/ietf-drums.el (ietf-drums-parse-address): Don't + bug out on addresses like + (ietf-drums-parse-address "\"Foo \"bar\" ") + (bug#18572). + +2017-01-25 Lars Ingebrigtsen + + Fix the %P (line number) thing in Gnus summary buffers + + * lisp/gnus/gnus-salt.el (gnus-pick-line-number): Remove hack. + + * lisp/gnus/gnus-sum.el (gnus-summary-read-group-1): Reset the + "pick" mode line number on entry instead of relying in a hack (bug#18311). + +2017-01-25 Lars Ingebrigtsen + + Fix wrong documentation on nnmairix keystrokes + + * doc/misc/gnus.texi (nnmairix keyboard shortcuts): The + nnmairix commands are on G G, not $ (bug#18260). + +2017-01-25 Lars Ingebrigtsen + + Bind C-c keys in the article buffer + + * lisp/gnus/gnus-art.el (gnus-article-mode-map): Also bind the + C-c keys so that they execute in the summary buffer + (bug#18257). This makes commands like `C-c C-f' work from the + article buffer. + +2017-01-25 Lars Ingebrigtsen + + Don't mark articles in Gnus as displayed when they aren't + + * lisp/gnus/gnus-sum.el (gnus-summary-read-group-1): Don't + mark any articles as selected if we're not selecting any + articles (bug#18255). + +2017-01-25 Vibhav Pant + + * lisp/emacs-lisp/disass.el: Display jump tables for switch. + + * lisp/emacs-lisp/bytecomp.el:Use correct size for switch jump-table + + * lisp/emacs-lisp/bytecomp.el: Simplify b-c-cond-valid-obj2-p + + * lisp/emacs-lisp/bytecomp.el: Fix byte-switch codegen with symbols. + +2017-01-25 Vibhav Pant + + * lisp/emacs-lisp/byte-opt.el: Add support for decompiling switch + + * lisp/emacs-lisp/byte-opt.el: (byte-decompile-bytecode-1) When the + constant encountered precedes a byte-switch op, replace all the + addresses in the jump table with tags. + +2017-01-25 Mark Oteiza + + Move cXXXr and cXXXXr to subr.el + + * etc/NEWS: Mention new core Elisp. + * doc/lispref/lists.texi (List Elements): Document and index the new + functions. + * doc/misc/cl.texi (List Functions): Change "defines" to "aliases". + * lisp/subr.el (caaar, caadr, cadar, caddr, cdaar, cdadr, cddar) + (cdddr, caaaar caaadr, caadar, caaddr, cadaar, cadadr, caddar): + (cadddr, cdaaar, cdaadr, cdadar, cdaddr, cddaar, cddadr, cdddar): + (cddddr): New functions. + * lisp/emacs-lisp/cl-lib.el (cl-caaar, cl-caadr, cl-cadar, cl-caddr): + (cl-cdaar, cl-cdadr, cl-cddar cl-cdddr, cl-caaaar cl-caaadr): + (cl-caadar, cl-caaddr, cl-cadaar, cl-cadadr, cl-caddar, cl-cadddr): + (cl-cdaaar, cl-cdaadr, cl-cdadar, cl-cdaddr, cl-cddaar, cl-cddadr): + (cl-cdddar, cl-cddddr): Alias to new subr functions. + * lisp/emacs-lisp/cl.el (cl-unload-function): Remove cXXXr and cXXXXr + elements. + +2017-01-25 Lars Ingebrigtsen + + Only save .newsrc file if the native method is NNTP + + * lisp/gnus/gnus-start.el (gnus-save-newsrc-file): Only save + the .newsrc file if the native select method is NNTP + (bug#18198). This avoids problems with invalid IMAP group + names and the like in the .newsrc file. + +2017-01-25 Lars Ingebrigtsen + + Only save .newsrc file if the native method is NNTP + + * lisp/gnus/gnus-start.el (gnus-save-newsrc-file): Only save + the .newsrc file if the native select method is NNTP + (bug#18198). This avoids problems with invalid IMAP group + names and the like in the .newsrc file. + +2017-01-25 Lars Ingebrigtsen + + Gnus custom spec fix + + * lisp/gnus/gnus-art.el (gnus-signature-limit): Fix customize + spec to match the doc string (bug#17679). + +2017-01-25 Lars Ingebrigtsen + + Clarify confusing Gnus error message + + * lisp/gnus/gnus-topic.el (gnus-topic-unindent): Clarify + confusing error message (bug#17677). + +2017-01-25 Lars Ingebrigtsen + + Make C-u C-x m work with Message as documented + + * lisp/gnus/message.el (message-mail): Respect the CONTINUE + parameter (bug#17175). + +2017-01-25 Lars Ingebrigtsen + + Fix problem with auto-mode and dir-locals-collect-variables + + * lisp/files.el (dir-locals-collect-variables): When run from + auto-mode, the file in question may not be an absolute path + name (bug#24016). + + Example backtrace: + + Debugger entered--Lisp error: (args-out-of-range "compile-1st-in-loa + dir-locals-collect-variables(((emacs-lisp-mode (indent-tabs-mode)) + hack-dir-local-variables() + hack-local-variables(no-mode) + run-mode-hooks(diff-mode-hook) + diff-mode() + mm-display-inline-fontify((# ("text/x-diff" ( + +2017-01-25 Lars Ingebrigtsen + + Attach text files correctly in Message + + * lisp/gnus/mml.el (mml-generate-mime-1): Detect which coding + system has been used in attached text files, and don't try to + do any encoding of these files (bug#13808). + +2017-01-25 Lars Ingebrigtsen + + Build fix for older gnutls versions + + * src/gnutls.c (emacs_gnutls_handle_error): + GNUTLS_E_PREMATURE_TERMINATION is apparently only present in + gnutls-3. + +2017-01-25 Tino Calancha + + ediff-difference-vector-alist: Drop duplicated definition + + * lisp/vc/ediff-init.el (ediff-difference-vector-alist): + Drop duplicated definition. + (ediff-difference-vector-A, ediff-difference-vector-B) + (ediff-difference-vector-C, ediff-difference-vector-Ancestor): + Move definition before 'ediff-difference-vector-alist'. + +2017-01-25 Lars Ingebrigtsen + + Revert "nnimap.el: support additional expunge options" + + This reverts commit 4e9baea6aba1633074889339dcc7cdc9d73880d3. + + The patch broke fetching new mail: + + Debugger entered--Lisp error: (error "Format specifier doesn’t match argument type") + format("%d .*\n" (t ("OK" ("HIGHESTMODSEQ" "914696") "Expunge" "completed.") ("VANISHED" "1825937") ("0" "RECENT"))) + (looking-at (format "%d .*\n" sequence)) + (not (looking-at (format "%d .*\n" sequence))) + (progn (while (and (not (bobp)) (progn (forward-line -1) (looking-at "\\*\\|[0-9]+ OK NOOP")))) (not (looking-at (format "%d .*\n" sequence)))) + +2017-01-25 Nikolaus Rath + + nnimap.el: support additional expunge options + + * lisp/gnus/nnimap.el (nnimap-close-group) + (nnimap-request-expire-articles, nnimap-delete-article) + (nnimap-request-scan): add new 'never, 'immediate, and 'on-exit + settings for nnimap-expunge (bug#20670). + +2017-01-25 Lars Ingebrigtsen + + Don't tag Gnus bugs with "gnus" + + * lisp/gnus/gnus-msg.el (gnus-bug): Remove the bug package tags. + + * lisp/gnus/gnus.el (gnus-bug-package): Removed; Gnus doesn't + have its own package any more in the bug tracker. + +2017-01-25 Lars Ingebrigtsen + + Tweak TLS error messaging on closed connections + + * src/gnutls.c (emacs_gnutls_handle_error): Demote the normal + peer-closed-connection "The TLS connection was non-properly + terminated" message to a lower level so that it isn't shown to + the user by default. + +2017-01-24 Lars Ingebrigtsen + + Avoid having eww unexpectedly open external browsers + + * lisp/net/eww.el (eww-render): Instead of opening unsupported + content types like audio/mpeg directly in an external browser + (which can be very confusing especially when something + redirects to a file like that), just display a simple + interstitial that people can choose to click on or not + (bug#22671). + +2017-01-24 Lars Ingebrigtsen + + When opening new eww buffers, use buffer names based on the host name + + * lisp/net/eww.el (eww-browse-url): When opening in a new + window, use a buffer name based on the host name (bug#23738). + (eww--dwim-expand-url): Refactored out into its own function + for easier reuse. + +2017-01-24 David Engster + + xml: Fix parsing of default namespace with quoted names + + * lisp/xml.el (xml-parse-attlist): Properly extract namespace when + parsing is done with quoted symbol names (bug#23440). + * test/lisp/xml-tests.el (xml-parse-test--default-namespace-qnames) + (xml-parse-test-default-namespace-qnames): Test for the above. + +2017-01-24 Lars Ingebrigtsen + + Fix rendering of some complex SVG images + + * lisp/net/shr.el (shr-parse-image-data): Don't transform + SVG->DOM->XML unless we're blocking images, as this is apt to + destroy the SVG (bug#24111). + +2017-01-24 Lars Ingebrigtsen + + Clarify the last clarification + + * lisp/net/shr.el (shr-width): Clarify the interaction with + `shr-use-fonts' (bug#24928). + +2017-01-24 Lars Ingebrigtsen + + shr-width doc clarification + + * lisp/net/shr.el (shr-width): Clarify the interaction with + `shr-use-fonts' (bug#24928). + +2017-01-24 Lars Ingebrigtsen + + Allow passing in max-width/height + + * lisp/net/shr.el (shr-rescale-image): Allow passing in + max-width/height (bug#25287). + +2017-01-24 Stefan Monnier + + * lisp/progmodes/vhdl-mode.el: Avoid add-to-list on local vars + + Require `cl' for `pushnew'. + (vhdl-scan-project-contents, vhdl-compose-wire-components) + (vhdl-uniquify): Use `pushnew' instead of `add-to-list'. + +2017-01-24 Noam Postavsky + + Fix comment detection on open parens + + Characters having both open paren syntax and comment start syntax were + being detected as open parens even when they should have been part a + comment starter (Bug#24870). + + * src/syntax.c (in_2char_comment_start): New function, extracted from + `scan_sexps_forward'. + (scan_sexps_forward): Add check for a 2-char comment starter before the + loop. Inside the loop, do that check after incrementing the 'from' + character index. Move the single char comment syntax cases into the + switch instead of special casing them before. + * test/src/syntax-tests.el (parse-partial-sexp-paren-comments): + (parse-partial-sexp-continue-over-comment-marker): New tests. + +2017-01-23 Alan Mackenzie + + Give , and .@ doc strings. Fixes bug #24561. + + Also make *Help* links to ``' possible. Also make usable as such doc strings + on the function-documentation property of a symbol. + + * lisp/emacs-lisp/backquote.el (top-level): Give , and '@ doc strings on the + function-documentation property. Also give these symbols a reader-construct + property. + + * lisp/help-fns.el (describe-function): Allow the function-documentation + property to work. Use princ rather than prin1 to print the function's name + when it has a reader-construct property. + (help-fns-signature): Don't insert `high-usage' for a reader-construct. + (describe-function-1): Adapt to process documentation on the + function-documentation property. Print "a reader construct" when appropriate. + + * lisp/help-mode.el (help-xref-symbol-regexp): Amend this regexp also to match + ``'. + +2017-01-22 Paul Eggert + + Improve uses of CHECK_LIST etc. + + * src/eval.c (FletX): Report an error for invalid constructs like + ‘(let* (a . 0))’, so that ‘let*’ is more consistent with ‘let’. + (lambda_arity): Use plain CHECK_CONS. + * src/fns.c (CHECK_LIST_END): Move from here to lisp.h. + (Fcopy_alist): Remove unnecessary CHECK_LIST call, since + concat does that for us. + (Fnthcdr, Fmember, Fmemql, Fdelete, Fnreverse): + Use CHECK_LIST_END, not CHECK_LIST_CONS. This hoists a + runtime check out of the loop. + (Fmemq): Simplify and use CHECK_LIST_END instead of CHECK_LIST. + (Fassq, Fassoc, Frassq, Frassoc): + Simplify and use CHECK_LIST_END instead of CAR. + (assq_no_quit, assoc_no_quit): Simplify and assume proper list. + (Fnconc): Use plain CHECK_CONS, and do-while instead of while loop. + * src/fontset.c (Fnew_fontset): + * src/frame.c (Fmodify_frame_parameters): + Use CHECK_LIST_END at end, rather than CHECK_LIST at start, for a + more-complete check. + * src/gfilenotify.c (Fgfile_add_watch): + Omit unnecessary CHECK_LIST, since Fmember does that for us. + * src/lisp.h (lisp_h_CHECK_LIST_CONS, CHECK_LIST_CONS): + Remove; no longer used. + (CHECK_LIST_END): New inline function. + +2017-01-22 Tino Calancha + + Prevent to use tabulated-list--near-rows unbound + + * lisp/emacs-lisp/tabulated-list.el (tabulated-list-print-entry): + Make sure 'tabulated-list--near-rows' is bound before use it (Bug#25506). + +2017-01-22 Juri Linkov + + * lisp/simple.el (region-bounds): New function. + + (region-noncontiguous-p): Use it. + http://lists.gnu.org/archive/html/emacs-devel/2017-01/msg00044.html + +2017-01-21 Alan Mackenzie + + Fix low-level handling of (big) C macros. + + In particular, ensure that a comment detected by its syntax is not a CPP + construct marked with generic comment delimiter syntax-table text + properties. + + * lisp/progmodes/cc-engine.el (c-beginning-of-macro, c-end-of-macro): Set + c-macro-cache-syntactic to nil when the cached macro changes. + (c-syntactic-end-of-macro, c-no-comment-end-of-macro) + (c-state-semi-pp-to-literal, c-state-full-pp-to-literal) + (c-state-pp-to-literal, c-parse-ps-state-to-cache) + (c-state-cache-non-literal-place, c-literal-limits, c-literal-start) + (c-determine-limit): When checking a parse syntax for a comment, check that + we're not in a CPP construct marked by syntax-table generic comment delimiter + text property. + (c-state-pp-to-literal): Change from a defsubst to a defun. + + * lisp/progmodes/cc-mode.el (c-neutralize-syntax-in-and-mark-CPP): Check a + parse syntax as described above under cc-engine.el. + +2017-01-21 Vibhav Pant + + * lisp/emacs-lisp/bytecomp.el: Remove unused debugging statements. + +2017-01-21 Noam Postavsky + + Don't wait for frame to become visible + + * src/xterm.c (x_make_frame_visible): Remove code that waits for the + frame to become visible. We have to deal with invisible frames anyway, + the loop could sometimes before the frame turned visible, and for some + window managers (e.g., XMonad, i3wm) it caused Emacs to get stuck in a + busy loop (Bug#24091). + +2017-01-21 Tino Calancha + + diff-hunk-kill independent of point inside headers + + Make diff-apply-hunk and diff-hunk-kill independent of the point + position in a diff header (Bug#17544). + This change allows to apply hunks in order. It also makes possible to + press M-k repeatedly to kill hunks in the order they appear in the buffer. + See discussion on #Bug25105. + * lisp/vc/diff-mode.el (diff-file-junk-re): + Move definition before it's used. + (diff--at-diff-header-p): New predicate; return non-nil when point + is inside a hunk header, a file header, or within a line + matching diff-file-junk-re. + (diff-beginning-of-hunk): Use it. + Check if the point is inside a diff header, in the middle of a hunk, + or before the first hunk. + (diff-apply-hunk): Call diff-beginning-of-hunk with non-nil arg + before apply the hunk. + (diff-hunk-kill, diff-file-kill): + Call diff-beginning-of-hunk with non-nil arg after kill the hunks. + (diff-post-command-hook): Call diff-beginning-of-hunk with non-nil argument. + +2017-01-20 Eli Zaretskii + + Improve documentation of hooks related to saving buffers + + * lisp/files.el (write-file-functions, write-contents-functions) + (before-save-hook, after-save-hook): Note that these are only used + by save-buffer. + + * doc/lispref/backups.texi (Auto-Saving): + * doc/lispref/files.texi (Saving Buffers): Mention that + save-related hooks are not run by auto-saving. (Bug#25460) + +2017-01-20 Eli Zaretskii + + Improve documentation of auto-save-visited-file-name + + * doc/emacs/files.texi (Auto Save Files): Mention subtle + differences between saving the buffer and auto-saving with + auto-save-visited-file-name set non-nil. (Bug#25478) + +2017-01-20 Noam Postavsky + + Fix free var FOO-mode-{syntax,abbrev}-table warnings + + * lisp/emacs-lisp/derived.el (define-derived-mode): Unconditionally + defvar the syntax and abbrev tables so that the compiler will know that + they are dynamically bound variables (Bug#25446). + +2017-01-19 Vibhav Pant + + * lisp/emacs-lisp/bytecomp.el: Fix errors with matching quoted forms + + * lisp/emacs-lisp/bytecomp.el: (byte-compile-cond-jump-table-info) + eval obj2 to avoid quoted forms being stored as is. + +2017-01-19 Vibhav Pant + + lisp/emacs-lisp/bytecomp.el: Use byte-switch only for quoted symbols + +2017-01-19 Vibhav Pant + + * lisp/emacs-lisp/bytecomp.el: Add default-case for last cond clause. + + * lisp/emacs-lisp/bytecomp.el: (byte-compile-cond-jump-table) Add + default-case for last cond clause. + +2017-01-19 Philipp Stephani + + Check that variable lists are actually lists + + 'let' and 'let*' document that their first argument has to be a list, + but don't check for that; instead, they allow (and silently ignore) + other types. Introduce an explicit type check. + + * src/eval.c (Flet, FletX): Check that the variable list is indeed a + list. + * test/src/eval-tests.el: Add unit tests. + +2017-01-19 Vibhav Pant + + Add type checking for Bswitch, when enabled at compile time. + + * src/bytecode.c: (exec_byte_code) If BYTE_CODE_SAFE is enabled at + compile time, use CHECK_TYPE to verify that the jump table is a hash table. + +2017-01-19 Vibhav Pant + + Use byte-switch for all symbols. + + * lisp/emacs-lisp/bytecomp.el: (byte-compile-cond-valid-obj2-p) Return + t for all symbols (instead for just keywords) + +2017-01-19 Noam Postavsky + + Avoid inefficient regex in diff-refine-hunk (Bug#25410) + + * lisp/vc/diff-mode.el (diff--forward-while-leading-char): New function. + (diff-refine-hunk): Use it instead of trying to match multiple lines + with a single lines. + +2017-01-18 Eli Zaretskii + + Remove lock file when auto-saving into the visited file + + * src/fileio.c (write_region): When auto-saving into the visited + file, unlock the file whenever we mark the buffer unmodified. + (Bug#25470) + +2017-01-18 Vibhav Pant + + * src/bytecode.c: (exec_byte_code) Use hash_lookup for Bswitch + + Fgethash type checks the provided table object, which is unnecessary + for compiled bytecode. + +2017-01-18 Tom Tromey + + fix typo in mailcap-mime-extensions + + * lisp/net/mailcap.el (mailcap-mime-extensions): Use "text/x-patch", + not "test/x-patch". (Bug#25472) + +2017-01-18 Lele Gaifax (tiny change) + + Fix typos in flymake.el + + * lisp/progmodes/flymake.el (flymake-check-patch-master-file-buffer): + Spelling fixes in the doc string. + +2017-01-18 Eli Zaretskii + + Fix a bug with signaling a thread that waits for condvar + + * src/thread.c (lisp_mutex_lock_for_thread): New function, + with all the guts of lisp_mutex_lock. + (lisp_mutex_lock): Call lisp_mutex_lock_for_thread. + (condition_wait_callback): Don't call post_acquire_global_lock + before locking the mutex, as that could cause a signaled thread to + exit prematurely, because the condvar's mutex is recorded to be + not owned by any thread, and with-mutex wants to unlock it as part + of unwinding the stack in response to the signal. + +2017-01-18 Eli Zaretskii + + Rudimentary error handling for non-main threads + + * src/thread.c (last_thread_error): New static variable. + (syms_of_threads): Staticpro it. + (record_thread_error, Fthread_last_error): New functions. + (syms_of_threads): Defsubr Fthread_last_error. + + * doc/lispref/threads.texi (Basic Thread Functions): Document + thread-last-error. + + * test/src/thread-tests.el (thread-errors, thread-signal-early) + (threads-condvar-wait): Test the values returned by + thread-last-error. + +2017-01-17 Tom Tromey + + Add info-lookup help for gdb-script-mode + + Bug#25464: + * lisp/info-look.el (info-lookup-guess-gdb-script-symbol): New + function. + Add help for gdb-script-mode. + +2017-01-17 Tom Tromey + + Treat ":root" as a css-selector + + * lisp/textmodes/css-mode.el (css--font-lock-keywords): Recognize bare + ":root" as selector. + +2017-01-17 Tom Tromey + + Fix JS regexp literal syntax propertization in expressions + + Bug#25465: + * lisp/progmodes/js.el (js-syntax-propertize): Recognize a regexp + literal after "!", "&", and "|". + test/lisp/progmodes/js-tests.el (js-mode-regexp-syntax): New test. + +2017-01-17 Glenn Morris + + More NEWS checking for admin.el's set-version + + * admin/admin.el (set-version): Warn if temporary NEWS markup + still present in release candidates. + +2017-01-17 Mark Oteiza + + Mark unused arguments and remove unused variables + + * lisp/play/dunnet.el (dun-mode, dun-die, dun-inven, dun-try-take): + (dun-dig, dun-type, dun-n, dun-s, dun-e, dun-w, dun-ne, dun-se): + (dun-nw, dun-sw, dun-up, dun-down, dun-in, dun-out, dun-long): + (dun-swim, dun-score, dun-flush, dun-piss, dun-sleep, dun-drive): + (dun-superb, dun-power, dun-unix-parse, dun-bin, dun-fascii): + (dun-ftpquit, dun-ftphelp, dun-uexit, dun-pwd, dun-dos-parse): + (dun-dos-invd, dun-dos-spawn, dun-dos-exit, dun-dos-nil): + (dungeon-nil): Mark arguments as unused. + (dun-drop, dun-objnum-from-args, dun-get-path, dun-ftp): + (dun-restore): Remove unused variable. + +2017-01-17 Michael Albinus + + Fix auto-save-file-name problem in Tramp on MS Windows + + * lisp/files.el (make-auto-save-file-name): Use `file-remote-p' + rather than an ange-ftp regexp. + + * lisp/net/tramp.el (tramp-handle-make-auto-save-file-name): + Fix a problem when running on MS Windows. + + * test/lisp/net/tramp-tests.el (tramp-test31-make-auto-save-file-name): + Adapt test. + +2017-01-17 Michael Albinus + + Fix auto-save-file-name problem in Tramp on MS Windows. Do not merge + + * lisp/net/tramp.el (tramp-handle-make-auto-save-file-name): + Fix a problem when running on MS Windows. + +2017-01-17 Paul Eggert + + Merge from origin/emacs-25 + + 42614fa Update remaining copyright years with admin.el M-x set-copyright + f17a006 * lisp/ffap.el (ffap-lax-url): Bump :version after recent cha... + +2017-01-17 Mark Oteiza + + Nix some uses of eval + + * lisp/play/dunnet.el: Fix triple negative. + (dun-doverb): Use funcall instead of eval. + (dun-echo): Just call dun-mprinc. + (dun-save-val): Just bind value without eval. + +2017-01-17 Tom Tromey + + Fix comment in css-mode.el + + * lisp/textmodes/css-mode.el: Remove obsolete comment. + +2017-01-16 Vibhav Pant + + update branch + +2017-01-16 Ian Dunn (tiny change) + + * lisp/net/eww.el (eww-tag-meta): Handle single quoted URLs (Bug#25445). + +2017-01-15 Noam Postavsky + + Improve ffap-gopher-at-point handling of long lines + + * lisp/ffap.el (ffap-gopher-regexp): Only match the KEY part. Note + setting to nil is now supported. + (ffap--gopher-var-on-line): New function. + (ffap-gopher-at-point): Use it instead of the old ffap-gopher-regexp + which could overflow the regexp stack on long lines (Bug#25391). Use + `let-alist' instead of calling `set' on local variables. + * test/lisp/ffap-tests.el (ffap-gopher-at-point): New test. + +2017-01-15 Vibhav Pant + + * lisp/emacs-lisp/byte-opt.el: Optimize how tags are checked for use. + + * byte-opt.el: (byte-optimize-lapcode): Return nil instantly on + finding the tag in a jump table. + +2017-01-15 Vibhav Pant + + * lisp/emacs-lisp/bytecomp.el: Add documentation, remove code duplication + +2017-01-14 Vibhav Pant + + Add new 'switch' byte-code. + + 'switch' takes two arguments from the stack: the variable to test, and + a jump table (implemented as a hash-table with the appropriate :test + function). By looking up the value of the variable in the hash table, + the interpreter can jump to the label pointed to by the value, if any. + This implementation can only be used for `cond' forms of the type + `(cond ((test x 'foo) 'bar) ...)`, such that the function `test` and + variable `x` is same for all clauses. + + * lisp/emacs-lisp/bytecomp.el: + + * Add (byte-compile-cond-valid-obj2-p), (byte-compile-cond-vars), + (byte-compile-cond-jump-table-info), (byte-compile-jump-table-add-tag), + (byte-compile-cond-jump-table), byte-compile-jump-tables. + + * Add defcustom `byte-compile-cond-use-jump-table'. + + * (byte-compile-cond): Use them. + + * (byte-compile-lapcode): Patch tags present in jump tables, if any. + + * lisp/emacs-lisp//byte-opt.el: (byte-optimize-lapcode): Add checks to + some peephole optimizations to prevent them from messing up any code + involving `byte-switch`. + + * src/bytecode.c: (exec_byte_code): Add bytecode Bswitch. + +2017-01-14 Alan Third + + Fix NS main thread check (bug#25265) + + * src/nsterm.m (ns_read_socket, ns_select): Replace mainThread with + isMainThread. + +2017-01-14 Stefan Monnier + + * lisp/progmodes/sql.el (sql-product-alist): Doc tweak + + `:sqli-comint-func' does not have to be a symbol. + +2017-01-14 Alan Mackenzie + + Correct c-parse-state-get-strategy for moving HERE backward into a macro. + + * list/progmodes/c-engine.el (c-parse-state-get-strategy): When HERE is below + its previous value, we chose strategy 'forward, and the new HERE is in a + (different) macro, ensure the returned START-POINT is not above the start of + the macro. + +2017-01-14 Eli Zaretskii + + Include "Date:" in mail messages filed by 'sendmail-send-it' + + * lisp/mail/sendmail.el (mail-do-fcc): Insert a 'Date:' header + into the filed message. In the outgoing message, sendmail will + add the date, but the composed message body doesn't have it. + (Bug#25436) + +2017-01-14 Eli Zaretskii + + * lisp/progmodes/sql.el (sql-product-alist): Doc fix. (Bug#25440) + +2017-01-14 Dmitry Gutov + + Remove leftover references to log-view-message-face + + * lisp/vc/vc-bzr.el (vc-bzr-log-view-mode): Use log-view-message. + + * lisp/vc/vc-git.el (vc-git-root-log-format): Same. + + * lisp/vc/vc-hg.el (vc-hg-root-log-format): Same. + +2017-01-13 Phillip Lord + + Record autoloads till emacs dump + + * admin/ldefs-clean.el (ldefs-clean-up): Record autoloads till emacs dump + * lisp/ldefs-boot-auto.el (batch-byte-compile): Update + + Previously, autoloads were collected till loaddefs.el was generated as + part of the build. However, bootstrap-emacs does not load + loaddefs (rather it is dumped), hence we must record autoloads until the + full emacs binary is dumped. + +2017-01-13 Tom Tromey + + Add chained indentation to js-mode + + Bug#20896 + * lisp/progmodes/js.el (js-chain-indent): New variable. + (js--skip-term-backward, js--skip-terms-backward) + (js--chained-expression-p): New functions. + (js--proper-indentation): Call js--chained-expression-p. + * test/manual/indent/js-chain.js: New file. + * test/manual/indent/js.js: Add (non-)chained indentation test. + +2017-01-13 Tom Tromey + + Fix js-mode indentation bug + + Bug#15582: + * lisp/progmodes/js.el (js--find-newline-backward): New function. + (js--continued-expression-p): Use it. + * test/manual/indent/js.js: Add new test. + +2017-01-13 Tom Tromey + + Fix definition of EMACS in test/manual/indent/Makefile + + * test/manual/indent/Makefile (EMACS): Add one more "..". + +2017-01-13 Tom Tromey + + Add .jsx to auto-mode-alist + + Bug#25389: + * lisp/files.el (auto-mode-alist): Add entry for .jsx. + +2017-01-13 Tom Tromey + + Fix two js-mode filling bugs + + Bug#19399 and Bug#22431: + * lisp/progmodes/js.el (js-mode): Set comment-line-break-function and + c-block-comment-start-regexp. + * test/lisp/progmodes/js-tests.el: New file. + +2017-01-13 Eli Zaretskii + + Fix last change + + * test/src/thread-tests.el (threads-condvar-wait): Revert + previous change. Make sure no other threads from previous + tests are running, to avoid interfering with our thread counts. + +2017-01-13 Eli Zaretskii + + Fix the new condvar test + + * test/src/thread-tests.el (threads-condvar-wait): Enlarge the + time we sleep in the main thread to let the other thread + process notifications. + +2017-01-13 Eli Zaretskii + + Minor improvements in the new condvar test + + * test/src/thread-tests.el (threads-test-condvar-wait): Use + with-mutex instead of emulating it inline. + (threads-condvar-wait): Improve comments. Check that the new + thread is alive before waiting for it to become blocked on the + conditional variable. + +2017-01-13 Eli Zaretskii + + Fix a bug in waiting for condition variable + + * src/thread.c (lisp_mutex_lock, lisp_mutex_unlock) + (lisp_mutex_unlock_for_wait, condition_wait_callback) + (condition_notify_callback): Improve commentary. + (condition_wait_callback): Call post_acquire_global_lock before + attempting to lock the mutex, to make sure the lock's owner is + recorded correctly. + + * test/src/thread-tests.el (threads-condvar-wait): New test. + +2017-01-13 Eli Zaretskii + + Improve documentation of dabbrevs + + * doc/emacs/abbrevs.texi (Dynamic Abbrevs): Add a cross reference + to "Dabbrev Customization". + (Dabbrev Customization): More details about the default value of + dabbrev-abbrev-char-regexp and use cases when it might not be good + enough. (Bug#25432) + +2017-01-13 Katsumi Yamaoka + + Fix last change of dd80ee6 (was: mm-uu.el: Don't dissect patch part) + +2017-01-13 Katsumi Yamaoka + + mm-uu.el: Don't dissect patch part + + This fixes a bug that the patch part is broken in the article + <87inpjzhpb.fsf@users.sourceforge.net> in the bug-gnu-emacs list. + + * lisp/gnus/mm-uu.el (mm-uu-dissect-text-parts): + Don't dissect patch part. + +2017-01-13 Dmitry Lazurkin + + Fix extracting async def type and name in python mode imenu + + * lisp/progmodes/python.el (python-imenu--get-defun-type-name): + New function. + (python-imenu--build-tree): Use python-imenu--get-defun-type-name for + extract async or simple def type and name at current + position (Bug#24820). + * test/lisp/progmodes/python-tests.el (python-imenu-create-index-1): + (python-imenu-create-flat-index-1): Add async def's. + +2017-01-13 Katsumi Yamaoka + + Remove garbage from Content-Transfer-Encoding value (bug#25420) + + * lisp/mail/ietf-drums.el (ietf-drums-strip-cte): New function. + (ietf-drums-remove-garbage): New function. + (ietf-drums-remove-whitespace): Remove CR as well. + + * lisp/mail/mail-parse.el (mail-header-strip-cte): + Alias to ietf-drums-strip-cte. + + * lisp/gnus/gnus-art.el (article-decode-charset): + * lisp/gnus/gnus-sum.el (gnus-summary-enter-digest-group): + * lisp/gnus/mm-decode.el (mm-dissect-buffer): + * lisp/gnus/nndoc.el (nndoc-decode-content-transfer-encoding) + (nndoc-rfc822-forward-generate-article): + * lisp/mh-e/mh-mime.el (mh-decode-message-body): + Replace mail-header-strip with mail-header-strip-cte. + +2017-01-13 Paul Eggert + + Restore behavior of ‘./autogen.sh autoconf git’ + + * autogen.sh: Do both autoconf and git setup when invoked + as ‘./autogen.sh autoconf git’. Avoid unnecessary newline in chatter. + Mention new --no-check option in usage message. (Bug#25359) + +2017-01-12 Glenn Morris + + * autogen.sh: Simplify argument parsing. + +2017-01-12 Noam Postavsky + + Clarify that easy-menu-add is a nop (Bug#25382) + + * lisp/emacs-lisp/easymenu.el (easy-menu-add): Make it into an alias of + `ignore', like `easy-menu-remove'. + +2017-01-12 Glenn Morris + + * lisp/textmodes/rst.el (rst-package-emacs-version-alist): Fix entry. + +2017-01-11 Glenn Morris + + * autogen.sh: Add --no-check option. (Bug#25359) + +2017-01-11 Glenn Morris + + Convert some network test failures to skipping + + These tests intermittently fail on hydra.nixos.org for unclear + reasons related to starting the external process. + This isn't an Emacs issue, and the failures cause noise on + the emacs-buildstatus list. (Bug#24503) + * test/lisp/net/network-stream-tests.el (echo-server-nowait) + (connect-to-tls-ipv4-nowait): Skip rather than fail if the + external process fails to start properly. + +2017-01-11 Eli Zaretskii + + Revert "Add DNS keywords and remove duplications" + + This reverts commit 1cb9aa5b14867983d0013a61709b4d0af18364ff. + +2017-01-11 Alexander Kuleshov + + Add DNS keywords and remove duplications + + * lisp/textmodes/dns-mode.el (dns-mode-types): Add two TLSA and + NSEC" DNS related keywords and remove duplication of "NSAP". + +2017-01-11 Alexander Kuleshov + + Add DNS keywords and remove duplications + + * lisp/textmodes/dns-mode.el (dns-mode-types): Add two TLSA and + NSEC" DNS related keywords and remove duplication of "NSAP". + +2017-01-11 Alan Mackenzie + + Handle syntactic WS cache properties more accurately at buffer changes. + + This fixes bug #25362. + + * lisp/progmodes/cc-engine.el (c-sws-lit-type, c-sws-lit-limits) + (c-invalidate-sws-region-before, c-invalidate-sws-region-after-del) + (c-invalidate-sws-region-after-ins): New variables and functions. + (c-invalidate-sws-region-after): Change from a defsubst to a defun. + Also pass + it the standard OLD-LEN argument. Call both + c-invalidate-sws-region-after-{ins,del} to check for "dangerous" WS + cache + properties. + + * lisp/progmodes/cc-langs.el (c-block-comment-ender-regexp): New language + variable. + + * lisp/progmodes/cc-mode.el (c-before-change): Call + c-invalidate-sws-region-before. + (c-after-change): Pass old-len to c-invalidate-sws-region-after. + +2017-01-11 Michael Albinus + + Support stat 8.26 in Tramp + + * lisp/net/tramp-sh.el (tramp-get-remote-stat): Use QUOTING_STYLE + environment variable of newer coreutils. (Bug#23422) + +2017-01-10 Eli Zaretskii + + Improve documentation of coding-systems + + * doc/lispref/nonascii.texi (Coding System Basics): Mention + 'prefer-utf-8'. Index it and 'undecided'. + (Encoding and I/O): Fix a typo. + (User-Chosen Coding Systems): Improve the documentation of + ACCEPT-DEFAULT-P argument to select-safe-coding-system. Document + select-safe-coding-system-function. + (Specifying Coding Systems): Document coding-system-require-warning. + +2017-01-10 Paul Eggert + + Merge from gnulib + + This incorporates: + 2017-01-09 maint: time stamp -> timestamp + 2017-01-07 stdioext: Port to Minix 3.2 and newer + 2017-01-06 glob, intprops, xalloc: work around Clang bug + 2017-01-02 revert copyright-year change to synced files + * doc/misc/texinfo.tex, lib/fpending.c, lib/intprops.h, lib/mktime.c: + * lib/stat-time.h, lib/stdio-impl.h, lib/time.in.h, lib/timespec.h: + * lib/utimens.c, lib/xalloc-oversized.h: + Copy from gnulib. + +2017-01-10 Eli Zaretskii + + Don't use unsafe encoding for the bookmark file + + * lisp/bookmark.el (bookmark-write-file): Handle the case when the + explicitly specified encoding of the bookmark file cannot encode the + additional bookmarks just added. (Bug#25365) + +2017-01-09 Eli Zaretskii + + Improve definition of 'variable-pitch' face on MS-Windows + + * lisp/faces.el (variable-pitch): Don't specify too many + attributes of the font, otherwise faces that request different + weight or slant or size will not get them. + +2017-01-09 Eli Zaretskii + + Fix an error message in python.el + + * lisp/progmodes/python.el (python-shell-get-process-or-error): + Don't repeat the same key binding twice. (Bug#25405) + +2017-01-09 Phillip Lord + + Remove unused ldefs-boot.el + + * lisp/ldefs-boot.el: Remove + + This file was not removed as reported in c27b645956a11, but accidentally + left. + +2017-01-09 Noam Postavsky + Eli Zaretskii + + Use expanded stack during regex matches + + While the stack is increased in main(), to allow the regex stack + allocation to use alloca we also need to modify regex.c to actually take + advantage of the increased stack, and not limit stack allocations to + SAFE_ALLOCA bytes. + + * src/regex.c (MATCH_MAY_ALLOCATE): Remove obsolete comment about + allocations in signal handlers which no longer happens and correct + description about when and why MATCH_MAY_ALLOCATE should be defined. + (emacs_re_safe_alloca): New variable. + (REGEX_USE_SAFE_ALLOCA): Use it as the limit of stack allocation instead + of MAX_ALLOCA. + (emacs_re_max_failures): Rename from `re_max_failures' to avoid + confusion with glibc's `re_max_failures'. + * src/emacs.c (main): Increase the amount of fixed 'extra' bytes we add + to the stack. Instead of changing emacs_re_max_failures based on the + new stack size, just change emacs_re_safe_alloca; emacs_re_max_failures + remains constant regardless, since if we run out stack space SAFE_ALLOCA + will fall back to heap allocation. + +2017-01-09 Noam Postavsky + + Fix computation of regex stack limit + + The regex stack limit was being computed as the number of stack entries, + whereas it was being compared with the current size as measured in + bytes. This could cause indefinite looping when nearing the stack limit + if re_max_failures happened not to be a multiple of sizeof + fail_stack_elt_t (Bug #24751). + + * src/regex.c (GROW_FAIL_STACK): Compute both current stack size and + limit as numbers of stack entries. + +2017-01-08 Alan Third + + Remove apploopnr + + * src/nsterm.m (ns_select, ns_read_socket): Remove apploopnr and only + allow app loop to run in main thread. + +2017-01-08 Glenn Morris + + Remove unused configure output variable + + * configure.ac (GNULIB_MK): + * Makefile.in (gnulib_mk): Remove, no longer used. + +2017-01-08 Glenn Morris + + Fix automake dependencies + + * Makefile.in (AUTOMAKE_INPUTS): Add nt/gnulib.mk. (Bug#25372) + All platforms need this file to exist. + +2017-01-08 Paul Eggert + + Remove unnecessary blankp code + + * src/character.c (blankp): Remove redundant code that slows Emacs + down a bit. The caller already does the test. + +2017-01-08 Stefan Merten + + * lisp/textmodes/rst.el: Fix rst-forward-indented-block. + + * rst.el (rst-cvs-header, rst-svn-rev, rst-svn-timestamp) + (rst-official-version, rst-official-cvs-rev) + (rst-package-emacs-version-alist): Maintain version numbers. + (rst-forward-indented-block): Fix. Start searching at next + line again. Fixes fontification of comments continuing on the + same line they started. + +2017-01-08 Paul Eggert + + Remove @SET_MAKE@ from manually-maintained files + + Emacs now assumes GNU Make, so @SET_MAKE@ is no longer needed. + * Makefile.in, lwlib/Makefile.in, nextstep/Makefile.in: + * src/Makefile.in: Remove @SET_MAKE@. + +2017-01-07 Michael Albinus + + Fix a problem with `start-file-process' in Tramp + + * lisp/net/tramp-sh.el (tramp-maybe-open-connection): + `start-file-process' shall work when `non-essential' is + non-nil, but there is already an established connection. + + +2017-01-07 Rolf Ade (tiny change) + + Fix selecting SQLite database files with sql-mode (Bug#23566) + + * lisp/progmodes/sql.el (sql-sqlite-login-params): Allow any name as + SQLite database file name, by default. + (sql-get-login-ext): Fixed read-file-name arguments to provide + path completion even if a database name pattern is customized and to + allow creation of new SQLite database files. + +2017-01-07 Noam Postavsky + + Clarify major mode switching + + * doc/emacs/modes.texi (Major Modes): + * doc/lispref/modes.texi (Modes, Major Modes): Explictly say that each + buffer has exactly one major mode and can't be "turned off", only + switched away from (Bug#25357). + +2017-01-07 Noam Postavsky + + Add helpful comment to compile-command's docstring + + * lisp/progmodes/compile.el (compile-command): Mention trailing space in + docstring (Bug#25337). + +2017-01-07 Eli Zaretskii + + Specify encoding of the bookmark file + + * lisp/bookmark.el (bookmark-insert-file-format-version-stamp): + Accept an argument CODING and include a 'coding:' cookie in the + bookmark file preamble. + (bookmark-upgrade-file-format-from-0): Call + 'bookmark-insert-file-format-version-stamp' with the file buffer's + encoding, as detected when it was read. + (bookmark-file-coding-system): New variable. + (bookmark-load): Set bookmark-file-coding-system to the encoding + of the loaded file. + (bookmark-write-file): Bind coding-system-for-write to either the + user setting via "C-x RET c" or to the existing file encoding, + defaulting to 'utf-8-emacs'. Update the value of + bookmark-file-coding-system. (Bug#25365) + +2017-01-07 Eli Zaretskii + + Avoid infloop in 'ispell-region' + + * lisp/textmodes/ispell.el (ispell-begin-skip-region-regexp): + Protect against 'ispell-skip-region-alist' being nil. Reported by + Ernest Adrogué , see + http://lists.gnu.org/archive/html/help-gnu-emacs/2017-01/msg00007.html. + +2017-01-06 Philipp Stephani + + Add support for Unicode whitespace in [:blank:] + + See Bug#25366. + + * src/character.c (blankp): New function for checking Unicode + horizontal whitespace. + * src/regex.c (ISBLANK): Use 'blankp' for non-ASCII horizontal + whitespace. + (BIT_BLANK): New bit for range table. + (re_wctype_to_bit, execute_charset): Use it. + * test/lisp/subr-tests.el (subr-tests--string-match-p--blank): Add + unit test for [:blank:] character class. + * test/src/regex-tests.el (test): Adapt unit test. + * doc/lispref/searching.texi (Char Classes): Document new Unicode + behavior for [:blank:]. + +2017-01-06 Eli Zaretskii + + Fix ':version' of 'select-enable-primary' + + * lisp/select.el (select-enable-primary): Fix a typo in + ':version'. (Bug#25375) + +2017-01-06 Stefan Monnier + + (feedmail-deduce-address-list): Avoid add-to-list on local variables. + + Author: + + * lisp/mail/feedmail.el (feedmail-deduce-address-list): + Avoid add-to-list on local variables. + +2017-01-06 Noam Postavsky + + Fix isearch handling of C-u C-u... + + * lisp/isearch.el: Add `isearch-scroll' property to + universal-argument-more so that `isearch-allow-scroll' will apply to it + as well. + (isearch-pre-command-hook): Let `isearch-allow-prefix' apply to + `universal-argument-more' as well (Bug#25302). + +2017-01-05 Paul Eggert + + Shorten autogen.sh script + + * autogen.sh: Use a shorter script, as some 'sed' implementations + mishandle long scripts. + +2017-01-05 Eli Zaretskii + + Yet another fix for autogen.sh + + * autogen.sh (gnulib.mk): Make the Sed script more portable. + + * nt/Makefile.in (${srcdir}/gnulib.mk): Adapt the Sed command to + the changes in autogen.sh. + +2017-01-05 Eli Zaretskii + + * autogen.sh (gnulib.mk): Another attempt to fix macOS build. + +2017-01-05 Eli Zaretskii + + Fix dependencies of nt/gnulib.mk + + * Makefile.in ($(srcdir)/nt/gnulib.mk): Avoid circular dependency + of nt/gnulib.mk on lib/Makefile.in. + +2017-01-05 Eli Zaretskii + + Unbreak macOS build + + * autogen.sh (gnulib.mk): Don't use non-portable extensions of GNU + Sed. + +2017-01-05 Johan Claesson (tiny change) + + Fix term.el handling of ^Z-sequences spanning chunks + + Bash will after each command send ?\032 and the current directory "/tmp" + to inform term.el. Bash output is buffered in 4096 bytes chunks. If a + command outputs roughly 4096 bytes then the end of the first chunk will + be "/tm" (Bug#13350). + + * lisp/term.el (term-emulate-terminal): Change the regexp to find the + end of the ?\032 sequence to use \n instead of $, the latter can match + end of string as well. + +2017-01-05 Mark Oteiza + + Turn on lexical-binding in mb-depth.el + + * lisp/mb-depth.el: Turn on lexical-binding. + (minibuffer-depth-setup): Bind things used multiple times. + +2017-01-04 Alan Third + + Revert "Rework NS event handling (bug#25265)" + + This reverts commit e0e5b0f4a4ce1d19ee0240c514dedd873d4165dc. + +2017-01-04 Glenn Morris + + Update remaining copyright years with admin.el M-x set-copyright + + * etc/refcards/ru-refcard.tex (cyear): Set to 2017. + +2017-01-04 Glenn Morris + + * lisp/ffap.el (ffap-lax-url): Bump :version after recent change. + +2017-01-04 Paul Eggert + + Port recent autogen.sh changes to Darwin + + Problem reported by Sam Steingold (Bug#25347). + * autogen.sh: Don't assume 'sed -f-' reads a script from stdin, as + POSIX does not require it and it does not work on Darwin. + +2017-01-04 Stefan Monnier + + Avoid add-to-list on local variables + + * lisp/gnus/nnir.el: Use lexical-binding and cl-lib. + (nnir-retrieve-headers): Use pcase. + (nnir-search-thread): Avoid add-to-list on local variables. + + * lisp/gnus/smime.el: Use lexical-binding and cl-lib. + (smime-verify-region): Avoid add-to-list on local variables. + + * lisp/mail/undigest.el: Use lexical-binding and cl-lib. + (rmail-digest-parse-mime, rmail-digest-rfc1153) + (rmail-digest-parse-rfc934): Avoid add-to-list on local variable. + + * lisp/net/ldap.el (ldap-search): Move init into declaration. + + * lisp/net/newst-backend.el (newsticker--cache-add): + Avoid add-to-list on local variables; Simplify code with `assq'. + + * lisp/net/zeroconf.el: Use lexical-binding and cl-lib. + (dbus-debug): Remove declaration, unused. + (zeroconf-service-add-hook, zeroconf-service-remove-hook) + (zeroconf-service-browser-handler, zeroconf-publish-service): + Avoid add-to-list and *-hook on local variables. + + * lisp/org/org-archive.el (org-all-archive-files): + * lisp/org/org-agenda.el (org-agenda-get-restriction-and-command): + Avoid add-to-list on local variables. + + * lisp/org/ox-publish.el (org-publish--run-functions): New function. + (org-publish-projects): Use it to avoid run-hooks on a local variable. + (org-publish-cache-file-needs-publishing): Avoid add-to-list on + local variables. + + * lisp/progmodes/ada-prj.el: Use setq instead of (set '...). + (ada-prj-load-from-file): Avoid add-to-list on local variables. + + * lisp/progmodes/ada-xref.el (ada-initialize-runtime-library): Simplify. + (ada-gnat-parse-gpr, ada-parse-prj-file-1) + (ada-xref-find-in-modified-ali): Avoid add-to-list on local variables. + + * lisp/progmodes/idlw-shell.el (idlwave-shell-update-bp-overlays): + Avoid add-to-list on local variables. + +2017-01-04 Mark Oteiza + + Turn on lexical-binding in md4.el + + * lisp/md4.el: Turn on lexical-binding. + * test/lisp/md4-tests.el: New file. + +2017-01-03 Stefan Merten + + Lots of refactorings and a few minor improvements. + + User visible improvements and changes: + * Improve and debug `rst-forward-section` and `rst-backward-section`. + * Auto-enumeration may be used with all styles for list insertion. + * Improve and debug `rst-toc-insert`. + * Adapt change in Emacs to use customization group `text` instead of `wp`. + * Bind `n` and `p` in `rst-toc-mode`. + * `z` in `toc-mode` returns to the previous window configuration. + * Require Emacs version >= 24.1. + + Lots of refactorings including: + * Silence byte compiler. + * Use lexical binding. + * Use `cl-lib`. + * Add tests and raise test coverage. + +2017-01-03 Stefan Monnier + + (cl-defstruct): Improve error message for slots w/o value (bug#25312) + + * lisp/emacs-lisp/cl-macs.el (cl-defstruct): Don't signal an error but + emit a warning for those coders who forgot to put a default value in + their slot. + +2017-01-03 Philipp Stephani + + Small patch for ffap.el + + * lisp/ffap.el (ffap-alist): Document that ffap sets the match data + while walking 'ffap-alist'. + +2017-01-03 Eli Zaretskii + + Generate nt/gnulib.mk from lib/gnulib.mk + + This was proposed by Paul Eggert , + with the purpose of avoiding manual maintenance of + nt/gnulib.mk. + + * nt/gnulib-modules-to-delete.cfg: New file. + * nt/Makefile.in (AM_V_GEN, am__v_GEN_, am__v_GEN_0) + (am__v_GEN_1): New variables. + (${srcdir}/gnulib.mk): Rules to generate gnulib.mk from + lib/gnulib.mk and list of modules in gnulib-modules-to-delete.cfg. + + * make-dist (nt): Add gnulib-modules-to-delete.cfg to the list of + files to link. + * configure.ac (GNULIB_MK): Compute the value according to $opsys. + * autogen.sh: Create nt/gnulib.mk if it doesn't exist, before + running autoreconf. + * Makefile.in (gnulib_mk): New variable. + ($(srcdir)/nt/gnulib.mk): Rule to produce it. + (AUTOMAKE_INPUTS): Use $(gnulib_mk) instead of a literal file + name. + * .gitignore: Add nt/gnulib.mk. + + * src/w32.c (acl_errno_valid): Implement it here, as we no longer + build the acl-permissions module from Gnulib. + +2017-01-03 Noam Postavsky + + Handle multibyte chars spanning chunks in term.el + + * lisp/term.el (term-terminal-undecoded-bytes): New variable. + (term-mode): Make it buffer local. Don't make `term-terminal-parameter' + buffer-local twice. + (term-emulate-terminal): Check for bytes of incompletely decoded + characters, and save them until the next call when they can be fully + decoded (Bug#25288). + +2017-01-03 Michael Albinus + + Finish work on filenotify-tests.el + + * test/lisp/filenotify-tests.el (file-notify--test-monitors): + New variable. + (file-notify--test-cleanup, file-notify--test-monitor): Use it. + (file-notify--test-read-event, file-notify-test02-events) + (file-notify-test04-file-validity): Handle "gvfs-monitor-dir.exe". + (file-notify-test03-autorevert) + (file-notify-test08-watched-file-in-watched-dir): + Set `file-notify--test-desc' for proper work of + `file-notify--test-monitor'. (Bug#21804) + +2017-01-02 Michael Albinus + + Check also for "gvfs-monitor-dir.exe" in Tramp + + * lisp/net/tramp-sh.el (tramp-get-remote-gvfs-monitor-dir): Check also + for "gvfs-monitor-dir.exe". + +2017-01-02 Eli Zaretskii + + Fix compilation --without-x + + * src/composite.c (autocmp_chars) [HAVE_WINDOW_SYSTEM]: Call + font_range only if it is compiled in. (Bug#25334) + +2017-01-02 Sašo Živanović + + Fix RefTeX to show table of contents for dtx files (tiny change) + + * lisp/textmodes/reftex.el (reftex-compile-variables): Change the + section regexp so that it accepts lines starting with the comment + character. (tiny change) + * lisp/textmodes/reftex-parse.el (reftex-parse-from-file): Filter + gathered toc entries, accepting a commented entry if and only if the + source file is a ".dtx" file. (tiny change) + +2017-01-02 Paul Eggert + + Remove mistakenly-added files + + Problem reported by Glenn Morris in: + http://lists.gnu.org/archive/html/emacs-devel/2017-01/msg00008.html + * lisp/gnus/gnus-ems.el, lisp/gnus/gnus-sync.el: + * lisp/gnus/messcompat.el, lisp/nxml/nxml-glyph.el: + * lisp/nxml/nxml-uchnm.el, lisp/obsolete/awk-mode.el: + * lisp/obsolete/iso-acc.el, lisp/obsolete/iso-insert.el: + * lisp/obsolete/iso-swed.el, lisp/obsolete/resume.el: + * lisp/obsolete/scribe.el, lisp/obsolete/spell.el: + * lisp/obsolete/swedish.el, lisp/obsolete/sym-comp.el: + Remove files that were added by mistake during a merge. + +2017-01-01 Noam Postavsky + + Warn about incomplete untarring of link files + + The current tar-mode doesn't really support unpacking symlinks, it + simply creates an empty file of the same name. + + * lisp/tar-mode.el (tar--describe-as-link): New function extracted from + `tar--check-descriptor'. + (tar-untar-buffer): Use it to warn about imperfectly untarred link + files. + +2017-01-01 Noam Postavsky + + Remove sh-mode's skeleton-end-hook + + * lisp/progmodes/sh-script.el (sh-mode): Remove local setting of + `skeleton-end-hook', `skeleton-insert' already does `newline-and-indent' + and also respects `skeleton-end-newline' (Bug#16634). + +2017-01-01 Paul Eggert + + * nt/gnulib.mk (stdint.h): Update to match lib/gnulib.mk here. + +2017-01-01 Mark Oteiza + + Add term/tmux.el + + Since tmux version 2.1, new tmux terminfos are shipped due to oddities + with xterm and screen terminfos. This is simply a duplication of + term/screen.el with screen -> tmux. + * lisp/term/tmux.el: New file. + +2017-01-01 Philipp Stephani + + Fix encoding of JSON surrogate pairs + + JSON requires that such pairs be treated as UTF-16 surrogate pairs, not + individual code points; cf. Bug #24784. + + * lisp/json.el (json-read-escaped-char): Fix decoding of surrogate + pairs. + (json--decode-utf-16-surrogates): New defun. + + * test/lisp/json-tests.el (test-json-read-string): Add test for + surrogate pairs. + +2017-01-01 Michael Albinus + + Remove tramp-gw.el, which was synced from emacs-25 by accident + +2017-01-01 Paul Eggert + + Do not use Gnulib’s m4/wint_t.m4. + + * admin/merge-gnulib: Remove m4/wint_t.m4 when merging. + Fix typo so that warn-on-use.m4 is removed too. + * configure.ac (gt_TYPE_WINT_T): New macro, replacing Gnulib’s. + * m4/wint_t.m4: Remove. + +2017-01-01 Paul Eggert + + Merge from gnulib, continued + + * m4/wint_t.m4: New file, copied from gnulib. + +2017-01-01 Paul Eggert + + Update copyright year to 2017 in master + + Run admin/update-copyright in the master branch. This fixes files + that were not already fixed in the emacs-25 branch before it was + merged here. + +2017-01-01 Paul Eggert + + Remove test/automated detritus from merge + +2017-01-01 Paul Eggert + + Merge from gnulib + + This incorporates: + 2016-12-19 stdint: Fix WINT_MAX to match wint_t on mingw + 2016-12-18 getopt: Fix link error for users of getopt() in + 2016-12-17 getlogin: Port to newer mingw + 2016-12-17 stdint: Fix WINT_MAX to match wint_t on MSVC + 2016-12-17 Avoid redefinition errors on MSVC + * lib/getopt.in.h, lib/stdint.in.h, lib/stdio.in.h, lib/unistd.in.h: + * m4/stdint.m4, m4/unistd_h.m4: + Copy from gnulib. + * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. + Plus, this commit updates the indenting on copyright notices to + match that of gnulib. + +2017-01-01 Paul Eggert + + Merge from origin/emacs-25 + + 2e2a806 Fix copyright years by hand + 5badc81 Update copyright year to 2017 + +2017-01-01 Paul Eggert + + Merge from origin/emacs-25 + + 665be69 ; Update ChangeLog.2 and AUTHORS files + + # Conflicts: + # etc/AUTHORS + +2017-01-01 Paul Eggert + + Merge from origin/emacs-25 + + 697167b ; Improve wording of previous change in variables.texi + d7973e8 Document 'default-toplevel-value' and 'set-default-toplevel-v... + 8b71826 Don't modify minibuffer variables globally + 5b5e036 Revert to pre-25.1 behavior in ffap + 19994a1 * lisp/ffap.el: Fix obsolete comment referencing ffap-bug. + 3ace730 Attempt to fix 64-bit AIX build + f69bd79 Clarify usage of 'ediff-cleanup-hook' (Bug#24675) + c04ac8a Document that variable binding order is unspecified + 272554a * lisp/desktop.el (desktop-buffers-not-to-save): Doc fix. + 08de101 Fix M-x hints on Mac port + 86a297a Work around reporting a dpi change in apply_xft_settings + cf1f985 ; lisp/skeleton.el (skeleton-insert): Fix typo in last change + 9e1209d Amend the version number of CC Mode 5.33 -> 5.32.99. Don't m... + 88cdf14 Improve skeleton docstrings + +2017-01-01 Paul Eggert + + Merge from origin/emacs-25 + + 4179238 Improve documentation of 'w32-scroll-lock-modifier' + +2017-01-01 Paul Eggert + + Merge from origin/emacs-25 + + 9adb101 Document 'describe-fontset' + 229315c ; Add missing symbol quoting. + 3d94931 Repair desktop restoration on text terminals + 43022f9 Ignore forward-sexp-function in js-mode indentation code + b19fb49 Improve documentation of 'define-coding-system' + 467768f Fix Bug#25162 + 6db78ae Fix a typo in define-abbrev-table + 5f7d906 Bump makeinfo requirement from 4.7 to 4.13 + 442e2f6 Fixes related to select-enable-clipboard + e4ac450 Define struct predicate before acccesors + 08decbd Doc fix for vc-git + 5531e75 Further improve make-dist checking + 953bf67 Improve previous make-dist change + 129645a Make make-dist --snapshot do some sanity checks + + # Conflicts: + # lisp/menu-bar.el + +2017-01-01 Alan Mackenzie + + Give eval-and-compile a correct edebug spec. Fixes bug #16184 properly. + + * lisp/emacs-lisp/edebug.el (edebug_offset_indices): Revert abortive commit + from Thu Dec 29 09:22:36 2016 +0000 which didn't really fix the bug. + + * lisp/emacs-lisp/byte-run.el (eval-and-compile): Change the edebug spec from + t to (&rest def-form). + +2017-01-01 Paul Eggert + + Fix copyright years by hand + + These are dates that admin/update-copyright did not update, or + updated incorrectly. + +2017-01-01 Paul Eggert + + Update copyright year to 2017 + + Run admin/update-copyright. + +2016-12-31 Paul Eggert + + Clarify internal_catch etc. + + The recent change to internal_catch and friends relied on some + confusion I introduced to the code in 2013. Attempt to fix + the confusion by clarifying the code instead. This saves an + instruction and a load dependency in the typical case. + * src/eval.c (internal_catch, internal_condition_case) + (internal_condition_case_1, internal_condition_case_2) + (internal_condition_case_n): Undo the previous change. Instead, + use use ‘c’ rather than ‘handlerlist’ in the typical case. + Also, use ‘eassert’ rather than ‘clobbered_eassert’ when possible. + +2016-12-31 Ken Brown + + Further improve filenotify-tests.el + + * test/lisp/filenotify-tests.el + (file-notify--test-read-event): Adapt to file monitors of type + GFamFileMonitor, which occur on Cygwin. + (file-notify--test-monitor): Update doc string. + +2016-12-31 Paul Eggert + + * src/xdisp.c (string_from_display_spec): Simplify. + +2016-12-31 Alan Third + + Rework NS event handling (bug#25265) + + * src/nsterm.m (unwind_apploopnr): Remove. + (ns_read_socket): Remove references to apploopnr. Make processing the + NS event loop conditional on being in the main thread. + (ns_select): Remove references to apploopnr. Remove all fd_handler + related stuff. Check if there are events waiting on the NS event + queue rather than running the event loop. Remove unused variables and + code. + (fd_handler): Remove. + (ns_term_init): Remove creation of fd_handler thread. + (hold_event, EmacsApp:sendEvent, EmacsView:mouseMoved, + EmacsView:windowDidExpose): Remove send_appdefined. + (ns_send_appdefined): Always check the event queue for + applicationDefined events rather than relying on send_appdefined var. + * src/nsterm.h: Remove reference to fd_handler method. + +2016-12-31 Philipp Stephani + + Checkdoc: use syntax functions instead of regex + + In checkdoc.el, get rid of the error-prone regex to find definition + forms, and use existing syntax-based navigation functions instead. + This fixes a corner case with one-argument `defvar' forms. + + * lisp/emacs-lisp/checkdoc.el (checkdoc--next-docstring): New function. + (checkdoc-next-docstring, checkdoc-defun): Use it. + * test/lisp/emacs-lisp/checkdoc-tests.el (checkdoc-tests--next-docstring): + Add unit test. + +2016-12-31 Eli Zaretskii + + Don't define NOMINMAX on MS-Windows + + * src/callproc.c (NOMINMAX) [WINDOWSNT]: Don't define. This is no + longer needed with the current sources and MinGW headers, while + defining NOMINMAX causes an annoying compiler warning. + +2016-12-31 Chris Gregory (tiny change) + + Simplify code in eval.c that calls 'setjmp' + + * src/eval.c (internal_catch, internal_condition_case) + (internal_condition_case_1, internal_condition_case_2) + (internal_condition_case_n): Factor out the common tail of the + functions. + +2016-12-31 Chris Gregory (tiny change) + + Simplify code in 'string_from_display_spec' + + * src/xdisp.c (string_from_display_spec): Eliminate a redundant + test before the loop. + +2016-12-31 Eli Zaretskii + + Serialize random number generation on MS-Windows + + * src/w32.c (rand_as183): New function. + (random): Use it instead of MS runtime's 'rand'. This avoids + producing separate and identical random series in each Lisp + thread. + (srandom): Modify to supply 3 seed values to 'rand_as183'. + +2016-12-31 Michael Albinus + + * src/gfilenotify.c (Fgfile_monitor_name): Return interned symbol. + +2016-12-30 Ken Raeburn + + Don't call xg_select for a NextStep build. + + NextStep builds use glib but don't use xg_select. + + * src/process.c (wait_reading_process_output): Don't call xg_select + for a NextStep build. + +2016-12-30 Ken Raeburn + + Increase the obarray size. + + In a typical GNU/Linux/X11 build, we wind up with over 15k symbols by + the time we've started. The old obarray size ensured an average chain + length of 10 or more. + + * src/lread.c (OBARRAY_SIZE): Increase to 15121. + +2016-12-30 Ken Raeburn + + Initialize thread support for Xlib. + + * src/xterm.c (x_initialize) [THREADS_ENABLED]: Call XInitThreads + before doing anything else with X. + +2016-12-30 Paul Eggert + + Rename primary_thread to main_thread + + This avoids the confusion of using two different phrases "main thread" + and "primary thread" internally to mean the same thing. See: + http://lists.gnu.org/archive/html/emacs-devel/2016-12/msg01142.html + * src/thread.c (main_thread): Rename from primary_thread, + since the new name no longer clashes with main_thread_id + and Emacs internals normally call this the "main thread". + (init_main_thread): Rename from init_primary_thread. + (main_thread_p): Rename from primary_thread_p. + All uses changed. + +2016-12-30 Paul Eggert + + Rename main_thread to main_thread_id and simplify + + * src/emacs-module.c: Include syssignal.h, for main_thread_id. + [HAVE_PTHREAD]: Do not include pthread.h. + (main_thread): Remove. All uses replaced by main_thread_id, + or by dwMainThreadId on NT. Since the HAVE_PTHREAD code is now using + the main_thread_id established by sysdep.c, there is no need for a + separate copy of the main thread ID here. + (module_init): Remove. All uses removed. + * src/sysdep.c (main_thread_id) [HAVE_PTHREAD]: + Rename from main_thread. All uses changed. Now extern. + +2016-12-30 Michael Albinus + + * src/gfilenotify.c (Fgfile_monitor_name): Return a symbol. + +2016-12-30 Paul Eggert + + * src/sysdep.c (deliver_process_signal): Improve comment. + +2016-12-30 Alan Mackenzie + + CC Mode: Fix the fontification of a spuriously recognised enum member. + + The "enum" was in an argument list, but triggered the fontification of a + following identifier in the function block as though it were in an enum + declaration. + + * lisp/progmodes/cc-fonts.el (c-font-lock-enum-body): New function. + (c-basic-matchers-after): Replace the inline stanza for enum elements with a + call to c-font-lock-enum-body. + + * lisp/progmodes/cc-langs.el (c-enum-clause-introduction-re): New language + variable. + +2016-12-30 Nicolas Petton + + Bump Emacs version to 25.1.91 + + * README: + * configure.ac: + * msdos/sed2v2.inp: + * nt/README.W32: Bump Emacs version. + * lisp/ldefs-boot.el: Update. + +2016-12-30 Eli Zaretskii + + Attempt to fix crashes with threads in GTK builds + + * src/xgselect.c (xg_select): Call pselect via thread_select, not + directly, to avoid running Lisp (via unblock_input) when more than + one thread could be running. (Bug#25247) + * src/process.c (wait_reading_process_output) [HAVE_GLIB]: Call + xg_select directly instead of through thread_select. + * src/xgselect.h (xg_select): Last 2 arguments are no longer + 'const', for consistency with thread_select. + +2016-12-30 Arash Esbati + + Add entry for biblatex + + * lisp/textmodes/reftex-vars.el (reftex-cite-format-builtin): Add + entry for biblatex macros. + +2016-12-30 Alan Mackenzie + + Backport: Remove an ambiguity from defvar's doc string. Fixes bug #25292. + + The ambiguity was whether INITVALUE is evaluated when it's not going to be + used to set SYMBOL's value. + + * src/eval.c (defvar): Rewrite a paragraph of the doc string. + + (cherry picked from commit 8295e97f18490a535d1188a3daf0b0fd1bf4fa0d) + +2016-12-30 Tino Calancha + + ffap-string-at-point: Limit max length of active region + + Prevents that 'ffap-guesser' waste time checking large strings + which are likely not valid candidates (Bug#25243). + * lisp/ffap.el (ffap-max-region-length): New variable. + (ffap-string-at-point): Use it. + * test/lisp/ffap-tests.el: New test suite. + (ffap-tests-25243): Add test for this bug. + +2016-12-30 Thien-Thi Nguyen + + last-chance: Also ignore NEWS files + typo fixes + + * admin/last-chance.el: Fix typo in copyright notice. + (last-chance-uninteresting-regexps): Add entry to match NEWS files. + (last-chance-cleanup): Fix typo in docstring. + +2016-12-29 Mike Kupfer + + * mh-e.el (mh-fetch-x-image-url): Fix a docstring typo. + +2016-12-29 Alan Mackenzie + + Remove an ambiguity from defvar's doc string. Fixes bug #25292. + + The ambiguity was whether INITVALUE is evaluated when it's not going to be + used to set SYMBOL's value. + + * src/eval.c (defvar): Rewrite a paragraph of the doc string. + +2016-12-29 Michael Albinus + + Improve filenotify-tests.el + + * src/inotify.c (Finotify_valid_p): + * src/kqueue.c (Fkqueue_valid_p): + * src/w32notify.c (Fw32notify_valid_p): + * src/gfilenotify.c (Fgfile_valid_p): Fix typo in docstring. + (Fgfile_monitor_name): New defun. + (syms_of_gfilenotify): Declare Sgfile_monitor_name. + + * test/lisp/filenotify-tests.el (file-notify--test-read-event): + New defun, derived from `file-notify--test-read-event-timeout'. + Replace all calls of `read-event' by this. + (file-notify--test-timeout): Fix docstring. + (file-notify--test-monitor): New defun. + (file-notify--deftest-remote): Do not bind + `file-notify--test-read-event-timeout' anymore. + (file-notify-test00-availability): Print also monitor, if existent. + (file-notify--test-with-events): Add an additional + `file-notify--test-read-event' call, in order to get it work + after `file-notify-add-watch'. Remove special timeout for cygwin. + (file-notify-test02-events): Make a better check for cygwin. + (file-notify-test06-many-events): Improve event list for cygwin. + (file-notify-test08-watched-file-in-watched-dir): Add cygwin case. + +2016-12-29 Alan Mackenzie + + Partially correct fontification of "(b*3)", and the like, in C++ Mode + + This problem is caused by the fundamental ambiguity in C++ between + argument declarations and initialisation clauses. + + * lisp/progmodes/cc-fonts.el (c-font-lock-declarations): If we have an open + paren preceded by an arithmetic operator, we give this the context nil, not + 'arglist. + + * lisp/progmodes/cc-langs.el (c-arithmetic-operators, c-arithmetic-op-regexp): + New lang consts and vars. + +2016-12-29 Alan Mackenzie + + Initialize edebug-offset-indices to a cons, not nil. Fixes bug #16184. + + This is because there are times when this variable is changed by setcar before + an atom is pushed onto it by debug-enter. This happens, for example, whilst + instrumenting c-font-lock-declarations in .../lisp/progmodes/cc-fonts.el. + + * lisp/emacs-lisp/edebug.el (edebug-offset-indices): initialize to '(0). + +2016-12-28 Stefan Monnier + + * lisp/emacs-lisp/inline.el: Fix apply-conversion (bug#25280) + + (inline--dont-quote): Quote the function with #' when passing it to `apply'. + Cherry picked from commit e6161f648903d821865b9610b3b6aa0f82a5dcb7. + +2016-12-27 Michael Albinus + + Release Tramp 2.3.1 + + * doc/misc/trampver.texi: + * lisp/net/trampver.el: Change version to "2.3.1". + + * lisp/net/tramp.el (tramp-eshell-directory-change): Add it to + `eshell-mode-hook' but `eshell-first-time-mode-hook'. + + * lisp/net/tramp-compat.el (tramp-compat-file-name-quoted-p) + (tramp-compat-file-name-quote) + (tramp-compat-file-name-unquote): Embed them in `eval-and-compile'. + +2016-12-27 Paul Eggert + + Simplify prog1 implementation + + Inspired by a suggestion from Chris Gregory in: + http://lists.gnu.org/archive/html/emacs-devel/2016-12/msg00965.html + On my platform, this generates exactly the same machine insns. + * src/eval.c (prog_ignore): Rename from unwind_body, since + it’s more general than that. All callers changed. + (Fprog1): Simplify by using prog_ignore. + (Fwhile): Clarify by using prog_ignore. + +2016-12-27 Stefan Monnier + + * lisp/emacs-lisp/inline.el: Fix apply-conversion (bug#25280) + + (inline--dont-quote): Quote the function with #' when passing it to `apply'. + +2016-12-27 Mark Oteiza + + Remove a use of lexical-let + + * lisp/gnus/message.el (message-completion-function): Just use let, + since the file now uses lexical-binding. + +2016-12-27 Ken Brown + + Improve filenotify-tests.el on Cygwin (Bug #21804) + + * test/lisp/filenotify-tests.el [CYGWIN] + (file-notify--test-read-event-timeout): Increase. + (file-notify--test-with-events): Add delay before executing body. + (file-notify-test02-events, file-notify-test04-file-validity): + Adjust expected results. + +2016-12-27 Eli Zaretskii + + Fix expand-file-name on DOS_NT systems when /: escaping is used + + * src/fileio.c (Fexpand_file_name) [DOS_NT]: Don't expand "~" in + file names escaped by "/:". Don't recursively expand + default-directory escaped with "/:" which is not followed by a + drive spec. (Bug#25183) + +2016-12-27 Bake Timmons <65pandas@gmail.com> + + Fix `mail-sources' value of `(group)' in Gnus manual (bug#25275) + + * doc/misc/gnus.texi (Mail Source Specifiers): + Replace wrong `mail-sources' value of `(group)' in Gnus manual with + the correct `((group))' value. (bug#25275) (tiny change) + +2016-12-27 Bake Timmons <65pandas@gmail.com> + + Fix bug in customizing `mail-sources' variable (bug#25274) + + * lisp/gnus/mail-source.el (mail-sources): Use list instead of cons + for lone argument. (bug#25274) (tiny change) + +2016-12-26 Philipp Stephani + + Checkdoc: Don't require a space before an arg list + + See Bug#24998. + + * lisp/emacs-lisp/checkdoc.el (checkdoc-defun-regexp): Don't require a + space before a argument list. + * test/lisp/emacs-lisp/checkdoc-tests.el (checkdoc-tests--bug-24998): + Add unit test. + +2016-12-26 Eli Zaretskii + + Document 'default-toplevel-value' and 'set-default-toplevel-value' + + * doc/lispref/variables.texi (Default Value): Document + 'default-toplevel-value' and 'set-default-toplevel-value'. + +2016-12-25 Michihito Shigemura (tiny change) + + Add zshrc and zshenv detection to sh-mode (bug#25217) + + * lisp/progmodes/sh-script.el (sh-mode): Add zsh string-match + +2016-12-25 Paul Eggert + + Fix typo in lisp.h reordering patch + + * src/lisp.h (XUNTAG) [!USE_LSB_TAG]: Remove duplicate defn. + Reported by Eli Zaretskii (Bug#25128#19). + +2016-12-25 Paul Eggert + + regex.h now includes sys/types.h + + * src/dired.c, src/emacs.c, src/search.c, src/syntax.c, src/thread.h: + Do not include sys/types.h; no longer needed. + * src/regex.h: Include , as that's what Gnulib and + glibc regex.h does, and POSIX has blessed this since 2008. + +2016-12-25 Paul Eggert + + Reorder lisp.h to declare types before using them + + This puts basic functions for types to be after the corresponding + type definitions. This is a more-common programming style in C, + and will make it easier to port Emacs to gcc + -fcheck-pointer-bounds, since the functions now have access to the + corresponding types' sizes. This patch does not change the code; + it just moves declarations and definitions and removes + no-longer-needed forward declarations (Bug#25128). + * src/buffer.c, src/data.c, src/image.c: + Include process.h, for PROCESSP. + * src/buffer.h (BUFFERP, CHECK_BUFFER, XBUFFER): + * src/process.h (PROCESSP, CHECK_PROCESS, XPROCESS): + * src/termhooks.h (TERMINALP, XTERMINAL): + * src/window.h (WINDOWP, CHECK_WINDOW, XWINDOW): + * src/thread.h (THREADP, CHECK_THREAD, XTHREAD, MUTEXP, CHECK_MUTEX) + (XMUTEX, CONDVARP, CHECK_CONDVAR, XCONDVAR): + Move here from lisp.h. + * src/intervals.h: Include buffer.h, for BUFFERP. + Include lisp.h, for Lisp_Object. + * src/lisp.h: Reorder declarations and definitions as described + above. Move thread includes to be later, so that they can use the + reordered definitions. Move some symbols to other headers (noted + elsewhere). Remove forward decls that are no longer needed. + * src/thread.h: Include systhread.h here, not in lisp.h, + since lisp.h itself does not need systhread.h. + +2016-12-25 Leo Liu + + Don't modify minibuffer variables globally + + * lisp/files.el (cd): Use setq-local instead. (Bug#25260) + +2016-12-25 Dima Kogan + + diff-mode auto-refines only after a successful motion + + Prior to this patch (if enabled) auto-refinement would kick in after all + hunk navigation commands, even if the motion failed. This would result + in a situation where the hunk navigation would signal an error and beep, + but yet still accomplish potentially useful work, by auto-refining. + This patch moves the auto-refinement code to only run when a motion was + successful + + * lisp/vc/diff-mode.el (diff--internal-hunk-next, + diff--internal-hunk-prev): Removed auto-refinement-triggering code + * lisp/vc/diff-mode.el (diff--wrap-navigation): Added + auto-refinement-triggering code + +2016-12-25 Dima Kogan + + diff-mode is able to better handle file headers + + This fixes a regression introduced in + + http://git.savannah.gnu.org/gitweb/?p=emacs.git;a=commit;h=2c8a7e50d24daf19ea7d86f1cfeaa98a41c56085 + + This bug was filed in + + https://debbugs.gnu.org/cgi/bugreport.cgi?bug=25105 + + Patches generated from a VCS such as git contain a patch message at the + start, and diff-mode is now once-again able to properly able to ignore + this message when issuing navigation commands around the message. + + * lisp/vc/diff-mode.el (diff-beginning-of-file-and-junk): More + thoroughly ignore the header when looking for a beginning of file + diffs. + +2016-12-25 Paul Eggert + + Use libpng-config --ldflags, not --libs + + Problem reported by James K. Lowden (Bug#25268). + * configure.ac (LIBPNG): Pass --ldflags, not --libs, to libpng-config. + +2016-12-24 Eli Zaretskii + + Revert to pre-25.1 behavior in ffap + + * lisp/ffap.el (ffap-lax-url): Change the default to t, to produce + the same behavior as in Emacs 24.x. (Bug#25264) + Explain the trade-offs of customizing this in the doc string. + +2016-12-24 Noam Postavsky + + * lisp/ffap.el: Fix obsolete comment referencing ffap-bug. + +2016-12-24 Noam Postavsky + + Remove redundant `save-match-data' in whitespace.el + + * lisp/whitespace.el (whitespace-cleanup, whitespace-cleanup-region): + (whitespace-report-region): Remove redundant `save-match-data' calls. + +2016-12-24 Noam Postavsky + + Fix whitespace eob cleanup + + * lisp/whitespace.el (whitespace-empty-at-eob-regexp): Match any number + of empty lines at end of buffer. + * test/lisp/whitespace-tests.el (whitespace-cleanup-eob): New test. + (whitespace-tests--cleanup-string): New helper function for tests. + +2016-12-24 Hong Xu + + Fix timezone detection of parse-iso8601-time-string + + * parse-time.el (parse-iso8601-time-string): Fix timezone + parsing. Add a doc string. (Bug#25086) + * editfns.c (Fdecode-time): Doc fix. + * emacs-mime.texi (time-date): Add an example for + parse-iso8601-time-string. + * parse-time-tests.el (parse-time-tests): Add tests for + parse-iso8601-time-string. + +2016-12-24 Paul Eggert + + Simplify exec_byte_code via moving decls etc. + + * src/bytecode.c (exec_byte_code): Simplify, mostly by moving + initializers into decls, and by omitting some unnecessary changes + to ‘top’. + +2016-12-24 Paul Eggert + + Remove interpreter’s byte stack + + This improves performance overall on my benchmark on x86-64, + since the interpreted program-counter resides in a machine + register rather than in RAM. + * etc/DEBUG, src/.gdbinit: Remove xbytecode GDB command, as there + is no longer a byte stack to decode. + * src/bytecode.c (struct byte_stack, byte_stack_list) + (relocate_byte_stack): Remove. All uses removed. + (FETCH): Simplify now that pc is now local (typically, in a + register) and no longer needs to be relocated. + (CHECK_RANGE): Remove. All uses now done inline, in a different way. + (BYTE_CODE_QUIT): Remove; now done by op_relative_branch. + (exec_byte_code): Allocate a copy of the function’s bytecode, + so that there is no problem if GC moves it. + * src/lisp.h (struct handler): Remove byte_stack member. + All uses removed. + * src/thread.c (unmark_threads): Remove. All uses removed. + * src/thread.h (struct thread_state): Remove m_byte_stack_list member. + All uses removed. m_stack_bottom is now the first non-Lisp field. + +2016-12-24 Paul Eggert + + BYTE_CODE_SAFE typo fix + + * src/bytecode.c (FETCH): Depend on the value of BYTE_CODE_SAFE, + not on whether it is defined. + +2016-12-24 Paul Eggert + + BYTE_CODE_SAFE cleanups + + * src/bytecode.c (BYTE_MAINTAIN_TOP): Remove; no longer needed. + (struct byte_stack) [BYTE_MAINTAIN_TOP]: + Remove unused members ‘top’ and ‘bottom’. + (exec_byte_code): Nest inside { } to avoid GCC warning about + jumping over declaration when compiled with -DBYTE_CODE_SAFE. + +2016-12-24 Thien-Thi Nguyen + + last-chance: new utility lib for dangling deterrence + + * admin/last-chance.el: New file. + +2016-12-24 Paul Eggert + + Use max_align_t instead of void * + + * src/thread.c (run_thread): Don’t assume void * is aligned enough. + +2016-12-23 Eli Zaretskii + + Attempt to fix 64-bit AIX build + + * src/unexaix.c (make_hdr, copy_text_and_data, write_segment): Fix + type-casts that assumed 32-bit pointers. (Bug#25141) + +2016-12-23 Philipp Stephani + + Clarify usage of 'ediff-cleanup-hook' (Bug#24675) + + * doc/misc/ediff.texi (Hooks): Clarify usage of 'ediff-cleanup-hook' + +2016-12-23 Philipp Stephani + + Document that variable binding order is unspecified + + * doc/lispref/variables.texi (Local Variables): + * cl.texi (Modify Macros): Document that binding order in 'let' and + 'cl-letf' is unspecified. + +2016-12-23 Eli Zaretskii + + Prevent infloops in redisplay due to truncate-lines and overlays + + * src/xdisp.c (hscroll_window_tree): Avoid inflooping in + redisplay_window when a screen line ends in an overlay string with + a newline. (Bug#25246) + +2016-12-23 Philipp Stephani + + Treat incomplete integer literals as errors + + See Bug#25120. + + * src/lread.c (read_integer): Treat incomplete integer literals as errors. + * test/src/lread-tests.el (lread-empty-int-literal): New unit test for + incomplete integer literals. + +2016-12-23 Eli Zaretskii + + * lisp/desktop.el (desktop-buffers-not-to-save): Doc fix. + +2016-12-23 Stefan Monnier + + Fix M-x hints on Mac port + + * lisp/simple.el (execute-extended-command--shorter): Call + input-pending-p to trigger input processing on some systems, such + as Mac port. (Bug#23002) + +2016-12-23 Michael Albinus + + * test/lisp/net/tramp-tests.el (tramp--test-check-files): Make it robust. + +2016-12-23 Eli Zaretskii + + Avoid aborts due to unaligned byte stack of threads + + * src/thread.c (run_thread): Make sure the pointers to thread byte + stack are properly aligned. (Bug#25247) + +2016-12-22 Paul Eggert + + Pacify --enable-gcc-warnings + + * src/charset.c (load_charset_map): + * src/coding.c (decode_coding_object): + * src/frame.c (make_frame): + * src/window.c (Frecenter): + Mark locals with UNINIT to silence false alarms from + -Wmaybe-uninitialized. + * src/lisp.h (SYMBOL_ALIAS, SYMBOL_BLV, SYMBOL_FWD) + (SET_SYMBOL_ALIAS, SET_SYMBOL_BLV, SET_SYMBOL_FWD): + Check and assume that values are nonnull. This pacifies + -Wmaybe-uninitialized in Fmake_variable_buffer_local and + Fmake_local_variable. + +2016-12-22 Eli Zaretskii + + Fix last change with thread marking under GC_CHECK_MARKED_OBJECTS + + * src/thread.c (primary_thread_p): New function. + * src/alloc.c (mark_object): Use 'primary_thread_p' to bypass tests + meant for thread objects allocated dynamically. + * src/thread.h (primary_thread_p): Add prototype. + +2016-12-22 Martin Rudalics + + Work around reporting a dpi change in apply_xft_settings + + * src/xsettings.c (apply_xft_settings): Don't report a change + when dpi settings do not differ substantially. + +2016-12-22 Noam Postavsky + + Use completion-at-point in verilog-mode + + There were some functions in verilog-mode that implemented in-buffer + completion, but this needlessly duplicates completion-at-point + functionality, and the popup window management had problems + (see Bug #23842). We need to keep them for backwards compatibility with + older emacs versions, but use completion-at-point if available. + + * lisp/progmodes/verilog-mode.el (verilog-toggle-completions): Mark as + obsolete if completion-cycle-threshold is available. + (verilog-mode-map, verilog-menu): Bind completion-at-point and + completion-help-at-point in preference to verilog-complete-word and + verilog-show-completions, respectively. + (verilog-mode): Add verilog-completion-at-point to + completion-at-point-functions. + (verilog-completion-at-point): New function. + (verilog-show-completions, verilog-complete-word): Use it to avoid code + duplication. + +2016-12-21 Reuben Thomas + + Keep default CASECHARS/NOT-CASECHARS for ispell built-in dictionaries + + * lisp/textmodes/ispell.el (ispell-set-spellchecker-params): Do not + override CASECHARS and NOT-CASECHARS. The ispell dictionaries + retain their hardwired values, and all other dictionaries are given + sensible defaults. + +2016-12-21 Stefan Monnier + + * tex-mode.el (tex-compile-commands): Add luatex and xetex commands + +2016-12-21 Eli Zaretskii + + Fix aborts in GC under GC_CHECK_MARKED_OBJECTS + + * src/alloc.c (mark_object) [GC_CHECK_MARKED_OBJECTS]: Don't abort + for thread objects. They are marked via the all_threads list, and + therefore don't need to be inserted into the red-black tree, so + mem_find will never find them. Reported by Daniel Colascione + in + http://lists.gnu.org/archive/html/emacs-devel/2016-12/msg00817.html. + +2016-12-21 Stefan Monnier + + * src/data.c (Fmake_variable_frame_local): Remove + + * src/lisp.h (struct Lisp_Buffer_Local_Value): Remove `frame_local'. + + * src/data.c (swap_in_symval_forwarding, set_internal) + (set_symbol_trapped_write, make_blv, Fmake_variable_buffer_local) + (Fmake_local_variable, Fkill_local_variable, Flocal_variable_p): + Don't pay attention to ->frame_local any more. + (syms_of_data): Remove Qtrapping_frame_local and don't defsubr + Smake_variable_frame_local. + + * etc/NEWS (Incompatible Lisp Changes in Emacs 26.1): Announce removal + of make-variable-frame-local. + + * lisp/help-fns.el (describe-variable): Don't handle the now impossible + frame-local case. + + * lisp/subr.el (make-variable-frame-local): Remove obsolescence data. + + * src/frame.c (store_frame_param): + * src/eval.c (specbind): Don't pay attention to ->frame_local any more. + + * src/widget.c (first_frame_p): Remove, unused. + +2016-12-21 Paul Eggert + + Port dumping better to WSL + + Problem reported by Angelo Graziosi in: + http://lists.gnu.org/archive/html/emacs-devel/2016-12/msg00822.html + * src/sysdep.c (disable_address_randomization): + Detect buggy platforms where 'personality' always returns 0. + +2016-12-21 Michael Albinus + + Remove gateway methods in Tramp + + * doc/misc/tramp.texi (Top, Configuration): Remove section + `Gateway methods', insert section `Firewalls' in menu. + (History): Gateways are removed now. + (Gateway methods): Remove section. + (Multi-hops, Traces and Profiles): Don't reference to gateways anymore. + (Firewalls): New section. + + * etc/NEWS: Gateway methods in Tramp have been removed. + + * lisp/net/tramp.el (tramp-methods): Adapt docstring. + (tramp-file-name-port, tramp-accept-process-output): Simplify. + + * lisp/net/tramp-gw.el: Remove. + + * lisp/net/tramp-sh.el (tramp-gw-tunnel-method) + (tramp-gw-socks-method): Remove declarations. + (tramp-methods) : + Remove `tramp-gw-args' and `tramp-default-port'. (Bug#18967) + (tramp-do-copy-or-rename-file-out-of-band) + (tramp-compute-multi-hops, tramp-maybe-open-connection): + Remove gateway support. + + * test/lisp/net/tramp-tests.el (tramp-test03-file-name-defaults): + Remove gateway tests. + +2016-12-20 Alan Mackenzie + + Amend the version number of CC Mode 5.33 -> 5.32.99. Don't merge to trunk. + + lisp/progmodes/cc-defs.el: Amend the version number. + etc/NEWS: Add an item explaining the change. + +2016-12-20 Tino Calancha + + files-test-read-file-in-: Delete temporary dir on exit + + * test/lisp/files-tests.el (files-test-read-file-in-~): + Create subdir inside dir. + +2016-12-20 Christopher Genovese + + ibuffer: New filters and commands + + Add several new filters and improve documentation. + See discussion on: + https://lists.gnu.org/archive/html/emacs-devel/2016-11/msg00399.html + * lisp/ibuf-ext.el: Add paragraph to file commentary. + (ibuffer-saved-filters, ibuffer-filtering-qualifiers) + (ibuffer-filter-groups): Update doc string. + (ibuffer-unary-operand): Add new function that transparently + handles 'not' formats for compound filters. + (ibuffer-included-in-filter-p): Handle 'not' fully; update doc string. + (ibuffer-included-in-filter-p-1): Handle 'and' compound filters. + (ibuffer-decompose-filter): Handle 'and' as well, + and handle 'not' consistently with other uses. + (ibuffer-and-filter): New defun analogous to 'ibuffer-or-filter'. + (ibuffer--or-and-filter): New defun. + (ibuffer-or-filter, ibuffer-and-filter): Use it. + (ibuffer-format-qualifier): Handle 'and' filters as well. + (ibuffer-filter-by-basename, ibuffer-filter-by-file-extension) + (ibuffer-filter-by-directory, ibuffer-filter-by-starred-name) + (ibuffer-filter-by-modified, ibuffer-filter-by-visiting-file): + Add new pre-defined filters. + (ibuffer-filter-chosen-by-completion): Add new interactive command + for easily choosing a filter from the descriptions. + * lisp/ibuffer.el (ibuffer-mode-map): + Bind ibuffer-filter-by-basename, ibuffer-filter-by-file-extension, + ibuffer-filter-by-starred-name, ibuffer-filter-by-modified, + ibuffer-filter-by-visiting-file to '/b', '/.', '/*', '/i', '/v' + respectively; bind 'ibuffer-or-filter', 'ibuffer-and-filter', + 'ibuffer-pop-filter' ,'ibuffer-pop-filter-group' and + 'ibuffer-filter-disable' to '/|', '/&', '/', '/S-' + and '/ DEL' respectively. + * test/lisp/ibuffer-tests.el (ibuffer-autoload): Add appropriate + skip specification. + Add menu entries for the new filters. + (ibuffer-filter-inclusion-1, ibuffer-filter-inclusion-2 + ibuffer-filter-inclusion-3, ibuffer-filter-inclusion-4 + ibuffer-filter-inclusion-5, ibuffer-filter-inclusion-6 + ibuffer-filter-inclusion-7, ibuffer-filter-inclusion-8 + ibuffer-decompose-filter, ibuffer-and-filter + ibuffer-or-filter): Add new tests; they are skipped unless + ibuf-ext is loaded. + +2016-12-20 Mark Oteiza + + Update NEWS + + * etc/NEWS (Image-Dired): New section. + +2016-12-20 Mark Oteiza + + Recognize graphicsmagick in image-dired + + * lisp/image-dired.el (image-dired-cmd-create-thumbnail-program): + (image-dired-cmd-create-thumbnail-options): + (image-dired-cmd-create-temp-image-program): + (image-dired-cmd-create-temp-image-options): + (image-dired-cmd-create-standard-thumbnail-options): + (image-dired-cmd-rotate-thumbnail-program): + (image-dired-cmd-rotate-thumbnail-options): Account for existence of + gm(1) executable. + +2016-12-20 Noam Postavsky + + Improve skeleton docstrings + + * lisp/skeleton.el (skeleton-end-newline): Remove mention of + `skeleton-end-hook', its default code was moved into `skeleton-insert'. + (skeleton-insert): Mention `skeleton-end-newline' and move reference to + `skeleton-end-hook' above the explanation of skeleton syntax. + +2016-12-20 Mark Oteiza + + Implement asynchronous thumbnail generation in image-dired + + Additionally, all FOO-options defcustoms that were in fact shell command + strings have been converted to argument lists. Another method for + shrinking PNG thumbs with optipng(1) has been added. + * lisp/image-dired.el: Remove TODO item in commentary. + (image-dired-cmd-create-thumbnail-options): + (image-dired-cmd-create-temp-image-options): + (image-dired-cmd-rotate-thumbnail-options): + (image-dired-cmd-rotate-original-options): + (image-dired-cmd-write-exif-data-options): + (image-dired-cmd-read-exif-data-options): Convert to argument lists. + (image-dired-cmd-pngnq-program, image-dired-cmd-pngcrush-program): + Change string type to file. + (image-dired-cmd-create-standard-thumbnail-command): Remove. + (image-dired-cmd-pngnq-options): + (image-dired-cmd-create-standard-thumbnail-options): + (image-dired-cmd-optipng-program, image-dired-cmd-optipng-options): + New defcustoms. + (image-dired-queue, image-dired-queue-active-jobs): + (image-dired-queue-active-limit): New variables. + (image-dired-pngnq-thumb, image-dired-pngcrush-thumb): + (image-dired-optipng-thumb): New functions. + (image-dired-create-thumb-1): Renamed from image-dired-create-thumb. + Use start-process instead of call-process. Set file modes. Trigger + PNG file optimization in process sentinel. + (image-dired-thumb-queue-run, image-dired-create-thumb): New functions. + (image-dired-display-thumbs): + (image-dired-create-thumbs): Don't expect call-process return value. + (image-dired-display-image, image-dired-rotate-thumbnail): Use + start-process instead of call-process. + (image-dired-rotate-original, image-dired-set-exif-data): + (image-dired-get-exif-data): Adapt to arguments being an arg list. + +2016-12-19 Andreas Schwab + + Protect change of window's buffer in vertical-motion against unwinds (bug#25209) + + * indent.c (restore_window_buffer): New function. + (Fvertical_motion): Use it to restore window's buffer. + +2016-12-19 Glenn Morris + + Improve default load-path for uninstalled CANNOT_DUMP builds + + * src/lread.c (load_path_default) [CANNOT_DUMP]: + Use build load-path if we seem to be running uninstalled. (Bug#24974) + I think this became an issue several years ago when we stopped + using EMACSLOADPATH in the Makefiles; however this change should + improve the CANNOT_DUMP uninstalled case in general. + +2016-12-19 Eli Zaretskii + + Improve documentation of 'w32-scroll-lock-modifier' + + * doc/emacs/msdos.texi (Windows Keyboard): Document how to set up + w32-scroll-lock-modifier so that Scroll Lock toggles the LED. + + * src/w32fns.c (syms_of_w32fns) : Doc + fix. (Bug#25204) + +2016-12-19 Eli Zaretskii + + Document 'describe-fontset' + + * doc/emacs/mule.texi (Fontsets): Document 'describe-fontset'. + (Bug#25216) + +2016-12-19 Eli Zaretskii + + Document 'describe-fontset' + + * doc/emacs/mule.texi (Fontsets): Document 'describe-fontset'. + (Bug#25216) + +2016-12-19 Eli Zaretskii + + Fix crashes upon C-g on Posix TTY frames + + * src/thread.h (struct thread_state): New member not_holding_lock. + (maybe_reacquire_global_lock): Add prototype. + * src/thread.c: Include syssignal.h. + (maybe_reacquire_global_lock): New function. + (really_call_select): Set the not_holding_lock member of the + thread state before releasing the lock, and rest it after + re-acquiring the lock when the select function returns. Block + SIGINT while doing this to make sure we are not interrupted on TTY + frames. + * src/sysdep.c (block_interrupt_signal, restore_signal_mask): New + functions. + * src/syssignal.h (block_interrupt_signal, restore_signal_mask): + Add prototypes. + * src/keyboard.c (read_char) [THREADS_ENABLED]: Call + maybe_reacquire_global_lock. (Bug#25178) + +2016-12-19 Sam Steingold + + avoid Eager macro-expansion failure: (void-function string-to-list) + + * loadup.el [ns]: "ucs-normalize" uses `string-to-list' which is defined + in "mule-util", so we have to load "mule-util" before "ucs-normalize", + otherwise I get "Eager macro-expansion failure" on "make bootstrap" + +2016-12-19 Michael Albinus + + Fix Bug#24980 + + * lisp/ido.el (ido-add-virtual-buffers-to-list): + Suppress Tramp invocation. (Bug#24980) + +2016-12-18 Philipp Stephani + + Add a new compile error regexp for Clang includes + + Clang uses a slight variation of GCC's include format, causing includes + to be treated as warnings instead of informational messages. Use a new + regular expression instead. + + * lisp/progmodes/compile.el + (compilation-error-regexp-alist-alist): New element + `clang-include' for Clang-style "included from" lines. + * test/lisp/progmodes/compile-tests.el + (compile-tests--test-regexps-data): Add unit test. + +2016-12-18 Alan Third + + Reinstate ispell character offset (bug#25219) + + * lisp/textmodes/ispell.el (ispell-process-line): insert -1 where + ispell-offset used to be. + +2016-12-18 Mark Oteiza + + Bind new image-mode scroll commands in image-dired + + * lisp/image-dired.el (image-dired-display-image-mode-map): Add bindings + to new image-mode commands. + +2016-12-18 Noam Postavsky + + Fix rx-any with range with ?\] and ?- + + * lisp/emacs-lisp/rx.el: Make sure not to produce a circular + list (Bug#25123). + * test/lisp/emacs-lisp/rx-tests.el (rx-char-any): New test. + +2016-12-18 Mark Oteiza + + Use floor of mtime instead of rounding for thumb property + + This seems to be the correct thing to do, at least more in line with + what at least one other implementation does. Anything using + gnome-desktop [0] effectively does the same, as + gnome_desktop_thumbnail_is_valid applies atol(3) to mtime for + comparison and time_t on GNU/Linux is a signed int. + [0] https://git.gnome.org/browse/gnome-desktop/ + * lisp/image-dired.el (image-dired-create-thumb): Use floor here. + +2016-12-17 Reuben Thomas + + Fix spelling mistake in private defun name (Bug#25218) + + lisp/textmodes/flyspell.el (flyspell-ajust-cursor-point): Rename to + `flyspell-adjust-cursor-point'. + +2016-12-17 Reuben Thomas + + Remove XEmacs support from flyspell.el (Bug#25218) + + lisp/textmodes/flyspell.el (flyspell-prog-mode, flyspell-mode-on): + (flyspell-word, flyspell-delete-region-overlays): + (flyspell-correct-word-before-point): Remove XEmacs support. + (flyspell-xemacs-popup): Remove XEmacs-specific defun. + +2016-12-17 Michael Albinus + + More tests for Tramp + + * lisp/net/tramp.el (tramp-drop-volume-letter): Handle quoted + file names. + + * lisp/net/tramp-sh.el (tramp-make-copy-program-file-name): Quote file + name properly. + + * test/lisp/net/tramp-tests.el (tramp-test05-expand-file-name): + Mark quoted file name as absolute. (Bug#25183) + (tramp--test-windows-nt-and-batch) + (tramp--test-windows-nt-and-pscp-psftp-p): New defuns. + (tramp--test-windows-nt-or-smb-p): Rename from + `tramp--test-smb-windows-nt-p'. Adapt callees. + (tramp--test-check-files): Improve checks for environment variables. + (tramp-test33-special-characters) + (tramp-test33-special-characters-with-stat) + (tramp-test33-special-characters-with-perl) + (tramp-test33-special-characters-with-ls, tramp-test34-utf8) + (tramp-test34-utf8-with-stat, tramp-test34-utf8-with-perl) + (tramp-test34-utf8-with-ls): Add more checks for skip. + +2016-12-17 Eli Zaretskii + + Fix comments + + * src/thread.h (struct thread_state): Fix comments. + * src/process.c (wait_reading_process_output): Fix a typo in + commentary. + +2016-12-17 Eli Zaretskii + + Repair desktop restoration on text terminals + + * lisp/desktop.el (desktop-restoring-frameset-p): Test for the GUI + frame here, instead of in desktop-restoring-frameset. That's + because desktop-read wants to know whether frameset will actually + be restored, and has fallback procedures up its sleeve when it + won't be; these fallbacks need to be invoked when the frameset is + not going to be restored. (Bug#24298) + +2016-12-17 Eli Zaretskii + + Fix crashes on MS-Windows during dumping + + * src/unexw32.c (get_section_info): Make extra_bss_size be the + maximum of extra_bss_size and extra_bss_size_static. This avoids + computing the size of the output file smaller than it actually + needs to be, which then causes copy_executable_and_dump_data to + write beyond the requested size of the file mapping, thus relying + on the OS roundup to page boundary to save us from ourselves. See + http://lists.gnu.org/archive/html/emacs-devel/2016-12/msg00642.html + for the details. + + * lib/stdio-impl.h: Revert the workaround fix of not including + errno.h for MinGW. + +2016-12-17 Dmitry Gutov + + Ignore forward-sexp-function in js-mode indentation code + + * lisp/progmodes/js.el (js--multi-line-declaration-indentation) + (js--maybe-goto-declaration-keyword-end): + Bind forward-sexp-function to nil (bug#25215). + +2016-12-16 Mark Oteiza + + Be more selective clearing the image cache + + * lisp/image-dired.el (image-dired-create-thumbs): + (image-dired-rotate-thumbnail, image-dired-refresh-thumb): Only clear + the current thumbnail file from the image cache. + +2016-12-16 Eli Zaretskii + + Unbreak the MinGW build + + * lib/stdio-impl.h [__MINGW32__]: Don't include errno.h. Without + this, temacs crashes while dumping. + +2016-12-16 Nicolas Petton + + Make seq-into return the sequence when no conversion needed + + * lisp/emacs-lisp/seq.el (seq-into): Do not convert the sequence when + no conversion is needed. + * test/lisp/emacs-lisp/seq-tests.el (test-seq-into-and-identity): Add + a regression test checking for identity. + +2016-12-16 Eli Zaretskii + + Improve documentation of 'define-coding-system' + + * lisp/international/mule.el (define-coding-system): Warn against + possible infinite recursion in pre-write-conversion and + post-read-conversion functions. (Bug#25203) + +2016-12-16 Mark Oteiza + + New commands image-scroll-left and image-scroll-right + + * etc/NEWS: Mention them. + * lisp/image-mode.el (image-scroll-left, image-scroll-right): New + functions. + +2016-12-16 Mark Oteiza + + More image-dired refactoring + + * lisp/image-dired.el (image-dired-thumbnail-mode): + (image-dired-display-image-mode): Add :group 'image-dired so + customize-mode works. + (image-dired-display-image): Rearrange. + (image-dired-copy-with-exif-file-name): This map is for side effect. + (image-dired-dired-edit-comment-and-tags): Just use #'identity. + +2016-12-16 Mark Oteiza + + * lisp/image-dired.el: Turn on lexical-binding. + +2016-12-16 Mark Oteiza + + Teach image-dired to also generate large thumbs + + * lisp/image-dired.el (image-dired-thumbnail-storage): Add + standard-large option. + (image-dired-thumb-size): Add condition for standard-large storage. + (image-dired-insert-thumbnail): Check for new option. Change + thumbnail path conditionally. + (image-dired-thumb-size): New function. + (image-dired-create-thumb, image-dired-line-up-dynamic): Use it. + +2016-12-15 Paul Eggert + + Merge from gnulib + + This incorporates: + 2016-12-14 xalloc-oversized: check for PTRDIFF_MAX too + 2016-12-12 fpending: port to native Windows with MSVC + * .gitignore: Do not ignore lib/stdio-impl.h. + * lib/fpending.c, lib/xalloc-oversized.h, m4/fpending.m4: + Copy from gnulib. + * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. + * lib/stdio-impl.h: + New file, copied from gnulib. + * nt/gnulib.mk (EXTRA_DIST): Add stdio-impl.h. + +2016-12-15 Paul Eggert + + * emacs-document.svg: Append newline. + +2016-12-15 Mark Oteiza + + Don't abuse princ and spam messages + + * lisp/image-dired.el (image-dired-format-properties-string): Nix princ. + (image-dired-display-thumb-properties): + (image-dired-dired-display-properties): Nix princ. Bind + message-log-max to nil. + +2016-12-15 Mark Oteiza + + Inherit things from special-mode and image-mode + + * lisp/image-dired.el: Require image-mode library. + (image-dired-thumbnail-mode-map): Remove superfluous binding. + (image-dired-display-image-mode-map): Remove superfluous binding. + Add movement remaps from image-mode-map. + (image-dired-thumbnail-mode): Derive from special-mode. + (image-dired-display-image-mode): Derive from special-mode. Call + image-mode-setup-winprops. + +2016-12-15 Mark Oteiza + + Fix some image-dired customization types + + * lisp/image-dired.el (image-dired): Add info link to defgroup. + (image-dired-dir, image-dired-gallery-dir): Set type to directory. + (image-dired-db-file, image-dired-temp-image-file): + (image-dired-cmd-create-thumbnail-program): + (image-dired-cmd-create-temp-image-program): + (image-dired-cmd-rotate-thumbnail-program): + (image-dired-cmd-rotate-original-program): + (image-dired-temp-rotate-image-file): + (image-dired-cmd-write-exif-data-program): + (image-dired-cmd-read-exif-data-program): Set type to file. + (image-dired-create-thumb, image-dired-line-up-dynamic): Check storage + type at runtime, since setting image-dired-thumb-size does not + automatically set image-dired-thumb-width and image-dired-thumb-height. + +2016-12-15 Eli Zaretskii + + Prevent crashes in xg_select due to concurrency + + * src/xgselect.c (xg_select): Don't call Glib functions that use + 'context' if we failed to acquire it. This means some other + thread owns the context, in which case both using the context and + calling block_input/unblock_input will step on that thread's toes + and eventually lead to crashes. (Bug#25172) + +2016-12-15 Nicolas Petton + + Fix circular list handling in seq-mapn + + * lisp/emacs-lisp/seq.el (seq-mapn): Do not copy list arguments. + * test/lisp/emacs-lisp/seq-tests.el (test-seq-mapn-circular-lists): + Add a regression test. + +2016-12-15 Michael Albinus + + Check in tramp-tests.el, that environment variables are set correctly + + * test/lisp/net/tramp-tests.el (tramp--test-check-files): + Check also, that environment variables are set correctly. + +2016-12-15 Dominique Quatravaux (tiny change) + + Protect environment variables with double quotes in Tramp + + * lisp/net/tramp-sh.el (tramp-open-connection-setup-interactive-shell): + Protect environment variables with double quotes. + +2016-12-15 Tino Calancha + + * lisp/ibuf-macs.el (define-ibuffer-filter): Wrap ,@body in a progn. + +2016-12-15 Mark Oteiza + + * lisp/image-dired.el (image-dired-create-thumb): Create parent directories. + +2016-12-14 Mark Oteiza + + * lisp/image-mode.el (image-mode-winprops-alist): Add docstring. + +2016-12-14 Mark Oteiza + + Recognize pngnq or pngnq-s9 + + * lisp/image-dired.el (image-dired-cmd-pngnq-program): Also consider + pngnq-s9 as a possible executable. + +2016-12-14 Mark Oteiza + + Update standard image-dired thumbnail location + + * lisp/image-dired.el (image-dired-thumb-name): Conform to the latest + standard: consider XDG_CACHE_HOME, falling back on ~/.cache. + +2016-12-14 Mark Oteiza + + Reset window scroll when displaying an image + + When viewing a large image full size and scrolling, for instance, to + the lower right corner, then selecting a much smaller image in the + thumbnail buffer, the window stays scrolled so the new image is out of + the window. One must scroll back to the "origin" to view the new + displayed image, or just kill the image-dired-display-image buffer and + try again. This fixes the issue. + * lisp/image-dired.el (image-dired-display-window-width): + (image-dired-display-window-height): Operate on a window as argument. + (image-dired-display-image): Bind (image-dired-display-window) and use + it. Set window vscroll and hscroll to zero when refreshing the + buffer's contents. + +2016-12-14 Mark Oteiza + + More image-dired polish + + * lisp/image-dired.el (image-dired-file-name-at-point): New function. + (image-dired-thumbnail-mode, image-dired-display-image-mode): Disable + undo list. Add image-dired-file-name-at-point to + file-name-at-point-functions to facilitate find-file and friends. + (image-dired-thumbnail-display-external): + (image-dired-dired-display-external): Use start-process instead, to + avoid needlessly blocking and using a shell. + +2016-12-13 Phillip Lord + + Replace ldefs-boot with a much smaller file + + * Makefile.in (bootstrap-build,generate-ldefs-boot): New targets. + (bootstrap): Depend on bootstrap-build. + * admin/ldefs-clean.el: New file. + * lisp/Makefile.in (compile-first): Depend on loaddefs.el + * lisp/ldefs-boot.el: Remove. + * lisp/ldefs-boot-auto.el: New file. + * lisp/ldefs-boot-manual.el: New file. + * lisp/loadup.el: Load ldefs-boot-manual.el. + * src/emacs.c (generating_ldefs_boot): New variable. + (main): Check whether we are generating ldefs. + * src/eval.c (autoload-do-load): Dump autoload forms to stderr when + requested. + * src/lisp.h (generating_ldefs_boot): New variable. + * admin/gitmerge.el, admin/make-tarball.txt, admin/notes/copyright, + lisp/Makefile.in, lisp/cus-dep.el, lisp/emacs-lisp/elint.el, + lisp/finder.el, lisp/loadup.el, msdos/mainmake.v2: Update reference to + ldefs-boot. + * admin/update_autogen: Alter mechanism for ldefs-boot generation. + +2016-12-13 Reuben Thomas + + Remove support for aspell < 0.60 (from 2004) + + lisp/textmodes/ispell.el (ispell-check-version): Require Aspell 0.60. + (ispell-aspell-dictionary-alist): Remove check that we have Aspell 0.60. + +2016-12-13 Reuben Thomas + + Minor docstring and comment fixes to ispell.el + + lisp/textmodes/ispell.el (ispell-aspell-dictionary-alist): Mention + ispell-aspell-dictionary-alist, not ispell-dictionary-alist. + (ispell-set-spellchecker-params): Change double-single quotes to + single single quotes in comment. + +2016-12-13 Reuben Thomas + + Remove unused variable + + * lisp/textmodes/ispell.el (current-ispell-directory): Remove. + +2016-12-13 Reuben Thomas + + Remove XEmacs-specific ispell-with-no-warnings + + * lisp/textmodes/ispell.el (ispell-with-no-warnings): Remove this + defmacro, needed only for XEmacs. + (ispell-command-loop, ispell-message): Use with-no-warnings directly. + +2016-12-13 Reuben Thomas + + Remove meaningless defconst ispell-version + + * lisp/textmodes/ispell.el (ispell-version): Since ispell.el is now + firmly part of Emacs, and the version hasn’t changed since 2003, and + isn’t used anywhere, remove it. 3rd-party code can better use the + Emacs version, or feature or function checks. + (ispell-check-version): No longer report ispell.el version. + +2016-12-13 Reuben Thomas + + Remove boundp test for always-bound symbol + + * lisp/textmodes/ispell.el (ispell-message): mail-yank-prefix is + defvar’d at the top of the file, so remove a test to see if it is + bound. + +2016-12-13 Reuben Thomas + + Remove support for ispell < 3.1.12 + + * lisp/textmodes/ispell.el (ispell-offset): Remove. + (ispell-check-version): Require ispell >= 3.1.12, released in 1994. + (ispell-process-line): No longer use ispell-offset. + +2016-12-13 Reuben Thomas + + Remove unused constant + + * lisp/textmodes/ispell.el (ispell-required-version): Remove. + +2016-12-13 Reuben Thomas + + Remove support for old versions of supercite and GNUS from ispell.el + + * lisp/textmodes/ispell.el (ispell-message): Require supercite >= 3.0 + and GNUS >= 5. Not exactly the bleeding edge! + +2016-12-13 Reuben Thomas + + Remove remaining mentions of XEmacs from ispell.el + + * lisp/textmodes/ispell.el (ispell-valid-dictionary-list): + (ispell-add-per-file-word-list): Remove mentions of XEmacs from + comments. + +2016-12-13 Reuben Thomas + + Remove XEmacs-specific minibuffer handling code from ispell.el + + * lisp/textmodes/ispell.el (ispell-init-process): Assume we are not in + XEmacs. + +2016-12-13 Reuben Thomas + + Remove XEmacs-specific horizontal scrollbar handling in ispell.el + + * lisp/textmodes/ispell.el (ispell-command-loop): Remove + XEmacs-specific code. + +2016-12-13 Reuben Thomas + + Remove XEmacs-specific code dealing with enable-multibyte-characters + + * lisp/textmodes/ispell.el (ispell-decode-string): + (ispell-init-process): Remove XEmacs-specific guard. + +2016-12-13 Reuben Thomas + + Remove XEmacs-specific code from ispell.el + + * lisp/textmodes/ispell.el (ispell-menu-xemacs): Remove + (ispell-menu-map-needed): Remove XEmacs-specific check. + (ispell-word): Remove XEmacs-specific extent code. + (ispell-init-process): Remove XEmacs workaround for local add-hook. + Assume we have set-process-query-on-exit-flag. + (ispell-kill-ispell, ispell-change-dictionary): Remove XEmacs + workaround for called-interactively-p. + +2016-12-13 Reuben Thomas + + Remove some commented-out code + + lisp/textmodes/ispell.el (ispell-process) + ispell-valid-dictionary-list): Remove commented-out code. + +2016-12-13 Reuben Thomas + + Remove XEmacs and old Emacs highlighting code + + * lisp/textmodes/ispell.el + (ispell-highlight-spelling-error-xemacs): Remove. + (ispell-highlight-spelling-error): Assume display-color-p exists. + +2016-12-13 Reuben Thomas + + Assume Emacs supports [:alpha:] in regexps + + * lisp/textmodes/ispell.el (ispell-emacs-alpha-regexp): Remove. + (ispell-set-spellchecker-params): Remove tests of + ispell-emacs-alpha-regexp. + +2016-12-13 Reuben Thomas + + Remove some XEmacs-specific code from ispell.el + + * lisp/textmodes/ispell.el: Remove XEmacs menubar setup. + (ispell-int-char): Remove. + +2016-12-13 Reuben Thomas + + Generalise over-specific documentation + + * lisp/textmodes/ispell.el (ispell-personal-dictionary): Rather than + document precise personal wordlist filenames for only two supported + spelling checkers, simply say that the default personal dictionary + depends on the chosen spelling checker. The user can check the + spelling checker’s documentation if necessary. This is simpler, and + works for other supported (and future, or unknown) spelling checkers. + +2016-12-13 Reuben Thomas + + Remove tests for built-in functions + + * lisp/textmodes/ispell.el (buffer-substring-no-properties): Remove + back-up definition. + (ispell-add-per-file-word-list): Remove tests for comment-padright and + comment-normalize-vars. + +2016-12-13 Reuben Thomas + + Remove unused ispell-looking-back + + * lisp/textmodes/ispell.el (ispell-looking-back): Remove unused alias. + +2016-12-13 Reuben Thomas + + Assume we have version<= for checking ispell version + + * lisp/textmodes/ispell.el (ispell-check-minver): Remove. + (ispell-check-version): Use version<= directly. + +2016-12-13 Reuben Thomas + + Remove ispell.el pre-GNU Emacs comments + + * lisp/textmodes/ispell.el (Commentary): Remove original maintainer + details, as Emacs version, bug report address and so forth should be + used instead for this version. Remove in-line change history; use + git instead. + +2016-12-13 Mark Oteiza + + More small fixes for image-dired + + * lisp/image-dired.el: Fix commentary to refer to correct Emacs manual + node. + (image-dired--with-db-file): Add declare forms. + (image-dired-hidden-p): Rewrite with cl-loop. It's not necessary to + run through the whole list. + +2016-12-13 Mark Oteiza + + Remove image-dired-kill-buffer-and-window + + This breaks window layout, especially when quitting a + image-dired-display-image-mode buffer. + * lisp/image-dired.el (image-dired-thumbnail-mode-map): + (image-dired-display-image-mode-map): Replace in keymap and menu items + bindings to image-dired-kill-buffer-and-window with quit-window. + (image-dired-kill-buffer-and-window): Remove. + +2016-12-13 Mark Oteiza + + Replace image-dired-setup-dired-keybindings with a minor mode + + * lisp/image-dired.el (image-dired-thumbnail-mode): Fix docstring to + remove mention of nonexistent image-dired-dired and to refer to the + new minor mode. + (image-dired-minor-mode-map): New keymap assimilated from + image-dired-setup-dired-keybindings. In the future, the keymap parent + should be removed, and perhaps also the duplicate bindings that + already exist in dired-mode-map. + (image-dired-setup-dired-keybindings): Remove. Replace with an + obsolete function alias. + (image-dired-minor-mode): New minor mode, assuming the role of + image-dired-setup-dired-keybindings. + +2016-12-13 Paul Eggert + + * test/src/regex-resources/PTESTS: Convert to UTF-8. + +2016-12-13 Noam Postavsky + + Clarify thread-signal semantics + + * doc/lispref/threads.texi (Basic Thread Functions): Explain that the + thread will be signaled as soon as possible. + +2016-12-13 Noam Postavsky + + Clean up var watcher disabling on thread switching + + * src/data.c (Fset_default): Move code into new C level function, + `set_default_internal'. + (set_default_internal): New function, like `Fset_default' but also takes + additional bindflag parameter. + (set_internal): Only call `notify_variable_watchers' if bindflag is not + SET_INTERNAL_THREAD_SWITCH. + * src/eval.c (do_specbind, do_one_unbind): Add bindflag parameter, + passed on to set_internal and set_default_internal. Adjust callers. + (rebind_for_thread_switch, unbind_for_thread_switch): Pass + SET_INTERNAL_THREAD_SWITCH to do_specbind, do_one_unbind instead of + temporarily adjusting symbol's trapped_write field. + +2016-12-13 Glenn Morris + + Minor fix for define-derived-mode + + * lisp/emacs-lisp/derived.el (define-derived-mode): + Do not let eg eval-defun reset the values of syntax or abbrev tables, + since they might have been defined externally. (Bug#16160) + +2016-12-12 Paul Eggert + + * build-aux/git-hooks/pre-commit: Add whitespace comment. + +2016-12-12 Clément Pit--Claudel + + Move backtrace to ELisp using a new mapbacktrace primitive + + * src/eval.c (get_backtrace_starting_at, backtrace_frame_apply) + (Fmapbacktrace, Fbacktrace_frame_internal): New functions. + (get_backtrace_frame, Fbacktrace_debug): Use `get_backtrace_starting_at'. + + * lisp/subr.el (backtrace--print-frame): New function. + (backtrace): Reimplement using `backtrace--print-frame' and `mapbacktrace'. + (backtrace-frame): Reimplement using `backtrace-frame--internal'. + + * lisp/emacs-lisp/debug.el (debugger-setup-buffer): Pass a base to + `mapbacktrace' instead of searching for "(debug" in the output of + `backtrace'. + + * test/lisp/subr-tests.el (subr-test-backtrace-simple-tests) + (subr-test-backtrace-integration-test): New tests. + + * doc/lispref/debugging.texi (Internals of Debugger): Document + `mapbacktrace' and missing argument BASE of `backtrace-frame'. + +2016-12-12 Paul Eggert + + Use C99 syntax for font drivers + + Problem reported by Daniel Colascione in: + http://lists.gnu.org/archive/html/emacs-devel/2016-12/msg00515.html + * src/ftcrfont.c (ftcrfont_driver): + * src/ftfont.c (ftfont_driver): + * src/ftxfont.c (ftxfont_driver): + * src/macfont.m (macfont_driver): + * src/nsfont.m (nsfont_driver): + * src/xfont.c (xfont_driver): + * src/xftfont.c (xftfont_driver): + Use C99 syntax, not the old GNU C syntax. + +2016-12-12 Glenn Morris + + Obsolete gs.el + + * lisp/gs.el: Move to lisp/obsolete. (Bug#1524) + * doc/lispref/display.texi (Image Formats): Remove postscript. + (PostScript Images): Remove section. + * doc/lispref/elisp.texi: Update menu. + +2016-12-12 Glenn Morris + + Un-revert recent Ffset change + + * src/data.c (Ffset): Reinstate the check for "nil". + +2016-12-12 Glenn Morris + + Minor advice.el fix + + * lisp/emacs-lisp/advice.el (ad-preactivate-advice): + Avoid setting the function definition of nil. + This was happening during bootstrap of org-compat.el, + apparently due to eager macro expansion of code behind + a (featurep 'xemacs) test. + +2016-12-12 Eli Zaretskii + + Make etags-tests work in out-of-tree builds + + * test/lisp/progmodes/etags-tests.el (etags-bug-158) + (etags-bug-23164): Make them work in an out-of-tree build. + Reported by Ken Brown . + +2016-12-12 Eli Zaretskii + + Avoid crashing if a new thread is signaled right away + + * src/thread.c (post_acquire_global_lock): Don't raise the pending + signal if the thread's handlers were not yet set up, as that will + cause Emacs to exit with a fatal error. This can happen if a + thread is signaled as soon as make-thread returns, before the new + thread had an opportunity to acquire the global lock, set up the + handlers, and call the thread function. + + * test/src/thread-tests.el (thread-signal-early): New test. + +2016-12-12 Eli Zaretskii + + Fix point motion in cloned buffers + + * src/thread.c (post_acquire_global_lock): Call + set_buffer_internal_2 instead of tricking set_buffer_internal_1 + into resetting the current buffer even if it didn't change. This + avoids bug#25165, caused by failing to record the modified values + of point and mark, because current_buffer was set to NULL. Also, + don't bother re-setting the buffer if there was no thread switch, + as that just wastes cycles. + * src/buffer.c (set_buffer_internal_2): New function, with most of + the body of set_buffer_internal_1, but without the test for B + being identical to the current buffer. + (set_buffer_internal_1): Call set_buffer_internal_2 if B is not + identical to the current buffer. + * src/buffer.h (set_buffer_internal_2): Add prototype. + + * test/src/thread-tests.el (thread-sticky-point): New test. + +2016-12-12 Michael Albinus + + Further improvements in Tramp's file name unquoting + + * lisp/net/tramp-adb.el (tramp-adb-handle-file-local-copy) + (tramp-adb-handle-write-region): Unquote localname. + (tramp-adb-handle-copy-file): Implement direct copy on remote device. + (tramp-adb-handle-rename-file): Quote arguments, add "-f" to force. + + * lisp/net/tramp.el (tramp-file-name-unquote-localname): New defun. + (tramp-handle-file-name-case-insensitive-p): + * lisp/net/tramp-gvfs.el (tramp-gvfs-get-file-attributes) + (tramp-gvfs-connection-mounted-p, tramp-gvfs-mount-spec) + (tramp-gvfs-maybe-open-connection): + * lisp/net/tramp-sh.el (tramp-make-copy-program-file-name): + * lisp/net/tramp-smb.el (tramp-smb-get-share) + (tramp-smb-get-localname): Use it. + + * test/lisp/net/tramp-tests.el (tramp--test-docker-p): New defun. + (tramp--test-special-characters, tramp-test34-utf8) + (tramp-test34-utf8-with-stat, tramp-test34-utf8-with-perl) + (tramp-test34-utf8-with-ls): Use it. + +2016-12-12 Martin Rudalics + + Strengthen conditions for resizing sibling windows (Bug#25169) + + * lisp/window.el (window-resize, delete-window): Resize other siblings + only if `window-combination-resize' equals t (Bug#25169). + +2016-12-12 Noam Postavsky + + Quote filenames containing '~' in prompts + + When in a directory named '~', the default value given by + `read-file-name' should be quoted by prepending '/:', in order to + prevent it from being interpreted as referring to the $HOME + directory (Bug#16984). + + * lisp/minibuffer.el (minibuffer-maybe-quote-filename): New function. + (completion--sifn-requote, read-file-name-default): Use it instead of + `minibuffer--double-dollars'. + * test/lisp/files-tests.el (files-test-read-file-in-~): Test it. + +2016-12-11 Eli Zaretskii + + Undo part of last change + + * src/thread.h: + * src/keyboard.c: + * src/keyboard.h: Undo part of last change: + input_available_clear_time is again a global variable. + +2016-12-11 Eli Zaretskii + + Avoid aborts when a thread signals an error + + * src/thread.h (struct thread_state): Add members + m_waiting_for_input and m_input_available_clear_time. + (waiting_for_input, input_available_clear_time): New macros. + * src/keyboard.c (waiting_for_input, input_available_clear_time): + Remove; they are now macros that reference the current thread. + (Bug#25171) + * src/w32select.c: Don't include keyboard.h. + + * test/src/thread-tests.el (thread-errors): New test. + +2016-12-11 Philipp Stephani + + Clean up compile-tests.el + + Switch to lexical binding. Make checkdoc happy. + + * test/lisp/progmodes/compile-tests.el (compile--test-error-line) + (compile-test-error-regexps): Instead of checking a single Boolean + value, use `should' for each attribute of the message to be compared. + (compile-tests--test-regexps-data): Document sixth list element + TYPE. + +2016-12-11 Michael Albinus + + Fix Bug#25162 + + * doc/emacs/files.texi (Reverting): Document + auto-revert-remote-files and auto-revert-verbose. + + * lisp/autorevert.el (auto-revert-verbose, auto-revert-mode) + (auto-revert-tail-mode, global-auto-revert-mode): Fix docstring. + +2016-12-11 Glenn Morris + + Fix a typo in define-abbrev-table + + * lisp/abbrev.el (define-abbrev-table): Fix typo in docstring handling. + +2016-12-11 Glenn Morris + + Improve previous cperl-mode change + + * lisp/progmodes/cperl-mode.el (cperl-mode-abbrev-table): + Improve previous change. + +2016-12-11 Glenn Morris + + Mark default cperl abbrevs as system ones + + * lisp/progmodes/cperl-mode.el (cperl-mode): + Mark our abbrevs as system ones. (Bug#10934) + +2016-12-11 Glenn Morris + + Revert earlier Ffset change + + * src/data.c (Ffset): Allow nil again, since it caused + eager macro-expansion failures. + +2016-12-11 Glenn Morris + + Tweaks for message bogus address detection + + * lisp/gnus/message.el (message-bogus-recipient-p): + Do not require "@", since some mailers deliver to local addresses + without one. (Bug#23054) + Move "@.*@" from here... + (message-bogus-addresses): ...to here, so it can be customized. + +2016-12-11 Glenn Morris + + Do not allow nil to be defined as a function + + * lisp/emacs-lisp/byte-run.el (defun): + * src/data.c (Ffset): Do not allow "nil". (Bug#25110) + +2016-12-11 Glenn Morris + + Bump makeinfo requirement from 4.7 to 4.13 + + * configure.ac: Bump makeinfo version requirement from 4.7 to 4.13. + We need at least 4.8, and that may be buggy, so go for the last + of the 4 series, which is 8 years old. (Bug#25108) + +2016-12-11 Glenn Morris + + Fixes related to select-enable-clipboard + + * lisp/menu-bar.el (clipboard-yank, clipboard-kill-ring-save) + (clipboard-kill-region): + * lisp/eshell/esh-io.el (eshell-virtual-targets) + (eshell-clipboard-append): + Replace option gui-select-enable-clipboard with + select-enable-clipboard; renamed October 2014. (Bug#25145) + +2016-12-11 Nicolas Richard + + Add some sanity checking of defun arglist + + * lisp/emacs-lisp/byte-run.el (defun): + Check for malformed argument lists. (Bug#15715) + +2016-12-11 Matt Armstrong (tiny change) + + Minor shell-mode fix for zsh + + * lisp/shell.el (shell-mode): Prevent shell-dirstack-query + becoming confused by zsh abbreviations. (Bug#24632) + +2016-12-10 Noam Postavsky + + Define struct predicate before acccesors + + The accessor functions use the predicate function, which causes problems + when reloading after unload-feature: the compiler-macro property is + still present on the predicate symbol, and the compiler fails to find + the definition when trying to inline it into the accessor + function (Bug#25088). + + * lisp/emacs-lisp/cl-macs.el (cl-defstruct): Move predicate definition + before field accessor definitions. + +2016-12-10 Andreas Schwab + + * Makefile.in (install-etc): Don't prepend $(DESTDIR) to commands in + system unit file. + +2016-12-10 Eli Zaretskii + + Support concurrency in Emacs Lisp + + Merge branch 'test-concurrency' + + * src/thread.c: + * src/thread.h: + * src/systhread.c: + * src/systhread.h: New files. + * src/xgselect.c (xg_select): Avoid using SAFE_NALLOCA and use + xnmalloc unconditionally. + * src/window.c (struct save_window_data): Rename current_buffer to + f_current_buffer. + * src/w32proc.c (sys_select): Change the function signature to + closer fit 'pselect' on Posix hosts. + * src/search.c: + * src/regex.h: Convert some globals to macros that reference + thread-specific values. + * src/process.c (pset_thread, add_non_keyboard_read_fd) + (add_process_read_fd, add_non_blocking_write_fd) + (recompute_input_desc, compute_input_wait_mask) + (compute_non_process_wait_mask, compute_non_keyboard_wait_mask) + (compute_write_mask, clear_waiting_thread_info) + (update_processes_for_thread_death, Fset_process_thread) + (Fprocess_thread): New functions. + (enum fd_bits): New enumeration. + (fd_callback_data): Add 'thread' and 'waiting_thread', rename + 'condition' to 'flags'. + (set_process_filter_masks, create_process, create_pty) + (Fmake_serial_process, finish_after_tls_connection) + (connect_network_socket, deactivate_process) + (server_accept_connection, wait_reading_process_output) + (Fcontinue_process, Fstop_process, keyboard_bit_set) + (add_timer_wait_descriptor, add_keyboard_wait_descriptor) + (delete_keyboard_wait_descriptor): Use the new functions instead + of manipulating fd flags and masks directly. + (syms_of_process): Defsubr the new primitives. + * src/print.c (print_object): Print threads, mutexes, and + conditional variables. + * src/lisp.h (enum pvec_type): New values PVEC_THREAD, PVEC_MUTEX, + and PVEC_CONDVAR. + (XTHREAD, XMUTEX, XCONDVAR, THREADP, MUTEXP, CONDVARP) + (CHECK_THREAD, CHECK_MUTEX, CHECK_CONDVAR): New inline functions. + (XSETTHREAD, XSETMUTEX, XSETCONDVAR): New macros. + (struct handler): Add back byte_stack. Rename lisp_eval_depth to + f_lisp_eval_depth. + * src/eval.c (specpdl_kind, specpdl_arg, do_specbind) + (rebind_for_thread_switch, do_one_unbind) + (unbind_for_thread_switch): New functions. + (init_eval): 'handlerlist' is not malloc'ed. + (specbind): Call do_specbind. + (unbind_to): Call do_one_unbind. + (mark_specpdl): Accept 2 arguments. + (mark_specpdl): Mark the saved value in a let-binding. + * src/emacs.c (main): Call init_threads_once, init_threads, and + syms_of_threads. + * src/data.c (Ftype_of): Support thread, mutex, and condvar + objects. + (Fthreadp, Fmutexp, Fcondition_variable_p): New functions. + (syms_of_data): DEFSYM and defsubr new symbols and primitives. + * src/bytecode.c (struct byte_stack, FETCH, CHECK_RANGE) + (BYTE_CODE_QUIT): Add back. + (exec_byte_code): Add back byte stack manipulation. + * src/alloc.c (cleanup_vector): Handle threads, mutexes, and + conditional variables. + (mark_stack): Now extern; accept additional argument 'bottom'. + (flush_stack_call_func): New function. + (garbage_collect_1): Call mark_threads and unmark_threads. Don't + mark handlers. + * src/.gdbinit (xbytecode): Add back. + + * test/src/thread-tests.el: New tests. + * test/src/data-tests.el (binding-test-manual) + (binding-test-setq-default, binding-test-makunbound) + (binding-test-defvar-bool, binding-test-defvar-int) + (binding-test-set-constant-t, binding-test-set-constant-nil) + (binding-test-set-constant-keyword) + (binding-test-set-constant-nil): New tests. + + * doc/lispref/processes.texi (Processes and Threads): New + subsection. + * doc/lispref/threads.texi: New file + * doc/lispref/elisp.texi (Top): Include it. + * doc/lispref/objects.texi (Thread Type, Mutex Type) + (Condition Variable Type): New subsections. + (Type Predicates): Add thread-related predicates. + * doc/lispref/objects.texi (Editing Types): + * doc/lispref/elisp.texi (Top): Update higher-level menus. + + * etc/NEWS: Mention concurrency features. + +2016-12-10 Eli Zaretskii + + Fix error messages in thread.c + + * src/thread.c (lisp_mutex_unlock, Fcondition_wait) + (Fcondition_notify, Fthread_join): Fix error messages. + +2016-12-10 Eli Zaretskii + + Improve doc strings in thread.c + + * src/thread.c (Fmake_condition_variable, Fcondition_wait) + (Fcondition_notify, Fcondition_mutex, Fcondition_name, Fmake_thread) + (Fthread_join, Fall_threads): Doc fixes. + +2016-12-10 Eli Zaretskii + + Fix building with check-lisp-object-type + + * src/thread.c (mark_one_thread): Use NILP to compare with + m_saved_last_thing_searched, which is a Lisp object. Reported by + Andreas Politz . + +2016-12-10 Eli Zaretskii + + Documentation and commentary improvements + + * src/lisp.h: + * src/regex.c: + * src/xgselect.c (xg_select): Improve commentary and formatting. + + * doc/lispref/objects.texi (Thread Type, Mutex Type) + (Condition Variable Type): New subsections. + (Type Predicates): Add thread-related predicates. + * doc/lispref/objects.texi (Editing Types): + * doc/lispref/elisp.texi (Top): Update higher-level menus. + +2016-12-09 Michael Albinus + + Fix further problems with quoted file names in Tramp + + * lisp/net/tramp.el (tramp-quoted-name-p, tramp-quote-name) + (tramp-unquote-name): Move defsubst ... + * lisp/net/tramp-compat.el (tramp-compat-file-name-quoted-p) + (tramp-compat-file-name-quote) + (tramp-compat-file-name-unquote): ... here. Adapt callees. + + * lisp/net/tramp-cache.el (tramp-flush-file-property) + (tramp-flush-directory-property): + * lisp/net/tramp-gvfs.el (tramp-gvfs-url-file-name): + * lisp/net/tramp-sh.el (tramp-make-copy-program-file-name): + * lisp/net/tramp-smb.el (tramp-smb-handle-copy-file) + (tramp-smb-handle-substitute-in-file-name) + (tramp-smb-get-share, tramp-smb-get-localname): Handle quoted files. + +2016-12-09 Eli Zaretskii + + *src/sysdep.c: Fix a comment. + +2016-12-09 Eli Zaretskii + + Fix compilation error on Fedora 24 + + * src/sysdep.c [HAVE_H_ERRNO]: Remove declaration of h_errno. + Reported by Paul Eggert . + +2016-12-09 Eli Zaretskii + + Fix compilation warnings due to prototype of thread_select + + * src/thread.h : Make the 5th and 6th arguments + be 'const'. + * src/process.c [WINDOWSNT]: + * src/w32proc.c: Make the 5th and 6th argument to sys_select be + 'const'. + +2016-12-09 Eli Zaretskii + + Fix compilation on Debian GNU/Linux + + * src/thread.h: Include sys/types.h, for ssize_t that regex.h + uses. Reported by Robert Marshall . + +2016-12-09 Eli Zaretskii + + Fix subtle errors with let-binding of localized variables + + * src/eval.c (do_specbind): Don't require a "symbol" that is + actually a cons cell, in order to call set-default, as there are + no longer such bindings. This makes do_specbind work like the + pre-concurrency implementation in specbind for bindings of + forwarded symbols. Use specpdl_kind to access the type of the + binding. + (specpdl_kind): New function. + +2016-12-09 Michael Albinus + + Document file-name-quote, file-name-unquote and file-name-quoted-p + + * doc/lispref/files.texi (File Name Expansion): + * etc/NEWS: Mention file-name-quote, file-name-unquote and + file-name-quoted-p. + + * lisp/files.el (file-name-non-special): Revert using + file-name-quote, file-name-unquote and file-name-quoted-p. + +2016-12-09 Noam Postavsky + + Fix bad quoting of python-shell-interpreter + + `python-shell-calculate-command' was using `shell-quote-argument' as if + it was generating a shell command, but its callers don't pass the result + to a shell, and they expect to parse it with `split-string-and-unquote'. + This caused problems depending on the flavor of shell quoting in + effect (Bug#25025). + + * lisp/progmodes/python.el (python-shell-calculate-command): Use + `combine-and-quote-strings' to quote the interpreter, so that it can be + parsed by `python-shell-make-comint' successfully using + `split-string-and-unquote'. + +2016-12-09 Paul Eggert + + * src/lisp.h (struct terminal): Remove unnecessary forward decl. + +2016-12-08 Paul Eggert + + Make read1 more reentrant + + This is needed if ‘read’ is called soon after startup, before the + Unicode tables have been set up, and it reads a \N escape and + needs to look up a value the Unicode tables, a lookup that in turn + calls read1 recursively. Although this change doesn’t make ‘read’ + fully reentrant, it’s good enough to handle this case. + * src/lread.c (read_buffer_size, read_buffer): Remove static vars. + (grow_read_buffer): Revamp to use locals, not statics, and to + record memory allocation un the specpdl. All callers changed. + (read1): Start with a stack-based buffer, and use the heap + only if the stack buffer is too small. Use unbind_to to + free any heap buffer allocated. Use bool for boolean. + Redo symbol loop so that only one call to grow_read_buffer + is needed. + (init_obarray): Remove no-longer-needed initialization. + +2016-12-08 Eli Zaretskii + + Fix compilation warnings + + * src/thread.c (Fmake_thread): Call emacs_abort, to avoid + compilation warning. + +2016-12-08 Paul Eggert + + Fix unlikely substitute-command-keys memory leak + + * src/doc.c (Fsubstitute_command_keys): + Free buffer when unwinding. + +2016-12-08 Michael Albinus + + Add file-name-quoted-p, file-name-quote, file-name-unquote + + * lisp/files.el (file-name-quoted-p, file-name-quote) + (file-name-unquote): New defsubst. + (find-file--read-only, find-file-noselect) + (file-name-non-special): Use them. + +2016-12-08 Eli Zaretskii + + Add a NEWS entry. + +2016-12-08 Glenn Morris + + Minor fix for symbol-file + + * lisp/subr.el (symbol-file): Avoid false matches with "require" + elements in load-history. (Bug#25109) + +2016-12-08 Glenn Morris + + Doc fix for vc-git + + * lisp/vc/vc-git.el (vc-git-region-history): Add a doc string. + +2016-12-08 Tino Calancha + + Fix Bug#24962 + + * lisp/buff-menu.el (list-buffers--refresh): + List buffers with name starting with " " if they visit a file. + * test/lisp/buff-menu-tests.el (buff-menu-24962): + Update test result as pass. + +2016-12-08 Tino Calancha + + ediff-fixup-patch-map: Improve prompt + + * lisp/vc/ediff-ptch.el (ediff-fixup-patch-map): + Make clear in the prompt when we are applying a multi patch. + +2016-12-08 Glenn Morris + + Retain message logging in map-y-or-n-p + + * lisp/emacs-lisp/map-ynp.el (map-y-or-n-p): + Stop disabling logging to Messages buffer. (Bug#13326) + +2016-12-08 Glenn Morris + + Doc fix for recent change + + * lisp/simple.el (region-modifiable-p): Doc fix. + +2016-12-08 Glenn Morris + + Quieten make-dist default operation + + * make-dist: Add --verbose option. Default to quieter operation. + +2016-12-08 Glenn Morris + + Further improve make-dist checking + + * make-dist: Print status messages when checking. + +2016-12-08 Glenn Morris + + Improve previous make-dist change + + * make-dist: Let make check the info files more thoroughly. + +2016-12-08 Glenn Morris + + Make make-dist --snapshot do some sanity checks + + * make-dist: Snapshot mode no longer disables checks. + Checks now includes checks for freshness. (Bug#25084) + Checks now exits with an error if problems were found. + +2016-12-07 Tino Calancha + + Fix regression introduced by commit 7b1e97f + + * lisp/ibuf-ext.el (ibuffer-decompose-filter): Use cdr instead + of cadr; required after commit 20f5a5b. + +2016-12-07 Paul Eggert + + Put post-25 ChangeLog entries into ChangeLog.3 + + * ChangeLog.2: Copy from emacs-25 branch. + * ChangeLog.3: New file, with changes only in master. + * Makefile.in (CHANGELOG_HISTORY_INDEX_MAX): Bump from 2 to 3. + +2016-12-07 Eli Zaretskii + + Fix network streams. + + The original code messed up flags in fd_callback_data[], and also + didn't call add_process_read_fd for process-related file descriptors. + +2016-12-07 Eli Zaretskii + + Minimize spurious diffs from master. + +2016-12-06 Eli Zaretskii + + Fix the test suite + + * test/automated/bindings.el: Contents moved to + test/src/data-tests.el. + * test/automated/threads.el: Moved to test/src/thread-tests.el. + +2016-12-06 Eli Zaretskii + + Fix a typo in bytecode.c. + +2016-12-05 Eli Zaretskii + + Fix compilation problems. + + Fix merged code in process.c and eval.c. + +2016-12-04 Eli Zaretskii + + Merge branch 'concurrency' + + Conflicts (resolved): + configure.ac + src/Makefile.in + src/alloc.c + src/bytecode.c + src/emacs.c + src/eval.c + src/lisp.h + src/process.c + src/regex.c + src/regex.h + +2015-11-02 Eli Zaretskii + + Fix the MS-Windows build + + * src/thread.h [WINDOWSNT]: Include sys/socket.h. + + * src/sysselect.h: Don't define fd_set and FD_* macros for + MS-Windows here. + * src/w32.h: Define them here. + + * src/process.h (sys_select): Declare prototype. + + * src/sysdep.c: + * src/process.c: + * src/filelock.c: + * src/emacs.c: + * src/callproc.c: Move inclusion of sys/select.h after lisp.h. + * nt/inc/socket.h: Include w32.h instead of sysselect.h + +2015-11-01 Ken Raeburn + + merge from trunk + +2013-10-19 Barry O'Reilly + + * src/eval.c (unbind_for_thread_switch): Fix iteration over the + specpdl stack. + +2013-10-18 Tom Tromey + + change condition-variablep to condition-variable-p + +2013-09-01 Eli Zaretskii + + Fix crashes when unbind_for_thread_switch signals an error. + + src/eval.c (unbind_for_thread_switch): Accept a 'struct + thread_state *' argument and use specpdl_ptr and specpdl of that + thread. Fixes crashes if find_symbol_value signals an error. + src/thread.c (post_acquire_global_lock): Update current_thread + before calling unbind_for_thread_switch. Pass the previous thread + to unbind_for_thread_switch. + +2013-08-31 Eli Zaretskii + + Improve MS-Windows implementation of threads. + + src/systhread.c (sys_cond_init): Set the 'initialized' member to + true only if initialization is successful. Initialize wait_count + and wait_count_lock. + (sys_cond_wait, sys_cond_signal, sys_cond_broadcast): If + 'initialized' is false, do nothing. + (sys_cond_wait): Fix the implementation to avoid the "missed + wakeup" bug: count the waiting threads, and reset the broadcast + event once the last thread was released. + (sys_cond_signal, sys_cond_broadcast): Use SetEvent instead of + PulseEvent. Don't signal the event if no threads are waiting. + (sys_cond_destroy): Only close non-NULL handles. + (sys_thread_create): Return zero if unsuccessful, 1 if successful. + src/systhread.h (w32thread_cond_t): New member 'initialized'. + Rename waiters_count and waiters_count_lock to wait_count and + wait_count_lock, respectively. + +2013-08-30 Eli Zaretskii + + Enable thread support in the MS-Windows build. + + src/systhread.h (w32thread_critsect, w32thread_cond_t, sys_mutex_t) + (sys_cond_t, sys_thread_t) [WINDOWSNT]: New data types. + src/systhread.c (sys_mutex_init, sys_mutex_lock, sys_mutex_unlock) + (sys_mutex_destroy, sys_cond_init, sys_cond_wait) + (sys_cond_signal, sys_cond_broadcast, sys_cond_destroy) + (sys_thread_self, sys_thread_equal, w32_beginthread_wrapper) + (sys_thread_create, sys_thread_yield) [WINDOWSNT]: New functions. + + configure.ac (THREADS_ENABLED): Enable threads for MinGW, even + if pthreads is not available. + +2013-08-27 Tom Tromey + + use condition-notify in the docs, not condition-signal + + zap until-condition docs + + zap until-condition + + rename thread-blocker to thread--blocker + + remove binding_symbol + + fix style of threadp, mutexp, and condition-variable-p + + make thread_check_current_buffer return bool + + add a comment before flush_stack_call_func + + fix whitespace_regexp warning + +2013-08-26 Eli Zaretskii + + Fix MS-Windows build. + + src/callproc.c: + src/emacs.c: + src/filelock.c: + src/process.c: + src/sysdep.c: + src/w32.c: Reshuffle Windows-specific headers to avoid errors with + redefinition of fd_set etc. + src/process.c: Don't use num_pending_connects when + NON_BLOCKING_CONNECT is not defined. + src/sysselect.h: Move definitions of FD_* macros and of SELECT_TYPE + here from w32.h. + src/w32proc.c (sys_select): Adjust the argument types to what + thread.h expects. + + nt/inc/sys/socket.h: Include stdint.h. Include sysselect.h instead + of w32.h. + +2013-08-26 Tom Tromey + + use record_unwind_protect_void, avoid warning + + implement --enable-threads and a thread-less mode + +2013-08-25 Tom Tromey + + merge from trunk + +2013-08-20 Tom Tromey + + fix up some merge errors in process.c + + remove a dead function + clean up a fixme I added in create_pty during the merge + +2013-08-20 Tom Tromey + + merge from trunk + +2013-07-26 Tom Tromey + + merge from trunk + +2013-07-13 Tom Tromey + + Merge from trunk + +2013-07-12 Tom Tromey + + Use thread_alive_p in a couple more spots + +2013-07-07 Tom Tromey + + fix xfree bug in run_thread + + this fixes run_thread to account for the dummy slot + in specpdl + +2013-07-07 Tom Tromey + + merge from trunk + + this merges frmo trunk and fixes various build issues. + this needed a few ugly tweaks. + this hangs in "make check" now + +2013-07-06 Tom Tromey + + add assertion to flush_stack_call_func + + functions called via flush_stack_call_func are assumed + to return with the global lock held again, and with + current_thread reset. this assertion verifies part of this + +2013-07-06 Tom Tromey + + call init_primary_thread from init_threads + +2013-07-05 Tom Tromey + + avoid SAFE_ALLOCA + + avoid SAFE_ALLOCA in xgselect.c. + in this code it is just as easy to always use malloc; + and it avoids thread-switching problems, as the safe-alloca + stuff implicitly refers to the current thread + +2013-07-05 Tom Tromey + + avoid current_thread sometimes + + this tweaks thread.c to use 'self' instead of current_thread + in a couple spots. this is clearer and more robust + +2013-07-05 Tom Tromey + + initialize saved_value + + initialize the saved_value field in all needed cases + also, add an assertion to do_one_unbind + +2013-07-04 Tom Tromey + + fix buglet in test case + +2013-07-04 Tom Tromey + + unlink thread later + + unlink thread from global list later + also remove some unnecessary destruction code + +2013-07-04 Tom Tromey + + introduce thread_alive_p macro + + This introduces the thread_alive_p macro and changes + thread-alive-p to use it. This is a minor cleanup. + It also changes all-threads to ignore dead threads. + +2013-07-03 Tom Tromey + + Don't call unbind_for_thread_switch in run_thread + + This removes the call to unbind_for_thread_switch from run_thread. + This isn't necessary because acquire_global_lock does it properly. + +2013-07-03 Tom Tromey + + remove unused field from struct thread_state + + Fix a comment. + +2013-06-13 Tom Tromey + + merge from trunk + +2013-06-06 Tom Tromey + + fix a few latent issues in the thread patch + + * we called unbind_for_thread_switch unconditionally, but this + is wrong if the previous thread exited + * likewise, exiting a thread should clear current_thread + * redundant assignment in run_thread + * clean up init_threads - no need to re-init the primary thread + + This patch still sometimes causes weird hangs in "make check". + However, I think that is a kernel bug, since Emacs enters the zombie + state but its parent process hangs in wait. This shouldn't happen. + +2013-06-04 Tom Tromey + + update eval.c to make it build again after the merge + +2013-06-03 Tom Tromey + + merge from trunk; clean up some issues + +2013-03-18 Tom Tromey + + don't let kill-buffer kill a buffer if it is current in any thread + +2013-03-18 Tom Tromey + + fix process bugs + + Fix some process-related bugs, mostly thinkos from the conversion to + recording fd state as flags. + This now passes the test suite without hanging. + +2013-03-17 Tom Tromey + + merge from trunk + +2013-03-08 Tom Tromey + + merge from trunk + +2013-01-16 Tom Tromey + + merge from trunk + +2013-01-06 Tom Tromey + + merge from trunk + +2012-12-23 Tom Tromey + + mention let bindings and lack of other ways to rewind + +2012-12-17 Tom Tromey + + Remove bit accidentally left over from the merge + + merge from trunk + +2012-09-04 Tom Tromey + + merge from trunk + + link from thread docs to match data + +2012-08-27 Tom Tromey + + cannot thread-join the current thread + + fix test suite for condition-variable-p name change + + add tests for variable bindings + +2012-08-25 Tom Tromey + + minor update to thread-join docs + +2012-08-24 Tom Tromey + + minor documentation updates + +2012-08-23 Tom Tromey + + document until-condition + + first draft of threads documentation + + rename condition-variablep to condition-variable-p + + document process-thread and set-process-thread + +2012-08-20 Tom Tromey + + pass the thread name to the OS if possible + + use prctl to pass the thread name to the OS, if possible + +2012-08-20 Tom Tromey + + add convenience macros with-mutex and until-condition + + with-mutex is a safe way to run some code with a mutex held. + until-condition is a safe way to wait on a condition variable. + +2012-08-20 Tom Tromey + + Merge from trunk + +2012-08-19 Tom Tromey + + another docstring fixlet + + minor docstring fixup + + add condition-mutex and condition-name + + ensure name of a thread is a string + + ensure name of a mutex is a string + + use NILP + +2012-08-19 Tom Tromey + + condition variables + + This implements condition variables for elisp. + This needs more tests. + +2012-08-19 Tom Tromey + + comment fixes + +2012-08-19 Tom Tromey + + refactor systhread.h + + This refactors systhread.h to move the notion of a "lisp mutex" + into thread.c. This lets us make make the global lock and + post_acquire_global_lock static. + +2012-08-17 Tom Tromey + + write docstrings for the thread functions + + declare unbind_for_thread_switch and rebind_for_thread_switch in lisp.h + +2012-08-15 Tom Tromey + + add test case for I/O switching + +2012-08-15 Tom Tromey + + process changes + + This changes wait_reading_process_output to handle threads better. It + introduces a wrapper for select that releases the global lock, and it + ensures that only a single thread can select a given file descriptor + at a time. + + This also adds the thread-locking feature to processes. By default a + process can only have its output accepted by the thread that created + it. This can be changed using set-process-thread. (If the thread + exits, the process is again available for waiting by any thread.) + + Note that thread-signal will not currently interrupt a thread blocked + on select. I'll fix this later. + +2012-08-15 Tom Tromey + + Prepare process.c for threads by not having global select masks. + The next step is to make it so selects can choose fds by thread. + +2012-08-15 Tom Tromey + + fix a latent bug in process.c + + * process.c (wait_reading_process_output): Check Writeok bits, + not write_mask. + +2012-08-15 Tom Tromey + + This adds thread-blocker, a function to examine what a thread is + blocked on. I thought this would be another nice debugging addition. + + This adds names to mutexes. This seemed like a nice debugging + extension. + + This adds some tests of the threading code. + +2012-08-15 Tom Tromey + + This supplies the mutex implementation for Emacs Lisp. + + A lisp mutex is implemented using a condition variable, so that we can + interrupt a mutex-lock operation by calling thread-signal on the + blocking thread. I did things this way because pthread_mutex_lock + can't readily be interrupted. + +2012-08-15 Tom Tromey + + This adds most of the thread features visible to emacs lisp. + + I roughly followed the Bordeaux threads API: + + http://trac.common-lisp.net/bordeaux-threads/wiki/ApiDocumentation + + ... but not identically. In particular I chose not to implement + interrupt-thread or destroy-thread, but instead a thread-signalling + approach. + + I'm still undecided about *default-special-bindings* (which I did not + implement). I think it would be more emacs-like to capture the let + bindings at make-thread time, but IIRC Stefan didn't like this idea + the first time around. + + There are one or two semantics issues pointed out in the patch where I + could use some advice. + +2012-08-15 Tom Tromey + + This turns thread_state into a pseudovector and updates various bits + of Emacs to cope. + +2012-08-15 Tom Tromey + + This introduces some new functions to handle the specpdl. The basic + idea is that when a thread loses the interpreter lock, it will unbind + the bindings it has put in place. Then when a thread acquires the + lock, it will restore its bindings. + + This code reuses an existing empty slot in struct specbinding to store + the current value when the thread is "swapped out". + + This approach performs worse than my previously planned approach. + However, it was one I could implement with minimal time and + brainpower. I hope that perhaps someone else could improve the code + once it is in. + +2012-08-15 Tom Tromey + + This introduces the low-level system threading support. It also adds + the global lock. The low-level support is a bit over-eager, in that + even at the end of the present series, it will not all be used. I + think thiat is ok since I plan to use it all eventually -- in + particular for the emacs lisp mutex implementation. + + I've only implemented the pthreads-based version. I think it should + be relatively clear how to port this to other systems, though. + + I'd also like to do a "no threads" port that will turn most things + into no-ops, and have thread-creation fail. I was thinking perhaps + I'd make a future (provide 'threads) conditional on threads actually + working. + + One other minor enhancement available here is to make it possible to + set the name of the new thread at the OS layer. That way gdb, e.g., + could display thread names. + +2012-08-15 Tom Tromey + + This parameterizes the GC a bit to make it thread-ready. + + The basic idea is that whenever a thread "exits lisp" -- that is, + releases the global lock in favor of another thread -- it must save + its stack boundaries in the thread object. This way the boundaries + are always available for marking. This is the purpose of + flush_stack_call_func. + + I haven't tested this under all the possible GC configurations. + There is a new FIXME in a spot that i didn't convert. + + Arguably all_threads should go in the previous patch. + +2012-08-15 Tom Tromey + + This introduces a thread-state object and moves various C globals + there. It also introduces #defines for these globals to avoid a + monster patch. + + The #defines mean that this patch also has to rename a few fields + whose names clash with the defines. + + There is currently just a single "thread"; so this patch does not + impact Emacs behavior in any significant way. + 2016-12-07 Paul Eggert Merge from origin/emacs-25 @@ -22610,7 +50754,7 @@ This file records repository revisions from commit 9d56a21e6a696ad19ac65c4b405aeca44785884a (exclusive) to -commit f15f6b53078ac2176f8d2c05d99d3d9b4d32986b (inclusive). +commit 82d2a05a74c120480dc1b68243430c9417bfc523 (inclusive). See ChangeLog.1 for earlier changes. ;; Local Variables: commit 82d2a05a74c120480dc1b68243430c9417bfc523 Author: Nicolas Petton Date: Thu Oct 5 22:23:49 2017 +0200 ; * Makefile.in: set PREFERRED_BRANCH to emacs-26 diff --git a/Makefile.in b/Makefile.in index b882da1928..8ad3f99a24 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1115,7 +1115,7 @@ ChangeLog: ./$(emacslog) -o $(CHANGELOG) -n $(CHANGELOG_HISTORY_INDEX_MAX) # Check that we are in a good state for changing history. -PREFERRED_BRANCH = master +PREFERRED_BRANCH = emacs-26 preferred-branch-is-current: git branch | grep -q '^\* $(PREFERRED_BRANCH)$$' unchanged-history-files: commit e933249f6bc82f0469792bd1fe1255dcaa813705 Author: Nicolas Petton Date: Wed Oct 4 21:49:22 2017 +0200 Update authors.el * admin/authors.el (authors-renamed-files-alist) (authors-valid-file-names): Additions. diff --git a/admin/authors.el b/admin/authors.el index f79c2e8877..5638efbc34 100644 --- a/admin/authors.el +++ b/admin/authors.el @@ -806,7 +806,10 @@ Changes to files in this list are not listed.") "srecode-tests.el" "make-test-deps.emacs-lisp" "nxml-uchnm.el" "decoder-tests.el" - "obsolete/scribe.el") + "obsolete/scribe.el" + "cp51932.el" + "eucjp-ms.el" + "lisp.mk") "File names which are valid, but no longer exist (or cannot be found) in the repository.") @@ -912,7 +915,7 @@ in the repository.") ("emulation/ws-mode.el" . "ws-mode.el") ("vc/vc-arch.el" . "vc-arch.el") ("lisp/gnus/messcompat.el" . "messcompat.el") - ("lisp/gnus/html2text.el" . "html2text.el") + ("html2text.el" . "html2text.el") ;; From lisp to etc/forms. ("forms-d2.el" . "forms-d2.el") ("forms-pass.el" . "forms-pass.el") commit 9a10c8713b8dd46738365d35263ae0687e2063cc Author: Gemini Lasswell Date: Thu Oct 5 12:41:35 2017 -0700 Fix dynamic binding wrapper in iter-lambda (bug#25965) * lisp/emacs-lisp/generator.el (cps--make-dynamic-binding-wrapper): Remove extra evaluation of form. * test/lisp/emacs-lisp/generator-tests.el (cps-iter-lambda-with-dynamic-binding): New test. diff --git a/lisp/emacs-lisp/generator.el b/lisp/emacs-lisp/generator.el index f3597cc387..3e9885900c 100644 --- a/lisp/emacs-lisp/generator.el +++ b/lisp/emacs-lisp/generator.el @@ -142,8 +142,7 @@ the CPS state machinery. `(let ((,dynamic-var ,static-var)) (unwind-protect ; Update the static shadow after evaluation is done ,form - (setf ,static-var ,dynamic-var)) - ,form))) + (setf ,static-var ,dynamic-var))))) (defmacro cps--with-dynamic-binding (dynamic-var static-var &rest body) "Evaluate BODY such that generated atomic evaluations run with diff --git a/test/lisp/emacs-lisp/generator-tests.el b/test/lisp/emacs-lisp/generator-tests.el index 4cc6c841da..cbb136ae91 100644 --- a/test/lisp/emacs-lisp/generator-tests.el +++ b/test/lisp/emacs-lisp/generator-tests.el @@ -282,3 +282,13 @@ identical output. (ert-deftest cps-test-declarations-preserved () (should (equal (documentation 'generator-with-docstring) "Documentation!")) (should (equal (get 'generator-with-docstring 'lisp-indent-function) 5))) + +(ert-deftest cps-iter-lambda-with-dynamic-binding () + "`iter-lambda' with dynamic binding produces correct result (bug#25965)." + (should (= 1 + (iter-next + (funcall (iter-lambda () + (let* ((fill-column 10) ;;any special variable will do + (i 0) + (j (setq i (1+ i)))) + (iter-yield i)))))))) commit 2a32ee1fbc148d440fc12aa390ed3d2cc7d6fe3b Author: Rasmus Date: Thu Oct 5 21:30:05 2017 +0200 Update Org to v9.1.2 Please note this is a bugfix release. See etc/ORG-NEWS for details. diff --git a/doc/misc/org.texi b/doc/misc/org.texi index b6a4fa2355..72fbbade59 100644 --- a/doc/misc/org.texi +++ b/doc/misc/org.texi @@ -4,7 +4,7 @@ @settitle The Org Manual @include docstyle.texi -@set VERSION 9.1.1 +@set VERSION 9.1.2 @set DATE 2017-09-17 @c Version and Contact Info @@ -1257,13 +1257,8 @@ Org uses just two commands, bound to @key{TAB} and @end example @vindex org-cycle-emulate-tab -@vindex org-cycle-global-at-bob The cursor must be on a headline for this to work@footnote{see, however, -the option @code{org-cycle-emulate-tab}.}. When the cursor is at the -beginning of the buffer and the first line is not a headline, then -@key{TAB} actually runs global cycling (see below)@footnote{see the -option @code{org-cycle-global-at-bob}.}. Also when called with a prefix -argument (@kbd{C-u @key{TAB}}), global cycling is invoked. +the option @code{org-cycle-emulate-tab}.}. @cindex global visibility states @cindex global cycling @@ -1283,6 +1278,11 @@ When @kbd{S-@key{TAB}} is called with a numeric prefix argument N, the CONTENTS view up to headlines of level N will be shown. Note that inside tables, @kbd{S-@key{TAB}} jumps to the previous field. +@vindex org-cycle-global-at-bob +You can run global cycling using @key{TAB} only if point is at the very +beginning of the buffer, but not on a headline, and +@code{org-cycle-global-at-bob} is set to a non-@code{nil} value. + @cindex set startup visibility, command @orgcmd{C-u C-u @key{TAB},org-set-startup-visibility} Switch back to the startup visibility of the buffer (@pxref{Initial visibility}). @@ -1659,11 +1659,9 @@ line. In particular, if an ordered list reaches number @samp{10.}, then the list. An item ends before the next line that is less or equally indented than its bullet/number. -@vindex org-list-empty-line-terminates-plain-lists A list ends whenever every item has ended, which means before any line less or equally indented than items at top level. It also ends before two blank -lines@footnote{See also @code{org-list-empty-line-terminates-plain-lists}.}. -In that case, all items are closed. Here is an example: +lines. In that case, all items are closed. Here is an example: @example @group @@ -14991,6 +14989,7 @@ directory on the local machine. '(("org" :base-directory "~/org/" :publishing-directory "~/public_html" + :publishing-function org-html-publish-to-html :section-numbers nil :with-toc nil :html-head " lob-ingest-count 1) "s" "")) lob-ingest-count)) diff --git a/lisp/org/org-clock.el b/lisp/org/org-clock.el index 2eec817735..9dc501500b 100644 --- a/lisp/org/org-clock.el +++ b/lisp/org/org-clock.el @@ -2984,6 +2984,7 @@ The details of what will be saved are regulated by the variable ;; Local variables: ;; generated-autoload-file: "org-loaddefs.el" +;; coding: utf-8 ;; End: ;;; org-clock.el ends here diff --git a/lisp/org/org-colview.el b/lisp/org/org-colview.el index 242bdc2655..eac29c50f6 100644 --- a/lisp/org/org-colview.el +++ b/lisp/org/org-colview.el @@ -464,7 +464,8 @@ for the duration of the command.") (kill-local-variable 'org-previous-header-line-format) (remove-hook 'post-command-hook 'org-columns-hscroll-title 'local)) (set-marker org-columns-begin-marker nil) - (set-marker org-columns-top-level-marker nil) + (when (markerp org-columns-top-level-marker) + (set-marker org-columns-top-level-marker nil)) (org-with-silent-modifications (mapc #'delete-overlay org-columns-overlays) (setq org-columns-overlays nil) diff --git a/lisp/org/org-table.el b/lisp/org/org-table.el index 66907e2cd9..8dc648eaec 100644 --- a/lisp/org/org-table.el +++ b/lisp/org/org-table.el @@ -1646,12 +1646,14 @@ In particular, this does handle wide and invisible characters." (if (not (org-at-table-p)) (user-error "Not at a table")) (let ((col (current-column)) - (dline (org-table-current-dline))) + (dline (and (not (org-match-line org-table-hline-regexp)) + (org-table-current-dline)))) (kill-region (point-at-bol) (min (1+ (point-at-eol)) (point-max))) (if (not (org-at-table-p)) (beginning-of-line 0)) (org-move-to-column col) - (when (or (not org-table-fix-formulas-confirm) - (funcall org-table-fix-formulas-confirm "Fix formulas? ")) + (when (and dline + (or (not org-table-fix-formulas-confirm) + (funcall org-table-fix-formulas-confirm "Fix formulas? "))) (org-table-fix-formulas "@" (list (cons (number-to-string dline) "INVALID")) dline -1 dline)))) diff --git a/lisp/org/org-version.el b/lisp/org/org-version.el index 523afd1ad3..30318ba92c 100644 --- a/lisp/org/org-version.el +++ b/lisp/org/org-version.el @@ -5,13 +5,13 @@ (defun org-release () "The release version of Org. Inserted by installing Org mode or when a release is made." - (let ((org-release "9.1.1")) + (let ((org-release "9.1.2")) org-release)) ;;;###autoload (defun org-git-version () "The Git version of org-mode. Inserted by installing Org or when a release is made." - (let ((org-git-version "release_9.1.1-37-gb1e8b5")) + (let ((org-git-version "release_9.1.2-40-g6ca906")) org-git-version)) (provide 'org-version) diff --git a/lisp/org/org.el b/lisp/org/org.el index c5759cb537..35405b4bf8 100644 --- a/lisp/org/org.el +++ b/lisp/org/org.el @@ -16071,7 +16071,9 @@ automatically performed, such drawers will be silently ignored." (when (memq (org-element-type element) '(keyword node-property)) (let ((value (org-element-property :value element)) (start 0)) - (while (string-match "%[0-9]*\\(\\S-+\\)" value start) + (while (string-match "%[0-9]*\\([[:alnum:]_-]+\\)\\(([^)]+)\\)?\ +\\(?:{[^}]+}\\)?" + value start) (setq start (match-end 0)) (let ((p (match-string-no-properties 1 value))) (unless (member-ignore-case p org-special-properties) @@ -19481,7 +19483,6 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names." (org-defkey org-mode-map [(shift return)] 'org-table-copy-down) (org-defkey org-mode-map [(meta shift return)] 'org-insert-todo-heading) -(org-defkey org-mode-map [(meta return)] 'org-meta-return) (org-defkey org-mode-map (kbd "M-RET") #'org-meta-return) ;; Cursor keys with modifiers @@ -24204,16 +24205,25 @@ convenience: - On an affiliated keyword, jump to the first one. - On a table or a property drawer, move to its beginning. - - On a verse or source block, stop before blank lines." + - On comment, example, export, src and verse blocks, stop + before blank lines." (interactive) (unless (bobp) (let* ((deactivate-mark nil) (element (org-element-at-point)) (type (org-element-type element)) - (contents-begin (org-element-property :contents-begin element)) (contents-end (org-element-property :contents-end element)) (post-affiliated (org-element-property :post-affiliated element)) - (begin (org-element-property :begin element))) + (begin (org-element-property :begin element)) + (special? ;blocks handled specially + (memq type '(comment-block example-block export-block src-block + verse-block))) + (contents-begin + (if special? + ;; These types have no proper contents. Fake line + ;; below the block opening line as contents beginning. + (save-excursion (goto-char begin) (line-beginning-position 2)) + (org-element-property :contents-begin element)))) (cond ((not element) (goto-char (point-min))) ((= (point) begin) @@ -24224,11 +24234,8 @@ convenience: (goto-char (org-element-property :post-affiliated (org-element-property :parent element)))) ((memq type '(property-drawer table)) (goto-char begin)) - ((memq type '(src-block verse-block)) - (when (eq type 'src-block) - (setq contents-begin - (save-excursion (goto-char begin) (forward-line) (point)))) - (if (= (point) contents-begin) (goto-char post-affiliated) + (special? + (if (<= (point) contents-begin) (goto-char post-affiliated) ;; Inside a verse block, see blank lines as paragraph ;; separators. (let ((origin (point))) @@ -24237,7 +24244,6 @@ convenience: (skip-chars-forward " \r\t\n" origin) (if (= (point) origin) (goto-char contents-begin) (beginning-of-line)))))) - ((not contents-begin) (goto-char (or post-affiliated begin))) ((eq type 'paragraph) (goto-char contents-begin) ;; When at first paragraph in an item or a footnote definition, diff --git a/lisp/org/ox-html.el b/lisp/org/ox-html.el index fb8c61334f..8ce4fb6adc 100644 --- a/lisp/org/ox-html.el +++ b/lisp/org/ox-html.el @@ -174,7 +174,6 @@ (:html-klipsify-src nil nil org-html-klipsify-src) (:html-klipse-css nil nil org-html-klipse-css) (:html-klipse-js nil nil org-html-klipse-js) - (:html-klipse-keep-old-src nil nil org-html-keep-old-src) (:html-klipse-selection-script nil nil org-html-klipse-selection-script) (:infojs-opt "INFOJS_OPT" nil nil) ;; Redefine regular options. @@ -1572,12 +1571,6 @@ https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag" :package-version '(Org . "9.1") :type 'string) -(defcustom org-html-keep-old-src nil - "When non-nil, use
 instead of 
."
-  :group 'org-export-html
-  :package-version '(Org . "9.1")
-  :type 'boolean)
-
 
 ;;;; Todos
 
@@ -3402,12 +3395,16 @@ contextual information."
 			      listing-number
 			      (org-trim (org-export-data caption info))))))
 		;; Contents.
-		(let ((open (if org-html-keep-old-src "" "
"))) - (format "%s class=\"src src-%s\"%s%s>%s%s" - open lang label (if (and klipsify (string= lang "html")) - " data-editor-type=\"html\"" "") - code close))))))) + (if klipsify + (format "
%s
" + lang + label + (if (string= lang "html") + " data-editor-type=\"html\"" + "") + code) + (format "
%s
" + lang label code))))))) ;;;; Statistics Cookie diff --git a/lisp/org/ox-publish.el b/lisp/org/ox-publish.el index a975abc487..957b0da7c5 100644 --- a/lisp/org/ox-publish.el +++ b/lisp/org/ox-publish.el @@ -435,8 +435,8 @@ This splices all the components into the list." (let* ((base-dir (file-name-as-directory (org-publish-property :base-directory project))) (extension (or (org-publish-property :base-extension project) "org")) - (match (and (not (eq extension 'any)) - (concat "^[^\\.].*\\.\\(" extension "\\)$"))) + (match (if (eq extension 'any) "" + (format "^[^\\.].*\\.\\(%s\\)$" extension))) (base-files (cl-remove-if #'file-directory-p (if (org-publish-property :recursive project) commit 57ab49f7da6e7c978932b565ed63d66c07ce8c30 Author: Alan Mackenzie Date: Mon Sep 18 08:52:24 2017 +0000 Fix irregularities with CC Mode fontification, particularly with "known types" * lisp/progmodes/cc-fonts.el (c-font-lock-declarators): Introduce a new optional parameter, template-class. In "class ", fontify "Y" as a type. (c-font-lock-single-decl): New variable template-class, set to non-nil when we have a construct like the above. Pass this as argument to c-font-lock-declarators. (c-font-lock-cut-off-declarators): Check more rigorously that a declaration being processed starts before the function's starting position. (c-complex-decl-matchers): Remove the redundant clause which fontified "types preceded by, e.g., "struct"". * lisp/progmodes/cc-langs.el (c-template-typename-kwds) (c-template-typename-key): New lang defconsts and defvar. diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index 5aefdea330..02b685d240 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -1026,7 +1026,8 @@ casts and declarations are fontified. Used on level 2 and higher." (goto-char pos))))) nil) -(defun c-font-lock-declarators (limit list types not-top) +(defun c-font-lock-declarators (limit list types not-top + &optional template-class) ;; Assuming the point is at the start of a declarator in a declaration, ;; fontify the identifier it declares. (If TYPES is set, it does this via ;; the macro `c-fontify-types-and-refs'.) @@ -1040,6 +1041,11 @@ casts and declarations are fontified. Used on level 2 and higher." ;; non-nil, we are not at the top-level ("top-level" includes being directly ;; inside a class or namespace, etc.). ;; + ;; TEMPLATE-CLASS is non-nil when the declaration is in template delimiters + ;; and was introduced by, e.g. "typename" or "class", such that if there is + ;; a default (introduced by "="), it will be fontified as a type. + ;; E.g. "". + ;; ;; Nil is always returned. The function leaves point at the delimiter after ;; the last declarator it processes. ;; @@ -1112,6 +1118,13 @@ casts and declarations are fontified. Used on level 2 and higher." (goto-char next-pos) (setq pos nil) ; So as to terminate the enclosing `while' form. + (if (and template-class + (eq got-init ?=) ; C++ ""? + (c-forward-token-2 1 nil limit) ; Over "=" + (let ((c-promote-possible-types t)) + (c-forward-type t))) ; Over "Y" + (setq list nil)) ; Shouldn't be needed. We can't have a list, here. + (when list ;; Jump past any initializer or function prototype to see if ;; there's a ',' to continue at. @@ -1340,8 +1353,12 @@ casts and declarations are fontified. Used on level 2 and higher." (c-backward-syntactic-ws) (and (c-simple-skip-symbol-backward) (looking-at c-paren-stmt-key)))) - t))) - + t)) + (template-class (and (eq context '<>) + (save-excursion + (goto-char match-pos) + (c-forward-syntactic-ws) + (looking-at c-template-typename-key))))) ;; Fix the `c-decl-id-start' or `c-decl-type-start' property ;; before the first declarator if it's a list. ;; `c-font-lock-declarators' handles the rest. @@ -1353,10 +1370,9 @@ casts and declarations are fontified. Used on level 2 and higher." (if (cadr decl-or-cast) 'c-decl-type-start 'c-decl-id-start))))) - (c-font-lock-declarators (min limit (point-max)) decl-list - (cadr decl-or-cast) (not toplev))) + (cadr decl-or-cast) (not toplev) template-class)) ;; A declaration has been successfully identified, so do all the ;; fontification of types and refs that've been recorded. @@ -1650,7 +1666,8 @@ casts and declarations are fontified. Used on level 2 and higher." ;; font-lock-keyword-face. It always returns NIL to inhibit this and ;; prevent a repeat invocation. See elisp/lispref page "Search-based ;; fontification". - (let ((decl-search-lim (c-determine-limit 1000)) + (let ((here (point)) + (decl-search-lim (c-determine-limit 1000)) paren-state encl-pos token-end context decl-or-cast start-pos top-level c-restricted-<>-arglists c-recognize-knr-p) ; Strictly speaking, bogus, but it @@ -1667,26 +1684,27 @@ casts and declarations are fontified. Used on level 2 and higher." (when (or (bobp) (memq (char-before) '(?\; ?{ ?}))) (setq token-end (point)) - (c-forward-syntactic-ws) - ;; We're now putatively at the declaration. - (setq start-pos (point)) - (setq paren-state (c-parse-state)) - ;; At top level or inside a "{"? - (if (or (not (setq encl-pos - (c-most-enclosing-brace paren-state))) - (eq (char-after encl-pos) ?\{)) - (progn - (setq top-level (c-at-toplevel-p)) - (let ((got-context (c-get-fontification-context - token-end nil top-level))) - (setq context (car got-context) - c-restricted-<>-arglists (cdr got-context))) - (setq decl-or-cast - (c-forward-decl-or-cast-1 token-end context nil)) - (when (consp decl-or-cast) - (goto-char start-pos) - (c-font-lock-single-decl limit decl-or-cast token-end - context top-level))))))) + (c-forward-syntactic-ws here) + (when (< (point) here) + ;; We're now putatively at the declaration. + (setq start-pos (point)) + (setq paren-state (c-parse-state)) + ;; At top level or inside a "{"? + (if (or (not (setq encl-pos + (c-most-enclosing-brace paren-state))) + (eq (char-after encl-pos) ?\{)) + (progn + (setq top-level (c-at-toplevel-p)) + (let ((got-context (c-get-fontification-context + token-end nil top-level))) + (setq context (car got-context) + c-restricted-<>-arglists (cdr got-context))) + (setq decl-or-cast + (c-forward-decl-or-cast-1 token-end context nil)) + (when (consp decl-or-cast) + (goto-char start-pos) + (c-font-lock-single-decl limit decl-or-cast token-end + context top-level)))))))) nil)) (defun c-font-lock-enclosing-decls (limit) @@ -1996,85 +2014,6 @@ on level 2 only and so aren't combined with `c-complex-decl-matchers'." 2 font-lock-type-face) `(,(concat "\\<\\(" re "\\)\\>") 1 'font-lock-type-face))) - - ;; Fontify types preceded by `c-type-prefix-kwds' (e.g. "struct"). - ,@(when (c-lang-const c-type-prefix-kwds) - `((,(byte-compile - `(lambda (limit) - (c-fontify-types-and-refs - ((c-promote-possible-types t) - ;; The font-lock package in Emacs is known to clobber - ;; `parse-sexp-lookup-properties' (when it exists). - (parse-sexp-lookup-properties - (cc-eval-when-compile - (boundp 'parse-sexp-lookup-properties)))) - (save-restriction - ;; Narrow to avoid going past the limit in - ;; `c-forward-type'. - (narrow-to-region (point) limit) - (while (re-search-forward - ,(concat "\\<\\(" - (c-make-keywords-re nil - (c-lang-const c-type-prefix-kwds)) - "\\)\\>") - limit t) - (unless (c-skip-comments-and-strings limit) - (c-forward-syntactic-ws) - ;; Handle prefix declaration specifiers. - (while - (or - (when (or (looking-at c-prefix-spec-kwds-re) - (and (c-major-mode-is 'java-mode) - (looking-at "@[A-Za-z0-9]+"))) - (c-forward-keyword-clause 1) - t) - (when (and c-opt-cpp-prefix - (looking-at - c-noise-macro-with-parens-name-re)) - (c-forward-noise-clause) - t))) - ,(if (c-major-mode-is 'c++-mode) - `(when (and (c-forward-type) - (eq (char-after) ?=)) - ;; In C++ we additionally check for a "class - ;; X = Y" construct which is used in - ;; templates, to fontify Y as a type. - (forward-char) - (c-forward-syntactic-ws) - (c-forward-type)) - `(c-forward-type)) - ))))))))) - - ;; Fontify symbols after closing braces as declaration - ;; identifiers under the assumption that they are part of - ;; declarations like "class Foo { ... } foo;". It's too - ;; expensive to check this accurately by skipping past the - ;; brace block, so we use the heuristic that it's such a - ;; declaration if the first identifier is on the same line as - ;; the closing brace. `c-font-lock-declarations' will later - ;; override it if it turns out to be an new declaration, but - ;; it will be wrong if it's an expression (see the test - ;; decls-8.cc). -;; ,@(when (c-lang-const c-opt-block-decls-with-vars-key) -;; `((,(c-make-font-lock-search-function -;; (concat "}" -;; (c-lang-const c-single-line-syntactic-ws) -;; "\\(" ; 1 + c-single-line-syntactic-ws-depth -;; (c-lang-const c-type-decl-prefix-key) -;; "\\|" -;; (c-lang-const c-symbol-key) -;; "\\)") -;; `((c-font-lock-declarators limit t nil) ; That nil says use `font-lock-variable-name-face'; -;; ; t would mean `font-lock-function-name-face'. -;; (progn -;; (c-put-char-property (match-beginning 0) 'c-type -;; 'c-decl-id-start) -;; ; 'c-decl-type-start) -;; (goto-char (match-beginning -;; ,(1+ (c-lang-const -;; c-single-line-syntactic-ws-depth))))) -;; (goto-char (match-end 0))))))) - ;; Fontify the type in C++ "new" expressions. ,@(when (c-major-mode-is 'c++-mode) ;; This pattern is a probably a "(MATCHER . ANCHORED-HIGHLIGHTER)" diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index bcda093678..227b3e1648 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -1896,6 +1896,17 @@ the type of that expression." t (c-make-keywords-re t (c-lang-const c-typeof-kwds))) (c-lang-defvar c-typeof-key (c-lang-const c-typeof-key)) +(c-lang-defconst c-template-typename-kwds + "Keywords which, within a template declaration, can introduce a +declaration with a type as a default value. This is used only in +C++ Mode, e.g. \"\"." + t nil + c++ '("class" "typename")) + +(c-lang-defconst c-template-typename-key + t (c-make-keywords-re t (c-lang-const c-template-typename-kwds))) +(c-lang-defvar c-template-typename-key (c-lang-const c-template-typename-key)) + (c-lang-defconst c-type-prefix-kwds "Keywords where the following name - if any - is a type name, and where the keyword together with the symbol works as a type in commit bd5326f879c089745c33871efc8682da5c206f80 Author: Eli Zaretskii Date: Thu Oct 5 17:57:58 2017 +0300 Fix breakage due to recent change in tabulated-list-print-entry * lisp/emacs-lisp/tabulated-list.el (tabulated-list-printer): Update the doc string. (tabulated-list-print-entry): Revert to using only 2 arguments. Update the doc string. (tabulated-list-entry-lnum-width): New defvar. (tabulated-list-print): Compute the width of line-number display once, then store that value in tabulated-list-entry-lnum-width, for tabulated-list-printer to use. (Bug#28704) diff --git a/lisp/emacs-lisp/tabulated-list.el b/lisp/emacs-lisp/tabulated-list.el index 6c5874598a..d1d7c0a804 100644 --- a/lisp/emacs-lisp/tabulated-list.el +++ b/lisp/emacs-lisp/tabulated-list.el @@ -98,12 +98,9 @@ This is commonly used to recompute `tabulated-list-entries'.") (defvar-local tabulated-list-printer 'tabulated-list-print-entry "Function for inserting a Tabulated List entry at point. -It is called with two mandatory arguments, ID and COLS, and one -optional argument, INDENT. ID is a Lisp object identifying the -entry, and COLS is a vector of column -descriptors, as documented in `tabulated-list-entries'. -INDENT, if present, is the initial indentation of the entry in -columns, it is used when `display-line-numbers' is in effect.") +It is called with two arguments, ID and COLS. ID is a Lisp +object identifying the entry, and COLS is a vector of column +descriptors, as documented in `tabulated-list-entries'.") (defvar tabulated-list--near-rows) @@ -332,6 +329,8 @@ Check the current row, the previous one and the next row." (string-width (if (stringp nt) nt (car nt))))) tabulated-list--near-rows))) +(defvar tabulated-list-entry-lnum-width nil) + (defun tabulated-list-print (&optional remember-pos update) "Populate the current Tabulated List mode buffer. This sorts the `tabulated-list-entries' list if sorting is @@ -353,7 +352,7 @@ changing `tabulated-list-sort-key'." (funcall tabulated-list-entries) tabulated-list-entries)) (sorter (tabulated-list--get-sorter)) - entry-id saved-pt saved-col window-line lnum-width) + entry-id saved-pt saved-col window-line) (and remember-pos (setq entry-id (tabulated-list-get-id)) (setq saved-col (current-column)) @@ -374,7 +373,7 @@ changing `tabulated-list-sort-key'." (unless tabulated-list-use-header-line (tabulated-list-print-fake-header))) ;; Finally, print the resulting list. - (setq lnum-width (tabulated-list-line-number-width)) + (setq tabulated-list-entry-lnum-width (tabulated-list-line-number-width)) (while entries (let* ((elt (car entries)) (tabulated-list--near-rows @@ -389,7 +388,7 @@ changing `tabulated-list-sort-key'." saved-pt (point))) ;; If the buffer is empty, simply print each elt. (if (or (not update) (eobp)) - (apply tabulated-list-printer (append elt (list lnum-width))) + (apply tabulated-list-printer elt) (while (let ((local-id (tabulated-list-get-id))) ;; If we find id, then nothing to update. (cond ((equal id local-id) @@ -402,8 +401,7 @@ changing `tabulated-list-sort-key'." ;; FIXME: Might be faster if ;; don't construct this list. (list local-id (tabulated-list-get-entry)))) - (apply tabulated-list-printer - (append elt (list lnum-width))) + (apply tabulated-list-printer elt) nil) ;; We find an entry that sorts before id, ;; it needs to be deleted. @@ -421,22 +419,18 @@ changing `tabulated-list-sort-key'." (recenter window-line))) (goto-char (point-min))))) -(defun tabulated-list-print-entry (id cols &optional indent) +(defun tabulated-list-print-entry (id cols) "Insert a Tabulated List entry at point. This is the default `tabulated-list-printer' function. ID is a Lisp object identifying the entry to print, and COLS is a vector -of column descriptors. -Optional argument INDENT is the initial indent of the entry, in -columns. This is used when `display-line-numbers' is in effect. -If INDENT is omitted or nil, it is treated as zero." +of column descriptors." (let ((beg (point)) (x (max tabulated-list-padding 0)) (ncols (length tabulated-list-format)) (inhibit-read-only t)) - (or indent (setq indent 0)) - (setq x (+ x indent)) + (setq x (+ x tabulated-list-entry-lnum-width)) (if (> tabulated-list-padding 0) - (insert (make-string (- x indent) ?\s))) + (insert (make-string (- x tabulated-list-entry-lnum-width) ?\s))) (let ((tabulated-list--near-rows ; Bind it if not bound yet (Bug#25506). (or (bound-and-true-p tabulated-list--near-rows) (list (or (tabulated-list-get-entry (point-at-bol 0)) commit 0c8f4e5ea122df9526037286e95844c64f3d964d Author: Gemini Lasswell Date: Thu Oct 5 16:49:34 2017 +0300 * lisp/ses.el (ses-print-cell): Fix alignment of text cells. (Bug#27653) diff --git a/lisp/ses.el b/lisp/ses.el index 9221476e7a..4c19c70c5d 100644 --- a/lisp/ses.el +++ b/lisp/ses.el @@ -1254,8 +1254,7 @@ preceding cell has spilled over." ((< len width) ;; Fill field to length with spaces. (setq len (make-string (- width len) ?\s) - text (if (or (stringp value) - (eq ses-call-printer-return t)) + text (if (eq ses-call-printer-return t) (concat text len) (concat len text)))) ((> len width) commit c625fb645afc75fb2b2ece771feb9472937c192d Author: Alexander Gramiak Date: Thu Oct 5 13:50:02 2017 +0300 Set xterm click count to 1 even with no last click * lisp/xt-mouse.el (xterm-mouse-event): Move the check for the last click so that click-count is initialized properly. Handle the value of t for double-click-time. (Bug#28658) diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el index 772a72d5c5..d268e1a3fe 100644 --- a/lisp/xt-mouse.el +++ b/lisp/xt-mouse.el @@ -288,8 +288,10 @@ which is the \"1006\" extension implemented in Xterm >= 277." (string-match "down-" last-name) (equal name (replace-match "" t t last-name))) (xterm-mouse--set-click-count event click-count))) - ((not last-time) nil) - ((and (> double-click-time (* 1000 (- this-time last-time))) + ((and last-time + double-click-time + (or (eq double-click-time t) + (> double-click-time (* 1000 (- this-time last-time)))) (equal last-name (replace-match "" t t name))) (setq click-count (1+ click-count)) (xterm-mouse--set-click-count event click-count)) commit e3f4b71c9de72bce59b4b7cb71627b626e82b573 Author: Vasilij Schneidermann Date: Thu Oct 5 13:00:13 2017 +0300 Support indirection for all shr-tag-* calls The 'shr-external-rendering-functions' variable was previously only honored in the shr-descend function, now all direct calls to the shr-tag-* functions have been replaced by a call to 'shr-indirect-call' which tries using an alternative rendering function first. * lisp/net/shr.el (shr-indirect-call): New helper function. (shr-descend, shr-tag-object, shr-tag-video): (shr-collect-extra-strings-in-table): Fix callers to call via shr-indirect-call. (Bug#28402) diff --git a/lisp/net/shr.el b/lisp/net/shr.el index 7af6148e47..fe5197b35f 100644 --- a/lisp/net/shr.el +++ b/lisp/net/shr.el @@ -470,12 +470,20 @@ size, and full-buffer size." (shr-insert sub) (shr-descend sub)))) +(defun shr-indirect-call (tag-name dom &rest args) + (let ((function (intern (concat "shr-tag-" (symbol-name tag-name)) obarray)) + ;; Allow other packages to override (or provide) rendering + ;; of elements. + (external (cdr (assq tag-name shr-external-rendering-functions)))) + (cond (external + (apply external dom args)) + ((fboundp function) + (apply function dom args)) + (t + (apply 'shr-generic dom args))))) + (defun shr-descend (dom) - (let ((function - (intern (concat "shr-tag-" (symbol-name (dom-tag dom))) obarray)) - ;; Allow other packages to override (or provide) rendering - ;; of elements. - (external (cdr (assq (dom-tag dom) shr-external-rendering-functions))) + (let ((tag-name (dom-tag dom)) (style (dom-attr dom 'style)) (shr-stylesheet shr-stylesheet) (shr-depth (1+ shr-depth)) @@ -490,12 +498,7 @@ size, and full-buffer size." (setq style nil))) ;; If we have a display:none, then just ignore this part of the DOM. (unless (equal (cdr (assq 'display shr-stylesheet)) "none") - (cond (external - (funcall external dom)) - ((fboundp function) - (funcall function dom)) - (t - (shr-generic dom))) + (shr-indirect-call tag-name dom) (when (and shr-target-id (equal (dom-attr dom 'id) shr-target-id)) ;; If the element was empty, we don't have anything to put the @@ -1404,7 +1407,7 @@ ones, in case fg and bg are nil." (when url (cond (image - (shr-tag-img dom url) + (shr-indirect-call 'img dom url) (setq dom nil)) (multimedia (shr-insert " [multimedia] ") @@ -1469,7 +1472,7 @@ The preference is a float determined from `shr-prefer-media-type'." (unless url (setq url (car (shr--extract-best-source dom)))) (if (> (length image) 0) - (shr-tag-img nil image) + (shr-indirect-call 'img nil image) (shr-insert " [video] ")) (shr-urlify start (shr-expand-url url)))) @@ -1964,9 +1967,9 @@ flags that control whether to collect or render objects." do (setq tag (dom-tag child)) and unless (memq tag '(comment style)) if (eq tag 'img) - do (shr-tag-img child) + do (shr-indirect-call 'img child) else if (eq tag 'object) - do (shr-tag-object child) + do (shr-indirect-call 'object child) else do (setq recurse t) and if (eq tag 'tr) @@ -1980,7 +1983,7 @@ flags that control whether to collect or render objects." do (setq flags nil) else if (car flags) do (setq recurse nil) - (shr-tag-table child) + (shr-indirect-call 'table child) end end end end end end end end end end when recurse append (shr-collect-extra-strings-in-table child flags))) commit 1c66720f3b2308acae4ed91cb65859c2bd7965ee Author: Eli Zaretskii Date: Thu Oct 5 12:55:47 2017 +0300 ; * etc/NEWS: Fix the "manual-update" marker of a recent addition. diff --git a/etc/NEWS b/etc/NEWS index 62a9ea2181..7f89ef92ec 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1112,7 +1112,7 @@ See the 'vc-faces' customization group. *** 'vc-dir-mode' now binds 'vc-log-outgoing' to 'O'; and has various branch-related commands on a keymap bound to 'B'. ---- ++++ *** 'vc-region-history' is now bound to 'C-x v h', replacing the older 'vc-insert-headers' binding. commit 3c4ff63bea662e2b89853894c5da69002a61ed5b Author: Eli Zaretskii Date: Thu Oct 5 12:41:36 2017 +0300 Speed up list-packages when 'visual' line numbers are displayed * lisp/emacs-lisp/tabulated-list.el (tabulated-list-printer): Update the doc string. (tabulated-list-print-entry): Accept an additional optional argument INDENT. Update the doc string. (tabulated-list-print): Compute the width of line-number display once, then call tabulated-list-printer with that value as 3rd argument. (Bug#28704) diff --git a/lisp/emacs-lisp/tabulated-list.el b/lisp/emacs-lisp/tabulated-list.el index e940588db7..6c5874598a 100644 --- a/lisp/emacs-lisp/tabulated-list.el +++ b/lisp/emacs-lisp/tabulated-list.el @@ -98,9 +98,12 @@ This is commonly used to recompute `tabulated-list-entries'.") (defvar-local tabulated-list-printer 'tabulated-list-print-entry "Function for inserting a Tabulated List entry at point. -It is called with two arguments, ID and COLS. ID is a Lisp -object identifying the entry, and COLS is a vector of column -descriptors, as documented in `tabulated-list-entries'.") +It is called with two mandatory arguments, ID and COLS, and one +optional argument, INDENT. ID is a Lisp object identifying the +entry, and COLS is a vector of column +descriptors, as documented in `tabulated-list-entries'. +INDENT, if present, is the initial indentation of the entry in +columns, it is used when `display-line-numbers' is in effect.") (defvar tabulated-list--near-rows) @@ -350,7 +353,7 @@ changing `tabulated-list-sort-key'." (funcall tabulated-list-entries) tabulated-list-entries)) (sorter (tabulated-list--get-sorter)) - entry-id saved-pt saved-col window-line) + entry-id saved-pt saved-col window-line lnum-width) (and remember-pos (setq entry-id (tabulated-list-get-id)) (setq saved-col (current-column)) @@ -371,6 +374,7 @@ changing `tabulated-list-sort-key'." (unless tabulated-list-use-header-line (tabulated-list-print-fake-header))) ;; Finally, print the resulting list. + (setq lnum-width (tabulated-list-line-number-width)) (while entries (let* ((elt (car entries)) (tabulated-list--near-rows @@ -383,9 +387,9 @@ changing `tabulated-list-sort-key'." (equal entry-id id) (setq entry-id nil saved-pt (point))) - ;; If the buffer this empty, simply print each elt. + ;; If the buffer is empty, simply print each elt. (if (or (not update) (eobp)) - (apply tabulated-list-printer elt) + (apply tabulated-list-printer (append elt (list lnum-width))) (while (let ((local-id (tabulated-list-get-id))) ;; If we find id, then nothing to update. (cond ((equal id local-id) @@ -398,7 +402,8 @@ changing `tabulated-list-sort-key'." ;; FIXME: Might be faster if ;; don't construct this list. (list local-id (tabulated-list-get-entry)))) - (apply tabulated-list-printer elt) + (apply tabulated-list-printer + (append elt (list lnum-width))) nil) ;; We find an entry that sorts before id, ;; it needs to be deleted. @@ -416,20 +421,22 @@ changing `tabulated-list-sort-key'." (recenter window-line))) (goto-char (point-min))))) -(defun tabulated-list-print-entry (id cols) +(defun tabulated-list-print-entry (id cols &optional indent) "Insert a Tabulated List entry at point. This is the default `tabulated-list-printer' function. ID is a Lisp object identifying the entry to print, and COLS is a vector -of column descriptors." +of column descriptors. +Optional argument INDENT is the initial indent of the entry, in +columns. This is used when `display-line-numbers' is in effect. +If INDENT is omitted or nil, it is treated as zero." (let ((beg (point)) (x (max tabulated-list-padding 0)) (ncols (length tabulated-list-format)) - (lnum-width (tabulated-list-line-number-width)) (inhibit-read-only t)) - (if display-line-numbers - (setq x (+ x lnum-width))) + (or indent (setq indent 0)) + (setq x (+ x indent)) (if (> tabulated-list-padding 0) - (insert (make-string (- x lnum-width) ?\s))) + (insert (make-string (- x indent) ?\s))) (let ((tabulated-list--near-rows ; Bind it if not bound yet (Bug#25506). (or (bound-and-true-p tabulated-list--near-rows) (list (or (tabulated-list-get-entry (point-at-bol 0)) commit 7a1133f1ff002943ce32b5a05a7261bba520288c Author: João Távora Date: Thu Oct 5 02:42:01 2017 +0100 Misc. minor adjustments to Flymake - Add a half-decent minor-mode menu; - Fix "waiting for backends" mode line message; - Adjust the flymake-diag-region API; - Autoload the flymake-log macro; - Auto-disable the legacy backend in more situations; - Fix a couple of warnings in legacy backend. * lisp/progmodes/flymake-proc.el (flymake-proc--diagnostics-for-pattern): Use new flymake-diag-region. * lisp/progmodes/flymake-proc.el (flymake-proc-legacy-flymake): Do error when no buffer-file-name or not writable. (flymake-proc-legacy-flymake) (flymake-proc-simple-cleanup): Don't reference flymake-last-change-time * lisp/progmodes/flymake.el (flymake-diag-region): Autoload. Take buffer as first argument. * lisp/progmodes/flymake.el (flymake-switch-to-log-buffer): New command. (flymake-menu): Add a simple menu. (flymake--mode-line-format): Use menu. Fix message. Switch to log buffer when clicking exceptional warnings. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 47ec27f611..d08819713a 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -522,13 +522,13 @@ Create parent directories as needed." for buffer = (and full-file (find-buffer-visiting full-file)) if (and (eq buffer (process-buffer proc)) message) - collect (with-current-buffer buffer - (pcase-let ((`(,beg . ,end) - (flymake-diag-region line-number col-number))) - (flymake-make-diagnostic - buffer beg end - (guess-type flymake-proc-diagnostic-type-pred message) - message))) + collect (pcase-let ((`(,beg . ,end) + (flymake-diag-region buffer line-number col-number))) + (flymake-make-diagnostic + buffer beg end + (with-current-buffer buffer + (guess-type flymake-proc-diagnostic-type-pred message)) + message)) else do (flymake-log 2 "Reference to file %s is out of scope" fname)) (error @@ -742,16 +742,18 @@ can also be executed interactively independently of "There's already a Flymake process running in this buffer") (kill-process proc)))) (when - ;; A number of situations make us not want to error right away - ;; (and disable ourselves), in case the situation changes in - ;; the near future. - (and buffer-file-name - ;; Since we write temp files in current dir, there's no point - ;; trying if the directory is read-only (bug#8954). - (file-writable-p (file-name-directory buffer-file-name)) - (or (not flymake-proc-compilation-prevents-syntax-check) + ;; This particular situation make us not want to error right + ;; away (and disable ourselves), in case the situation changes + ;; in the near future. + (and (or (not flymake-proc-compilation-prevents-syntax-check) (not (flymake-proc--compilation-is-running)))) - (let ((init-f (flymake-proc--get-init-function buffer-file-name))) + (let ((init-f + (and + buffer-file-name + ;; Since we write temp files in current dir, there's no point + ;; trying if the directory is read-only (bug#8954). + (file-writable-p (file-name-directory buffer-file-name)) + (flymake-proc--get-init-function buffer-file-name)))) (unless init-f (error "Can find a suitable init function")) (flymake-proc--clear-buildfile-cache) (flymake-proc--clear-project-include-dirs-cache) @@ -768,7 +770,6 @@ can also be executed interactively independently of (flymake-log 0 "init function %s for %s failed, cleaning up" init-f buffer-file-name)) (t - (setq flymake-last-change-time nil) (setq proc (let ((default-directory (or dir default-directory))) (when dir @@ -878,8 +879,7 @@ can also be executed interactively independently of (defun flymake-proc-simple-cleanup () "Do cleanup after `flymake-proc-init-create-temp-buffer-copy'. Delete temp file." - (flymake-proc--safe-delete-file flymake-proc--temp-source-file-name) - (setq flymake-last-change-time nil)) + (flymake-proc--safe-delete-file flymake-proc--temp-source-file-name)) (defun flymake-proc-get-real-file-name (file-name-from-err-msg) "Translate file name from error message to \"real\" file name. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 0b28dc31cf..acc0637ec3 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -180,6 +180,11 @@ If nil, never start checking buffer automatically like this." level) "*Flymake log*"))) +(defun flymake-switch-to-log-buffer () + "Go to the *Flymake log* buffer." + (interactive) + (switch-to-buffer "*Flymake log*")) + ;;;###autoload (defmacro flymake-log (level msg &rest args) "Log, at level LEVEL, the message MSG formatted with ARGS. @@ -282,41 +287,43 @@ verify FILTER, a function, and sort them by COMPARE (using KEY)." (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") -(defun flymake-diag-region (line &optional col) - "Compute region (BEG . END) corresponding to LINE and COL. -If COL is nil, return a region just for LINE. -Return nil if the region is invalid." +;;;###autoload +(defun flymake-diag-region (buffer line &optional col) + "Compute BUFFER's region (BEG . END) corresponding to LINE and COL. +If COL is nil, return a region just for LINE. Return nil if the +region is invalid." (condition-case-unless-debug _err - (let ((line (min (max line 1) - (line-number-at-pos (point-max) 'absolute)))) - (save-excursion - (goto-char (point-min)) - (forward-line (1- line)) - (cl-flet ((fallback-bol - () (progn (back-to-indentation) (point))) - (fallback-eol - (beg) - (progn - (end-of-line) - (skip-chars-backward " \t\f\t\n" beg) - (if (eq (point) beg) - (line-beginning-position 2) - (point))))) - (if (and col (cl-plusp col)) - (let* ((beg (progn (forward-char (1- col)) - (point))) - (sexp-end (ignore-errors (end-of-thing 'sexp))) - (end (or (and sexp-end - (not (= sexp-end beg)) - sexp-end) - (ignore-errors (goto-char (1+ beg))))) - (safe-end (or end - (fallback-eol beg)))) - (cons (if end beg (fallback-bol)) - safe-end)) - (let* ((beg (fallback-bol)) - (end (fallback-eol beg))) - (cons beg end)))))) + (with-current-buffer buffer + (let ((line (min (max line 1) + (line-number-at-pos (point-max) 'absolute)))) + (save-excursion + (goto-char (point-min)) + (forward-line (1- line)) + (cl-flet ((fallback-bol + () (progn (back-to-indentation) (point))) + (fallback-eol + (beg) + (progn + (end-of-line) + (skip-chars-backward " \t\f\t\n" beg) + (if (eq (point) beg) + (line-beginning-position 2) + (point))))) + (if (and col (cl-plusp col)) + (let* ((beg (progn (forward-char (1- col)) + (point))) + (sexp-end (ignore-errors (end-of-thing 'sexp))) + (end (or (and sexp-end + (not (= sexp-end beg)) + sexp-end) + (ignore-errors (goto-char (1+ beg))))) + (safe-end (or end + (fallback-eol beg)))) + (cons (if end beg (fallback-bol)) + safe-end)) + (let* ((beg (fallback-bol)) + (end (fallback-eol beg))) + (cons beg end))))))) (error (flymake-error "Invalid region line=%s col=%s" line col)))) (defvar flymake-diagnostic-functions nil @@ -872,8 +879,17 @@ applied." (flymake-goto-next-error (- (or n 1)) filter interactive)) -;;; Mode-line fanciness +;;; Mode-line and menu ;;; +(easy-menu-define flymake-menu flymake-mode-map "Flymake" + `("Flymake" + [ "Go to next error" flymake-goto-next-error t ] + [ "Go to previous error" flymake-goto-prev-error t ] + [ "Check now" flymake-start t ] + [ "Go to log buffer" flymake-switch-to-log-buffer t ] + "--" + [ "Turn off Flymake" flymake-mode t ])) + (defvar flymake--mode-line-format `(:eval (flymake--mode-line-format))) (put 'flymake--mode-line-format 'risky-local-variable t) @@ -903,18 +919,16 @@ applied." "mouse-1: go to log buffer ") keymap ,(let ((map (make-sparse-keymap))) - (define-key map [mode-line mouse-1] - (lambda (_event) - (interactive "e") - (switch-to-buffer "*Flymake log*"))) + (define-key map [mode-line down-mouse-1] + flymake-menu) map)) ,@(pcase-let ((`(,ind ,face ,explain) (cond ((null known) `("?" mode-line "No known backends")) (some-waiting `("Wait" compilation-mode-line-run - ,(format "Waiting for %s running backends" - (length running)))) + ,(format "Waiting for %s running backend(s)" + (length some-waiting)))) (all-disabled `("!" compilation-mode-line-run "All backends disabled")) @@ -924,7 +938,12 @@ applied." `((":" (:propertize ,ind face ,face - help-echo ,explain))))) + help-echo ,explain + keymap + ,(let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + 'flymake-switch-to-log-buffer) + map)))))) ,@(unless (or all-disabled (null known)) (cl-loop commit 3d8df4d63604fd64c9feb3b43c4b9fa2fc487347 Author: Johan Bockgård Date: Wed Oct 4 15:24:43 2017 -0700 Fix search for ~/.Xdefaults-HOSTNAME * src/xrdb.c (get_environ_db): Fix typo when handling ~/.Xdefaults-HOSTNAME (Bug#28708). diff --git a/src/xrdb.c b/src/xrdb.c index 15a01475b7..3c1bad1c73 100644 --- a/src/xrdb.c +++ b/src/xrdb.c @@ -345,6 +345,7 @@ get_user_db (Display *display) db = XrmGetStringDatabase (xdefs); else { + /* Use ~/.Xdefaults. */ char *home = gethomedir (); ptrdiff_t homelen = strlen (home); char *filename = xrealloc (home, homelen + sizeof xdefaults); @@ -375,13 +376,15 @@ get_environ_db (void) if (!p) { + /* Use ~/.Xdefaults-HOSTNAME. */ char *home = gethomedir (); ptrdiff_t homelen = strlen (home); Lisp_Object system_name = Fsystem_name (); ptrdiff_t filenamesize = (homelen + sizeof xdefaults - + SBYTES (system_name)); + + 1 + SBYTES (system_name)); p = filename = xrealloc (home, filenamesize); - lispstpcpy (stpcpy (filename + homelen, xdefaults), system_name); + lispstpcpy (stpcpy (stpcpy (filename + homelen, xdefaults), "-"), + system_name); } db = XrmGetFileDatabase (p); commit 3db388b0bf83d3138562f09ce25fab8ba89bcc81 Author: Paul Eggert Date: Wed Oct 4 14:29:58 2017 -0700 Speed up (format "%s" STRING) and the like Although the Lisp manual said that ‘format’ returns a newly-allocated string, this was not true for a few cases like (format "%s" ""), and fixing the documentation to allow reuse of arguments lets us improve performance in common cases like (format "foo") and (format "%s" "foo") (Bug#28625). * doc/lispref/strings.texi (Formatting Strings): * etc/NEWS: Say that the result of ‘format’ might not be newly allocated. * src/callint.c (Fcall_interactively): * src/dbusbind.c (XD_OBJECT_TO_STRING): * src/editfns.c (Fmessage, Fmessage_box): * src/xdisp.c (vadd_to_log, Ftrace_to_stderr): Just use Fformat or Fformat_message, as that’s simpler and no longer makes unnecessary copies. * src/editfns.c (styled_format): Remove last argument, as it is no longer needed: all callers now want it to behave as if it were true. All remaining callers changed. Make this function static again. Simplify the function now that we no longer need to worry about whether the optimization is allowed. diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index dd004927ca..09c3bdf71f 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -812,15 +812,19 @@ formatting feature described here; they differ from @code{format-message} only in how they use the result of formatting. @defun format string &rest objects -This function returns a new string that is made by copying -@var{string} and then replacing any format specification -in the copy with encodings of the corresponding @var{objects}. The +This function returns a string equal to @var{string}, replacing any format +specifications with encodings of the corresponding @var{objects}. The arguments @var{objects} are the computed values to be formatted. The characters in @var{string}, other than the format specifications, are copied directly into the output, including their text properties, if any. Any text properties of the format specifications are copied to the produced string representations of the argument @var{objects}. + +The output string need not be newly-allocated. For example, if +@code{x} is the string @code{"foo"}, the expressions @code{(eq x +(format x))} and @code{(eq x (format "%s" x))} might both yield +@code{t}. @end defun @defun format-message string &rest objects diff --git a/etc/NEWS b/etc/NEWS index 62d2450f9a..62a9ea2181 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1556,6 +1556,13 @@ Emacs integers with %e, %f, or %g conversions. For example, on these hosts (eql N (string-to-number (format "%.0f" N))) now returns t for all Emacs integers N. ++++ +** 'format' is no longer documented to return a newly-allocated string. +This documentation was not correct, as (eq x (format x)) returned t +when x was the empty string. 'format' now takes advantage of the doc +change to avoid making copies of strings in common cases like (format +"foo") and (format "%s" "foo"). + --- ** Calls that accept floating-point integers (for use on hosts with limited integer range) now signal an error if arguments are not diff --git a/src/callint.c b/src/callint.c index 469205cc38..5d88082e38 100644 --- a/src/callint.c +++ b/src/callint.c @@ -272,7 +272,7 @@ invoke it. If KEYS is omitted or nil, the return value of { /* `args' will contain the array of arguments to pass to the function. `visargs' will contain the same list but in a nicer form, so that if we - pass it to styled_format it will be understandable to a human. */ + pass it to Fformat_message it will be understandable to a human. */ Lisp_Object *args, *visargs; Lisp_Object specs; Lisp_Object filter_specs; @@ -502,7 +502,7 @@ invoke it. If KEYS is omitted or nil, the return value of for (i = 2; *tem; i++) { visargs[1] = make_string (tem + 1, strcspn (tem + 1, "\n")); - callint_message = styled_format (i - 1, visargs + 1, true, false); + callint_message = Fformat_message (i - 1, visargs + 1); switch (*tem) { diff --git a/src/dbusbind.c b/src/dbusbind.c index 789aa00861..4a7068416f 100644 --- a/src/dbusbind.c +++ b/src/dbusbind.c @@ -237,8 +237,7 @@ static char * XD_OBJECT_TO_STRING (Lisp_Object object) { AUTO_STRING (format, "%s"); - Lisp_Object args[] = { format, object }; - return SSDATA (styled_format (ARRAYELTS (args), args, false, false)); + return SSDATA (CALLN (Fformat, format, object)); } #define XD_DBUS_VALIDATE_BUS_ADDRESS(bus) \ diff --git a/src/editfns.c b/src/editfns.c index e326604467..d88a913c66 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -74,6 +74,7 @@ static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec, static long int tm_gmtoff (struct tm *); static int tm_diff (struct tm *, struct tm *); static void update_buffer_properties (ptrdiff_t, ptrdiff_t); +static Lisp_Object styled_format (ptrdiff_t, Lisp_Object *, bool); #ifndef HAVE_TM_GMTOFF # define HAVE_TM_GMTOFF false @@ -3958,7 +3959,7 @@ usage: (message FORMAT-STRING &rest ARGS) */) } else { - Lisp_Object val = styled_format (nargs, args, true, false); + Lisp_Object val = Fformat_message (nargs, args); message3 (val); return val; } @@ -3984,7 +3985,7 @@ usage: (message-box FORMAT-STRING &rest ARGS) */) } else { - Lisp_Object val = styled_format (nargs, args, true, false); + Lisp_Object val = Fformat_message (nargs, args); Lisp_Object pane, menu; pane = list1 (Fcons (build_string ("OK"), Qt)); @@ -4140,7 +4141,7 @@ produced text. usage: (format STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { - return styled_format (nargs, args, false, true); + return styled_format (nargs, args, false); } DEFUN ("format-message", Fformat_message, Sformat_message, 1, MANY, 0, @@ -4156,16 +4157,13 @@ and right quote replacement characters are specified by usage: (format-message STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { - return styled_format (nargs, args, true, true); + return styled_format (nargs, args, true); } -/* Implement ‘format-message’ if MESSAGE is true, ‘format’ otherwise. - If NEW_RESULT, the result is a new string; otherwise, the result - may be one of the arguments. */ +/* Implement ‘format-message’ if MESSAGE is true, ‘format’ otherwise. */ -Lisp_Object -styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, - bool new_result) +static Lisp_Object +styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) { ptrdiff_t n; /* The number of the next arg to substitute. */ char initial_buffer[4000]; @@ -4195,9 +4193,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, /* The start and end bytepos in the output string. */ ptrdiff_t start, end; - /* Whether the argument is a newly created string. */ - bool_bf new_string : 1; - /* Whether the argument is a string with intervals. */ bool_bf intervals : 1; } *info; @@ -4241,6 +4236,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, ptrdiff_t ispec; ptrdiff_t nspec = 0; + /* True if a string needs to be allocated to hold the result. */ + bool new_result = false; + /* If we start out planning a unibyte result, then discover it has to be multibyte, we jump back to retry. */ retry: @@ -4360,7 +4358,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, if (nspec < ispec) { spec->argument = args[n]; - spec->new_string = false; spec->intervals = false; nspec = ispec; } @@ -4378,7 +4375,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, { Lisp_Object noescape = conversion == 'S' ? Qnil : Qt; spec->argument = arg = Fprin1_to_string (arg, noescape); - spec->new_string = true; if (STRING_MULTIBYTE (arg) && ! multibyte) { multibyte = true; @@ -4397,7 +4393,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, goto retry; } spec->argument = arg = Fchar_to_string (arg); - spec->new_string = true; } if (!EQ (arg, args[n])) @@ -4421,7 +4416,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, if (conversion == 's') { if (format == end && format - format_start == 2 - && (!new_result || spec->new_string) && ! string_intervals (args[0])) return arg; diff --git a/src/lisp.h b/src/lisp.h index bdb162aea4..266370333f 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3970,7 +3970,6 @@ extern _Noreturn void time_overflow (void); extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, bool); -extern Lisp_Object styled_format (ptrdiff_t, Lisp_Object *, bool, bool); extern void init_editfns (bool); extern void syms_of_editfns (void); diff --git a/src/xdisp.c b/src/xdisp.c index 86164eb9f6..141275f15a 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -10194,7 +10194,7 @@ vadd_to_log (char const *format, va_list ap) for (ptrdiff_t i = 1; i <= nargs; i++) args[i] = va_arg (ap, Lisp_Object); Lisp_Object msg = Qnil; - msg = styled_format (nargs, args, true, false); + msg = Fformat_message (nargs, args); ptrdiff_t len = SBYTES (msg) + 1; USE_SAFE_ALLOCA; @@ -19525,7 +19525,7 @@ DEFUN ("trace-to-stderr", Ftrace_to_stderr, Strace_to_stderr, 1, MANY, "", usage: (trace-to-stderr STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { - Lisp_Object s = styled_format (nargs, args, false, false); + Lisp_Object s = Fformat (nargs, args); fwrite (SDATA (s), 1, SBYTES (s), stderr); return Qnil; } commit 4e0b67ed27114fa2cbebca32567089fd8fa78425 Author: Alan Mackenzie Date: Wed Oct 4 17:34:27 2017 +0000 Fontify untyped function declarations in C Mode correctly. Also correct two bugs where deleting WS at a BOL could leave an untyped function declaration unfontified. * lisp/progmodes/cc-engine.el (c-find-decl-spots): Don't set the flag "top-level" when we're in a macro. (c-forward-decl-or-cast-1): Recognize top-level "foo(bar)" or "foo()" in C Mode as a implicitly typed function declaration. (c-just-after-func-arglist-p): Don't get confused by "defined (foo)" inside a macro. It's not a function plus arglist. * lisp/progmodes/cc-langs.el (c-cpp-expr-functions-key): New defconst and defvar. * lisp/progmodes/cc-mode.el (c-fl-decl-end): After c-forward-declarator, move over any following parenthesis expression (i.e. parameter list). (c-change-expand-fl-region): When c-new-END is at a BOL, include that line in the returned region, to cope with deletions at column 0. diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 9d65383e25..3792835752 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -132,7 +132,7 @@ ;; ;; 'c-not-decl ;; Put on the brace which introduces a brace list and on the commas -;; which separate the element within it. +;; which separate the elements within it. ;; ;; 'c-awk-NL-prop ;; Used in AWK mode to mark the various kinds of newlines. See @@ -5403,8 +5403,8 @@ comment at the start of cc-engine.el for more info." (min c-bs-cache-limit pos))) (defun c-update-brace-stack (stack from to) - ;; Give a brace-stack which has the value STACK at position FROM, update it - ;; to it's value at position TO, where TO is after (or equal to) FROM. + ;; Given a brace-stack which has the value STACK at position FROM, update it + ;; to its value at position TO, where TO is after (or equal to) FROM. ;; Return a cons of either TO (if it is outside a literal) and this new ;; value, or of the next position after TO outside a literal and the new ;; value. @@ -5649,11 +5649,13 @@ comment at the start of cc-engine.el for more info." ;; Call CFD-FUN for each possible spot for a declaration, cast or ;; label from the point to CFD-LIMIT. ;; - ;; CFD-FUN is called with point at the start of the spot. It's passed two + ;; CFD-FUN is called with point at the start of the spot. It's passed three ;; arguments: The first is the end position of the token preceding the spot, ;; or 0 for the implicit match at bob. The second is a flag that is t when - ;; the match is inside a macro. Point should be moved forward by at least - ;; one token. + ;; the match is inside a macro. The third is a flag that is t when the + ;; match is at "top level", i.e. outside any brace block, or directly inside + ;; a class or namespace, etc. Point should be moved forward by at least one + ;; token. ;; ;; If CFD-FUN adds `c-decl-end' properties somewhere below the current spot, ;; it should return non-nil to ensure that the next search will find them. @@ -6040,6 +6042,8 @@ comment at the start of cc-engine.el for more info." (setq cfd-macro-end 0) nil)))) ; end of when condition + (when (> cfd-macro-end 0) + (setq cfd-top-level nil)) ; In a macro is "never" at top level. (c-debug-put-decl-spot-faces cfd-match-pos (point)) (if (funcall cfd-fun cfd-match-pos (/= cfd-macro-end 0) cfd-top-level) (setq cfd-prop-match nil)) @@ -8577,7 +8581,13 @@ comment at the start of cc-engine.el for more info." (looking-at c-noise-macro-with-parens-name-re)) (c-forward-noise-clause)) - ((looking-at c-type-decl-suffix-key) + ((and (looking-at c-type-decl-suffix-key) + ;; We avoid recognizing foo(bar) or foo() at top level as a + ;; construct here in C, since we want to recognize this as a + ;; typeless function declaration. + (not (and (c-major-mode-is 'c-mode) + (eq context 'top) + (eq (char-after) ?\))))) (if (eq (char-after) ?\)) (when (> paren-depth 0) (setq paren-depth (1- paren-depth)) @@ -8620,7 +8630,12 @@ comment at the start of cc-engine.el for more info." (save-excursion (goto-char after-paren-pos) (c-forward-syntactic-ws) - (c-forward-type))))) + (or (c-forward-type) + ;; Recognize a top-level typeless + ;; function declaration in C. + (and (c-major-mode-is 'c-mode) + (eq context 'top) + (eq (char-after) ?\)))))))) (setq pos (c-up-list-forward (point))) (eq (char-before pos) ?\))) (c-fdoc-shift-type-backward) @@ -9037,9 +9052,12 @@ comment at the start of cc-engine.el for more info." ;; (in at least C++) that anything that can be parsed as a declaration ;; is a declaration. Now we're being more defensive and prefer to ;; highlight things like "foo (bar);" as a declaration only if we're - ;; inside an arglist that contains declarations. - ;; CASE 19 - (eq context 'decl)))) + ;; inside an arglist that contains declarations. Update (2017-09): We + ;; now recognize a top-level "foo(bar);" as a declaration in C. + ;; CASE 19 + (or (eq context 'decl) + (and (c-major-mode-is 'c-mode) + (eq context 'top)))))) ;; The point is now after the type decl expression. @@ -9547,6 +9565,7 @@ Note that this function might do hidden buffer changes. See the comment at the start of cc-engine.el for more info." ;; Note to maintainers: this function consumes a great mass of CPU cycles. ;; Its use should thus be minimized as far as possible. + ;; Consider instead using `c-bs-at-toplevel-p'. (let ((paren-state (c-parse-state))) (or (not (c-most-enclosing-brace paren-state)) (c-search-uplist-for-classkey paren-state)))) @@ -9576,8 +9595,15 @@ comment at the start of cc-engine.el for more info." (not (and (c-major-mode-is 'objc-mode) (c-forward-objc-directive))) + ;; Don't confuse #if .... defined(foo) for a function arglist. + (not (and (looking-at c-cpp-expr-functions-key) + (save-excursion + (save-restriction + (widen) + (c-beginning-of-macro lim))))) (setq id-start (car-safe (c-forward-decl-or-cast-1 (c-point 'bosws) 'top nil))) + (numberp id-start) (< id-start beg) ;; There should not be a '=' or ',' between beg and the diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index 7a285f93d3..bcda093678 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -952,6 +952,11 @@ expression, or nil if there aren't any in the language." '("defined")) pike '("defined" "efun" "constant")) +(c-lang-defconst c-cpp-expr-functions-key + ;; Matches a function in a cpp expression. + t (c-make-keywords-re t (c-lang-const c-cpp-expr-functions))) +(c-lang-defvar c-cpp-expr-functions-key (c-lang-const c-cpp-expr-functions-key)) + (c-lang-defconst c-assignment-operators "List of all assignment operators." t '("=" "*=" "/=" "%=" "+=" "-=" ">>=" "<<=" "&=" "^=" "|=") diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 8867453e85..b0e5fe47a7 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -1571,6 +1571,8 @@ Note that this is a strict tail, so won't match, e.g. \"0x....\".") (and (c-beginning-of-macro) (progn (c-end-of-macro) (point)))))) (when (and (c-forward-declarator lim) + (or (not (eq (char-after) ?\()) + (c-go-list-forward nil lim)) (eq (c-forward-token-2 1 nil lim) 0)) (c-backward-syntactic-ws) (point)))))) @@ -1589,7 +1591,7 @@ Note that this is a strict tail, so won't match, e.g. \"0x....\".") (or (c-fl-decl-start c-new-BEG) (c-point 'bol c-new-BEG)) c-new-END (or (c-fl-decl-end c-new-END) - (c-point 'bonl (max (1- c-new-END) (point-min))))))) + (c-point 'bonl c-new-END))))) (defun c-context-expand-fl-region (beg end) ;; Return a cons (NEW-BEG . NEW-END), where NEW-BEG is the beginning of a commit 0b558b4acb8326c6f26fcde47ca85777716ae831 Author: Michael Albinus Date: Wed Oct 4 11:48:37 2017 +0200 * lisp/net/tramp.el (tramp-tramp-file-p): Use `string-match-p'. Reported by Clément Pit-Claudel . diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index ac882abae5..c8b6e68f71 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -1269,14 +1269,14 @@ entry does not exist, return nil." ;;;###tramp-autoload (defun tramp-tramp-file-p (name) "Return t if NAME is a string with Tramp file name syntax." - (save-match-data - (and (stringp name) - ;; No "/:" and "/c:". This is not covered by `tramp-file-name-regexp'. - (not (string-match - (if (memq system-type '(cygwin windows-nt)) - "^/[[:alpha:]]?:" "^/:") - name)) - (string-match tramp-file-name-regexp name)))) + (and (stringp name) + ;; No "/:" and "/c:". This is not covered by `tramp-file-name-regexp'. + (not (string-match-p + (if (memq system-type '(cygwin windows-nt)) + "^/[[:alpha:]]?:" "^/:") + name)) + (string-match-p tramp-file-name-regexp name) + t)) (defun tramp-find-method (method user host) "Return the right method string to use. commit ea39d470bf35e45f1d8e39795f06ac74b3c37fc7 Author: Eli Zaretskii Date: Wed Oct 4 10:27:49 2017 +0300 Avoid crashes on C-g when several threads wait for input * src/thread.h (m_getcjmp): New member of 'struct thread_state'. (getcjmp): Define to current thread's 'm_getcjmp'. * src/thread.c (maybe_reacquire_global_lock): Switch to main thread, since this is called from a SIGINT handler, which always runs in the context of the main thread. * src/lisp.h (sys_jmp_buf, sys_setjmp, sys_longjmp): Move the definitions before thread.h is included, as thread.h now uses sys_jmp_buf. * src/keyboard.c (getcjmp): Remove declaration. (read_char): Don't call maybe_reacquire_global_lock here. (handle_interrupt): Call maybe_reacquire_global_lock here, if invoked from the SIGINT handler, to make sure quit_throw_to_read_char runs with main thread's Lisp bindings and uses the main thread's jmp_buf buffer. (Bug#28630) diff --git a/src/keyboard.c b/src/keyboard.c index e8701b8870..ee353d2b07 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -145,10 +145,6 @@ static Lisp_Object recover_top_level_message; /* Message normally displayed by Vtop_level. */ static Lisp_Object regular_top_level_message; -/* For longjmp to where kbd input is being done. */ - -static sys_jmp_buf getcjmp; - /* True while displaying for echoing. Delays C-g throwing. */ static bool echoing; @@ -2570,9 +2566,6 @@ read_char (int commandflag, Lisp_Object map, so restore it now. */ restore_getcjmp (save_jump); pthread_sigmask (SIG_SETMASK, &empty_mask, 0); -#if THREADS_ENABLED - maybe_reacquire_global_lock (); -#endif unbind_to (jmpcount, Qnil); XSETINT (c, quit_char); internal_last_event_frame = selected_frame; @@ -10508,6 +10501,13 @@ handle_interrupt (bool in_signal_handler) outside of polling since we don't get SIGIO like X and we don't have a separate event loop thread like W32. */ #ifndef HAVE_NS +#ifdef THREADS_ENABLED + /* If we were called from a signal handler, we must be in the main + thread, see deliver_process_signal. So we must make sure the + main thread holds the global lock. */ + if (in_signal_handler) + maybe_reacquire_global_lock (); +#endif if (waiting_for_input && !echoing) quit_throw_to_read_char (in_signal_handler); #endif diff --git a/src/lisp.h b/src/lisp.h index 680c25d4c4..bdb162aea4 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -1865,6 +1865,26 @@ verify (offsetof (struct Lisp_Sub_Char_Table, contents) == (offsetof (struct Lisp_Vector, contents) + SUB_CHAR_TABLE_OFFSET * sizeof (Lisp_Object))); + +/* Save and restore the instruction and environment pointers, + without affecting the signal mask. */ + +#ifdef HAVE__SETJMP +typedef jmp_buf sys_jmp_buf; +# define sys_setjmp(j) _setjmp (j) +# define sys_longjmp(j, v) _longjmp (j, v) +#elif defined HAVE_SIGSETJMP +typedef sigjmp_buf sys_jmp_buf; +# define sys_setjmp(j) sigsetjmp (j, 0) +# define sys_longjmp(j, v) siglongjmp (j, v) +#else +/* A platform that uses neither _longjmp nor siglongjmp; assume + longjmp does not affect the sigmask. */ +typedef jmp_buf sys_jmp_buf; +# define sys_setjmp(j) setjmp (j) +# define sys_longjmp(j, v) longjmp (j, v) +#endif + #include "thread.h" /*********************************************************************** @@ -3003,25 +3023,6 @@ extern void defvar_kboard (struct Lisp_Kboard_Objfwd *, const char *, int); static struct Lisp_Kboard_Objfwd ko_fwd; \ defvar_kboard (&ko_fwd, lname, offsetof (KBOARD, vname ## _)); \ } while (false) - -/* Save and restore the instruction and environment pointers, - without affecting the signal mask. */ - -#ifdef HAVE__SETJMP -typedef jmp_buf sys_jmp_buf; -# define sys_setjmp(j) _setjmp (j) -# define sys_longjmp(j, v) _longjmp (j, v) -#elif defined HAVE_SIGSETJMP -typedef sigjmp_buf sys_jmp_buf; -# define sys_setjmp(j) sigsetjmp (j, 0) -# define sys_longjmp(j, v) siglongjmp (j, v) -#else -/* A platform that uses neither _longjmp nor siglongjmp; assume - longjmp does not affect the sigmask. */ -typedef jmp_buf sys_jmp_buf; -# define sys_setjmp(j) setjmp (j) -# define sys_longjmp(j, v) longjmp (j, v) -#endif /* Elisp uses several stacks: diff --git a/src/thread.c b/src/thread.c index 42d7791ad0..d075bdb3a1 100644 --- a/src/thread.c +++ b/src/thread.c @@ -101,14 +101,20 @@ acquire_global_lock (struct thread_state *self) post_acquire_global_lock (self); } -/* This is called from keyboard.c when it detects that SIGINT - interrupted thread_select before the current thread could acquire - the lock. We must acquire the lock to prevent a thread from - running without holding the global lock, and to avoid repeated - calls to sys_mutex_unlock, which invokes undefined behavior. */ +/* This is called from keyboard.c when it detects that SIGINT was + delivered to the main thread and interrupted thread_select before + the main thread could acquire the lock. We must acquire the lock + to prevent a thread from running without holding the global lock, + and to avoid repeated calls to sys_mutex_unlock, which invokes + undefined behavior. */ void maybe_reacquire_global_lock (void) { + /* SIGINT handler is always run on the main thread, see + deliver_process_signal, so reflect that in our thread-tracking + variables. */ + current_thread = &main_thread; + if (current_thread->not_holding_lock) { struct thread_state *self = current_thread; diff --git a/src/thread.h b/src/thread.h index 7fce8674f0..cb2133d72d 100644 --- a/src/thread.h +++ b/src/thread.h @@ -158,6 +158,13 @@ struct thread_state bool m_waiting_for_input; #define waiting_for_input (current_thread->m_waiting_for_input) + /* For longjmp to where kbd input is being done. This is per-thread + so that if more than one thread calls read_char, they don't + clobber each other's getcjmp, which will cause + quit_throw_to_read_char crash due to using a wrong stack. */ + sys_jmp_buf m_getcjmp; +#define getcjmp (current_thread->m_getcjmp) + /* The OS identifier for this thread. */ sys_thread_t thread_id; commit fdbaebde08f4e53e3fc06fae99398c68a4e285fb Author: Paul Eggert Date: Tue Oct 3 16:04:30 2017 -0700 ; Spelling fixes diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index c1a15bf924..0b28dc31cf 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -36,7 +36,7 @@ ;; ;; The main entry points are `flymake-mode' and `flymake-start' ;; -;; The docstrings of these variabless are relevant to understanding how +;; The docstrings of these variables are relevant to understanding how ;; Flymake works for both the user and the backend programmer: ;; ;; * `flymake-diagnostic-functions' @@ -370,8 +370,8 @@ Currently accepted values for REPORT-ACTION are: functions should first cancel any ongoing processing from previous calls. -* The symbol `:panic', signalling that the backend has - encountered an exceptional situation and should be disabled. +* The symbol `:panic', signaling that the backend has encountered + an exceptional situation and should be disabled. Currently accepted REPORT-KEY arguments are: @@ -401,7 +401,7 @@ the diagnostics of each type. The recognized properties are: * `bitmap', an image displayed in the fringe according to `flymake-fringe-indicator-position'. The value actually follows the syntax of `flymake-error-bitmap' (which see). It - is overriden by any `before-string' overlay property. + is overridden by any `before-string' overlay property. * `severity', a non-negative integer specifying the diagnostic's severity. The higher, the more serious. If the overlay @@ -501,7 +501,7 @@ associated `flymake-category' return DEFAULT." "\n"))) (default-maybe 'severity (warning-numeric-level :error)) (default-maybe 'priority (+ 100 (overlay-get ov 'severity)))) - ;; Some properties can't be overriden + ;; Some properties can't be overridden. ;; (overlay-put ov 'evaporate t) (overlay-put ov 'flymake t) commit b5c965dbd8eef12e8e7c79e59d2fa58c2b8d60d7 Author: Paul Eggert Date: Tue Oct 3 15:42:10 2017 -0700 Warn if --without-pop is now the default * configure.ac (with_pop): Set to no-by-default if defaulting to "no". Warn about the change if defaulting to "no". Update URLs. diff --git a/configure.ac b/configure.ac index 3feac73bed..d92b95cb54 100644 --- a/configure.ac +++ b/configure.ac @@ -259,7 +259,7 @@ AC_ARG_WITH([pop], [], [case $host in *-mingw*) with_pop=yes;; - *) with_pop=no;; + *) with_pop=no-by-default;; esac]) if test "$with_pop" = yes; then AC_DEFINE(MAIL_USE_POP) @@ -1320,7 +1320,7 @@ dnl For a long time, -znocombreloc was added to LDFLAGS rather than dnl LD_SWITCH_SYSTEM_TEMACS. That is: dnl * inappropriate, as LDFLAGS is a user option but this is essential. dnl Eg "make LDFLAGS=... all" could run into problems, -dnl http://bugs.debian.org/684788 +dnl https://bugs.debian.org/684788 dnl * unnecessary, since temacs is the only thing that actually needs it. dnl Indeed this is where it was originally, prior to: dnl https://lists.gnu.org/archive/html/emacs-pretest-bug/2004-03/msg00170.html @@ -1399,10 +1399,6 @@ case "$opsys" in # The resulting binary has a complete symbol table, and is better # for debugging and other observability tools (debuggers, pstack, etc). # - # If you encounter a problem using dldump(), please consider sending - # a message to the OpenSolaris tools-linking mailing list: - # http://mail.opensolaris.org/mailman/listinfo/tools-linking - # # It is likely that dldump() works with older Solaris too, but this has # not been tested, so for now this change is for Solaris 10 or newer. UNEXEC_OBJ=unexsol.o @@ -2651,7 +2647,7 @@ if test x"$pkg_check_gtk" = xyes; then closing open displays. This is no problem if you just use one display, but if you use more than one and close one of them Emacs may crash. - See http://bugzilla.gnome.org/show_bug.cgi?id=85715]]) + See https://bugzilla.gnome.org/show_bug.cgi?id=85715]]) fi fi @@ -4464,7 +4460,6 @@ emacs_broken_SIGIO=no case $opsys in dnl SIGIO exists, but the feature doesn't work in the way Emacs needs. - dnl See eg . hpux* | nacl | openbsd | sol2* | unixware ) emacs_broken_SIGIO=yes ;; @@ -5575,6 +5570,12 @@ if test ! "$with_mailutils"; then AC_MSG_WARN([This configuration installs a 'movemail' program that retrieves POP3 email via only insecure channels. To omit insecure POP3, you can use '$0 --without-pop'.]) + elif test "$with_pop" = no-by-default; then + AC_MSG_WARN([This configuration installs a 'movemail' program +that does not retrieve POP3 email. By default, Emacs 25 and earlier +installed a 'movemail' program that retrieved POP3 email via only +insecure channels, a practice that is no longer recommended but that +you can continue to support by using '$0 --with-pop'.]) fi case $opsys in @@ -5586,7 +5587,7 @@ To omit insecure POP3, you can use '$0 --without-pop'.]) case `(movemail --version) 2>/dev/null` in *Mailutils*) ;; *) emacs_fix_movemail="install GNU Mailutils - and $emacs_fix_movemail";; + and $emacs_fix_movemail";; esac AC_MSG_NOTICE([You might want to $emacs_fix_movemail.]);; esac commit af2a40fbd37a663a8ee5a722f9fecb80c425c128 Author: Paul Eggert Date: Tue Oct 3 15:42:10 2017 -0700 --with-pop is now the default only on MS-Windows Problem reported by N. Jackson (Bug#28597). This improves an earlier suggestion by Robert Pluim (Bug#28597#47). * INSTALL, configure.ac, etc/NEWS: Make --with-pop the default only on native MS-Windows. diff --git a/INSTALL b/INSTALL index e76e843ce2..e93b3064fc 100644 --- a/INSTALL +++ b/INSTALL @@ -273,8 +273,10 @@ a POP3 server by default. Versions of the POP protocol older than POP3 are not supported. While POP3 support is typically enabled, whether Emacs actually uses POP3 is controlled by individual users; see the Rmail chapter of the Emacs manual. Unless --with-mailutils is -in effect, it is a good idea to configure --without-pop so that users -are less likely to inadvertently read email via insecure channels. +in effect, it is a good idea to configure without POP3 support so that +users are less likely to inadvertently read email via insecure +channels. On native MS-Windows, --with-pop is the default; on other +platforms, --without-pop is the default. For image support you may have to download, build, and install the appropriate image support libraries for image types other than XBM and diff --git a/configure.ac b/configure.ac index eb2c684040..3feac73bed 100644 --- a/configure.ac +++ b/configure.ac @@ -232,9 +232,9 @@ AC_DEFUN([OPTION_DEFAULT_ON], [dnl m4_bpatsubst([with_$1], [[^0-9a-z]], [_])=$with_features])dnl ])dnl -# FIXME: The default options '--without-mailutils --with-pop' result -# in a movemail implementation that supports only unencrypted POP3 -# connections. Encrypted connections should be the default. +# For retrieving mail, unencrypted network connections are the default +# only on native MS-Windows platforms. (FIXME: These platforms should +# also be secure by default.) AC_ARG_WITH([mailutils], [AS_HELP_STRING([--with-mailutils], @@ -251,9 +251,16 @@ if test "$with_mailutils" = no; then fi AC_SUBST([with_mailutils]) -OPTION_DEFAULT_ON([pop], - [don't support POP mail retrieval with movemail (--without-pop or - --with-mailutils is recommended, as movemail POP is insecure)]) +AC_ARG_WITH([pop], + [AS_HELP_STRING([--with-pop], + [Support POP mail retrieval if Emacs movemail is used (not recommended, + as Emacs movemail POP is insecure). This is the default only on + native MS-Windows.])], + [], + [case $host in + *-mingw*) with_pop=yes;; + *) with_pop=no;; + esac]) if test "$with_pop" = yes; then AC_DEFINE(MAIL_USE_POP) fi diff --git a/etc/NEWS b/etc/NEWS index b734e8dd19..62d2450f9a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -35,8 +35,9 @@ GNU Mailutils to retrieve email. It is recommended, and is the default if GNU Mailutils is installed. When --with-mailutils is not in effect, the Emacs build procedure by default continues to build and install a limited 'movemail' substitute that retrieves POP3 email only -via insecure channels; to avoid this problem, use either ---with-mailutils or --without-pop when configuring. +via insecure channels. To avoid this problem, use either +--with-mailutils or --without-pop when configuring; --without-pop +is the default on platforms other than native MS-Windows. ** The new option 'configure --enable-gcc-warnings=warn-only' causes GCC to issue warnings without stopping the build. This behavior is commit 92045f4546b9708dc9f69954799d211c1f56ff1e Author: Michael Albinus Date: Tue Oct 3 16:15:08 2017 +0200 Add file name handler support for file-system-info * doc/lispref/files.texi (Magic File Names): Add file-system-info. * etc/NEWS: Mention get-free-disk-space working on remote systems. * lisp/files.el (get-free-disk-space): Do not block on remote systems. * src/w32fns.c (Ffile_system_info): * src/fileio.c (Ffile_system_info): Call file name handler if exists. (syms_of_fileio): Add Qfile_system_info. diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 0a37eeb2a8..0f0ce157ca 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -3135,7 +3135,8 @@ first, before handlers for jobs such as remote file access. @code{file-ownership-preserved-p}, @code{file-readable-p}, @code{file-regular-p}, @code{file-remote-p}, @code{file-selinux-context}, -@code{file-symlink-p}, @code{file-truename}, @code{file-writable-p}, +@code{file-symlink-p}, @code{file-system-info}, +@code{file-truename}, @code{file-writable-p}, @code{find-backup-file-name},@* @code{get-file-buffer}, @code{insert-directory}, @@ -3191,7 +3192,8 @@ first, before handlers for jobs such as remote file access. @code{file-ownership-pre@discretionary{}{}{}served-p}, @code{file-readable-p}, @code{file-regular-p}, @code{file-remote-p}, @code{file-selinux-context}, -@code{file-symlink-p}, @code{file-truename}, @code{file-writable-p}, +@code{file-symlink-p}, @code{file-system-info}, +@code{file-truename}, @code{file-writable-p}, @code{find-backup-file-name}, @code{get-file-buffer}, @code{insert-directory}, diff --git a/etc/NEWS b/etc/NEWS index 28789a956a..15661808c7 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -70,13 +70,19 @@ Programs that called it with multiple arguments before should pass them through 'format' first. Even that is discouraged: for ElDoc support, you should set 'eldoc-documentation-function' instead of calling 'eldoc-message' directly. + * Lisp Changes in Emacs 27.1 +--- ** The 'file-system-info' function is now available on all platforms. -instead of just Microsoft platforms. This fixes a get-free-disk-space +instead of just Microsoft platforms. This fixes a 'get-free-disk-space' bug on OS X 10.8 and later (Bug#28639). +--- +** The function 'get-free-disk-space' returns now a non-nil value for +remote systems, which support this check. + * Changes in Emacs 27.1 on Non-Free Operating Systems diff --git a/lisp/files.el b/lisp/files.el index 194c87ab68..666654da2c 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -6407,12 +6407,10 @@ This variable is obsolete; Emacs no longer uses it." The return value is a string describing the amount of free space (normally, the number of free 1KB blocks). -If DIR's free space cannot be obtained, or if DIR is a remote -directory, this function returns nil." - (unless (file-remote-p (expand-file-name dir)) - (let ((avail (nth 2 (file-system-info dir)))) - (if avail - (format "%.0f" (/ avail 1024)))))) +If DIR's free space cannot be obtained, this function returns nil." + (let ((avail (nth 2 (file-system-info dir)))) + (if avail + (format "%.0f" (/ avail 1024))))) ;; The following expression replaces `dired-move-to-filename-regexp'. (defvar directory-listing-before-filename-regexp diff --git a/src/fileio.c b/src/fileio.c index 11370279d1..d460f123a8 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -5789,6 +5789,18 @@ If the underlying system call fails, value is nil. */) (Lisp_Object filename) { Lisp_Object encoded = ENCODE_FILE (Fexpand_file_name (filename, Qnil)); + + /* If the file name has special constructs in it, + call the corresponding file handler. */ + Lisp_Object handler = Ffind_file_name_handler (encoded, Qfile_system_info); + if (!NILP (handler)) + { + Lisp_Object result = call2 (handler, Qfile_system_info, encoded); + if (CONSP (result) || NILP (result)) + return result; + error ("Invalid handler in `file-name-handler-alist'"); + } + struct fs_usage u; if (get_fs_usage (SSDATA (encoded), NULL, &u) != 0) return Qnil; @@ -5870,6 +5882,7 @@ syms_of_fileio (void) DEFSYM (Qwrite_region, "write-region"); DEFSYM (Qverify_visited_file_modtime, "verify-visited-file-modtime"); DEFSYM (Qset_visited_file_modtime, "set-visited-file-modtime"); + DEFSYM (Qfile_system_info, "file-system-info"); /* The symbol bound to coding-system-for-read when insert-file-contents is called for recovering a file. This is not diff --git a/src/w32fns.c b/src/w32fns.c index efbd81b22d..e3de22d68a 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -9336,6 +9336,17 @@ If the underlying system call fails, value is nil. */) filename = Fexpand_file_name (filename, Qnil); encoded = ENCODE_FILE (filename); + /* If the file name has special constructs in it, + call the corresponding file handler. */ + Lisp_Object handler = Ffind_file_name_handler (encoded, Qfile_system_info); + if (!NILP (handler)) + { + value = call2 (handler, Qfile_system_info, encoded); + if (CONSP (value) || NILP (value)) + return value; + error ("Invalid handler in `file-name-handler-alist'"); + } + value = Qnil; /* Determining the required information on Windows turns out, sadly, commit 4ac5ac50c94b20acdad3746ee168a72d8dd18b4f Merge: a9ac20c179 ca08b69549 Author: Michael Albinus Date: Tue Oct 3 16:08:04 2017 +0200 Merge branch 'emacs-26' of git.sv.gnu.org:/srv/git/emacs into emacs-26 commit a9ac20c179e62f69c77a068f6107e4b186e4c24d Author: Michael Albinus Date: Tue Oct 3 16:07:32 2017 +0200 Add support for `file-system-info' in Tramp * lisp/net/tramp.el (tramp-file-name-for-operation): Add `file-system-info'. * lisp/net/tramp-adb.el (tramp-adb-handle-file-system-info): New defun. (tramp-adb-file-name-handler-alist): Use it. * lisp/net/tramp-gvfs.el (tramp-gvfs-file-system-attributes) (tramp-gvfs-file-system-attributes-regexp): New defconst. (tramp-gvfs-handle-file-system-info): New defun. (tramp-gvfs-file-name-handler-alist): Use it. (tramp-gvfs-get-directory-attributes): Fix property name. (tramp-gvfs-get-root-attributes): Support also file system attributes. * lisp/net/tramp-sh.el (tramp-sh-handle-file-system-info): New defun. (tramp-sh-file-name-handler-alist): Use it. (tramp-sh-handle-insert-directory): Insert size information. (tramp-get-remote-df): New defun. * lisp/net/tramp-smb.el (tramp-smb-handle-file-system-info): New defun. (tramp-smb-file-name-handler-alist): Use it. (tramp-smb-handle-insert-directory): Insert size information. * test/lisp/net/tramp-tests.el (tramp-test37-file-system-info): New test. (tramp-test38-asynchronous-requests) (tramp-test39-recursive-load, tramp-test40-remote-load-path) (tramp-test41-unload): Rename. diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 760d020f67..5268e80a33 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -139,6 +139,7 @@ It is used for TCP/IP devices." (file-remote-p . tramp-handle-file-remote-p) (file-selinux-context . ignore) (file-symlink-p . tramp-handle-file-symlink-p) + (file-system-info . tramp-adb-handle-file-system-info) (file-truename . tramp-adb-handle-file-truename) (file-writable-p . tramp-adb-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) @@ -255,6 +256,30 @@ pass to the OPERATION." (file-attributes (file-truename filename))) t)) +(defun tramp-adb-handle-file-system-info (filename) + "Like `file-system-info' for Tramp files." + (ignore-errors + (with-parsed-tramp-file-name (expand-file-name filename) nil + (tramp-message v 5 "file system info: %s" localname) + (tramp-adb-send-command + v (format "df -k %s" (tramp-shell-quote-argument localname))) + (with-current-buffer (tramp-get-connection-buffer v) + (goto-char (point-min)) + (forward-line) + (when (looking-at + (concat "[[:space:]]*[^[:space:]]+" + "[[:space:]]+\\([[:digit:]]+\\)" + "[[:space:]]+\\([[:digit:]]+\\)" + "[[:space:]]+\\([[:digit:]]+\\)")) + ;; The values are given as 1k numbers, so we must change + ;; them to number of bytes. + (list (* 1024 (string-to-number (concat (match-string 1) "e0"))) + ;; The second value is the used size. We need the + ;; free size. + (* 1024 (- (string-to-number (concat (match-string 1) "e0")) + (string-to-number (concat (match-string 2) "e0")))) + (* 1024 (string-to-number (concat (match-string 3) "e0"))))))))) + ;; This is derived from `tramp-sh-handle-file-truename'. Maybe the ;; code could be shared? (defun tramp-adb-handle-file-truename (filename) diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index e55dd1178d..237d6896e2 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -448,6 +448,18 @@ Every entry is a list (NAME ADDRESS).") ":[[:blank:]]+\\(.*\\)$") "Regexp to parse GVFS file attributes with `gvfs-info'.") +(defconst tramp-gvfs-file-system-attributes + '("filesystem::free" + "filesystem::size" + "filesystem::used") + "GVFS file system attributes.") + +(defconst tramp-gvfs-file-system-attributes-regexp + (concat "^[[:blank:]]*" + (regexp-opt tramp-gvfs-file-system-attributes t) + ":[[:blank:]]+\\(.*\\)$") + "Regexp to parse GVFS file system attributes with `gvfs-info'.") + ;; New handlers should be added here. ;;;###tramp-autoload @@ -494,6 +506,7 @@ Every entry is a list (NAME ADDRESS).") (file-remote-p . tramp-handle-file-remote-p) (file-selinux-context . ignore) (file-symlink-p . tramp-handle-file-symlink-p) + (file-system-info . tramp-gvfs-handle-file-system-info) (file-truename . tramp-handle-file-truename) (file-writable-p . tramp-gvfs-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) @@ -825,7 +838,7 @@ file names." (let ((last-coding-system-used last-coding-system-used) result) (with-parsed-tramp-file-name directory nil - (with-tramp-file-property v localname "directory-gvfs-attributes" + (with-tramp-file-property v localname "directory-attributes" (tramp-message v 5 "directory gvfs attributes: %s" localname) ;; Send command. (tramp-gvfs-send-command @@ -860,23 +873,34 @@ file names." (forward-line))) result))))) -(defun tramp-gvfs-get-root-attributes (filename) - "Return GVFS attributes association list of FILENAME." +(defun tramp-gvfs-get-root-attributes (filename &optional file-system) + "Return GVFS attributes association list of FILENAME. +If FILE-SYSTEM is non-nil, return file system attributes." (ignore-errors ;; Don't modify `last-coding-system-used' by accident. (let ((last-coding-system-used last-coding-system-used) result) (with-parsed-tramp-file-name filename nil - (with-tramp-file-property v localname "file-gvfs-attributes" - (tramp-message v 5 "file gvfs attributes: %s" localname) + (with-tramp-file-property + v localname + (if file-system "file-system-attributes" "file-attributes") + (tramp-message + v 5 "file%s gvfs attributes: %s" + (if file-system " system" "") localname) ;; Send command. - (tramp-gvfs-send-command - v "gvfs-info" (tramp-gvfs-url-file-name filename)) + (if file-system + (tramp-gvfs-send-command + v "gvfs-info" "--filesystem" (tramp-gvfs-url-file-name filename)) + (tramp-gvfs-send-command + v "gvfs-info" (tramp-gvfs-url-file-name filename))) ;; Parse output. (with-current-buffer (tramp-get-connection-buffer v) (goto-char (point-min)) (while (re-search-forward - tramp-gvfs-file-attributes-with-gvfs-info-regexp nil t) + (if file-system + tramp-gvfs-file-system-attributes-regexp + tramp-gvfs-file-attributes-with-gvfs-info-regexp) + nil t) (push (cons (match-string 1) (match-string 2)) result)) result)))))) @@ -1127,6 +1151,22 @@ file-notify events." (with-tramp-file-property v localname "file-readable-p" (tramp-check-cached-permissions v ?r)))) +(defun tramp-gvfs-handle-file-system-info (filename) + "Like `file-system-info' for Tramp files." + (setq filename (directory-file-name (expand-file-name filename))) + (with-parsed-tramp-file-name filename nil + ;; We don't use cached values. + (tramp-set-file-property v localname "file-system-attributes" 'undef) + (let* ((attr (tramp-gvfs-get-root-attributes filename 'file-system)) + (size (cdr (assoc "filesystem::size" attr))) + (used (cdr (assoc "filesystem::used" attr))) + (free (cdr (assoc "filesystem::free" attr)))) + (when (and (stringp size) (stringp used) (stringp free)) + (list (string-to-number (concat size "e0")) + (- (string-to-number (concat size "e0")) + (string-to-number (concat used "e0"))) + (string-to-number (concat free "e0"))))))) + (defun tramp-gvfs-handle-file-writable-p (filename) "Like `file-writable-p' for Tramp files." (with-parsed-tramp-file-name filename nil diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index a744a53ca4..bdb7a13240 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -1020,6 +1020,7 @@ of command line.") (file-remote-p . tramp-handle-file-remote-p) (file-selinux-context . tramp-sh-handle-file-selinux-context) (file-symlink-p . tramp-handle-file-symlink-p) + (file-system-info . tramp-sh-handle-file-system-info) (file-truename . tramp-sh-handle-file-truename) (file-writable-p . tramp-sh-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) @@ -2739,6 +2740,17 @@ The method used must be an out-of-band method." beg 'noerror) (replace-match (file-relative-name filename) t)) + ;; Try to insert the amount of free space. + (goto-char (point-min)) + ;; First find the line to put it on. + (when (re-search-forward "^\\([[:space:]]*total\\)" nil t) + (let ((available (get-free-disk-space "."))) + (when available + ;; Replace "total" with "total used", to avoid confusion. + (replace-match "\\1 used in directory") + (end-of-line) + (insert " available " available)))) + (goto-char (point-max))))))) ;; Canonicalization of file names. @@ -3701,6 +3713,30 @@ file-notify events." 'file-notify-handle-event `(file-notify ,object file-notify-callback))))))) +(defun tramp-sh-handle-file-system-info (filename) + "Like `file-system-info' for Tramp files." + (ignore-errors + (with-parsed-tramp-file-name (expand-file-name filename) nil + (when (tramp-get-remote-df v) + (tramp-message v 5 "file system info: %s" localname) + (tramp-send-command + v (format + "%s --block-size=1 --output=size,used,avail %s" + (tramp-get-remote-df v) (tramp-shell-quote-argument localname))) + (with-current-buffer (tramp-get-connection-buffer v) + (goto-char (point-min)) + (forward-line) + (when (looking-at + (concat "[[:space:]]*\\([[:digit:]]+\\)" + "[[:space:]]+\\([[:digit:]]+\\)" + "[[:space:]]+\\([[:digit:]]+\\)")) + (list (string-to-number (concat (match-string 1) "e0")) + ;; The second value is the used size. We need the + ;; free size. + (- (string-to-number (concat (match-string 1) "e0")) + (string-to-number (concat (match-string 2) "e0"))) + (string-to-number (concat (match-string 3) "e0"))))))))) + ;;; Internal Functions: (defun tramp-maybe-send-script (vec script name) @@ -5404,6 +5440,17 @@ This command is returned only if `delete-by-moving-to-trash' is non-nil." (delete-file tmpfile)) result))) +(defun tramp-get-remote-df (vec) + "Determine remote `df' command." + (with-tramp-connection-property vec "df" + (tramp-message vec 5 "Finding a suitable `df' command") + (let ((result (tramp-find-executable vec "df" (tramp-get-remote-path vec)))) + (and + result + (tramp-send-command-and-check + vec (format "%s --block-size=1 --output=size,used,avail /" result)) + result)))) + (defun tramp-get-remote-gvfs-monitor-dir (vec) "Determine remote `gvfs-monitor-dir' command." (with-tramp-connection-property vec "gvfs-monitor-dir" diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index 35aa811094..620c93828d 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -255,6 +255,7 @@ See `tramp-actions-before-shell' for more info.") (file-remote-p . tramp-handle-file-remote-p) ;; `file-selinux-context' performed by default handler. (file-symlink-p . tramp-handle-file-symlink-p) + (file-system-info . tramp-smb-handle-file-system-info) (file-truename . tramp-handle-file-truename) (file-writable-p . tramp-smb-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) @@ -954,6 +955,38 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (nth 0 x)))) (tramp-smb-get-file-entries directory)))))))) +(defun tramp-smb-handle-file-system-info (filename) + "Like `file-system-info' for Tramp files." + (ignore-errors + (unless (file-directory-p filename) + (setq filename (file-name-directory filename))) + (with-parsed-tramp-file-name (expand-file-name filename) nil + (tramp-message v 5 "file system info: %s" localname) + (tramp-smb-send-command v (format "du %s/*" (tramp-smb-get-localname v))) + (with-current-buffer (tramp-get-connection-buffer v) + (let (total avail blocksize) + (goto-char (point-min)) + (forward-line) + (when (looking-at + (concat "[[:space:]]*\\([[:digit:]]+\\)" + " blocks of size \\([[:digit:]]+\\)" + "\\. \\([[:digit:]]+\\) blocks available")) + (setq blocksize (string-to-number (concat (match-string 2) "e0")) + total (* blocksize + (string-to-number (concat (match-string 1) "e0"))) + avail (* blocksize + (string-to-number (concat (match-string 3) "e0"))))) + (forward-line) + (when (looking-at "Total number of bytes: \\([[:digit:]]+\\)") + ;; The used number of bytes is not part of the result. As + ;; side effect, we store it as file property. + (tramp-set-file-property + v localname "used-bytes" + (string-to-number (concat (match-string 1) "e0")))) + ;; Result. + (when (and total avail) + (list total (- total avail) avail))))))) + (defun tramp-smb-handle-file-writable-p (filename) "Like `file-writable-p' for Tramp files." (if (file-exists-p filename) @@ -984,7 +1017,14 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." ;; We should not destroy the cache entry. (entries (copy-sequence (tramp-smb-get-file-entries - (file-name-directory filename))))) + (file-name-directory filename)))) + (avail (get-free-disk-space filename)) + ;; `get-free-disk-space' calls `file-system-info', which + ;; sets file property "used-bytes" as side effect. + (used + (format + "%.0f" + (/ (tramp-get-file-property v localname "used-bytes" 0) 1024)))) (when wildcard (string-match "\\." base) @@ -1032,6 +1072,12 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (setcar x (concat (car x) "*")))))) entries)) + ;; Insert size information. + (insert + (if avail + (format "total used in directory %s available %s\n" used avail) + (format "total %s\n" used))) + ;; Print entries. (mapc (lambda (x) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index e253db0883..ac882abae5 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -2079,7 +2079,9 @@ ARGS are the arguments OPERATION has been called with." substitute-in-file-name unhandled-file-name-directory vc-registered ;; Emacs 26+ only. - file-name-case-insensitive-p)) + file-name-case-insensitive-p + ;; Emacs 27+ only. + file-system-info)) (if (file-name-absolute-p (nth 0 args)) (nth 0 args) default-directory)) diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index d430caec8a..a8fe06d4e6 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -3438,7 +3438,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (fboundp 'connection-local-set-profiles))) ;; `connection-local-set-profile-variables' and - ;; `connection-local-set-profiles' exists since Emacs 26. We don't + ;; `connection-local-set-profiles' exist since Emacs 26. We don't ;; want to see compiler warnings for older Emacsen. (let ((default-directory tramp-test-temporary-file-directory) explicit-shell-file-name kill-buffer-query-functions) @@ -4108,12 +4108,29 @@ Use the `ls' command." tramp-connection-properties))) (tramp--test-utf8))) +(ert-deftest tramp-test37-file-system-info () + "Check that `file-system-info' returns proper values." + (skip-unless (tramp--test-enabled)) + ;; Since Emacs 27.1. + (skip-unless (fboundp 'file-system-info)) + + ;; `file-system-info' exists since Emacs 27. We don't + ;; want to see compiler warnings for older Emacsen. + (let ((fsi (with-no-warnings + (file-system-info tramp-test-temporary-file-directory)))) + (skip-unless fsi) + (should (and (consp fsi) + (= (length fsi) 3) + (numberp (nth 0 fsi)) + (numberp (nth 1 fsi)) + (numberp (nth 2 fsi)))))) + (defun tramp--test-timeout-handler () (interactive) (ert-fail (format "`%s' timed out" (ert-test-name (ert-running-test))))) ;; This test is inspired by Bug#16928. -(ert-deftest tramp-test37-asynchronous-requests () +(ert-deftest tramp-test38-asynchronous-requests () "Check parallel asynchronous requests. Such requests could arrive from timers, process filters and process sentinels. They shall not disturb each other." @@ -4270,7 +4287,7 @@ process sentinels. They shall not disturb each other." (ignore-errors (cancel-timer timer)) (ignore-errors (delete-directory tmp-name 'recursive))))))) -(ert-deftest tramp-test38-recursive-load () +(ert-deftest tramp-test39-recursive-load () "Check that Tramp does not fail due to recursive load." (skip-unless (tramp--test-enabled)) @@ -4293,7 +4310,7 @@ process sentinels. They shall not disturb each other." (mapconcat 'shell-quote-argument load-path " -L ") (shell-quote-argument code)))))))) -(ert-deftest tramp-test39-remote-load-path () +(ert-deftest tramp-test40-remote-load-path () "Check that Tramp autoloads its packages with remote `load-path'." ;; `tramp-cleanup-all-connections' is autoloaded from tramp-cmds.el. ;; It shall still work, when a remote file name is in the @@ -4316,7 +4333,7 @@ process sentinels. They shall not disturb each other." (mapconcat 'shell-quote-argument load-path " -L ") (shell-quote-argument code))))))) -(ert-deftest tramp-test40-unload () +(ert-deftest tramp-test41-unload () "Check that Tramp and its subpackages unload completely. Since it unloads Tramp, it shall be the last test to run." :tags '(:expensive-test) @@ -4374,7 +4391,7 @@ Since it unloads Tramp, it shall be the last test to run." ;; * Fix `tramp-test05-expand-file-name-relative' in `expand-file-name'. ;; * Fix `tramp-test06-directory-file-name' for `ftp'. ;; * Fix `tramp-test27-start-file-process' on MS Windows (`process-send-eof'?). -;; * Fix Bug#16928 in `tramp-test37-asynchronous-requests'. +;; * Fix Bug#16928 in `tramp-test38-asynchronous-requests'. (defun tramp-test-all (&optional interactive) "Run all tests for \\[tramp]." commit ca08b6954974555ba929b4f9b6b0a52d48d1f8e6 Merge: 0fff900c18 5d922e5d7e Author: João Távora Date: Tue Oct 3 14:48:22 2017 +0100 Merge branch 'scratch/flymake-refactor-clean-for-emacs-26' into emacs-26 commit 5d922e5d7eb3eace966a7f6bb834c8cb77f1cf14 Author: João Távora Date: Wed Sep 27 18:42:02 2017 +0100 Start rewriting Flymake manual Missing the parts pertaining to the new customization API. * doc/misc/flymake.texi (Overview of Flymake): Rewrite a bit. (Installing Flymake): Delete most of this. (Running the syntax check): Mention flymake-start. (Navigating to error lines): Rewrite. (Viewing error messages): Commente out. (Syntax check statuses, Troubleshooting): Rewrite a bit. (Customizable variables): New section under "Using Flymake". Don't mention any proc variables here. (Configuring Flymake): Delete (Proc backend): New chapter (Proc customization variables): New chapter. * doc/misc/flymake.texi (Overview of Flymake): Rewrite a bit. (Installing Flymake): Mostly scratch. Flymake comes with Emacs. (Running the syntax check): Simplify. (Viewing error messages): Dekete, (Syntax check statuses): Rewrite. (Troubleshooting): Simplify. (Customizable variables): Rewrite. (Extending Flymake): New chapter, empty for now. (The legacy Proc backend): New chapter. (Proc customizable variables) (Adding support for a new syntax check tool) (Implementation overview) (Making a temporary copy) (Locating a master file) (Getting the include directories) (Locating the buildfile) (Starting the syntax check process) (Parsing the output) (Interaction with other modes) (Example---Configuring a tool called via make) (Example---Configuring a tool called directly): Rewrite a bit. diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index 01849b7d9a..5dd72f81e2 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -4,7 +4,7 @@ @set VERSION 0.3 @set UPDATED April 2004 @settitle GNU Flymake @value{VERSION} -@include docstyle.texi +@include ../emacs/docstyle.texi @syncodeindex pg cp @comment %**end of header @@ -35,7 +35,7 @@ modify this GNU manual.'' @titlepage @title GNU Flymake @subtitle for version @value{VERSION}, @value{UPDATED} -@author Pavel Kobiakov(@email{pk_at_work@@yahoo.com}) +@author Pavel Kobiakov(@email{pk_at_work@@yahoo.com}) and João Távora. @page @vskip 0pt plus 1filll @insertcopying @@ -53,8 +53,8 @@ modify this GNU manual.'' * Overview of Flymake:: * Installing Flymake:: * Using Flymake:: -* Configuring Flymake:: -* Flymake Implementation:: +* Extending Flymake:: +* The legacy Proc backend:: * GNU Free Documentation License:: * Index:: @end menu @@ -63,67 +63,56 @@ modify this GNU manual.'' @chapter Overview @cindex Overview of Flymake -Flymake is a universal on-the-fly syntax checker implemented as an -Emacs minor mode. Flymake runs the pre-configured syntax check tool -(compiler for C++ files, @code{perl} for perl files, etc.)@: in the -background, passing it a temporary copy of the current buffer, and -parses the output for known error/warning message patterns. Flymake -then highlights erroneous lines (i.e., lines for which at least one -error or warning has been reported by the syntax check tool), and -displays an overall buffer status in the mode line. Status information -displayed by Flymake contains total number of errors and warnings -reported for the buffer during the last syntax check. - -@code{flymake-goto-next-error} and @code{flymake-goto-prev-error} -functions allow for easy navigation to the next/previous erroneous -line, respectively. - -Calling @code{flymake-display-err-menu-for-current-line} will popup a -menu containing error messages reported by the syntax check tool for -the current line. Errors/warnings belonging to another file, such as a -@code{.h} header file included by a @code{.c} file, are shown in the -current buffer as belonging to the first line. Menu items for such -messages also contain a filename and a line number. Selecting such a -menu item will automatically open the file and jump to the line with -error. +Flymake is a universal on-the-fly buffer checker implemented as an +Emacs minor mode. When enabled, Flymake visually annotates the buffer +with diagnostic information coming from one or more different sources, +or @emph{backends}. + +Historically, Flymake used to accept diagnostics from a single, albeit +reasonably flexible, backend. + +This backend isn't (yet) obsolete and so is still available as a +fallback and active by default(@pxref{The legacy Proc backend}). It works by +selecting a syntax check tool from a preconfigured list (compiler for +C++ files, @code{perl} for perl files, etc.), and executing it in the +background, passing it a temporary file which is a copy of the current +buffer, and parsing the output for known error/warning message +patterns. + +Flymake annotates the buffer by highlighting problematic buffer +regions with a special space. It also displays an overall buffer +status in the mode line. Status information displayed by Flymake +contains totals for different types of diagnostics. + +@code{flymake-goto-next-error} and @code{flymake-goto-prev-error} are +commands that allow easy navigation to the next/previous erroneous +line, respectively. If might be a good idea to map them to @kbd{M-n} +and @kbd{M-p} in @code{flymake-mode}, by adding to your init file: + +@lisp +(define-key flymake-mode-map (kbd "M-n") 'flymake-goto-next-error) +(define-key flymake-mode-map (kbd "M-p") 'flymake-goto-prev-error) +@end lisp Syntax check is done ``on-the-fly''. It is started whenever @itemize @bullet -@item buffer is loaded -@item a newline character is added to the buffer +@item @code{flymake-mode} is started; +@item a newline character is added to the buffer; @item some changes were made to the buffer more than @code{0.5} seconds ago (the delay is configurable). @end itemize Flymake is a universal syntax checker in the sense that it's easily -extended to support new syntax check tools and error message -patterns. @xref{Configuring Flymake}. +extended to support new backends. @xref{Customizable variables}. @node Installing Flymake @chapter Installing @cindex Installing Flymake - -Flymake is packaged in a single file, @code{flymake.el}. - -To install/update Flymake, place @code{flymake.el} to a directory -somewhere on Emacs load path. You might also want to byte-compile -@code{flymake.el} to improve performance. - -Also, place the following line in the @code{.emacs} file. - -@lisp -(require 'flymake) -@end lisp - -You might also map the most frequently used Flymake functions, such as -@code{flymake-goto-next-error}, to some keyboard shortcuts: - -@lisp -(global-set-key [f3] 'flymake-display-err-menu-for-current-line) -(global-set-key [f4] 'flymake-goto-next-error) -@end lisp +Flymake is included with Emacs and its main commands, like +@code{flymake-mode}, are autoloaded. This means there is usually +nothing to do by way of installation. @node Using Flymake @chapter Using Flymake @@ -132,10 +121,10 @@ You might also map the most frequently used Flymake functions, such as @menu * Flymake mode:: * Running the syntax check:: -* Navigating to error lines:: -* Viewing error messages:: +* Navigating to error lines:: @c * Viewing error messages:: * Syntax check statuses:: * Troubleshooting:: +* Customizable variables:: @end menu @node Flymake mode @@ -161,10 +150,8 @@ line in @code{.emacs}: When @code{flymake-mode} is active, syntax check is started automatically on any of the three conditions mentioned above. Syntax -check can also be started manually by using the -@code{flymake-start-syntax-check-for-current-buffer} function. This -can be used, for example, when changes were made to some other buffer -affecting the current buffer. +check can also be started manually by using the @code{flymake-start} +function. @node Navigating to error lines @section Navigating to error lines @@ -185,21 +172,10 @@ navigate the highlighted lines. @end multitable -These functions treat erroneous lines as a linked list. Therefore, -@code{flymake-goto-next-error} will go to the first erroneous line -when invoked in the end of the buffer. - -@node Viewing error messages -@section Viewing error messages -@cindex Viewing error messages - -To view error messages belonging to the current line, use the -@code{flymake-display-err-menu-for-current-line} function. If there's -at least one error or warning reported for the current line, this -function will display a popup menu with error/warning texts. -Selecting the menu item whose error belongs to another file brings -forward that file with the help of the -@code{flymake-goto-file-and-line} function. +If the user option @code{flymake-wrap-around} is active +(@pxref{Customizable variables}), these functions treat diagnostics +as a linked list. Therefore, @code{flymake-goto-next-error} will go +to the first diagnostic when invoked in the end of the buffer. @node Syntax check statuses @section Syntax check statuses @@ -209,45 +185,24 @@ After syntax check is finished, its status is displayed in the mode line. The following statuses are defined. @multitable @columnfractions 0.25 0.75 -@item Flymake* or Flymake:E/W* -@tab Flymake is currently running. For the second case, E/W contains the -error and warning count for the previous run. - -@item Flymake -@tab Syntax check is not running. Usually this means syntax check was -successfully passed (no errors, no warnings). Other possibilities are: -syntax check was killed as a result of executing -@code{flymake-compile}, or syntax check cannot start as compilation -is currently in progress. - -@item Flymake:E/W -@tab Number of errors/warnings found by the syntax check process. - -@item Flymake:! -@tab Flymake was unable to find master file for the current buffer. -@end multitable - -The following errors cause a warning message and switch flymake mode -OFF for the buffer. - -@multitable @columnfractions 0.25 0.75 -@item CFGERR -@tab Syntax check process returned nonzero exit code, but no -errors/warnings were reported. This indicates a possible configuration -error (for example, no suitable error message patterns for the -syntax check tool). - -@item NOMASTER -@tab Flymake was unable to find master file for the current buffer. - -@item NOMK -@tab Flymake was unable to find a suitable buildfile for the current buffer. - -@item PROCERR -@tab Flymake was unable to launch a syntax check process. +@item @code{Wait} +@tab Some flymake backends haven't reported since the last time they +where questioned. + +@item @code{!} +@tab All the configured Flymake backends have disabled themselves. +Left-clicking the ``Flymake'' mode line indicator beings the user +@code{*Flymake log*} buffer where these situations may be investigated + +@item @code{?} +@tab There are no configured Flymake backends in +@code{flymake-diagnostic-functions}. + +@item @emph{[nerrors nwarnings]} +@tab Normal operation, number of errors/warnings found by the syntax +check process. @end multitable - @node Troubleshooting @section Troubleshooting @cindex Logging @@ -255,70 +210,20 @@ syntax check tool). Flymake uses a simple logging facility for indicating important points in the control flow. The logging facility sends logging messages to -the @file{*Messages*} buffer. The information logged can be used for +the @file{*Flymake log*} buffer. The information logged can be used for resolving various problems related to Flymake. -Logging output is controlled by the @code{flymake-log-level} -variable. @code{3} is the most verbose level, and @code{-1} switches -logging off. - -@node Configuring Flymake -@chapter Configuring and Extending Flymake -@cindex Configuring and Extending Flymake - -@menu -* Customizable variables:: -* Adding support for a new syntax check tool:: -@end menu - -Flymake was designed to be easily extended for supporting new syntax -check tools and error message patterns. +Logging output is controlled by the Emacs @code{warning-minimum-log-level} +and @code{warning-minimum-level} variables. @node Customizable variables @section Customizable variables @cindex Customizable variables -This section summarizes variables used for Flymake -configuration. +This section summarizes variables used for the configuration of the +Flymake user interface. @table @code -@item flymake-log-level -Controls logging output, see @ref{Troubleshooting}. - -@item flymake-allowed-file-name-masks -A list of @code{(filename-regexp, init-function, cleanup-function -getfname-function)} for configuring syntax check tools. @xref{Adding -support for a new syntax check tool}. - -@ignore -@item flymake-buildfile-dirs -A list of directories (relative paths) for searching a -buildfile. @xref{Locating the buildfile}. -@end ignore - -@item flymake-master-file-dirs -A list of directories for searching a master file. @xref{Locating a -master file}. - -@item flymake-get-project-include-dirs-function -A function used for obtaining a list of project include dirs (C/C++ -specific). @xref{Getting the include directories}. - -@item flymake-master-file-count-limit -@itemx flymake-check-file-limit -Used when looking for a master file. @xref{Locating a master file}. - -@item flymake-err-line-patterns -Patterns for error/warning messages in the form @code{(regexp file-idx -line-idx col-idx err-text-idx)}. @xref{Parsing the output}. - -@item flymake-warning-predicate -Predicate to classify error text as warning. @xref{Parsing the output}. - -@item flymake-compilation-prevents-syntax-check -A flag indicating whether compilation and syntax check of the same -file cannot be run simultaneously. - @item flymake-no-changes-timeout If any changes are made to the buffer, syntax check is automatically started after @code{flymake-no-changes-timeout} seconds. @@ -327,13 +232,17 @@ started after @code{flymake-no-changes-timeout} seconds. A boolean flag indicating whether to start syntax check after a newline character is added to the buffer. -@item flymake-errline -A custom face for highlighting lines for which at least one error has -been reported. +@item flymake-error +A custom face for highlighting regions for which an error has been +reported. + +@item flymake-warning +A custom face for highlighting regions for which a warning has been +reported. -@item flymake-warnline -A custom face for highlighting lines for which at least one warning -and no errors have been reported. +@item flymake-note +A custom face for highlighting regions for which a note has been +reported. @item flymake-error-bitmap A bitmap used in the fringe to mark lines for which an error has @@ -346,6 +255,76 @@ been reported. @item flymake-fringe-indicator-position Which fringe (if any) should show the warning/error bitmaps. +@item flymake-wrap-around +If non-nil, moving to errors with @code{flymake-goto-next-error} and +@code{flymake-goto-prev-error} wraps around buffer boundaries. + +@end table + +@node Extending Flymake +@chapter Extending Flymake +@cindex Extending Flymake + +@node The legacy Proc backend +@chapter The legacy ``Proc'' backend +@cindex The legacy Proc backend + +@menu +* Proc customization variables:: +* Adding support for a new syntax check tool:: +* Implementation overview:: +* Making a temporary copy:: +* Locating a master file:: +* Getting the include directories:: +* Locating the buildfile:: +* Starting the syntax check process:: +* Parsing the output:: +* Interaction with other modes:: +@end menu + +The backend @code{flymake-proc-legacy-backend} was originally designed +to be extended for supporting new syntax check tools and error message +patterns. It is also controlled by its own set of customization variables + +@node Proc customization variables +@section Customization variables for the Proc backend +@cindex Proc customization variables + +@table @code +@item flymake-proc-allowed-file-name-masks +A list of @code{(filename-regexp, init-function, cleanup-function +getfname-function)} for configuring syntax check tools. @xref{Adding +support for a new syntax check tool}. + +@item flymake-proc-master-file-dirs +A list of directories for searching a master file. @xref{Locating a +master file}. + +@item flymake-proc-get-project-include-dirs-function +A function used for obtaining a list of project include dirs (C/C++ +specific). @xref{Getting the include directories}. + +@item flymake-proc-master-file-count-limit +@itemx flymake-proc-check-file-limit +Used when looking for a master file. @xref{Locating a master file}. + +@item flymake-proc-err-line-patterns +Patterns for error/warning messages in the form @code{(regexp file-idx +line-idx col-idx err-text-idx)}. @xref{Parsing the output}. + +@item flymake-proc-diagnostic-type-pred +A function to classify a diagnostic text as particular type of +error. Should be a function taking an error text and returning one of +the symbols indexing @code{flymake-diagnostic-types-alist}. If non-nil +is returned but there is no such symbol in that table, a warning is +assumed. If nil is returned, an error is assumed. Can also be a +regular expression that should match only warnings. This variable +replaces the old @code{flymake-warning-re} and +@code{flymake-warning-predicate}. + +@item flymake-proc-compilation-prevents-syntax-check +A flag indicating whether compilation and syntax check of the same +file cannot be run simultaneously. @end table @node Adding support for a new syntax check tool @@ -358,7 +337,7 @@ Which fringe (if any) should show the warning/error bitmaps. @end menu Syntax check tools are configured using the -@code{flymake-allowed-file-name-masks} list. Each item of this list +@code{flymake-proc-allowed-file-name-masks} list. Each item of this list has the following format: @lisp @@ -369,8 +348,8 @@ has the following format: @item filename-regexp This field is used as a key for locating init/cleanup/getfname functions for the buffer. Items in -@code{flymake-allowed-file-name-masks} are searched sequentially. The -first item with @code{filename-regexp} matching buffer filename is +@code{flymake-proc-allowed-file-name-masks} are searched sequentially. +The first item with @code{filename-regexp} matching buffer filename is selected. If no match is found, @code{flymake-mode} is switched off. @item init-function @@ -390,8 +369,8 @@ This function is used for translating filenames reported by the syntax check tool into ``real'' filenames. Filenames reported by the tool will be different from the real ones, as actually the tool works with the temporary copy. In most cases, the default implementation -provided by Flymake, @code{flymake-get-real-file-name}, can be used as -@code{getfname-function}. +provided by Flymake, @code{flymake-proc-get-real-file-name}, can be +used as @code{getfname-function}. @end table @@ -399,7 +378,7 @@ To add support for a new syntax check tool, write corresponding @code{init-function}, and, optionally @code{cleanup-function} and @code{getfname-function}. If the format of error messages reported by the new tool is not yet supported by Flymake, add a new entry to -the @code{flymake-err-line-patterns} list. +the @code{flymake-proc-err-line-patterns} list. The following sections contain some examples of configuring Flymake support for various syntax check tools. @@ -415,42 +394,42 @@ checking. First, we write the @code{init-function}: @lisp -(defun flymake-perl-init () - (let* ((temp-file (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)) +(defun flymake-proc-perl-init () + (let* ((temp-file (flymake-proc-init-create-temp-buffer-copy + 'flymake-proc-create-temp-inplace)) (local-file (file-relative-name temp-file (file-name-directory buffer-file-name)))) (list "perl" (list "-wc " local-file)))) @end lisp -@code{flymake-perl-init} creates a temporary copy of the buffer +@code{flymake-proc-perl-init} creates a temporary copy of the buffer contents with the help of -@code{flymake-init-create-temp-buffer-copy}, and builds an appropriate +@code{flymake-proc-init-create-temp-buffer-copy}, and builds an appropriate command line. Next, we add a new entry to the -@code{flymake-allowed-file-name-masks}: +@code{flymake-proc-allowed-file-name-masks}: @lisp -(setq flymake-allowed-file-name-masks +(setq flymake-proc-allowed-file-name-masks (cons '(".+\\.pl$" - flymake-perl-init - flymake-simple-cleanup - flymake-get-real-file-name) - flymake-allowed-file-name-masks)) + flymake-proc-perl-init + flymake-proc-simple-cleanup + flymake-proc-get-real-file-name) + flymake-proc-allowed-file-name-masks)) @end lisp Note that we use standard @code{cleanup-function} and @code{getfname-function}. -Finally, we add an entry to @code{flymake-err-line-patterns}: +Finally, we add an entry to @code{flymake-proc-err-line-patterns}: @lisp -(setq flymake-err-line-patterns +(setq flymake-proc-err-line-patterns (cons '("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1) - flymake-err-line-patterns)) + flymake-proc-err-line-patterns)) @end lisp @node Example---Configuring a tool called via make @@ -462,18 +441,18 @@ In this example we will add support for C files syntax checked by We're not required to write any new functions, as Flymake already has functions for @command{make}. We just add a new entry to the -@code{flymake-allowed-file-name-masks}: +@code{flymake-proc-allowed-file-name-masks}: @lisp -(setq flymake-allowed-file-name-masks +(setq flymake-proc-allowed-file-name-masks (cons '(".+\\.c$" - flymake-simple-make-init - flymake-simple-cleanup - flymake-get-real-file-name) - flymake-allowed-file-name-masks)) + flymake-proc-simple-make-init + flymake-proc-simple-cleanup + flymake-proc-get-real-file-name) + flymake-proc-allowed-file-name-masks)) @end lisp -@code{flymake-simple-make-init} builds the following @command{make} +@code{flymake-proc-simple-make-init} builds the following @command{make} command line: @lisp @@ -507,39 +486,22 @@ check-syntax: $(COMPILE) -o /dev/null -S ${CHK_SOURCES} || true @end verbatim -@node Flymake Implementation -@chapter Flymake Implementation -@cindex Implementation details - -@menu -* Determining whether syntax check is possible:: -* Making a temporary copy:: -* Locating a master file:: -* Getting the include directories:: -* Locating the buildfile:: -* Starting the syntax check process:: -* Parsing the output:: -* Highlighting erroneous lines:: -* Interaction with other modes:: -@end menu - -Syntax check is started by calling @code{flymake-start-syntax-check-for-current-buffer}. -Flymake first determines whether it is able to do syntax -check. It then saves a copy of the buffer in a temporary file in the -buffer's directory (or in the system temp directory, for java -files), creates a syntax check command and launches a process with -this command. The output is parsed using a list of error message patterns, -and error information (file name, line number, type and text) is -saved. After the process has finished, Flymake highlights erroneous -lines in the buffer using the accumulated error information. - -@node Determining whether syntax check is possible -@section Determining whether syntax check is possible +@node Implementation overview +@section Implementation overview @cindex Syntax check models @cindex Master file +@code{flymake-proc-legacy-backend} saves a copy of the buffer in a +temporary file in the buffer's directory (or in the system temp +directory, for java files), creates a syntax check command and +launches a process with this command. The output is parsed using a +list of error message patterns, and error information (file name, line +number, type and text) is saved. After the process has finished, +Flymake highlights erroneous lines in the buffer using the accumulated +error information. + Syntax check is considered possible if there's an entry in -@code{flymake-allowed-file-name-masks} matching buffer's filename and +@code{flymake-proc-allowed-file-name-masks} matching buffer's filename and its @code{init-function} returns non-@code{nil} value. Two syntax check modes are distinguished: @@ -564,10 +526,10 @@ will also check syntax in the current file. Examples are C/C++ (.h, These modes are handled inside init/cleanup/getfname functions, see @ref{Adding support for a new syntax check tool}. -Flymake contains implementations of all functionality required to -support different syntax check modes described above (making temporary -copies, finding master files, etc.), as well as some tool-specific -(routines for Make, Ant, etc.)@: code. +The Proc backend contains implementations of all functionality +required to support different syntax check modes described above +(making temporary copies, finding master files, etc.), as well as some +tool-specific (routines for Make, Ant, etc.)@: code. @node Making a temporary copy @@ -609,15 +571,16 @@ Master file is located in two steps. First, a list of possible master files is built. A simple name matching is used to find the files. For a C++ header @code{file.h}, -Flymake searches for all @code{.cpp} files in the directories whose relative paths are -stored in a customizable variable @code{flymake-master-file-dirs}, which -usually contains something like @code{("." "./src")}. No more than -@code{flymake-master-file-count-limit} entries is added to the master file -list. The list is then sorted to move files with names @code{file.cpp} to -the top. +the Proc backend searches for all @code{.cpp} files in the directories +whose relative paths are stored in a customizable variable +@code{flymake-proc-master-file-dirs}, which usually contains something +like @code{("." "./src")}. No more than +@code{flymake-proc-master-file-count-limit} entries is added to the +master file list. The list is then sorted to move files with names +@code{file.cpp} to the top. Next, each master file in a list is checked to contain the appropriate -include directives. No more than @code{flymake-check-file-limit} of each +include directives. No more than @code{flymake-proc-check-file-limit} of each file are parsed. For @code{file.h}, the include directives to look for are @@ -639,10 +602,10 @@ and project include directories. The former is just the contents of the @code{INCLUDE} environment variable. The latter is not so easy to obtain, and the way it can be obtained can vary greatly for different projects. Therefore, a customizable variable -@code{flymake-get-project-include-dirs-function} is used to provide the +@code{flymake-proc-get-project-include-dirs-function} is used to provide the way to implement the desired behavior. -The default implementation, @code{flymake-get-project-include-dirs-imp}, +The default implementation, @code{flymake-proc-get-project-include-dirs-imp}, uses a @command{make} call. This requires a correct base directory, that is, a directory containing a correct @file{Makefile}, to be determined. @@ -656,27 +619,27 @@ of every syntax check attempt. @cindex buildfile, locating @cindex Makefile, locating -Flymake can be configured to use different tools for performing syntax -checks. For example, it can use direct compiler call to syntax check a perl -script or a call to @command{make} for a more complicated case of a -@code{C/C++} source. The general idea is that simple files, like perl -scripts and html pages, can be checked by directly invoking a -corresponding tool. Files that are usually more complex and generally -used as part of larger projects, might require non-trivial options to -be passed to the syntax check tool, like include directories for -C++. The latter files are syntax checked using some build tool, like -Make or Ant. +The Proc backend can be configured to use different tools for +performing syntax checks. For example, it can use direct compiler +call to syntax check a perl script or a call to @command{make} for a +more complicated case of a @code{C/C++} source. The general idea is +that simple files, like perl scripts and html pages, can be checked by +directly invoking a corresponding tool. Files that are usually more +complex and generally used as part of larger projects, might require +non-trivial options to be passed to the syntax check tool, like +include directories for C++. The latter files are syntax checked +using some build tool, like Make or Ant. All Make configuration data is usually stored in a file called @code{Makefile}. To allow for future extensions, flymake uses a notion of buildfile to reference the 'project configuration' file. -Special function, @code{flymake-find-buildfile} is provided for locating buildfiles. +Special function, @code{flymake-proc-find-buildfile} is provided for locating buildfiles. Searching for a buildfile is done in a manner similar to that of searching for possible master files. @ignore A customizable variable -@code{flymake-buildfile-dirs} holds a list of relative paths to the +@code{flymake-proc-buildfile-dirs} holds a list of relative paths to the buildfile. They are checked sequentially until a buildfile is found. @end ignore In case there's no build file, syntax check is aborted. @@ -687,12 +650,12 @@ Buildfile values are also cached. @section Starting the syntax check process @cindex Syntax check process -The command line (command name and the list of arguments) for launching a process is returned by the -initialization function. Flymake then just calls @code{start-process} -to start an asynchronous process and configures a process filter and -sentinel, which are used for processing the output of the syntax check -tool. When exiting Emacs, running Flymake processes will be killed -without prompting the user. +The command line (command name and the list of arguments) for +launching a process is returned by the initialization function. The +Proc backend then just starts an asynchronous process and configures a +process filter and sentinel, which are used for processing the output +of the syntax check tool. When exiting Emacs, running processes will +be killed without prompting the user. @node Parsing the output @section Parsing the output @@ -700,7 +663,7 @@ without prompting the user. The output generated by the syntax check tool is parsed in the process filter/sentinel using the error message patterns stored in the -@code{flymake-err-line-patterns} variable. This variable contains a +@code{flymake-proc-err-line-patterns} variable. This variable contains a list of items of the form @code{(regexp file-idx line-idx err-text-idx)}, used to determine whether a particular line is an error message and extract file name, line number and error text, @@ -709,66 +672,39 @@ error text with the '@code{^[wW]arning}' pattern. Anything that was not classified as a warning is considered an error. Type is then used to sort error menu items, which shows error messages first. -Flymake is also able to interpret error message patterns missing err-text-idx -information. This is done by merely taking the rest of the matched line -(@code{(substring line (match-end 0))}) as error text. This trick allows -making use of a huge collection of error message line patterns from -@code{compile.el}. All these error patterns are appended to -the end of @code{flymake-err-line-patterns}. +The Proc backend is also able to interpret error message patterns +missing err-text-idx information. This is done by merely taking the +rest of the matched line (@code{(substring line (match-end 0))}) as +error text. This trick allows making use of a huge collection of +error message line patterns from @code{compile.el}. All these error +patterns are appended to the end of +@code{flymake-proc-err-line-patterns}. The error information obtained is saved in a buffer local variable. The buffer for which the process output belongs is determined from the process-id@w{}->@w{}buffer mapping updated after every process launch/exit. -@node Highlighting erroneous lines -@section Highlighting erroneous lines -@cindex Erroneous lines, faces - -Highlighting is implemented with overlays and happens in the process -sentinel, after calling the cleanup function. Two customizable faces -are used: @code{flymake-errline} and -@code{flymake-warnline}. Errors belonging outside the current -buffer are considered to belong to line 1 of the current buffer. - -@c This manual does not use vindex. -@c @vindex flymake-fringe-indicator-position -@c @vindex flymake-error-bitmap -@c @vindex flymake-warning-bitmap -If the option @code{flymake-fringe-indicator-position} is non-@code{nil}, -errors and warnings are also highlighted in the left or right fringe, -using the bitmaps specified by @code{flymake-error-bitmap} -and @code{flymake-warning-bitmap}. - @node Interaction with other modes @section Interaction with other modes @cindex Interaction with other modes @cindex Interaction with compile mode -The only mode flymake currently knows about is @code{compile}. +The only mode the Proc backend currently knows about is +@code{compile}. -Flymake can be configured to not start syntax check if it thinks the -compilation is in progress. The check is made by the -@code{flymake-compilation-is-running}, which tests the +The Proc backend can be configured to not start syntax check if it +thinks the compilation is in progress, by testing the @code{compilation-in-progress} variable. The reason why this might be useful is saving CPU time in case both syntax check and compilation are very CPU intensive. The original reason for adding this feature, though, was working around a locking problem with MS Visual C++ -compiler. +compiler. The variable in question is +@code{flymake-proc-compilation-prevents-syntax-check}. -Flymake also provides an alternative command for starting compilation, -@code{flymake-compile}: - -@lisp -(defun flymake-compile () - "Kill all flymake syntax checks then start compilation." - (interactive) - (flymake-stop-all-syntax-checks) - (call-interactively 'compile)) -@end lisp - -It just kills all the active syntax check processes before calling -@code{compile}. +The Proc backend also provides an alternative command for starting +compilation, @code{flymake-proc-compile}. It just kills all the active +syntax check processes before calling @code{compile}. @node GNU Free Documentation License @appendix GNU Free Documentation License commit f964aa99733e087bc51bd46fde655eaeaa5ff52d Author: João Távora Date: Mon Oct 2 13:28:18 2017 +0100 Minimal tweak as an attempt to future-proof Flymake API Discussed with Stefan that this should allow Flymake to request more from backends in the future, while also allowing backends to report more accurately. * lisp/progmodes/elisp-mode.el (elisp-flymake-checkdoc) (elisp-flymake-byte-compile): Adjust to new API. * lisp/progmodes/flymake-proc.el () (flymake-proc-legacy-flymake): Adjust to new API. * lisp/progmodes/flymake.el (flymake-diagnostic-functions): Review API again. (flymake--handle-report): Allow other keys. Change ACTION to REPORT-ACTION. diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index f7d2f39767..3690f67383 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -1617,7 +1617,7 @@ ARGLIST is either a string, or a list of strings or symbols." collected)) ;;;###autoload -(defun elisp-flymake-checkdoc (report-fn) +(defun elisp-flymake-checkdoc (report-fn &rest _args) "A Flymake backend for `checkdoc'. Calls REPORT-FN directly." (unless (derived-mode-p 'emacs-lisp-mode) @@ -1670,7 +1670,7 @@ Calls REPORT-FN directly." "Buffer-local process started for byte-compiling the buffer.") ;;;###autoload -(defun elisp-flymake-byte-compile (report-fn) +(defun elisp-flymake-byte-compile (report-fn &rest _args) "A Flymake backend for elisp byte compilation. Spawn an Emacs process that byte-compiles a file representing the current buffer state and calls REPORT-FN when done." diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index b76d18ed31..47ec27f611 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -715,7 +715,7 @@ May only be called in a dynamic environment where (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) -(defun flymake-proc-legacy-flymake (report-fn &optional interactive) +(defun flymake-proc-legacy-flymake (report-fn &rest args) "Flymake backend based on the original Flymake implementation. This function is suitable for inclusion in `flymake-diagnostic-types-alist'. For backward compatibility, it @@ -729,8 +729,9 @@ can also be executed interactively independently of (apply (flymake-make-report-fn 'flymake-proc-legacy-flymake) diags (append args '(:force t)))) - t)) - (let ((proc flymake-proc--current-process) + :interactive t)) + (let ((interactive (plist-get args :interactive)) + (proc flymake-proc--current-process) (flymake-proc--report-fn report-fn)) (when (processp proc) (process-put proc 'flymake-proc--obsolete t) diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 48ec361600..c1a15bf924 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -320,20 +320,28 @@ Return nil if the region is invalid." (error (flymake-error "Invalid region line=%s col=%s" line col)))) (defvar flymake-diagnostic-functions nil - "Special hook of Flymake backends to check a buffer. + "Special hook of Flymake backends that check a buffer. The functions in this hook diagnose problems in a buffer’s -contents and provide the Flymake user interface with information +contents and provide information to the Flymake user interface about where and how to annotate problems diagnosed in a buffer. Whenever Flymake or the user decides to re-check the buffer, each -function is called with a common calling convention, a single -REPORT-FN argument and a list of keword value pairs, detailed -below. Backend functions are expected to initiate the buffer -check, but aren't required to complete it check before exiting: -if the computation involved is expensive, especially for large -buffers, that task can be scheduled for the future using -asynchronous processes or other asynchronous mechanisms. +function is called with an arbitrary number of arguments: + +* the first argument is always REPORT-FN, a callback function + detailed below; + +* the remaining arguments are keyword-value pairs in the + form (:KEY VALUE :KEY2 VALUE2...). Currently, Flymake provides + no such arguments, but backend functions must be prepared to + accept to accept and possibly ignore any number of them. + +Backend functions are expected to initiate the buffer check, but +aren't required to complete it check before exiting: if the +computation involved is expensive, especially for large buffers, +that task can be scheduled for the future using asynchronous +processes or other asynchronous mechanisms. In any case, backend functions are expected to return quickly or signal an error, in which case the backend is disabled. Flymake @@ -344,10 +352,10 @@ and on again, reset the list of disabled backends. If the function returns, Flymake considers the backend to be \"running\". If it has not done so already, the backend is expected to call the function REPORT-FN with a single argument -ACTION followed by an optional list of keyword-value pairs -their values (:KEY1 VALUE1 :KEY2 VALUE2...). +REPORT-ACTION also followed by an optional list of keyword-value +pairs in the form (:REPORT-KEY VALUE :REPORT-KEY2 VALUE2...). -The possible values for ACTION are. +Currently accepted values for REPORT-ACTION are: * A (possibly empty) list of diagnostic objects created with `flymake-make-diagnostic', causing Flymake to annotate the @@ -365,7 +373,7 @@ The possible values for ACTION are. * The symbol `:panic', signalling that the backend has encountered an exceptional situation and should be disabled. -The recognized optional keyword arguments are: +Currently accepted REPORT-KEY arguments are: * ‘:explanation’: value should give user-readable details of the situation encountered, if any. @@ -544,11 +552,12 @@ present the backend is disabled.") "Tell if Flymake has running backends in this buffer" (flymake-running-backends)) -(cl-defun flymake--handle-report (backend token action - &key explanation force) +(cl-defun flymake--handle-report (backend token report-action + &key explanation force + &allow-other-keys) "Handle reports from BACKEND identified by TOKEN. -BACKEND, ACTION and EXPLANATION, and FORCE conform to the calling +BACKEND, REPORT-ACTION and EXPLANATION, and FORCE conform to the calling convention described in `flymake-diagnostic-functions' (which see). Optional FORCE says to handle a report even if TOKEN was not expected." @@ -573,14 +582,14 @@ not expected." (not force)) (flymake-error "Obsolete report from backend %s with explanation %s" backend explanation)) - ((eq :panic action) + ((eq :panic report-action) (flymake--disable-backend backend explanation)) - ((not (listp action)) + ((not (listp report-action)) (flymake--disable-backend backend - (format "Unknown action %S" action)) - (flymake-error "Expected report, but got unknown key %s" action)) + (format "Unknown action %S" report-action)) + (flymake-error "Expected report, but got unknown key %s" report-action)) (t - (setq new-diags action) + (setq new-diags report-action) (save-restriction (widen) ;; only delete overlays if this is the first report commit 602d9376dbce2646f601f87c1311443ee3feb021 Author: João Távora Date: Sun Oct 1 15:24:15 2017 +0100 Integrate Flymake elisp checkers into elisp-mode.el directly * lisp/progmodes/elisp-mode.el (emacs-lisp-mode): Use elisp-flymake-checkdoc and elisp-flymake-byte-compile. (elisp-flymake--checkdoc-1, elisp-flymake-checkdoc) (elisp-flymake--byte-compile-done) (elisp-flymake--byte-compile-process) (elisp-flymake-byte-compile): Rename from flymake-elisp counterparts in deleted flymake-elisp.el (elisp-flymake--batch-compile-for-flymake): New helper. (checkdoc-create-error-function) (checkdoc-autofix-flag) (checkdoc-generate-compile-warnings-flag) (checkdoc-diagnostic-buffer): Forward declare. * lisp/progmodes/flymake-elisp.el: Delete. diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index cace2bd749..f7d2f39767 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -244,8 +244,8 @@ Blank lines separate paragraphs. Semicolons start comments. (setq-local project-vc-external-roots-function #'elisp-load-path-roots) (add-hook 'completion-at-point-functions #'elisp-completion-at-point nil 'local) - (add-hook 'flymake-diagnostic-functions #'flymake-elisp-checkdoc nil t) - (add-hook 'flymake-diagnostic-functions #'flymake-elisp-byte-compile nil t)) + (add-hook 'flymake-diagnostic-functions #'elisp-flymake-checkdoc nil t) + (add-hook 'flymake-diagnostic-functions #'elisp-flymake-byte-compile nil t)) ;; Font-locking support. @@ -812,7 +812,7 @@ non-nil result supercedes the xrefs produced by (apply #'nconc (let (lst) (dolist (sym (apropos-internal regexp)) - (push (elisp--xref-find-definitions sym) lst)) + (push (elisp--xref-find-definitions sym) lst)) (nreverse lst)))) (defvar elisp--xref-identifier-completion-table @@ -1111,7 +1111,7 @@ If CHAR is not a character, return nil." ;; interactive call would use it. ;; FIXME: Is it really the right place for this? (when (eq (car-safe expr) 'interactive) - (setq expr + (setq expr `(call-interactively (lambda (&rest args) ,expr args)))) expr))))) @@ -1176,7 +1176,7 @@ POS specifies the starting position where EXP was found and defaults to point." (and (not (special-variable-p var)) (save-excursion (zerop (car (syntax-ppss (match-beginning 0))))) - (push var vars)))) + (push var vars)))) `(progn ,@(mapcar (lambda (v) `(defvar ,v)) vars) ,exp))))) (defun eval-last-sexp (eval-last-sexp-arg-internal) @@ -1381,7 +1381,7 @@ or elsewhere, return a 1-line docstring." (t (help-function-arglist sym))))) ;; Stringify, and store before highlighting, downcasing, etc. (elisp--last-data-store sym (elisp-function-argstring args) - 'function)))))) + 'function)))))) ;; Highlight, truncate. (if argstring (elisp--highlight-function-argument @@ -1590,5 +1590,164 @@ ARGLIST is either a string, or a list of strings or symbols." (replace-match "(" t t str) str))) +;;; Flymake support + +;; Don't require checkdoc, but forward declare these checkdoc special +;; variables. Autoloading them on `checkdoc-current-buffer' is too +;; late, they won't be bound dynamically. +(defvar checkdoc-create-error-function) +(defvar checkdoc-autofix-flag) +(defvar checkdoc-generate-compile-warnings-flag) +(defvar checkdoc-diagnostic-buffer) +(defun elisp-flymake--checkdoc-1 () + "Do actual work for `elisp-flymake-checkdoc'." + (let (collected) + (let* ((checkdoc-create-error-function + (lambda (text start end &optional unfixable) + (push (list text start end unfixable) collected) + nil)) + (checkdoc-autofix-flag nil) + (checkdoc-generate-compile-warnings-flag nil) + (buf (generate-new-buffer " *checkdoc-temp*")) + (checkdoc-diagnostic-buffer buf)) + (unwind-protect + (save-excursion + (checkdoc-current-buffer t)) + (kill-buffer buf))) + collected)) + +;;;###autoload +(defun elisp-flymake-checkdoc (report-fn) + "A Flymake backend for `checkdoc'. +Calls REPORT-FN directly." + (unless (derived-mode-p 'emacs-lisp-mode) + (error "Can only work on `emacs-lisp-mode' buffers")) + (funcall report-fn + (cl-loop for (text start end _unfixable) in + (elisp-flymake--checkdoc-1) + collect + (flymake-make-diagnostic + (current-buffer) + start end :note text)))) + +(defun elisp-flymake--byte-compile-done (report-fn + origin-buffer + output-buffer + temp-file) + (unwind-protect + (with-current-buffer + origin-buffer + (save-excursion + (save-restriction + (widen) + (funcall + report-fn + (cl-loop with data = + (with-current-buffer output-buffer + (goto-char (point-min)) + (search-forward ":elisp-flymake-output-start") + (read (point-marker))) + for (string pos _fill level) in data + do (goto-char pos) + for beg = (if (< (point) (point-max)) + (point) + (line-beginning-position)) + for end = (min + (line-end-position) + (or (cdr + (bounds-of-thing-at-point 'sexp)) + (point-max))) + collect (flymake-make-diagnostic + (current-buffer) + (if (= beg end) (1- beg) beg) + end + level + string)))))) + (kill-buffer output-buffer) + (ignore-errors (delete-file temp-file)))) + +(defvar-local elisp-flymake--byte-compile-process nil + "Buffer-local process started for byte-compiling the buffer.") + +;;;###autoload +(defun elisp-flymake-byte-compile (report-fn) + "A Flymake backend for elisp byte compilation. +Spawn an Emacs process that byte-compiles a file representing the +current buffer state and calls REPORT-FN when done." + (interactive (list (lambda (stuff) + (message "aha %s" stuff)))) + (unless (derived-mode-p 'emacs-lisp-mode) + (error "Can only work on `emacs-lisp-mode' buffers")) + (when elisp-flymake--byte-compile-process + (process-put elisp-flymake--byte-compile-process 'elisp-flymake--obsolete t) + (when (process-live-p elisp-flymake--byte-compile-process) + (kill-process elisp-flymake--byte-compile-process))) + (let ((temp-file (make-temp-file "elisp-flymake-byte-compile")) + (origin-buffer (current-buffer))) + (save-restriction + (widen) + (write-region (point-min) (point-max) temp-file nil 'nomessage)) + (let* ((output-buffer (generate-new-buffer " *elisp-flymake-byte-compile*"))) + (setq + elisp-flymake--byte-compile-process + (make-process + :name "elisp-flymake-byte-compile" + :buffer output-buffer + :command (list (expand-file-name invocation-name invocation-directory) + "-Q" + "--batch" + ;; "--eval" "(setq load-prefer-newer t)" ; for testing + "-L" default-directory + "-f" "elisp-flymake--batch-compile-for-flymake" + temp-file) + :connection-type 'pipe + :sentinel + (lambda (proc _event) + (unless (process-live-p proc) + (unwind-protect + (cond + ((zerop (process-exit-status proc)) + (elisp-flymake--byte-compile-done report-fn + origin-buffer + output-buffer + temp-file)) + ((process-get proc 'elisp-flymake--obsolete) + (flymake-log :warning "byte-compile process %s obsolete" proc)) + (t + (funcall report-fn + :panic + :explanation + (format "byte-compile process %s died" proc))))))))) + :stderr null-device + :noquery t))) + +(defun elisp-flymake--batch-compile-for-flymake (&optional file) + "Helper for `elisp-flymake-byte-compile'. +Runs in a batch-mode Emacs. Interactively use variable +`buffer-file-name' for FILE." + (interactive (list buffer-file-name)) + (let* ((file (or file + (car command-line-args-left))) + (dummy-elc-file) + (byte-compile-log-buffer + (generate-new-buffer " *dummy-byte-compile-log-buffer*")) + (byte-compile-dest-file-function + (lambda (source) + (setq dummy-elc-file (make-temp-file (file-name-nondirectory source))))) + (collected) + (byte-compile-log-warning-function + (lambda (string &optional position fill level) + (push (list string position fill level) + collected) + t))) + (unwind-protect + (byte-compile-file file) + (ignore-errors + (delete-file dummy-elc-file) + (kill-buffer byte-compile-log-buffer))) + (prin1 :elisp-flymake-output-start) + (terpri) + (pp collected))) + (provide 'elisp-mode) ;;; elisp-mode.el ends here diff --git a/lisp/progmodes/flymake-elisp.el b/lisp/progmodes/flymake-elisp.el deleted file mode 100644 index b433dc24e1..0000000000 --- a/lisp/progmodes/flymake-elisp.el +++ /dev/null @@ -1,184 +0,0 @@ -;;; flymake-elisp.el --- Flymake backends for emacs-lisp-mode -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2017 Free Software Foundation, Inc. - -;; Author: João Távora -;; Keywords: languages tools - -;; 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 . - -;;; Commentary: - -;; Flymake backends for elisp work, `flymake-elisp-checkdoc' and -;; `flymake-elisp-byte-compile'. - -;;; Code: -(require 'flymake) -(require 'checkdoc) -(eval-when-compile (require 'cl-lib)) -(require 'bytecomp) - -(defun flymake-elisp--checkdoc-1 () - "Do actual work for `flymake-elisp-checkdoc'." - (let (collected) - (let* ((checkdoc-create-error-function - (lambda (text start end &optional unfixable) - (push (list text start end unfixable) collected) - nil)) - (checkdoc-autofix-flag nil) - (checkdoc-generate-compile-warnings-flag nil) - (buf (generate-new-buffer " *checkdoc-temp*")) - (checkdoc-diagnostic-buffer buf)) - (unwind-protect - (save-excursion - (checkdoc-current-buffer t)) - (kill-buffer buf))) - collected)) - -;;;###autoload -(defun flymake-elisp-checkdoc (report-fn) - "A Flymake backend for `checkdoc'. -Calls REPORT-FN directly." - (unless (derived-mode-p 'emacs-lisp-mode) - (error "Can only work on `emacs-lisp-mode' buffers")) - (funcall report-fn - (cl-loop for (text start end _unfixable) in - (flymake-elisp--checkdoc-1) - collect - (flymake-make-diagnostic - (current-buffer) - start end :note text)))) - -(defun flymake-elisp--byte-compile-done (report-fn - origin-buffer - output-buffer - temp-file) - (unwind-protect - (with-current-buffer - origin-buffer - (save-excursion - (save-restriction - (widen) - (funcall - report-fn - (ignore-errors - (cl-loop with data = - (with-current-buffer output-buffer - (goto-char (point-min)) - (search-forward ":flymake-elisp-output-start") - (read (point-marker))) - for (string pos _fill level) in data - do (goto-char pos) - for beg = (if (< (point) (point-max)) - (point) - (line-beginning-position)) - for end = (min - (line-end-position) - (or (cdr - (bounds-of-thing-at-point 'sexp)) - (point-max))) - collect (flymake-make-diagnostic - (current-buffer) - (if (= beg end) (1- beg) beg) - end - level - string))))))) - (kill-buffer output-buffer) - (ignore-errors (delete-file temp-file)))) - -(defvar-local flymake-elisp--byte-compile-process nil - "Buffer-local process started for byte-compiling the buffer.") - -;;;###autoload -(defun flymake-elisp-byte-compile (report-fn) - "A Flymake backend for elisp byte compilation. -Spawn an Emacs process that byte-compiles a file representing the -current buffer state and calls REPORT-FN when done." - (interactive (list (lambda (stuff) - (message "aha %s" stuff)))) - (unless (derived-mode-p 'emacs-lisp-mode) - (error "Can only work on `emacs-lisp-mode' buffers")) - (when flymake-elisp--byte-compile-process - (process-put flymake-elisp--byte-compile-process 'flymake-elisp--obsolete t) - (when (process-live-p flymake-elisp--byte-compile-process) - (kill-process flymake-elisp--byte-compile-process))) - (let ((temp-file (make-temp-file "flymake-elisp-byte-compile")) - (origin-buffer (current-buffer))) - (save-restriction - (widen) - (write-region (point-min) (point-max) temp-file nil 'nomessage)) - (let* ((output-buffer (generate-new-buffer " *flymake-elisp-byte-compile*"))) - (setq - flymake-elisp--byte-compile-process - (make-process - :name "flymake-elisp-byte-compile" - :buffer output-buffer - :command (list (expand-file-name invocation-name invocation-directory) - "-Q" - "--batch" - ;; "--eval" "(setq load-prefer-newer t)" ; for testing - "-L" default-directory - "-l" "flymake-elisp" - "-f" "flymake-elisp--batch-byte-compile" - temp-file) - :connection-type 'pipe - :sentinel - (lambda (proc _event) - (unless (process-live-p proc) - (unwind-protect - (cond - ((zerop (process-exit-status proc)) - (flymake-elisp--byte-compile-done report-fn - origin-buffer - output-buffer - temp-file)) - ((process-get proc 'flymake-elisp--obsolete) - (flymake-log 3 "proc %s considered obsolete" proc)) - (t - (funcall report-fn - :panic - :explanation (format "proc %s died violently" proc))))))))) - :stderr null-device - :noquery t))) - -(defun flymake-elisp--batch-byte-compile (&optional file) - "Helper for `flymake-elisp-byte-compile'. -Runs in a batch-mode Emacs. Interactively use variable -`buffer-file-name' for FILE." - (interactive (list buffer-file-name)) - (let* ((file (or file - (car command-line-args-left))) - (dummy-elc-file) - (byte-compile-log-buffer - (generate-new-buffer " *dummy-byte-compile-log-buffer*")) - (byte-compile-dest-file-function - (lambda (source) - (setq dummy-elc-file (make-temp-file (file-name-nondirectory source))))) - (collected) - (byte-compile-log-warning-function - (lambda (string &optional position fill level) - (push (list string position fill level) - collected) - t))) - (unwind-protect - (byte-compile-file file) - (ignore-errors - (delete-file dummy-elc-file) - (kill-buffer byte-compile-log-buffer))) - (prin1 :flymake-elisp-output-start) - (terpri) - (pp collected))) - -(provide 'flymake-elisp) -;;; flymake-elisp.el ends here commit 30ea272fe472ed77eab40179f43bb7bee5184912 Author: João Távora Date: Sun Oct 1 13:30:38 2017 +0100 Hook Flymake onto proper checkdoc and byte-compile interfaces The interfaces in bytecomp.el and checkdoc.el are mostly boilerplate, with little knowledge of actual internals or thought given to the usefulness of said interfaces in contexts other than Flymake's. * lisp/emacs-lisp/bytecomp.el (byte-compile-log-warning-function): New variable. (byte-compile-log-warning): Use it. (byte-compile--log-warning-for-byte-compile): New function. * lisp/emacs-lisp/checkdoc.el (checkdoc-create-error-function): New variable. (checkdoc-create-error): Use it. (checkdoc--create-error-for-checkdoc): New function.xo * lisp/progmodes/flymake-elisp.el (flymake-elisp--checkdoc-1): Use checkdoc-create-error-function. (flymake-elisp--batch-byte-compile): Use byte-compile-log-warning-function. diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 1b42961f1a..590db570c5 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -1183,7 +1183,29 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'." (compilation-forget-errors) pt)))) +(defvar byte-compile-log-warning-function + #'byte-compile--log-warning-for-byte-compile + "Function called when encountering a warning or error. +Called with arguments (STRING POSITION FILL LEVEL). STRING is a +message describing the problem. POSITION is a buffer position +where the problem was detected. FILL is a prefix as in +`warning-fill-prefix'. LEVEL is the level of the +problem (`:warning' or `:error'). POSITION, FILL and LEVEL may be +nil.") + (defun byte-compile-log-warning (string &optional fill level) + "Log a byte-compilation warning. +STRING, FILL and LEVEL are as described in +`byte-compile-log-warning-function', which see." + (funcall byte-compile-log-warning-function + string byte-compile-last-position + fill + level)) + +(defun byte-compile--log-warning-for-byte-compile (string &optional + _position + fill + level) "Log a message STRING in `byte-compile-log-buffer'. Also log the current function and file if not already done. If FILL is non-nil, set `warning-fill-prefix' to four spaces. LEVEL diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el index 7997ba6014..72f82f26f6 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el @@ -1147,14 +1147,27 @@ Prefix argument is the same as for `checkdoc-defun'" ;; features and behaviors, so we need some ways of specifying ;; them, and making them easier to use in the wacked-out interfaces ;; people are requesting -(defun checkdoc-create-error (text start end &optional unfixable) - "Used to create the return error text returned from all engines. +(defvar checkdoc-create-error-function #'checkdoc--create-error-for-checkdoc + "Function called when Checkdoc encounters an error. +Should accept as arguments (TEXT START END &optional UNFIXABLE). + TEXT is the descriptive text of the error. START and END define the region it is sensible to highlight when describing the problem. Optional argument UNFIXABLE means that the error has no auto-fix available. A list of the form (TEXT START END UNFIXABLE) is returned if we are not -generating a buffered list of errors." +generating a buffered list of errors.") + +(defun checkdoc-create-error (text start end &optional unfixable) + "Used to create the return error text returned from all engines. +TEXT, START, END and UNFIXABLE conform to +`checkdoc-create-error-function', which see." + (funcall checkdoc-create-error-function text start end unfixable)) + +(defun checkdoc--create-error-for-checkdoc (text start end &optional unfixable) + "Create an error for Checkdoc. +TEXT, START, END and UNFIXABLE conform to +`checkdoc-create-error-function', which see." (if checkdoc-generate-compile-warnings-flag (progn (checkdoc-error start text) nil) diff --git a/lisp/progmodes/flymake-elisp.el b/lisp/progmodes/flymake-elisp.el index b42767c3fa..b433dc24e1 100644 --- a/lisp/progmodes/flymake-elisp.el +++ b/lisp/progmodes/flymake-elisp.el @@ -32,18 +32,18 @@ (defun flymake-elisp--checkdoc-1 () "Do actual work for `flymake-elisp-checkdoc'." (let (collected) - (cl-letf (((symbol-function 'checkdoc-create-error) - (lambda (text start end &optional unfixable) - (push (list text start end unfixable) collected) - nil))) - (let* ((checkdoc-autofix-flag nil) - (checkdoc-generate-compile-warnings-flag nil) - (buf (generate-new-buffer " *checkdoc-temp*")) - (checkdoc-diagnostic-buffer buf)) - (unwind-protect - (save-excursion - (checkdoc-current-buffer t)) - (kill-buffer buf)))) + (let* ((checkdoc-create-error-function + (lambda (text start end &optional unfixable) + (push (list text start end unfixable) collected) + nil)) + (checkdoc-autofix-flag nil) + (checkdoc-generate-compile-warnings-flag nil) + (buf (generate-new-buffer " *checkdoc-temp*")) + (checkdoc-diagnostic-buffer buf)) + (unwind-protect + (save-excursion + (checkdoc-current-buffer t)) + (kill-buffer buf))) collected)) ;;;###autoload @@ -165,14 +165,14 @@ Runs in a batch-mode Emacs. Interactively use variable (byte-compile-dest-file-function (lambda (source) (setq dummy-elc-file (make-temp-file (file-name-nondirectory source))))) - (collected)) + (collected) + (byte-compile-log-warning-function + (lambda (string &optional position fill level) + (push (list string position fill level) + collected) + t))) (unwind-protect - (cl-letf (((symbol-function 'byte-compile-log-warning) - (lambda (string &optional fill level) - (push (list string byte-compile-last-position fill level) - collected) - t))) - (byte-compile-file file)) + (byte-compile-file file) (ignore-errors (delete-file dummy-elc-file) (kill-buffer byte-compile-log-buffer))) commit 2e84179a291c6fce57d5e6c44121d77160487615 Author: João Távora Date: Sun Oct 1 01:24:31 2017 +0100 Tweak Flymake autoloads and dependencies * lisp/progmodes/elisp-mode.el (emacs-lisp-mode): Add to flymake-diagnostic-functions here. * lisp/progmodes/flymake-elisp.el[top]: Don't add to emacs-lisp-mode-hook. Don't call flymake-elisp-setup-backends in every buffer. (flymake-elisp-checkdoc) (flymake-elisp-byte-compile): Autoload. (flymake-elisp-setup-backends): Remove. * lisp/progmodes/flymake.el: Add some top-level comments. (flymake-make-diagnostic) (flymake-mode, flymake-mode-on, flymake-mode-off): Add autoloads Where to fixup this shit? diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 2f8e081a29..cace2bd749 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -243,7 +243,9 @@ Blank lines separate paragraphs. Semicolons start comments. (add-hook 'xref-backend-functions #'elisp--xref-backend nil t) (setq-local project-vc-external-roots-function #'elisp-load-path-roots) (add-hook 'completion-at-point-functions - #'elisp-completion-at-point nil 'local)) + #'elisp-completion-at-point nil 'local) + (add-hook 'flymake-diagnostic-functions #'flymake-elisp-checkdoc nil t) + (add-hook 'flymake-diagnostic-functions #'flymake-elisp-byte-compile nil t)) ;; Font-locking support. diff --git a/lisp/progmodes/flymake-elisp.el b/lisp/progmodes/flymake-elisp.el index 94907e66cc..b42767c3fa 100644 --- a/lisp/progmodes/flymake-elisp.el +++ b/lisp/progmodes/flymake-elisp.el @@ -20,7 +20,8 @@ ;;; Commentary: -;; Flymake backends for elisp work. +;; Flymake backends for elisp work, `flymake-elisp-checkdoc' and +;; `flymake-elisp-byte-compile'. ;;; Code: (require 'flymake) @@ -45,6 +46,7 @@ (kill-buffer buf)))) collected)) +;;;###autoload (defun flymake-elisp-checkdoc (report-fn) "A Flymake backend for `checkdoc'. Calls REPORT-FN directly." @@ -98,6 +100,7 @@ Calls REPORT-FN directly." (defvar-local flymake-elisp--byte-compile-process nil "Buffer-local process started for byte-compiling the buffer.") +;;;###autoload (defun flymake-elisp-byte-compile (report-fn) "A Flymake backend for elisp byte compilation. Spawn an Emacs process that byte-compiles a file representing the @@ -177,18 +180,5 @@ Runs in a batch-mode Emacs. Interactively use variable (terpri) (pp collected))) -(defun flymake-elisp-setup-backends () - "Setup Flymake for elisp work." - (add-hook 'flymake-diagnostic-functions 'flymake-elisp-checkdoc t t) - (add-hook 'flymake-diagnostic-functions 'flymake-elisp-byte-compile t t)) - -(add-hook 'emacs-lisp-mode-hook - 'flymake-elisp-setup-backends) - -(dolist (buffer (buffer-list)) - (with-current-buffer buffer - (when (derived-mode-p 'emacs-lisp-mode) - (flymake-elisp-setup-backends)))) - (provide 'flymake-elisp) ;;; flymake-elisp.el ends here diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 060f164b79..48ec361600 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -26,9 +26,21 @@ ;; ;; Flymake is a minor Emacs mode performing on-the-fly syntax checks. ;; +;; Flymake collects diagnostic information for multiple sources, +;; called backends, and visually annotates the relevant portions in +;; the buffer. +;; ;; This file contains the UI for displaying and interacting with the -;; results of such checks, as well as entry points for backends to -;; hook on to. Backends are sources of diagnostic info. +;; results produced by these backends, as well as entry points for +;; backends to hook on to. +;; +;; The main entry points are `flymake-mode' and `flymake-start' +;; +;; The docstrings of these variabless are relevant to understanding how +;; Flymake works for both the user and the backend programmer: +;; +;; * `flymake-diagnostic-functions' +;; * `flymake-diagnostic-types-alist' ;; ;;; Code: @@ -195,6 +207,7 @@ generated it." (:constructor flymake--diag-make)) buffer beg end type text backend) +;;;###autoload (defun flymake-make-diagnostic (buffer beg end @@ -681,9 +694,8 @@ Interactively, with a prefix arg, FORCE is t." (start)))) (defvar flymake-mode-map - (let ((map (make-sparse-keymap))) - map) - "Keymap for `flymake-mode'.") + (let ((map (make-sparse-keymap))) map) + "Keymap for `flymake-mode'") ;;;###autoload (define-minor-mode flymake-mode nil @@ -770,7 +782,6 @@ Do it only if `flymake-no-changes-timeout' is non-nil." (cancel-timer flymake-timer) (setq flymake-timer nil))) -;;;###autoload (defun flymake-find-file-hook () (unless (or flymake-mode (null flymake-diagnostic-functions)) @@ -956,11 +967,8 @@ applied." '(:propertize " ")) (:propertize "]"))))))) - - - (provide 'flymake) (require 'flymake-proc) -(require 'flymake-elisp) + ;;; flymake.el ends here commit 5d3f8a8b804d76d6a7cb97738ca79db4b1df06f2 Author: João Távora Date: Sat Sep 30 18:04:45 2017 +0100 Capitalize "Flymake" in docstrings and comments * lisp/progmodes/flymake-elisp.el (flymake-elisp-checkdoc) (flymake-elisp-setup-backends): Capitalize "Flymake" * lisp/progmodes/flymake-proc.el: (flymake-proc-reformat-err-line-patterns-from-compile-el) (flymake-proc--panic, flymake-proc-legacy-flymake) (flymake-start-syntax-check, flymake-proc-compile) (define-obsolete-variable-alias): Capitalize "Flymake" * lisp/progmodes/flymake.el (flymake-fringe-indicator-position) (flymake-make-diagnostic, flymake-delete-own-overlays) (flymake-diagnostic-functions) (flymake-diagnostic-types-alist, flymake-is-running) (flymake-make-report-fn, flymake-mode-on, flymake-mode-off) (flymake-goto-next-error, flymake-goto-prev-error): Capitalize "Flymake" diff --git a/lisp/progmodes/flymake-elisp.el b/lisp/progmodes/flymake-elisp.el index f54badfc83..94907e66cc 100644 --- a/lisp/progmodes/flymake-elisp.el +++ b/lisp/progmodes/flymake-elisp.el @@ -46,7 +46,7 @@ collected)) (defun flymake-elisp-checkdoc (report-fn) - "A flymake backend for `checkdoc'. + "A Flymake backend for `checkdoc'. Calls REPORT-FN directly." (unless (derived-mode-p 'emacs-lisp-mode) (error "Can only work on `emacs-lisp-mode' buffers")) @@ -178,7 +178,7 @@ Runs in a batch-mode Emacs. Interactively use variable (pp collected))) (defun flymake-elisp-setup-backends () - "Setup flymake for elisp work." + "Setup Flymake for elisp work." (add-hook 'flymake-diagnostic-functions 'flymake-elisp-checkdoc t t) (add-hook 'flymake-diagnostic-functions 'flymake-elisp-byte-compile t t)) diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index b0ee7d6502..b76d18ed31 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -110,14 +110,14 @@ NAME is the file name function to use, default `flymake-proc-get-real-file-name' function)))) (defvar-local flymake-proc--current-process nil - "Currently active flymake process for a buffer, if any.") + "Currently active Flymake process for a buffer, if any.") (defvar flymake-proc--report-fn nil "If bound, function used to report back to flymake's UI.") (defun flymake-proc-reformat-err-line-patterns-from-compile-el (original-list) "Grab error line patterns from ORIGINAL-LIST in compile.el format. -Convert it to flymake internal format." +Convert it to Flymake internal format." (let* ((converted-list '())) (dolist (item original-list) (setq item (cdr item)) @@ -624,7 +624,7 @@ Create parent directories as needed." (kill-buffer output-buffer))))))) (defun flymake-proc--panic (problem explanation) - "Tell flymake UI about a fatal PROBLEM with this backend. + "Tell Flymake UI about a fatal PROBLEM with this backend. May only be called in a dynamic environment where `flymake-proc--dynamic-report-fn' is bound" (flymake-log 0 "%s: %s" problem explanation) @@ -716,7 +716,7 @@ May only be called in a dynamic environment where (defun flymake-proc-legacy-flymake (report-fn &optional interactive) - "Flymake backend based on the original flymake implementation. + "Flymake backend based on the original Flymake implementation. This function is suitable for inclusion in `flymake-diagnostic-types-alist'. For backward compatibility, it can also be executed interactively independently of @@ -738,7 +738,7 @@ can also be executed interactively independently of (when (process-live-p proc) (when interactive (user-error - "There's already a flymake process running in this buffer") + "There's already a Flymake process running in this buffer") (kill-process proc)))) (when ;; A number of situations make us not want to error right away @@ -815,7 +815,7 @@ can also be executed interactively independently of compilation-in-progress)) (defun flymake-proc-compile () - "Kill all flymake syntax checks, start compilation." + "Kill all Flymake syntax checks, start compilation." (interactive) (flymake-proc-stop-all-syntax-checks "Stopping for proper compilation") (call-interactively 'compile)) diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index d167397776..060f164b79 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -88,7 +88,7 @@ this is used." (face :tag "Face")))) (defcustom flymake-fringe-indicator-position 'left-fringe - "The position to put flymake fringe indicator. + "The position to put Flymake fringe indicator. The value can be nil (do not use indicators), `left-fringe' or `right-fringe'. See `flymake-error-bitmap' and `flymake-warning-bitmap'." :version "24.3" @@ -200,7 +200,7 @@ generated it." end type text) - "Make a flymake diagnostic for BUFFER's region from BEG to END. + "Make a Flymake diagnostic for BUFFER's region from BEG to END. TYPE is a key to `flymake-diagnostic-types-alist' and TEXT is a description of the problem detected in this region." (flymake--diag-make :buffer buffer :beg beg :end end :type type :text text)) @@ -239,7 +239,7 @@ verify FILTER, a function, and sort them by COMPARE (using KEY)." ovs)))) (defun flymake-delete-own-overlays (&optional filter) - "Delete all flymake overlays in BUFFER." + "Delete all Flymake overlays in BUFFER." (mapc #'delete-overlay (flymake--overlays :filter filter))) (defface flymake-error @@ -315,12 +315,12 @@ about where and how to annotate problems diagnosed in a buffer. Whenever Flymake or the user decides to re-check the buffer, each function is called with a common calling convention, a single -REPORT-FN argument, detailed below. Backend functions are -expected to initiate the buffer check, but aren't required to -complete it check before exiting: if the computation involved is -expensive, especially for large buffers, that task can be -scheduled for the future using asynchronous processes or other -asynchronous mechanisms. +REPORT-FN argument and a list of keword value pairs, detailed +below. Backend functions are expected to initiate the buffer +check, but aren't required to complete it check before exiting: +if the computation involved is expensive, especially for large +buffers, that task can be scheduled for the future using +asynchronous processes or other asynchronous mechanisms. In any case, backend functions are expected to return quickly or signal an error, in which case the backend is disabled. Flymake @@ -331,7 +331,7 @@ and on again, reset the list of disabled backends. If the function returns, Flymake considers the backend to be \"running\". If it has not done so already, the backend is expected to call the function REPORT-FN with a single argument -ACTION followed by an optional list of keyword arguments and +ACTION followed by an optional list of keyword-value pairs their values (:KEY1 VALUE1 :KEY2 VALUE2...). The possible values for ACTION are. @@ -367,7 +367,7 @@ The recognized optional keyword arguments are: . ((flymake-category . flymake-warning))) (:note . ((flymake-category . flymake-note)))) - "Alist ((KEY . PROPS)*) of properties of flymake error types. + "Alist ((KEY . PROPS)*) of properties of Flymake error types. KEY can be anything passed as `:type' to `flymake-diag-make'. PROPS is an alist of properties that are applied, in order, to @@ -486,7 +486,7 @@ associated `flymake-category' return DEFAULT." (overlay-put ov 'flymake t) (overlay-put ov 'flymake--diagnostic diagnostic))) -;; Nothing in flymake uses this at all any more, so this is just for +;; Nothing in Flymake uses this at all any more, so this is just for ;; third-party compatibility. (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") @@ -528,10 +528,11 @@ present the backend is disabled.") ,@body))) (defun flymake-is-running () - "Tell if flymake has running backends in this buffer" + "Tell if Flymake has running backends in this buffer" (flymake-running-backends)) -(cl-defun flymake--handle-report (backend token action &key explanation force) +(cl-defun flymake--handle-report (backend token action + &key explanation force) "Handle reports from BACKEND identified by TOKEN. BACKEND, ACTION and EXPLANATION, and FORCE conform to the calling @@ -590,8 +591,8 @@ not expected." (defun flymake-make-report-fn (backend &optional token) "Make a suitable anonymous report function for BACKEND. -BACKEND is used to help flymake distinguish different diagnostic -sources. If provided, TOKEN helps flymake distinguish between +BACKEND is used to help Flymake distinguish different diagnostic +sources. If provided, TOKEN helps Flymake distinguish between different runs of the same backend." (let ((buffer (current-buffer))) (lambda (&rest args) @@ -740,12 +741,12 @@ Do it only if `flymake-no-changes-timeout' is non-nil." ;;;###autoload (defun flymake-mode-on () - "Turn flymake mode on." + "Turn Flymake mode on." (flymake-mode 1)) ;;;###autoload (defun flymake-mode-off () - "Turn flymake mode off." + "Turn Flymake mode off." (flymake-mode 0)) (make-obsolete 'flymake-mode-on 'flymake-mode "26.1") @@ -777,7 +778,7 @@ Do it only if `flymake-no-changes-timeout' is non-nil." (flymake-log :warning "Turned on in `flymake-find-file-hook'"))) (defun flymake-goto-next-error (&optional n filter interactive) - "Go to Nth next flymake error in buffer matching FILTER. + "Go to Nth next Flymake error in buffer matching FILTER. Interactively, always move to the next error. Interactively, and with a prefix arg, skip any diagnostics with a severity less than @@ -827,13 +828,13 @@ applied." (funcall (overlay-get target 'help-echo) nil nil (point))))) (interactive - (user-error "No more flymake errors%s" + (user-error "No more Flymake errors%s" (if filter (format " of types %s" filter) "")))))) (defun flymake-goto-prev-error (&optional n filter interactive) - "Go to Nth previous flymake error in buffer matching FILTER. + "Go to Nth previous Flymake error in buffer matching FILTER. Interactively, always move to the previous error. Interactively, and with a prefix arg, skip any diagnostics with a severity less commit f6e909b41e927a6715bad5fc5386257f29e7c0bb Author: João Távora Date: Sat Sep 30 17:32:53 2017 +0100 Flymake backends can report multiple times per check Rewrote a significant part of the Flymake backend API. Flymake now ignores the return value of backend functions: a function can either returns or errors. If it doesn't error, a backend is no longer constrained to call REPORT-FN exactly once. It may do so any number of times, cumulatively reporting diagnostics. Flymake keeps track of outdated REPORT-FN instances and disconsiders obsolete reports. Backends should avoid reporting obsolete data by cancelling any ongoing processing at every renewed call to the backend function. Consolidated flymake.el internal data structures to require less buffer-local variables. Adjusted Flymake's mode-line indicator to the new semantics. Adapted and simplified the implementation of elisp and legacy backends, fixing potential race conditions when calling backends in rapid succession. Added a new test for a backend that calls REPORT-FN multiple times. Simplify test infrastructure. * lisp/progmodes/flymake-elisp.el (flymake-elisp-checkdoc) (flymake-elisp-byte-compile): Error instead of returning nil if not in emacs-lisp-mode. (flymake-elisp--byte-compile-process): New buffer-local variable. (flymake-elisp-byte-compile): Mark (and kill) previous process obsolete process before starting a new one. Don't report if obsolete process. * lisp/progmodes/flymake-proc.el (flymake-proc--current-process): New buffer-local variable. (flymake-proc--processes): Remove. (flymake-proc--process-filter): Don't bind flymake-proc--report-fn. (flymake-proc--process-sentinel): Rewrite. Don't report if obsolete process. (flymake-proc-legacy-flymake): Rewrite. Mark (and kill) previous process obsolete process before starting a new one. Integrate flymake-proc--start-syntax-check-process helper. (flymake-proc--start-syntax-check-process): Delete. (flymake-proc-stop-all-syntax-checks): Don't use flymake-proc--processes, iterate buffers. (flymake-proc-compile): * lisp/progmodes/flymake.el (subr-x): Require it explicitly. (flymake-diagnostic-functions): Reword docstring. (flymake--running-backends, flymake--disabled-backends) (flymake--diagnostics-table): Delete. (flymake--backend-state): New buffer-local variable and new defstruct. (flymake--with-backend-state, flymake--collect) (flymake-running-backends, flymake-disabled-backends) (flymake-reporting-backends): New helpers. (flymake-is-running): Use flymake-running-backends. (flymake--handle-report): Rewrite. (flymake-make-report-fn): Ensure REPORT-FN runs in the correct buffer or not at all. (flymake--disable-backend, flymake--run-backend): Rewrite. (flymake-start): Rewrite. (flymake-mode): Set flymake--backend-state. (flymake--mode-line-format): Rewrite. * test/lisp/progmodes/flymake-tests.el (flymake-tests--wait-for-backends): New helper. (flymake-tests--call-with-fixture): Use it. (included-c-header-files): Fix whitespace. (flymake-tests--diagnose-words): New helper. (dummy-backends): Rewrite for new semantics. Use cl-letf. (flymake-tests--assert-set): Use quote. (recurrent-backend): New test. diff --git a/lisp/progmodes/flymake-elisp.el b/lisp/progmodes/flymake-elisp.el index 7797d278e3..f54badfc83 100644 --- a/lisp/progmodes/flymake-elisp.el +++ b/lisp/progmodes/flymake-elisp.el @@ -48,14 +48,15 @@ (defun flymake-elisp-checkdoc (report-fn) "A flymake backend for `checkdoc'. Calls REPORT-FN directly." - (when (derived-mode-p 'emacs-lisp-mode) - (funcall report-fn - (cl-loop for (text start end _unfixable) in - (flymake-elisp--checkdoc-1) - collect - (flymake-make-diagnostic - (current-buffer) - start end :note text))))) + (unless (derived-mode-p 'emacs-lisp-mode) + (error "Can only work on `emacs-lisp-mode' buffers")) + (funcall report-fn + (cl-loop for (text start end _unfixable) in + (flymake-elisp--checkdoc-1) + collect + (flymake-make-diagnostic + (current-buffer) + start end :note text)))) (defun flymake-elisp--byte-compile-done (report-fn origin-buffer @@ -94,40 +95,59 @@ Calls REPORT-FN directly." (kill-buffer output-buffer) (ignore-errors (delete-file temp-file)))) +(defvar-local flymake-elisp--byte-compile-process nil + "Buffer-local process started for byte-compiling the buffer.") + (defun flymake-elisp-byte-compile (report-fn) - "A flymake backend for elisp byte compilation. + "A Flymake backend for elisp byte compilation. Spawn an Emacs process that byte-compiles a file representing the current buffer state and calls REPORT-FN when done." (interactive (list (lambda (stuff) (message "aha %s" stuff)))) - (when (derived-mode-p 'emacs-lisp-mode) - (let ((temp-file (make-temp-file "flymake-elisp-byte-compile")) - (origin-buffer (current-buffer))) - (save-restriction - (widen) - (write-region (point-min) (point-max) temp-file nil 'nomessage)) - (let* ((output-buffer (generate-new-buffer " *flymake-elisp-byte-compile*"))) - (make-process - :name "flymake-elisp-byte-compile" - :buffer output-buffer - :command (list (expand-file-name invocation-name invocation-directory) - "-Q" - "--batch" - ;; "--eval" "(setq load-prefer-newer t)" ; for testing - "-L" default-directory - "-l" "flymake-elisp" - "-f" "flymake-elisp--batch-byte-compile" - temp-file) - :connection-type 'pipe - :sentinel - (lambda (proc _event) - (unless (process-live-p proc) - (flymake-elisp--byte-compile-done report-fn - origin-buffer - output-buffer - temp-file)))) - :stderr null-device - :noquery t)))) + (unless (derived-mode-p 'emacs-lisp-mode) + (error "Can only work on `emacs-lisp-mode' buffers")) + (when flymake-elisp--byte-compile-process + (process-put flymake-elisp--byte-compile-process 'flymake-elisp--obsolete t) + (when (process-live-p flymake-elisp--byte-compile-process) + (kill-process flymake-elisp--byte-compile-process))) + (let ((temp-file (make-temp-file "flymake-elisp-byte-compile")) + (origin-buffer (current-buffer))) + (save-restriction + (widen) + (write-region (point-min) (point-max) temp-file nil 'nomessage)) + (let* ((output-buffer (generate-new-buffer " *flymake-elisp-byte-compile*"))) + (setq + flymake-elisp--byte-compile-process + (make-process + :name "flymake-elisp-byte-compile" + :buffer output-buffer + :command (list (expand-file-name invocation-name invocation-directory) + "-Q" + "--batch" + ;; "--eval" "(setq load-prefer-newer t)" ; for testing + "-L" default-directory + "-l" "flymake-elisp" + "-f" "flymake-elisp--batch-byte-compile" + temp-file) + :connection-type 'pipe + :sentinel + (lambda (proc _event) + (unless (process-live-p proc) + (unwind-protect + (cond + ((zerop (process-exit-status proc)) + (flymake-elisp--byte-compile-done report-fn + origin-buffer + output-buffer + temp-file)) + ((process-get proc 'flymake-elisp--obsolete) + (flymake-log 3 "proc %s considered obsolete" proc)) + (t + (funcall report-fn + :panic + :explanation (format "proc %s died violently" proc))))))))) + :stderr null-device + :noquery t))) (defun flymake-elisp--batch-byte-compile (&optional file) "Helper for `flymake-elisp-byte-compile'. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 3ab5523128..b0ee7d6502 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -109,12 +109,9 @@ NAME is the file name function to use, default `flymake-proc-get-real-file-name' (const :tag "flymake-proc-get-real-file-name" nil) function)))) -(defvar-local flymake-proc--process nil +(defvar-local flymake-proc--current-process nil "Currently active flymake process for a buffer, if any.") -(defvar flymake-proc--processes nil - "List of currently active flymake processes.") - (defvar flymake-proc--report-fn nil "If bound, function used to report back to flymake's UI.") @@ -543,9 +540,7 @@ Create parent directories as needed." "Parse STRING and collect diagnostics info." (flymake-log 3 "received %d byte(s) of output from process %d" (length string) (process-id proc)) - (let ((output-buffer (process-get proc 'flymake-proc--output-buffer)) - (flymake-proc--report-fn - (process-get proc 'flymake-proc--report-fn))) + (let ((output-buffer (process-get proc 'flymake-proc--output-buffer))) (when (and (buffer-live-p (process-buffer proc)) output-buffer) (with-current-buffer output-buffer @@ -578,49 +573,55 @@ Create parent directories as needed." (defun flymake-proc--process-sentinel (proc _event) "Sentinel for syntax check buffers." - (when (memq (process-status proc) '(signal exit)) - (let* ((exit-status (process-exit-status proc)) - (command (process-command proc)) - (source-buffer (process-buffer proc)) - (flymake-proc--report-fn (process-get proc - 'flymake-proc--report-fn)) - (cleanup-f (flymake-proc--get-cleanup-function - (buffer-file-name source-buffer))) - (diagnostics (process-get - proc - 'flymake-proc--collected-diagnostics)) - (interrupted (process-get proc 'flymake-proc--interrupted)) - (panic nil) - (output-buffer (process-get proc 'flymake-proc--output-buffer))) - (flymake-log 2 "process %d exited with code %d" - (process-id proc) exit-status) - (condition-case-unless-debug err - (progn - (flymake-log 3 "cleaning up using %s" cleanup-f) - (with-current-buffer source-buffer - (funcall cleanup-f) - (cond ((equal 0 exit-status) - (funcall flymake-proc--report-fn diagnostics)) - (interrupted - (flymake-proc--panic :stopped interrupted)) - (diagnostics - ;; non-zero exit but some diagnostics is quite - ;; normal... - (funcall flymake-proc--report-fn diagnostics)) - ((null diagnostics) - ;; ...but no diagnostics is strange, so panic. - (setq panic t) - (flymake-proc--panic - :configuration-error - (format "Command %s errored, but no diagnostics" - command)))))) - (delete-process proc) - (setq flymake-proc--processes - (delq proc flymake-proc--processes)) - (if panic - (flymake-log 1 "Output buffer %s kept alive for debugging" - output-buffer) - (kill-buffer output-buffer)))))) + (let (debug + (pid (process-id proc)) + (source-buffer (process-buffer proc))) + (unwind-protect + (when (buffer-live-p source-buffer) + (with-current-buffer source-buffer + (cond ((process-get proc 'flymake-proc--obsolete) + (flymake-log 3 "proc %s considered obsolete" + pid)) + ((process-get proc 'flymake-proc--interrupted) + (flymake-log 3 "proc %s interrupted by user" + pid)) + ((not (process-live-p proc)) + (let* ((exit-status (process-exit-status proc)) + (command (process-command proc)) + (diagnostics (process-get + proc + 'flymake-proc--collected-diagnostics))) + (flymake-log 2 "process %d exited with code %d" + pid exit-status) + (cond + ((equal 0 exit-status) + (funcall flymake-proc--report-fn diagnostics + :explanation (format "a gift from %s" (process-id proc)) + )) + (diagnostics + ;; non-zero exit but some diagnostics is quite + ;; normal... + (funcall flymake-proc--report-fn diagnostics + :explanation (format "a gift from %s" (process-id proc)))) + ((null diagnostics) + ;; ...but no diagnostics is strange, so panic. + (setq debug debug-on-error) + (flymake-proc--panic + :configuration-error + (format "Command %s errored, but no diagnostics" + command))))))))) + (let ((output-buffer (process-get proc 'flymake-proc--output-buffer))) + (cond (debug + (flymake-log 3 "Output buffer %s kept alive for debugging" + output-buffer)) + (t + (when (buffer-live-p source-buffer) + (with-current-buffer source-buffer + (let ((cleanup-f (flymake-proc--get-cleanup-function + (buffer-file-name)))) + (flymake-log 3 "cleaning up using %s" cleanup-f) + (funcall cleanup-f)))) + (kill-buffer output-buffer))))))) (defun flymake-proc--panic (problem explanation) "Tell flymake UI about a fatal PROBLEM with this backend. @@ -729,87 +730,85 @@ can also be executed interactively independently of diags (append args '(:force t)))) t)) - (cond - ((process-live-p flymake-proc--process) - (when interactive - (user-error - "There's already a flymake process running in this buffer"))) - ((and buffer-file-name - ;; Since we write temp files in current dir, there's no point - ;; trying if the directory is read-only (bug#8954). - (file-writable-p (file-name-directory buffer-file-name)) - (or (not flymake-proc-compilation-prevents-syntax-check) - (not (flymake-proc--compilation-is-running)))) - (let ((init-f (flymake-proc--get-init-function buffer-file-name))) - (unless init-f (error "Can find a suitable init function")) - (flymake-proc--clear-buildfile-cache) - (flymake-proc--clear-project-include-dirs-cache) - - (let* ((flymake-proc--report-fn report-fn) - (cleanup-f (flymake-proc--get-cleanup-function buffer-file-name)) - (cmd-and-args (funcall init-f)) - (cmd (nth 0 cmd-and-args)) - (args (nth 1 cmd-and-args)) - (dir (nth 2 cmd-and-args))) - (cond ((not cmd-and-args) - (progn - (flymake-log 0 "init function %s for %s failed, cleaning up" - init-f buffer-file-name) - (funcall cleanup-f))) - (t - (setq flymake-last-change-time nil) - (flymake-proc--start-syntax-check-process cmd - args - dir) - t))))))) + (let ((proc flymake-proc--current-process) + (flymake-proc--report-fn report-fn)) + (when (processp proc) + (process-put proc 'flymake-proc--obsolete t) + (flymake-log 3 "marking %s obsolete" (process-id proc)) + (when (process-live-p proc) + (when interactive + (user-error + "There's already a flymake process running in this buffer") + (kill-process proc)))) + (when + ;; A number of situations make us not want to error right away + ;; (and disable ourselves), in case the situation changes in + ;; the near future. + (and buffer-file-name + ;; Since we write temp files in current dir, there's no point + ;; trying if the directory is read-only (bug#8954). + (file-writable-p (file-name-directory buffer-file-name)) + (or (not flymake-proc-compilation-prevents-syntax-check) + (not (flymake-proc--compilation-is-running)))) + (let ((init-f (flymake-proc--get-init-function buffer-file-name))) + (unless init-f (error "Can find a suitable init function")) + (flymake-proc--clear-buildfile-cache) + (flymake-proc--clear-project-include-dirs-cache) + + (let* ((cleanup-f (flymake-proc--get-cleanup-function buffer-file-name)) + (cmd-and-args (funcall init-f)) + (cmd (nth 0 cmd-and-args)) + (args (nth 1 cmd-and-args)) + (dir (nth 2 cmd-and-args)) + (success nil)) + (unwind-protect + (cond + ((not cmd-and-args) + (flymake-log 0 "init function %s for %s failed, cleaning up" + init-f buffer-file-name)) + (t + (setq flymake-last-change-time nil) + (setq proc + (let ((default-directory (or dir default-directory))) + (when dir + (flymake-log 3 "starting process on dir %s" dir)) + (make-process + :name "flymake-proc" + :buffer (current-buffer) + :command (cons cmd args) + :noquery t + :filter + (lambda (proc string) + (let ((flymake-proc--report-fn report-fn)) + (flymake-proc--process-filter proc string))) + :sentinel + (lambda (proc event) + (let ((flymake-proc--report-fn report-fn)) + (flymake-proc--process-sentinel proc event)))))) + (process-put proc 'flymake-proc--output-buffer + (generate-new-buffer + (format " *flymake output for %s*" (current-buffer)))) + (setq flymake-proc--current-process proc) + (flymake-log 2 "started process %d, command=%s, dir=%s" + (process-id proc) (process-command proc) + default-directory) + (setq success t))) + (unless success + (funcall cleanup-f)))))))) (define-obsolete-function-alias 'flymake-start-syntax-check 'flymake-proc-legacy-flymake "26.1") -(defun flymake-proc--start-syntax-check-process (cmd args dir) - "Start syntax check process." - (condition-case-unless-debug err - (let* ((process - (let ((default-directory (or dir default-directory))) - (when dir - (flymake-log 3 "starting process on dir %s" dir)) - (make-process :name "flymake-proc" - :buffer (current-buffer) - :command (cons cmd args) - :noquery t - :filter 'flymake-proc--process-filter - :sentinel 'flymake-proc--process-sentinel)))) - (process-put process 'flymake-proc--output-buffer - (generate-new-buffer - (format " *flymake output for %s*" (current-buffer)))) - (process-put process 'flymake-proc--report-fn - flymake-proc--report-fn) - - (setq-local flymake-proc--process process) - (push process flymake-proc--processes) - - (setq flymake-is-running t) - (setq flymake-last-change-time nil) - - (flymake-log 2 "started process %d, command=%s, dir=%s" - (process-id process) (process-command process) - default-directory) - process) - (error - (flymake-proc--panic :make-process-error - (format-message - "Failed to launch syntax check process `%s' with args %s: %s" - cmd args (error-message-string err))) - (funcall (flymake-proc--get-cleanup-function buffer-file-name))))) - (defun flymake-proc-stop-all-syntax-checks (&optional reason) "Kill all syntax check processes." (interactive (list "Interrupted by user")) - (mapc (lambda (proc) - (kill-process proc) - (process-put proc 'flymake-proc--interrupted reason) - (flymake-log 2 "killed process %d" (process-id proc))) - flymake-proc--processes)) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (let (p flymake-proc--current-process) + (when (process-live-p p) + (kill-process p) + (process-put p 'flymake-proc--interrupted reason) + (flymake-log 2 "killed process %d" (process-id p))))))) (defun flymake-proc--compilation-is-running () (and (boundp 'compilation-in-progress) diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 1068b3889d..d167397776 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -36,7 +36,7 @@ (require 'thingatpt) ; end-of-thing (require 'warnings) ; warning-numeric-level, display-warning (require 'compile) ; for some faces -(eval-when-compile (require 'subr-x)) ; when-let*, if-let* +(require 'subr-x) ; when-let*, if-let*, hash-table-keys, hash-table-values (defgroup flymake nil "Universal on-the-fly syntax checker." @@ -315,42 +315,39 @@ about where and how to annotate problems diagnosed in a buffer. Whenever Flymake or the user decides to re-check the buffer, each function is called with a common calling convention, a single -REPORT-FN argument, detailed below. Backend functions are first -expected to quickly and inexpensively announce the feasibility of -checking the buffer via the return value (i.e. they aren't -required to immediately start checking the buffer): - -* If the backend function returns nil, Flymake forgets about this - backend for the current check, but will call it again for the - next one; - -* If the backend function returns non-nil, Flymake expects this - backend to check the buffer and call its REPORT-FN callback - function exactly once. If the computation involved is - inexpensive, the backend function may do so synchronously, - before returning. If it is not, it should do so after - returning, using idle timers, asynchronous processes or other - asynchronous mechanisms. - -* If the backend function signals an error, it is disabled, - i.e. Flymake will not use it again for the current or any - future checks of this buffer. Certain commands, like turning - `flymake-mode' on and off again, resets the list of disabled - backends. - -Backends are required to call REPORT-FN with a single argument -ACTION followed by an optional list of keywords parameters and +REPORT-FN argument, detailed below. Backend functions are +expected to initiate the buffer check, but aren't required to +complete it check before exiting: if the computation involved is +expensive, especially for large buffers, that task can be +scheduled for the future using asynchronous processes or other +asynchronous mechanisms. + +In any case, backend functions are expected to return quickly or +signal an error, in which case the backend is disabled. Flymake +will not try disabled backends again for any future checks of +this buffer. Certain commands, like turning `flymake-mode' off +and on again, reset the list of disabled backends. + +If the function returns, Flymake considers the backend to be +\"running\". If it has not done so already, the backend is +expected to call the function REPORT-FN with a single argument +ACTION followed by an optional list of keyword arguments and their values (:KEY1 VALUE1 :KEY2 VALUE2...). The possible values for ACTION are. -* A (possibly empty) list of objects created with +* A (possibly empty) list of diagnostic objects created with `flymake-make-diagnostic', causing Flymake to annotate the - buffer with this information and consider the backend has - having finished its check normally. + buffer with this information. -* The symbol `:progress', signalling that the backend is still - working and will call REPORT-FN again in the future. + A backend may call REPORT-FN repeatedly in this manner, but + only until Flymake considers that the most recently requested + buffer check is now obsolete because, say, buffer contents have + changed in the meantime. The backend is only given notice of + this via a renewed call to the backend function. Thus, to + prevent making obsolete reports and wasting resources, backend + functions should first cancel any ongoing processing from + previous calls. * The symbol `:panic', signalling that the backend has encountered an exceptional situation and should be disabled. @@ -360,8 +357,8 @@ The recognized optional keyword arguments are: * ‘:explanation’: value should give user-readable details of the situation encountered, if any. -* ‘:force’: value should be a boolean forcing the Flymake UI - to consider the report even if was somehow unexpected.") +* ‘:force’: value should be a boolean suggesting that the Flymake + considers the report even if was somehow unexpected.") (defvar flymake-diagnostic-types-alist `((:error @@ -493,122 +490,189 @@ associated `flymake-category' return DEFAULT." ;; third-party compatibility. (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") -(defvar-local flymake--running-backends nil - "List of currently active flymake backends. -An active backend is a member of `flymake-diagnostic-functions' -that has been invoked but hasn't reported any final status yet.") - -(defvar-local flymake--disabled-backends nil - "List of currently disabled flymake backends. -A backend is disabled if it reported `:panic'.") - -(defvar-local flymake--diagnostics-table nil - "Hash table of all diagnostics indexed by backend.") +(defvar-local flymake--backend-state nil + "Buffer-local hash table of a Flymake backend's state. +The keys to this hash table are functions as found in +`flymake-diagnostic-functions'. The values are structures +of the type `flymake--backend-state', with these slots + +`running', a symbol to keep track of a backend's replies via its +REPORT-FN argument. A backend is running if this key is +present. If the key is absent if the backend isn't expecting any +replies from the backend. + +`diags', a (possibly empty) list of diagnostic objects created +with `flymake-make-diagnostic'. This key is absent if the +backend hasn't reported anything yet. + +`reported-p', a boolean indicating if the backend has replied +since it last was contacted. + +`disabled', a string with the explanation for a previous +exceptional situation reported by the backend. If this key is +present the backend is disabled.") + +(cl-defstruct (flymake--backend-state + (:constructor flymake--make-backend-state)) + running reported-p disabled diags) + +(defmacro flymake--with-backend-state (backend state-var &rest body) + "Bind BACKEND's STATE-VAR to its state, run BODY." + (declare (indent 2) (debug (sexp sexp &rest form))) + (let ((b (make-symbol "b"))) + `(let* ((,b ,backend) + (,state-var + (or (gethash ,b flymake--backend-state) + (puthash ,b (flymake--make-backend-state) + flymake--backend-state)))) + ,@body))) (defun flymake-is-running () "Tell if flymake has running backends in this buffer" - flymake--running-backends) - -(defun flymake--disable-backend (backend action &optional explanation) - (cl-pushnew backend flymake--disabled-backends) - (flymake-log :warning "Disabled the backend %s due to reports of %s (%s)" - backend action explanation)) - -(cl-defun flymake--handle-report (backend action &key explanation force) - "Handle reports from flymake backend identified by BACKEND. - -BACKEND, ACTION and EXPLANATION conform to the calling convention -described in `flymake-diagnostic-functions' (which see). Optional -FORCE says to handle a report even if it was not expected." - (cond - ((and (not (memq backend flymake--running-backends)) - (not force)) - (flymake-error "Ignoring unexpected report from backend %s" backend)) - ((eq action :progress) - (flymake-log 3 "Backend %s reports progress: %s" backend explanation)) - ((eq :panic action) - (flymake--disable-backend backend action explanation)) - ((listp action) - (let ((diagnostics action)) - (save-restriction - (widen) - (flymake-delete-own-overlays - (lambda (ov) - (eq backend - (flymake--diag-backend - (overlay-get ov 'flymake--diagnostic))))) - (puthash backend diagnostics flymake--diagnostics-table) - (mapc (lambda (diag) - (flymake--highlight-line diag) - (setf (flymake--diag-backend diag) backend)) - diagnostics) - (when flymake-check-start-time - (flymake-log 2 "backend %s reported %d diagnostics in %.2f second(s)" - backend - (length diagnostics) - (- (float-time) flymake-check-start-time)))))) - (t - (flymake--disable-backend "?" - :strange - (format "unknown action %s (%s)" - action explanation)))) - (unless (eq action :progress) - (flymake--stop-backend backend))) - -(defun flymake-make-report-fn (backend) + (flymake-running-backends)) + +(cl-defun flymake--handle-report (backend token action &key explanation force) + "Handle reports from BACKEND identified by TOKEN. + +BACKEND, ACTION and EXPLANATION, and FORCE conform to the calling +convention described in `flymake-diagnostic-functions' (which +see). Optional FORCE says to handle a report even if TOKEN was +not expected." + (let* ((state (gethash backend flymake--backend-state)) + (first-report (not (flymake--backend-state-reported-p state)))) + (setf (flymake--backend-state-reported-p state) t) + (let (expected-token + new-diags) + (cond + ((null state) + (flymake-error + "Unexpected report from unknown backend %s" backend)) + ((flymake--backend-state-disabled state) + (flymake-error + "Unexpected report from disabled backend %s" backend)) + ((progn + (setq expected-token (flymake--backend-state-running state)) + (null expected-token)) + ;; should never happen + (flymake-error "Unexpected report from stopped backend %s" backend)) + ((and (not (eq expected-token token)) + (not force)) + (flymake-error "Obsolete report from backend %s with explanation %s" + backend explanation)) + ((eq :panic action) + (flymake--disable-backend backend explanation)) + ((not (listp action)) + (flymake--disable-backend backend + (format "Unknown action %S" action)) + (flymake-error "Expected report, but got unknown key %s" action)) + (t + (setq new-diags action) + (save-restriction + (widen) + ;; only delete overlays if this is the first report + (when first-report + (flymake-delete-own-overlays + (lambda (ov) + (eq backend + (flymake--diag-backend + (overlay-get ov 'flymake--diagnostic)))))) + (mapc (lambda (diag) + (flymake--highlight-line diag) + (setf (flymake--diag-backend diag) backend)) + new-diags) + (setf (flymake--backend-state-diags state) + (append new-diags (flymake--backend-state-diags state))) + (when flymake-check-start-time + (flymake-log :debug "backend %s reported %d diagnostics in %.2f second(s)" + backend + (length new-diags) + (- (float-time) flymake-check-start-time))))))))) + +(defun flymake-make-report-fn (backend &optional token) "Make a suitable anonymous report function for BACKEND. -BACKEND is used to help flymake distinguish diagnostic -sources." - (lambda (&rest args) - (apply #'flymake--handle-report backend args))) - -(defun flymake--stop-backend (backend) - "Stop the backend BACKEND." - (setq flymake--running-backends (delq backend flymake--running-backends))) +BACKEND is used to help flymake distinguish different diagnostic +sources. If provided, TOKEN helps flymake distinguish between +different runs of the same backend." + (let ((buffer (current-buffer))) + (lambda (&rest args) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (apply #'flymake--handle-report backend token args)))))) + +(defun flymake--collect (fn) + (let (retval) + (maphash (lambda (backend state) + (when (funcall fn state) (push backend retval))) + flymake--backend-state) + retval)) + +(defun flymake-running-backends () + "Compute running Flymake backends in current buffer." + (flymake--collect #'flymake--backend-state-running)) + +(defun flymake-disabled-backends () + "Compute disabled Flymake backends in current buffer." + (flymake--collect #'flymake--backend-state-disabled)) + +(defun flymake-reporting-backends () + "Compute reporting Flymake backends in current buffer." + (flymake--collect #'flymake--backend-state-reported-p)) + +(defun flymake--disable-backend (backend &optional explanation) + "Disable BACKEND because EXPLANATION. +If is is running also stop it." + (flymake-log :warning "Disabling backend %s because %s" backend explanation) + (flymake--with-backend-state backend state + (setf (flymake--backend-state-running state) nil + (flymake--backend-state-disabled state) explanation + (flymake--backend-state-reported-p state) t))) (defun flymake--run-backend (backend) - "Run the backend BACKEND." - (push backend flymake--running-backends) - (remhash backend flymake--diagnostics-table) - ;; FIXME: Should use `condition-case-unless-debug' here, but that - ;; won't let me catch errors from inside `ert-deftest' where - ;; `debug-on-error' is always t - (condition-case err - (unless (funcall backend - (flymake-make-report-fn backend)) - (flymake--stop-backend backend)) - (error - (flymake--disable-backend backend :error - err) - (flymake--stop-backend backend)))) - -(defun flymake-start (&optional deferred interactive) + "Run the backend BACKEND, reenabling if necessary." + (flymake-log :debug "Running backend %s" backend) + (let ((run-token (cl-gensym "backend-token"))) + (flymake--with-backend-state backend state + (setf (flymake--backend-state-running state) run-token + (flymake--backend-state-disabled state) nil + (flymake--backend-state-diags state) nil + (flymake--backend-state-reported-p state) nil)) + ;; FIXME: Should use `condition-case-unless-debug' here, for don't + ;; for two reasons: (1) that won't let me catch errors from inside + ;; `ert-deftest' where `debug-on-error' appears to be always + ;; t. (2) In cases where the user is debugging elisp somewhere + ;; else, and using flymake, the presence of a frequently + ;; misbehaving backend in the global hook (most likely the legacy + ;; backend) will trigger an annoying backtrace. + ;; + (condition-case err + (funcall backend + (flymake-make-report-fn backend run-token)) + (error + (flymake--disable-backend backend err))))) + +(defun flymake-start (&optional deferred force) "Start a syntax check. Start it immediately, or after current command if DEFERRED is -non-nil. With optional INTERACTIVE or interactively, clear any -stale information about running and automatically disabled -backends." - (interactive (list nil t)) +non-nil. With optional FORCE run even disabled backends. + +Interactively, with a prefix arg, FORCE is t." + (interactive (list nil current-prefix-arg)) (cl-labels ((start () (remove-hook 'post-command-hook #'start 'local) (setq flymake-check-start-time (float-time)) - (when interactive - (setq flymake--diagnostics-table (make-hash-table) - flymake--running-backends nil - flymake--disabled-backends nil)) (run-hook-wrapped 'flymake-diagnostic-functions (lambda (backend) - (cond ((memq backend flymake--running-backends) - (flymake-log :debug "Backend %s still running, not restarting" - backend)) - ((memq backend flymake--disabled-backends) - (flymake-log :debug "Backend %s is disabled, not starting" - backend)) - (t - (flymake--run-backend backend))) + (cond + ((and (not force) + (flymake--with-backend-state backend state + (flymake--backend-state-disabled state))) + (flymake-log :debug "Backend %s is disabled, not starting" + backend)) + (t + (flymake--run-backend backend))) nil)))) (if (and deferred this-command) @@ -623,8 +687,6 @@ backends." ;;;###autoload (define-minor-mode flymake-mode nil :group 'flymake :lighter flymake--mode-line-format :keymap flymake-mode-map - (setq flymake--running-backends nil - flymake--disabled-backends nil) (cond ;; Turning the mode ON. (flymake-mode @@ -636,7 +698,7 @@ backends." (add-hook 'after-save-hook 'flymake-after-save-hook nil t) (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) - (setq flymake--diagnostics-table (make-hash-table)) + (setq flymake--backend-state (make-hash-table)) (when flymake-start-syntax-check-on-find-file (flymake-start))))) @@ -797,20 +859,26 @@ applied." (defun flymake--mode-line-format () "Produce a pretty minor mode indicator." - (let ((running flymake--running-backends) - (reported (cl-plusp - (hash-table-count flymake--diagnostics-table)))) + (let* ((known (hash-table-keys flymake--backend-state)) + (running (flymake-running-backends)) + (disabled (flymake-disabled-backends)) + (reported (flymake-reporting-backends)) + (diags-by-type (make-hash-table)) + (all-disabled (and disabled (null running))) + (some-waiting (cl-set-difference running reported))) + (maphash (lambda (_b state) + (mapc (lambda (diag) + (push diag + (gethash (flymake--diag-type diag) + diags-by-type))) + (flymake--backend-state-diags state))) + flymake--backend-state) `((:propertize " Flymake" mouse-face mode-line-highlight - ,@(when (not reported) - `(face compilation-mode-line-fail)) help-echo - ,(concat (format "%s registered backends\n" - (length flymake-diagnostic-functions)) - (format "%s running\n" - (length running)) - (format "%s disabled\n" - (length flymake--disabled-backends)) + ,(concat (format "%s known backends\n" (length known)) + (format "%s running\n" (length running)) + (format "%s disabled\n" (length disabled)) "mouse-1: go to log buffer ") keymap ,(let ((map (make-sparse-keymap))) @@ -819,69 +887,73 @@ applied." (interactive "e") (switch-to-buffer "*Flymake log*"))) map)) - ,@(when running - `(":" (:propertize "Run" - face compilation-mode-line-run - help-echo - ,(format "%s running backends" - (length running))))) - ,@(when reported - (let ((by-type (make-hash-table))) - (maphash (lambda (_backend diags) - (mapc (lambda (diag) - (push diag - (gethash (flymake--diag-type diag) - by-type))) - diags)) - flymake--diagnostics-table) - (cl-loop - for (type . severity) - in (cl-sort (mapcar (lambda (type) - (cons type (flymake--lookup-type-property - type - 'severity - (warning-numeric-level :error)))) - (cl-union (hash-table-keys by-type) - '(:error :warning))) - #'> - :key #'cdr) - for diags = (gethash type by-type) - for face = (flymake--lookup-type-property type - 'mode-line-face - 'compilation-error) - when (or diags - (>= severity (warning-numeric-level :warning))) - collect `(:propertize - ,(format "%d" (length diags)) - face ,face - mouse-face mode-line-highlight - keymap - ,(let ((map (make-sparse-keymap)) - (type type)) - (define-key map [mode-line mouse-4] - (lambda (_event) - (interactive "e") - (flymake-goto-prev-error 1 (list type) t))) - (define-key map [mode-line mouse-5] - (lambda (_event) - (interactive "e") - (flymake-goto-next-error 1 (list type) t))) - map) - help-echo - ,(concat (format "%s diagnostics of type %s\n" - (propertize (format "%d" - (length diags)) - 'face face) - (propertize (format "%s" type) - 'face face)) - "mouse-4/mouse-5: previous/next of this type\n")) - into forms - finally return - `((:propertize "[") - ,@(cl-loop for (a . rest) on forms by #'cdr - collect a when rest collect - '(:propertize " ")) - (:propertize "]")))))))) + ,@(pcase-let ((`(,ind ,face ,explain) + (cond ((null known) + `("?" mode-line "No known backends")) + (some-waiting + `("Wait" compilation-mode-line-run + ,(format "Waiting for %s running backends" + (length running)))) + (all-disabled + `("!" compilation-mode-line-run + "All backends disabled")) + (t + `(nil nil nil))))) + (when ind + `((":" + (:propertize ,ind + face ,face + help-echo ,explain))))) + ,@(unless (or all-disabled + (null known)) + (cl-loop + for (type . severity) + in (cl-sort (mapcar (lambda (type) + (cons type (flymake--lookup-type-property + type + 'severity + (warning-numeric-level :error)))) + (cl-union (hash-table-keys diags-by-type) + '(:error :warning))) + #'> + :key #'cdr) + for diags = (gethash type diags-by-type) + for face = (flymake--lookup-type-property type + 'mode-line-face + 'compilation-error) + when (or diags + (>= severity (warning-numeric-level :warning))) + collect `(:propertize + ,(format "%d" (length diags)) + face ,face + mouse-face mode-line-highlight + keymap + ,(let ((map (make-sparse-keymap)) + (type type)) + (define-key map [mode-line mouse-4] + (lambda (_event) + (interactive "e") + (flymake-goto-prev-error 1 (list type) t))) + (define-key map [mode-line mouse-5] + (lambda (_event) + (interactive "e") + (flymake-goto-next-error 1 (list type) t))) + map) + help-echo + ,(concat (format "%s diagnostics of type %s\n" + (propertize (format "%d" + (length diags)) + 'face face) + (propertize (format "%s" type) + 'face face)) + "mouse-4/mouse-5: previous/next of this type\n")) + into forms + finally return + `((:propertize "[") + ,@(cl-loop for (a . rest) on forms by #'cdr + collect a when rest collect + '(:propertize " ")) + (:propertize "]"))))))) diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index 222c8f1184..5e042f2b08 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -36,6 +36,26 @@ ;; ;; +(defun flymake-tests--wait-for-backends () + ;; Weirdness here... http://debbugs.gnu.org/17647#25 + ;; ... meaning `sleep-for', and even + ;; `accept-process-output', won't suffice as ways to get + ;; process filters and sentinels to run, though they do work + ;; fine in a non-interactive batch session. The only thing + ;; that will indeed unblock pending process output is + ;; reading an input event, so, as a workaround, use a dummy + ;; `read-event' with a very short timeout. + (unless noninteractive (read-event "" nil 0.1)) + (cl-loop repeat 5 + for notdone = (cl-set-difference (flymake-running-backends) + (flymake-reporting-backends)) + while notdone + unless noninteractive do (read-event "" nil 0.1) + do (sleep-for (+ 0.5 flymake-no-changes-timeout)) + finally (when notdone (ert-fail + (format "Some backends not reporting yet %s" + notdone))))) + (cl-defun flymake-tests--call-with-fixture (fn file &key (severity-predicate nil sev-pred-supplied-p)) @@ -46,7 +66,6 @@ SEVERITY-PREDICATE is used to setup (visiting (find-buffer-visiting file)) (buffer (or visiting (find-file-noselect file))) (process-environment (cons "LC_ALL=C" process-environment)) - (i 0) (warning-minimum-log-level :error)) (unwind-protect (with-current-buffer buffer @@ -55,18 +74,7 @@ SEVERITY-PREDICATE is used to setup (setq-local flymake-proc-diagnostic-type-pred severity-predicate)) (goto-char (point-min)) (unless flymake-mode (flymake-mode 1)) - ;; Weirdness here... http://debbugs.gnu.org/17647#25 - ;; ... meaning `sleep-for', and even - ;; `accept-process-output', won't suffice as ways to get - ;; process filters and sentinels to run, though they do work - ;; fine in a non-interactive batch session. The only thing - ;; that will indeed unblock pending process output is - ;; reading an input event, so, as a workaround, use a dummy - ;; `read-event' with a very short timeout. - (unless noninteractive (read-event "" nil 0.1)) - (while (and (flymake-is-running) (< (setq i (1+ i)) 10)) - (unless noninteractive (read-event "" nil 0.1)) - (sleep-for (+ 0.5 flymake-no-changes-timeout))) + (flymake-tests--wait-for-backends) (funcall fn))) (and buffer (not visiting) @@ -119,38 +127,37 @@ SEVERITY-PREDICATE is used to setup (ert-deftest different-diagnostic-types () "Test GCC warning via function predicate." (skip-unless (and (executable-find "gcc") (executable-find "make"))) - (flymake-tests--with-flymake - ("errors-and-warnings.c") - (flymake-goto-next-error) - (should (eq 'flymake-error (face-at-point))) - (flymake-goto-next-error) - (should (eq 'flymake-note (face-at-point))) - (flymake-goto-next-error) - (should (eq 'flymake-warning (face-at-point))) - (flymake-goto-next-error) - (should (eq 'flymake-error (face-at-point))) - (flymake-goto-next-error) - (should (eq 'flymake-warning (face-at-point))) - (flymake-goto-next-error) - (should (eq 'flymake-warning (face-at-point))) - (let ((flymake-wrap-around nil)) - (should-error (flymake-goto-next-error nil nil t))) )) + (let ((flymake-wrap-around nil)) + (flymake-tests--with-flymake + ("errors-and-warnings.c") + (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-note (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) + (should-error (flymake-goto-next-error nil nil t))))) (ert-deftest included-c-header-files () "Test inclusion of .h header files." (skip-unless (and (executable-find "gcc") (executable-find "make"))) - (flymake-tests--with-flymake - ("some-problems.h") - (flymake-goto-next-error) - (should (eq 'flymake-warning (face-at-point))) - (flymake-goto-next-error) - (should (eq 'flymake-error (face-at-point))) - (let ((flymake-wrap-around nil)) - (should-error (flymake-goto-next-error nil nil t))) ) - (flymake-tests--with-flymake - ("no-problems.h") - (let ((flymake-wrap-around nil)) - (should-error (flymake-goto-next-error nil nil t))) )) + (let ((flymake-wrap-around nil)) + (flymake-tests--with-flymake + ("some-problems.h") + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) + (should-error (flymake-goto-next-error nil nil t))) + (flymake-tests--with-flymake + ("no-problems.h") + (should-error (flymake-goto-next-error nil nil t))))) (defmacro flymake-tests--assert-set (set should @@ -159,19 +166,15 @@ SEVERITY-PREDICATE is used to setup `(progn ,@(cl-loop for s in should - collect `(should (memq ,s ,set))) + collect `(should (memq (quote ,s) ,set))) ,@(cl-loop for s in should-not - collect `(should-not (memq ,s ,set))))) + collect `(should-not (memq (quote ,s) ,set))))) -(ert-deftest dummy-backends () - "Test GCC warning via function predicate." - (with-temp-buffer - (cl-labels - ((diagnose - (report-fn type words) - (funcall - report-fn +(defun flymake-tests--diagnose-words + (report-fn type words) + "Helper. Call REPORT-FN with diagnostics for WORDS in buffer." + (funcall report-fn (cl-loop for word in words append @@ -184,32 +187,34 @@ SEVERITY-PREDICATE is used to setup (match-end 0) type (concat word " is wrong"))))))) - (error-backend - (report-fn) - (run-with-timer - 0.5 nil - #'diagnose report-fn :error '("manha" "prognata"))) - (warning-backend - (report-fn) - (run-with-timer - 0.5 nil - #'diagnose report-fn :warning '("ut" "dolor"))) - (sync-backend - (report-fn) - (diagnose report-fn :note '("quis" "commodo"))) - (refusing-backend - (_report-fn) - nil) - (panicking-backend - (report-fn) - (run-with-timer - 0.5 nil - report-fn :panic :explanation "The spanish inquisition!")) - (crashing-backend - (_report-fn) - ;; HACK: Shoosh log during tests - (setq-local warning-minimum-log-level :emergency) - (error "crashed"))) + +(ert-deftest dummy-backends () + "Test many different kinds of backends." + (with-temp-buffer + (cl-letf + (((symbol-function 'error-backend) + (lambda (report-fn) + (run-with-timer + 0.5 nil + #'flymake-tests--diagnose-words report-fn :error '("manha" "prognata")))) + ((symbol-function 'warning-backend) + (lambda (report-fn) + (run-with-timer + 0.5 nil + #'flymake-tests--diagnose-words report-fn :warning '("ut" "dolor")))) + ((symbol-function 'sync-backend) + (lambda (report-fn) + (flymake-tests--diagnose-words report-fn :note '("quis" "commodo")))) + ((symbol-function 'panicking-backend) + (lambda (report-fn) + (run-with-timer + 0.5 nil + report-fn :panic :explanation "The spanish inquisition!"))) + ((symbol-function 'crashing-backend) + (lambda (_report-fn) + ;; HACK: Shoosh log during tests + (setq-local warning-minimum-log-level :emergency) + (error "crashed")))) (insert "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore manha aliqua. Ut enim ad minim veniam, quis nostrud @@ -220,31 +225,27 @@ SEVERITY-PREDICATE is used to setup sunt in culpa qui officia deserunt mollit anim id est laborum.") (let ((flymake-diagnostic-functions - (list #'error-backend #'warning-backend #'sync-backend - #'refusing-backend #'panicking-backend - #'crashing-backend - ))) + (list 'error-backend 'warning-backend 'sync-backend + 'panicking-backend + 'crashing-backend + )) + (flymake-wrap-around nil)) (flymake-mode) - ;; FIXME: accessing some flymake-ui's internals here... - (flymake-tests--assert-set flymake--running-backends - (#'error-backend #'warning-backend #'panicking-backend) - (#'sync-backend #'crashing-backend #'refusing-backend)) - (flymake-tests--assert-set flymake--disabled-backends - (#'crashing-backend) - (#'error-backend #'warning-backend #'sync-backend - #'panicking-backend #'refusing-backend)) + (flymake-tests--assert-set (flymake-running-backends) + (error-backend warning-backend panicking-backend) + (crashing-backend)) - (cl-loop repeat 10 while (flymake-is-running) - unless noninteractive do (read-event "" nil 0.1) - do (sleep-for (+ 0.5 flymake-no-changes-timeout))) + (flymake-tests--assert-set (flymake-disabled-backends) + (crashing-backend) + (error-backend warning-backend sync-backend + panicking-backend)) - (should (eq flymake--running-backends '())) + (flymake-tests--wait-for-backends) - (flymake-tests--assert-set flymake--disabled-backends - (#'crashing-backend #'panicking-backend) - (#'error-backend #'warning-backend #'sync-backend - #'refusing-backend)) + (flymake-tests--assert-set (flymake-disabled-backends) + (crashing-backend panicking-backend) + (error-backend warning-backend sync-backend)) (goto-char (point-min)) (flymake-goto-next-error) @@ -265,8 +266,55 @@ SEVERITY-PREDICATE is used to setup (should (eq 'flymake-warning (face-at-point))) ; dolor (flymake-goto-next-error) (should (eq 'flymake-error (face-at-point))) ; prognata - (let ((flymake-wrap-around nil)) - (should-error (flymake-goto-next-error nil nil t))))))) + (should-error (flymake-goto-next-error nil nil t)))))) + +(ert-deftest recurrent-backend () + "Test a backend that calls REPORT-FN multiple times" + (with-temp-buffer + (let (tick) + (cl-letf + (((symbol-function 'eager-backend) + (lambda (report-fn) + (funcall report-fn nil :explanation "very eager but no diagnostics") + (display-buffer (current-buffer)) + (run-with-timer + 0.5 nil + (lambda () + (flymake-tests--diagnose-words report-fn :warning '("consectetur")) + (setq tick t) + (run-with-timer + 0.5 nil + (lambda () + (flymake-tests--diagnose-words report-fn :error '("fugiat")) + (setq tick t)))))))) + (insert "Lorem ipsum dolor sit amet, consectetur adipiscing + elit, sed do eiusmod tempor incididunt ut labore et dolore + manha aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in + voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non prognata + sunt in culpa qui officia deserunt mollit anim id est + laborum.") + (let ((flymake-diagnostic-functions + (list 'eager-backend)) + (flymake-wrap-around nil)) + (flymake-mode) + (flymake-tests--assert-set (flymake-running-backends) + (eager-backend) ()) + (cl-loop until tick repeat 4 do (sleep-for 0.2)) + (setq tick nil) + (goto-char (point-max)) + (flymake-goto-prev-error) + (should (eq 'flymake-warning (face-at-point))) ; consectetur + (should-error (flymake-goto-prev-error nil nil t)) + (cl-loop until tick repeat 4 do (sleep-for 0.2)) + (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) ; fugiat + (flymake-goto-prev-error) + (should (eq 'flymake-warning (face-at-point))) ; back at consectetur + (should-error (flymake-goto-prev-error nil nil t)) + ))))) (provide 'flymake-tests) commit 22a7372faba317a3589c49fef912e542f3197f0d Author: João Távora Date: Sat Sep 30 10:45:48 2017 +0100 Flymake uses proper idle timers Also, flymake-no-changes-timeout can be set to nil to disable automatic periodic checks. But even in that situation the idle timer still runs at a reduced rate to detect changes in the variable and revert that decision. * lisp/progmodes/flymake.el (flymake-no-changes-timeout): Improve doc. (flymake-last-change-time): Delete. (flymake--schedule-timer-maybe): New helper. (flymake-after-change-function): Use it. (flymake-on-timer-event): Delete (flymake-mode): Don't scheduler timer. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 88a305aa54..1068b3889d 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -101,7 +101,8 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." :type 'boolean) (defcustom flymake-no-changes-timeout 0.5 - "Time to wait after last change before starting compilation." + "Time to wait after last change before automatically checking buffer. +If nil, never start checking buffer automatically like this." :type 'number) (defcustom flymake-gui-warnings-enabled t @@ -147,9 +148,6 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." (defvar-local flymake-timer nil "Timer for starting syntax check.") -(defvar-local flymake-last-change-time nil - "Time of last buffer change.") - (defvar-local flymake-check-start-time nil "Time at which syntax check was started.") @@ -491,19 +489,6 @@ associated `flymake-category' return DEFAULT." (overlay-put ov 'flymake t) (overlay-put ov 'flymake--diagnostic diagnostic))) -(defun flymake-on-timer-event (buffer) - "Start a syntax check for buffer BUFFER if necessary." - (when (buffer-live-p buffer) - (with-current-buffer buffer - (when (and (not (flymake-is-running)) - flymake-last-change-time - (> (- (float-time) flymake-last-change-time) - flymake-no-changes-timeout)) - - (setq flymake-last-change-time nil) - (flymake-log :debug "starting syntax check after no changes for some time") - (flymake-start))))) - ;; Nothing in flymake uses this at all any more, so this is just for ;; third-party compatibility. (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") @@ -651,8 +636,6 @@ backends." (add-hook 'after-save-hook 'flymake-after-save-hook nil t) (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) - (setq flymake-timer - (run-at-time nil 1 'flymake-on-timer-event (current-buffer))) (setq flymake--diagnostics-table (make-hash-table)) (when flymake-start-syntax-check-on-find-file @@ -671,6 +654,28 @@ backends." (cancel-timer flymake-timer) (setq flymake-timer nil))))) +(defun flymake--schedule-timer-maybe () + "(Re)schedule an idle timer for checking the buffer. +Do it only if `flymake-no-changes-timeout' is non-nil." + (when flymake-timer (cancel-timer flymake-timer)) + (when flymake-no-changes-timeout + (setq + flymake-timer + (run-with-idle-timer + (seconds-to-time flymake-no-changes-timeout) + nil + (lambda (buffer) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (when (and flymake-mode + flymake-no-changes-timeout) + (flymake-log + :debug "starting syntax check after idle for %s seconds" + flymake-no-changes-timeout) + (flymake-start)) + (setq flymake-timer nil)))) + (current-buffer))))) + ;;;###autoload (defun flymake-mode-on () "Turn flymake mode on." @@ -690,7 +695,7 @@ backends." (when (and flymake-start-syntax-check-on-newline (equal new-text "\n")) (flymake-log :debug "starting syntax check as new-line has been seen") (flymake-start 'deferred)) - (setq flymake-last-change-time (float-time)))) + (flymake--schedule-timer-maybe))) (defun flymake-after-save-hook () (when flymake-mode commit 91851c3ea05392701ca779961e4fb518635fa5b2 Author: João Távora Date: Fri Sep 29 12:18:30 2017 +0100 Flymake variable flymake-diagnostic-functions now a special hook * lisp/progmodes/flymake-proc.el: Use add-hook to affect flymake-diagnostic-functions. * lisp/progmodes/flymake-elisp.el (flymake-elisp-setup-backends): Use add-hook. * lisp/progmodes/flymake.el (flymake-diagnostic-functions): Revise docstring. (flymake-start): Use run-hook-wrapped. diff --git a/lisp/progmodes/flymake-elisp.el b/lisp/progmodes/flymake-elisp.el index 6e7fe3142d..7797d278e3 100644 --- a/lisp/progmodes/flymake-elisp.el +++ b/lisp/progmodes/flymake-elisp.el @@ -159,10 +159,8 @@ Runs in a batch-mode Emacs. Interactively use variable (defun flymake-elisp-setup-backends () "Setup flymake for elisp work." - (add-to-list (make-local-variable 'flymake-diagnostic-functions) - 'flymake-elisp-checkdoc t) - (add-to-list (make-local-variable 'flymake-diagnostic-functions) - 'flymake-elisp-byte-compile t)) + (add-hook 'flymake-diagnostic-functions 'flymake-elisp-checkdoc t t) + (add-hook 'flymake-diagnostic-functions 'flymake-elisp-byte-compile t t)) (add-hook 'emacs-lisp-mode-hook 'flymake-elisp-setup-backends) diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 966f358551..3ab5523128 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -1114,8 +1114,7 @@ Use CREATE-TEMP-F for creating temp copy." ;;;; Hook onto flymake-ui -(add-to-list 'flymake-diagnostic-functions - 'flymake-proc-legacy-flymake) +(add-hook 'flymake-diagnostic-functions 'flymake-proc-legacy-flymake) ;;;; diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 3387f47ed1..88a305aa54 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -309,36 +309,36 @@ Return nil if the region is invalid." (error (flymake-error "Invalid region line=%s col=%s" line col)))) (defvar flymake-diagnostic-functions nil - "List of flymake backends i.e. sources of flymake diagnostics. + "Special hook of Flymake backends to check a buffer. -This variable holds an arbitrary number of \"backends\" or -\"checkers\" providing the flymake user interface with -information about where and how to annotate problems diagnosed in -a buffer. +The functions in this hook diagnose problems in a buffer’s +contents and provide the Flymake user interface with information +about where and how to annotate problems diagnosed in a buffer. -Backends are lisp functions sharing a common calling -convention. Whenever flymake decides it is time to re-check the -buffer, each backend is called with a single argument, a -REPORT-FN callback, detailed below. Backend functions are first +Whenever Flymake or the user decides to re-check the buffer, each +function is called with a common calling convention, a single +REPORT-FN argument, detailed below. Backend functions are first expected to quickly and inexpensively announce the feasibility of -checking the buffer (i.e. they aren't expected to immediately -start checking the buffer): +checking the buffer via the return value (i.e. they aren't +required to immediately start checking the buffer): -* If the backend function returns nil, flymake forgets about this - backend for the current check, but will call it again the next - time; +* If the backend function returns nil, Flymake forgets about this + backend for the current check, but will call it again for the + next one; -* If the backend function returns non-nil, flymake expects this +* If the backend function returns non-nil, Flymake expects this backend to check the buffer and call its REPORT-FN callback - function exactly once. If the computation involved is - inexpensive, the backend function may do so synchronously - before returning. If it is not, it may do so after returning, - using idle timers, asynchronous processes or other asynchronous - mechanisms. + function exactly once. If the computation involved is + inexpensive, the backend function may do so synchronously, + before returning. If it is not, it should do so after + returning, using idle timers, asynchronous processes or other + asynchronous mechanisms. * If the backend function signals an error, it is disabled, - i.e. flymake will not attempt it again for this buffer until - `flymake-mode' is turned off and on again. + i.e. Flymake will not use it again for the current or any + future checks of this buffer. Certain commands, like turning + `flymake-mode' on and off again, resets the list of disabled + backends. Backends are required to call REPORT-FN with a single argument ACTION followed by an optional list of keywords parameters and @@ -347,7 +347,7 @@ their values (:KEY1 VALUE1 :KEY2 VALUE2...). The possible values for ACTION are. * A (possibly empty) list of objects created with - `flymake-make-diagnostic', causing flymake to annotate the + `flymake-make-diagnostic', causing Flymake to annotate the buffer with this information and consider the backend has having finished its check normally. @@ -362,7 +362,7 @@ The recognized optional keyword arguments are: * ‘:explanation’: value should give user-readable details of the situation encountered, if any. -* ‘:force’: value should be a boolean forcing the flymake UI +* ‘:force’: value should be a boolean forcing the Flymake UI to consider the report even if was somehow unexpected.") (defvar flymake-diagnostic-types-alist @@ -613,15 +613,18 @@ backends." (setq flymake--diagnostics-table (make-hash-table) flymake--running-backends nil flymake--disabled-backends nil)) - (dolist (backend flymake-diagnostic-functions) - (cond ((memq backend flymake--running-backends) - (flymake-log :debug "Backend %s still running, not restarting" - backend)) - ((memq backend flymake--disabled-backends) - (flymake-log :debug "Backend %s is disabled, not starting" - backend)) - (t - (flymake--run-backend backend)))))) + (run-hook-wrapped + 'flymake-diagnostic-functions + (lambda (backend) + (cond ((memq backend flymake--running-backends) + (flymake-log :debug "Backend %s still running, not restarting" + backend)) + ((memq backend flymake--disabled-backends) + (flymake-log :debug "Backend %s is disabled, not starting" + backend)) + (t + (flymake--run-backend backend))) + nil)))) (if (and deferred this-command) (add-hook 'post-command-hook #'start 'append 'local) commit bd8ea8873d19e6885e7d6c18c112ed0020d4f744 Author: João Távora Date: Fri Sep 29 11:02:36 2017 +0100 Batch of minor Flymake cleanup actions agreed to with Stefan Discussed with Stefan, in no particular order - Remove aliases for symbols thought to be internal to flymake-proc.el - Don’t need :group in defcustom and defface in flymake.el - Fix docstring of flymake-make-diagnostic - Fix docstring of flymake-diagnostic-functions to clarify keywords. - Mark overlays with just the property ’flymake, not ’flymake-overlay - Tune flymake-overlays for performance - Make flymake-mode-on and flymake-mode-off obsolete - Don’t use hash-table-keys unless necessary. - Copyright notice in flymake-elisp. Added some more - Clarify docstring of flymake-goto-next-error - Clarify a comment in flymake--run-backend complaining about ert-deftest. - Prevent compilation warnings in flymake-proc.el - Remove doctring from obsolete aliases Now the changelog: * lisp/progmodes/flymake-elisp.el: Proper copyright notice. * lisp/progmodes/flymake-proc.el (flymake-warning-re) (flymake-proc-diagnostic-type-pred) (flymake-proc-default-guess) (flymake-proc--get-file-name-mode-and-masks): Move up to beginning of file to shoosh compiler warnings (define-obsolete-variable-alias): Delete many obsolete aliases. * lisp/progmodes/flymake.el (flymake-error-bitmap) (flymake-warning-bitmap, flymake-note-bitmap) (flymake-fringe-indicator-position) (flymake-start-syntax-check-on-newline) (flymake-no-changes-timeout, flymake-gui-warnings-enabled) (flymake-start-syntax-check-on-find-file, flymake-log-level) (flymake-wrap-around, flymake-error, flymake-warning) (flymake-note): Don't need :group in these defcustom and defface. (flymake--run-backend): Clarify comment (flymake-mode-map): Remove. (flymake-make-diagnostic): Fix docstring. (flymake--highlight-line, flymake--overlays): Identify flymake overlays with just ’flymake. (flymake--overlays): Reverse order of invocation for cl-remove-if-not and cl-sort. (flymake-mode-on) (flymake-mode-off): Make obsolete. (flymake-goto-next-error, flymake-goto-prev-error): Fix docstring. (flymake-diagnostic-functions): Clarify keyword arguments in docstring. Maybe squash in that one where I remove many obsoletes diff --git a/lisp/progmodes/flymake-elisp.el b/lisp/progmodes/flymake-elisp.el index bf60f57c82..6e7fe3142d 100644 --- a/lisp/progmodes/flymake-elisp.el +++ b/lisp/progmodes/flymake-elisp.el @@ -1,9 +1,9 @@ ;;; flymake-elisp.el --- Flymake backends for emacs-lisp-mode -*- lexical-binding: t; -*- -;; Copyright (C) 2017 João Távora +;; Copyright (C) 2003-2017 Free Software Foundation, Inc. ;; Author: João Távora -;; Keywords: +;; Keywords: languages tools ;; 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 diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 48d35598b7..966f358551 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -118,6 +118,78 @@ NAME is the file name function to use, default `flymake-proc-get-real-file-name' (defvar flymake-proc--report-fn nil "If bound, function used to report back to flymake's UI.") +(defun flymake-proc-reformat-err-line-patterns-from-compile-el (original-list) + "Grab error line patterns from ORIGINAL-LIST in compile.el format. +Convert it to flymake internal format." + (let* ((converted-list '())) + (dolist (item original-list) + (setq item (cdr item)) + (let ((regexp (nth 0 item)) + (file (nth 1 item)) + (line (nth 2 item)) + (col (nth 3 item))) + (if (consp file) (setq file (car file))) + (if (consp line) (setq line (car line))) + (if (consp col) (setq col (car col))) + + (when (not (functionp line)) + (setq converted-list (cons (list regexp file line col) converted-list))))) + converted-list)) + +(defvar flymake-proc-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text + (append + '( + ;; MS Visual C++ 6.0 + ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)" + 1 3 nil 4) + ;; jikes + ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)" + 1 3 nil 4) + ;; MS midl + ("midl[ ]*:[ ]*\\(command line error .*\\)" + nil nil nil 1) + ;; MS C# + ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)" + 1 3 nil 4) + ;; perl + ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1) + ;; PHP + ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1) + ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1) + ;; ant/javac. Note this also matches gcc warnings! + (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?:[ \t\n]*\\(.+\\)" + 2 4 5 6)) + ;; compilation-error-regexp-alist) + (flymake-proc-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist)) + "Patterns for matching error/warning lines. Each pattern has the form +\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). +Use `flymake-proc-reformat-err-line-patterns-from-compile-el' to add patterns +from compile.el") + +(define-obsolete-variable-alias 'flymake-warning-re 'flymake-proc-diagnostic-type-pred "26.1") +(defvar flymake-proc-diagnostic-type-pred + 'flymake-proc-default-guess + "Predicate matching against diagnostic text to detect its type. +Takes a single argument, the diagnostic's text and should return +a value suitable for indexing +`flymake-diagnostic-types-alist' (which see). If the returned +value is nil, a type of `:error' is assumed. For some backward +compatibility, if a non-nil value is returned that that doesn't +index that alist, a type of `:warning' is assumed. + +Instead of a function, it can also be a string, a regular +expression. A match indicates `:warning' type, otherwise +`:error'") + +(defun flymake-proc-default-guess (text) + "Guess if TEXT means a warning, a note or an error." + (cond ((string-match "^[wW]arning" text) + :warning) + ((string-match "^[nN]ote" text) + :note) + (t + :error))) + (defun flymake-proc--get-file-name-mode-and-masks (file-name) "Return the corresponding entry from `flymake-proc-allowed-file-name-masks'." (unless (stringp file-name) @@ -242,10 +314,10 @@ to the beginning of the list (File.h -> File.cpp moved to top)." Nil means search the entire file.") (defun flymake-proc--check-patch-master-file-buffer - (master-file-temp-buffer - master-file-name patched-master-file-name - source-file-name patched-source-file-name - include-dirs regexp) + (master-file-temp-buffer + master-file-name patched-master-file-name + source-file-name patched-source-file-name + include-dirs regexp) "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME. If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME instead of SOURCE-FILE-NAME. @@ -296,7 +368,7 @@ instead of reading master file from disk." (length source-file-nondir)) nil)) (flymake-log 3 "inc-name=%s" inc-name) (when (flymake-proc--check-include source-file-name inc-name - include-dirs) + include-dirs) (setq found t) ;; replace-match is not used here as it fails in ;; XEmacs with 'last match not a buffer' error as @@ -562,80 +634,8 @@ May only be called in a dynamic environment where (flymake-error "Trouble telling flymake-ui about problem %s(%s)" problem explanation))) -(defun flymake-proc-reformat-err-line-patterns-from-compile-el (original-list) - "Grab error line patterns from ORIGINAL-LIST in compile.el format. -Convert it to flymake internal format." - (let* ((converted-list '())) - (dolist (item original-list) - (setq item (cdr item)) - (let ((regexp (nth 0 item)) - (file (nth 1 item)) - (line (nth 2 item)) - (col (nth 3 item))) - (if (consp file) (setq file (car file))) - (if (consp line) (setq line (car line))) - (if (consp col) (setq col (car col))) - - (when (not (functionp line)) - (setq converted-list (cons (list regexp file line col) converted-list))))) - converted-list)) - (require 'compile) -(defvar flymake-proc-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text - (append - '( - ;; MS Visual C++ 6.0 - ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)" - 1 3 nil 4) - ;; jikes - ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)" - 1 3 nil 4) - ;; MS midl - ("midl[ ]*:[ ]*\\(command line error .*\\)" - nil nil nil 1) - ;; MS C# - ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)" - 1 3 nil 4) - ;; perl - ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1) - ;; PHP - ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1) - ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1) - ;; ant/javac. Note this also matches gcc warnings! - (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?:[ \t\n]*\\(.+\\)" - 2 4 5 6)) - ;; compilation-error-regexp-alist) - (flymake-proc-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist)) - "Patterns for matching error/warning lines. Each pattern has the form -\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). -Use `flymake-proc-reformat-err-line-patterns-from-compile-el' to add patterns -from compile.el") - -(define-obsolete-variable-alias 'flymake-warning-re 'flymake-proc-diagnostic-type-pred "26.1") -(defvar flymake-proc-diagnostic-type-pred - 'flymake-proc-default-guess - "Predicate matching against diagnostic text to detect its type. -Takes a single argument, the diagnostic's text and should return -a value suitable for indexing -`flymake-diagnostic-types-alist' (which see). If the returned -value is nil, a type of `error' is assumed. For some backward -compatibility, if a non-nil value is returned that that doesn't -index that alist, a type of `:warning' is assumed. - -Instead of a function, it can also be a string, a regular -expression. A match indicates `:warning' type, otherwise -`:error'") - -(defun flymake-proc-default-guess (text) - "Guess if TEXT means a warning, a note or an error." - (cond ((string-match "^[wW]arning" text) - :warning) - ((string-match "^[nN]ote" text) - :note) - (t - :error))) - (defun flymake-proc-get-project-include-dirs-imp (basedir) "Include dirs for the project current file belongs to." (if (flymake-proc--get-project-include-dirs-from-cache basedir) @@ -717,7 +717,7 @@ expression. A match indicates `:warning' type, otherwise (defun flymake-proc-legacy-flymake (report-fn &optional interactive) "Flymake backend based on the original flymake implementation. This function is suitable for inclusion in -`flymake-dianostic-types-alist'. For backward compatibility, it +`flymake-diagnostic-types-alist'. For backward compatibility, it can also be executed interactively independently of `flymake-mode'." ;; Interactively, behave as if flymake had invoked us through its @@ -764,8 +764,7 @@ can also be executed interactively independently of t))))))) (define-obsolete-function-alias 'flymake-start-syntax-check - 'flymake-proc-legacy-flymake "26.1" - "Flymake backend based on the original flymake implementation.") + 'flymake-proc-legacy-flymake "26.1") (defun flymake-proc--start-syntax-check-process (cmd args dir) "Start syntax check process." @@ -1016,8 +1015,8 @@ Use CREATE-TEMP-F for creating temp copy." (if buildfile-dir (let* ((temp-source-file-name (flymake-proc-init-create-temp-buffer-copy create-temp-f))) (setq args (flymake-proc--get-syntax-check-program-args temp-source-file-name buildfile-dir - use-relative-base-dir use-relative-source - get-cmdline-f)))) + use-relative-base-dir use-relative-source + get-cmdline-f)))) args)) (defun flymake-proc-simple-make-init () @@ -1123,291 +1122,72 @@ Use CREATE-TEMP-F for creating temp copy." (progn (define-obsolete-variable-alias 'flymake-compilation-prevents-syntax-check - 'flymake-proc-compilation-prevents-syntax-check "26.1" - "If non-nil, don't start syntax check if compilation is running.") + 'flymake-proc-compilation-prevents-syntax-check "26.1") (define-obsolete-variable-alias 'flymake-xml-program - 'flymake-proc-xml-program "26.1" - "Program to use for XML validation.") + 'flymake-proc-xml-program "26.1") (define-obsolete-variable-alias 'flymake-master-file-dirs - 'flymake-proc-master-file-dirs "26.1" - "Dirs where to look for master files.") + 'flymake-proc-master-file-dirs "26.1") (define-obsolete-variable-alias 'flymake-master-file-count-limit 'flymake-proc-master-file-count-limit "26.1" "Max number of master files to check.") (define-obsolete-variable-alias 'flymake-allowed-file-name-masks - 'flymake-proc-allowed-file-name-masks "26.1" - "Files syntax checking is allowed for. -This is an alist with elements of the form: - REGEXP INIT [CLEANUP [NAME]] -REGEXP is a regular expression that matches a file name. -INIT is the init function to use. -CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'. -NAME is the file name function to use, default `flymake-get-real-file-name'.") - (define-obsolete-variable-alias 'flymake-processes - 'flymake-proc--processes "26.1" - "List of currently active flymake processes.") - (define-obsolete-function-alias 'flymake-get-file-name-mode-and-masks - 'flymake-proc--get-file-name-mode-and-masks "26.1" - "Return the corresponding entry from ‘flymake-allowed-file-name-masks’.") - (define-obsolete-function-alias 'flymake-get-init-function - 'flymake-proc--get-init-function "26.1" - "Return init function to be used for the file.") - (define-obsolete-function-alias 'flymake-get-cleanup-function - 'flymake-proc--get-cleanup-function "26.1" - "Return cleanup function to be used for the file.") - (define-obsolete-function-alias 'flymake-get-real-file-name-function - 'flymake-proc--get-real-file-name-function "26.1" - nil) - (define-obsolete-variable-alias 'flymake-find-buildfile-cache - 'flymake-proc--find-buildfile-cache "26.1" - nil) - (define-obsolete-function-alias 'flymake-get-buildfile-from-cache - 'flymake-proc--get-buildfile-from-cache "26.1" - "Look up DIR-NAME in cache and return its associated value. -If DIR-NAME is not found, return nil.") - (define-obsolete-function-alias 'flymake-add-buildfile-to-cache - 'flymake-proc--add-buildfile-to-cache "26.1" - "Associate DIR-NAME with BUILDFILE in the buildfile cache.") - (define-obsolete-function-alias 'flymake-clear-buildfile-cache - 'flymake-proc--clear-buildfile-cache "26.1" - "Clear the buildfile cache.") - (define-obsolete-function-alias 'flymake-find-buildfile - 'flymake-proc--find-buildfile "26.1" - "Find buildfile starting from current directory. -Buildfile includes Makefile, build.xml etc. -Return its file name if found, or nil if not found.") - (define-obsolete-function-alias 'flymake-fix-file-name - 'flymake-proc--fix-file-name "26.1" - "Replace all occurrences of ‘\\’ with ‘/’.") - (define-obsolete-function-alias 'flymake-same-files - 'flymake-proc--same-files "26.1" - "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file. -Return t if so, nil if not. - -(fn FILE-NAME-ONE FILE-NAME-TWO)") - (define-obsolete-variable-alias 'flymake-included-file-name\) - 'flymake-proc--included-file-name\) "26.1" - nil) - (define-obsolete-function-alias 'flymake-find-possible-master-files - 'flymake-proc--find-possible-master-files "26.1" - "Find (by name and location) all possible master files. - -Name is specified by FILE-NAME and location is specified by -MASTER-FILE-DIRS. Master files include .cpp and .c for .h. -Files are searched for starting from the .h directory and max -max-level parent dirs. File contents are not checked.") - (define-obsolete-function-alias 'flymake-master-file-compare - 'flymake-proc--master-file-compare "26.1" - "Compare two files specified by FILE-ONE and FILE-TWO. -This function is used in sort to move most possible file names -to the beginning of the list (File.h -> File.cpp moved to top).") + 'flymake-proc-allowed-file-name-masks "26.1") (define-obsolete-variable-alias 'flymake-check-file-limit - 'flymake-proc-check-file-limit "26.1" - "Maximum number of chars to look at when checking possible master file. -Nil means search the entire file.") - (define-obsolete-function-alias 'flymake-check-patch-master-file-buffer - 'flymake-proc--check-patch-master-file-buffer "26.1" - "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME. -If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME -instead of SOURCE-FILE-NAME. - -For example, foo.cpp is a master file if it includes foo.h. - -When a buffer for MASTER-FILE-NAME exists, use it as a source -instead of reading master file from disk.") - (define-obsolete-function-alias 'flymake-replace-region - 'flymake-proc--replace-region "26.1" - "Replace text in BUFFER in region (BEG END) with REP.") - (define-obsolete-function-alias 'flymake-read-file-to-temp-buffer - 'flymake-proc--read-file-to-temp-buffer "26.1" - "Insert contents of FILE-NAME into newly created temp buffer.") - (define-obsolete-function-alias 'flymake-copy-buffer-to-temp-buffer - 'flymake-proc--copy-buffer-to-temp-buffer "26.1" - "Copy contents of BUFFER into newly created temp buffer.") - (define-obsolete-function-alias 'flymake-check-include - 'flymake-proc--check-include "26.1" - "Check if SOURCE-FILE-NAME can be found in include path. -Return t if it can be found via include path using INC-NAME.") - (define-obsolete-function-alias 'flymake-find-buffer-for-file - 'flymake-proc--find-buffer-for-file "26.1" - "Check if there exists a buffer visiting FILE-NAME. -Return t if so, nil if not.") - (define-obsolete-function-alias 'flymake-create-master-file - 'flymake-proc--create-master-file "26.1" - "Save SOURCE-FILE-NAME with a different name. -Find master file, patch and save it.") - (define-obsolete-function-alias 'flymake-save-buffer-in-file - 'flymake-proc--save-buffer-in-file "26.1" - "Save the entire buffer contents into file FILE-NAME. -Create parent directories as needed.") - (define-obsolete-function-alias 'flymake-process-filter - 'flymake-proc--process-filter "26.1" - "Parse OUTPUT and highlight error lines. -It’s flymake process filter.") - (define-obsolete-function-alias 'flymake-process-sentinel - 'flymake-proc--process-sentinel "26.1" - "Sentinel for syntax check buffers.") - (define-obsolete-function-alias 'flymake-post-syntax-check - 'flymake-proc--post-syntax-check "26.1" - nil) + 'flymake-proc-check-file-limit "26.1") (define-obsolete-function-alias 'flymake-reformat-err-line-patterns-from-compile-el - 'flymake-proc-reformat-err-line-patterns-from-compile-el "26.1" - "Grab error line patterns from ORIGINAL-LIST in compile.el format. -Convert it to flymake internal format.") + 'flymake-proc-reformat-err-line-patterns-from-compile-el "26.1") (define-obsolete-variable-alias 'flymake-err-line-patterns - 'flymake-proc-err-line-patterns "26.1" - "Patterns for matching error/warning lines. Each pattern has the form -(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). -Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns -from compile.el") + 'flymake-proc-err-line-patterns "26.1") (define-obsolete-function-alias 'flymake-parse-line - 'flymake-proc-parse-line "26.1" - "Parse LINE to see if it is an error or warning. -Return its components if so, nil otherwise.") - (define-obsolete-function-alias 'flymake-get-project-include-dirs-imp - 'flymake-proc--get-project-include-dirs-imp "26.1" - "Include dirs for the project current file belongs to.") - (define-obsolete-variable-alias 'flymake-get-project-include-dirs-function - 'flymake-proc--get-project-include-dirs-function "26.1" - "Function used to get project include dirs, one parameter: basedir name.") - (define-obsolete-function-alias 'flymake-get-project-include-dirs - 'flymake-proc--get-project-include-dirs "26.1" - nil) - (define-obsolete-function-alias 'flymake-get-system-include-dirs - 'flymake-proc--get-system-include-dirs "26.1" - "System include dirs - from the ‘INCLUDE’ env setting.") - (define-obsolete-variable-alias 'flymake-project-include-dirs-cache - 'flymake-proc--project-include-dirs-cache "26.1" - nil) - (define-obsolete-function-alias 'flymake-add-project-include-dirs-to-cache - 'flymake-proc--add-project-include-dirs-to-cache "26.1" - nil) - (define-obsolete-function-alias 'flymake-clear-project-include-dirs-cache - 'flymake-proc--clear-project-include-dirs-cache "26.1" - nil) + 'flymake-proc-parse-line "26.1") (define-obsolete-function-alias 'flymake-get-include-dirs - 'flymake-proc-get-include-dirs "26.1" - "Get dirs to use when resolving local file names.") - (define-obsolete-variable-alias 'flymake-restore-formatting - 'flymake-proc--restore-formatting "26.1" - nil) - (define-obsolete-variable-alias 'flymake-get-program-dir - 'flymake-proc--get-program-dir "26.1" - nil) - (define-obsolete-function-alias 'flymake-safe-delete-file - 'flymake-proc--safe-delete-file "26.1" - nil) - (define-obsolete-function-alias 'flymake-safe-delete-directory - 'flymake-proc--safe-delete-directory "26.1" - nil) + 'flymake-proc-get-include-dirs "26.1") (define-obsolete-function-alias 'flymake-stop-all-syntax-checks - 'flymake-proc-stop-all-syntax-checks "26.1" - "Kill all syntax check processes.") - (define-obsolete-function-alias 'flymake-compilation-is-running - 'flymake-proc--compilation-is-running "26.1" - nil) + 'flymake-proc-stop-all-syntax-checks "26.1") (define-obsolete-function-alias 'flymake-compile - 'flymake-proc-compile "26.1" - "Kill all flymake syntax checks, start compilation.") + 'flymake-proc-compile "26.1") (define-obsolete-function-alias 'flymake-create-temp-inplace - 'flymake-proc-create-temp-inplace "26.1" - nil) + 'flymake-proc-create-temp-inplace "26.1") (define-obsolete-function-alias 'flymake-create-temp-with-folder-structure - 'flymake-proc-create-temp-with-folder-structure "26.1" - nil) - (define-obsolete-function-alias 'flymake-delete-temp-directory - 'flymake-proc--delete-temp-directory "26.1" - "Attempt to delete temp dir created by ‘flymake-create-temp-with-folder-structure’, do not fail on error.") - (define-obsolete-variable-alias 'flymake-temp-source-file-name - 'flymake-proc--temp-source-file-name "26.1" - nil) - (define-obsolete-variable-alias 'flymake-master-file-name - 'flymake-proc--master-file-name "26.1" - nil) - (define-obsolete-variable-alias 'flymake-temp-master-file-name - 'flymake-proc--temp-master-file-name "26.1" - nil) - (define-obsolete-variable-alias 'flymake-base-dir - 'flymake-proc--base-dir "26.1" - nil) + 'flymake-proc-create-temp-with-folder-structure "26.1") (define-obsolete-function-alias 'flymake-init-create-temp-buffer-copy - 'flymake-proc-init-create-temp-buffer-copy "26.1" - "Make a temporary copy of the current buffer, save its name in buffer data and return the name.") + 'flymake-proc-init-create-temp-buffer-copy "26.1") (define-obsolete-function-alias 'flymake-simple-cleanup - 'flymake-proc-simple-cleanup "26.1" - "Do cleanup after ‘flymake-init-create-temp-buffer-copy’. -Delete temp file.") + 'flymake-proc-simple-cleanup "26.1") (define-obsolete-function-alias 'flymake-get-real-file-name - 'flymake-proc-get-real-file-name "26.1" - "Translate file name from error message to \"real\" file name. -Return full-name. Names are real, not patched.") - (define-obsolete-function-alias 'flymake-get-full-patched-file-name - 'flymake-proc--get-full-patched-file-name "26.1" - nil) - (define-obsolete-function-alias 'flymake-get-full-nonpatched-file-name - 'flymake-proc--get-full-nonpatched-file-name "26.1" - nil) - (define-obsolete-function-alias 'flymake-init-find-buildfile-dir - 'flymake-proc--init-find-buildfile-dir "26.1" - "Find buildfile, store its dir in buffer data and return its dir, if found.") - (define-obsolete-function-alias 'flymake-init-create-temp-source-and-master-buffer-copy - 'flymake-proc--init-create-temp-source-and-master-buffer-copy "26.1" - "Find master file (or buffer), create its copy along with a copy of the source file.") + 'flymake-proc-get-real-file-name "26.1") (define-obsolete-function-alias 'flymake-master-cleanup - 'flymake-proc-master-cleanup "26.1" - nil) - (define-obsolete-function-alias 'flymake-get-syntax-check-program-args - 'flymake-proc--get-syntax-check-program-args "26.1" - "Create a command line for syntax check using GET-CMD-LINE-F.") + 'flymake-proc-master-cleanup "26.1") (define-obsolete-function-alias 'flymake-get-make-cmdline - 'flymake-proc-get-make-cmdline "26.1" - nil) + 'flymake-proc-get-make-cmdline "26.1") (define-obsolete-function-alias 'flymake-get-ant-cmdline - 'flymake-proc-get-ant-cmdline "26.1" - nil) + 'flymake-proc-get-ant-cmdline "26.1") (define-obsolete-function-alias 'flymake-simple-make-init-impl - 'flymake-proc-simple-make-init-impl "26.1" - "Create syntax check command line for a directly checked source file. -Use CREATE-TEMP-F for creating temp copy.") + 'flymake-proc-simple-make-init-impl "26.1") (define-obsolete-function-alias 'flymake-simple-make-init - 'flymake-proc-simple-make-init "26.1" - nil) + 'flymake-proc-simple-make-init "26.1") (define-obsolete-function-alias 'flymake-master-make-init - 'flymake-proc-master-make-init "26.1" - "Create make command line for a source file checked via master file compilation.") + 'flymake-proc-master-make-init "26.1") (define-obsolete-function-alias 'flymake-find-make-buildfile - 'flymake-proc--find-make-buildfile "26.1" - nil) + 'flymake-proc--find-make-buildfile "26.1") (define-obsolete-function-alias 'flymake-master-make-header-init - 'flymake-proc-master-make-header-init "26.1" - nil) + 'flymake-proc-master-make-header-init "26.1") (define-obsolete-function-alias 'flymake-simple-make-java-init - 'flymake-proc-simple-make-java-init "26.1" - nil) + 'flymake-proc-simple-make-java-init "26.1") (define-obsolete-function-alias 'flymake-simple-ant-java-init - 'flymake-proc-simple-ant-java-init "26.1" - nil) + 'flymake-proc-simple-ant-java-init "26.1") (define-obsolete-function-alias 'flymake-simple-java-cleanup - 'flymake-proc-simple-java-cleanup "26.1" - "Cleanup after ‘flymake-simple-make-java-init’ -- delete temp file and dirs.") + 'flymake-proc-simple-java-cleanup "26.1") (define-obsolete-function-alias 'flymake-perl-init - 'flymake-proc-perl-init "26.1" - nil) + 'flymake-proc-perl-init "26.1") (define-obsolete-function-alias 'flymake-php-init - 'flymake-proc-php-init "26.1" - nil) - (define-obsolete-function-alias 'flymake-get-tex-args - 'flymake-proc--get-tex-args "26.1" - nil) + 'flymake-proc-php-init "26.1") (define-obsolete-function-alias 'flymake-simple-tex-init - 'flymake-proc-simple-tex-init "26.1" - nil) + 'flymake-proc-simple-tex-init "26.1") (define-obsolete-function-alias 'flymake-master-tex-init - 'flymake-proc-master-tex-init "26.1" - nil) + 'flymake-proc-master-tex-init "26.1") (define-obsolete-function-alias 'flymake-xml-init - 'flymake-proc-xml-init "26.1" - nil)) + 'flymake-proc-xml-init "26.1")) diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index c628b4e824..3387f47ed1 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -36,7 +36,7 @@ (require 'thingatpt) ; end-of-thing (require 'warnings) ; warning-numeric-level, display-warning (require 'compile) ; for some faces -(eval-when-compile (require 'subr-x)) ; when-let*, if-let*, hash-table-keys +(eval-when-compile (require 'subr-x)) ; when-let*, if-let* (defgroup flymake nil "Universal on-the-fly syntax checker." @@ -53,7 +53,6 @@ symbols, see `fringe-bitmaps'. See also `flymake-warning-bitmap'. The option `flymake-fringe-indicator-position' controls how and where this is used." - :group 'flymake :version "24.3" :type '(choice (symbol :tag "Bitmap") (list :tag "Bitmap and face" @@ -68,7 +67,6 @@ symbols, see `fringe-bitmaps'. See also `flymake-error-bitmap'. The option `flymake-fringe-indicator-position' controls how and where this is used." - :group 'flymake :version "24.3" :type '(choice (symbol :tag "Bitmap") (list :tag "Bitmap and face" @@ -83,7 +81,6 @@ symbols, see `fringe-bitmaps'. See also `flymake-error-bitmap'. The option `flymake-fringe-indicator-position' controls how and where this is used." - :group 'flymake :version "26.1" :type '(choice (symbol :tag "Bitmap") (list :tag "Bitmap and face" @@ -94,7 +91,6 @@ this is used." "The position to put flymake fringe indicator. The value can be nil (do not use indicators), `left-fringe' or `right-fringe'. See `flymake-error-bitmap' and `flymake-warning-bitmap'." - :group 'flymake :version "24.3" :type '(choice (const left-fringe) (const right-fringe) @@ -102,24 +98,20 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." (defcustom flymake-start-syntax-check-on-newline t "Start syntax check if newline char was added/removed from the buffer." - :group 'flymake :type 'boolean) (defcustom flymake-no-changes-timeout 0.5 "Time to wait after last change before starting compilation." - :group 'flymake :type 'number) (defcustom flymake-gui-warnings-enabled t "Enables/disables GUI warnings." - :group 'flymake :type 'boolean) (make-obsolete-variable 'flymake-gui-warnings-enabled "it no longer has any effect." "26.1") (defcustom flymake-start-syntax-check-on-find-file t "Start syntax check on find file." - :group 'flymake :type 'boolean) (defcustom flymake-log-level -1 @@ -131,7 +123,7 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." (defcustom flymake-wrap-around t "If non-nil, moving to errors wraps around buffer boundaries." - :group 'flymake :type 'boolean) + :type 'boolean) (define-fringe-bitmap 'flymake-double-exclamation-mark (vector #b00000000 @@ -210,7 +202,7 @@ generated it." end type text) - "Mark BUFFER's region from BEG to END with a flymake diagnostic. + "Make a flymake diagnostic for BUFFER's region from BEG to END. TYPE is a key to `flymake-diagnostic-types-alist' and TEXT is a description of the problem detected in this region." (flymake--diag-make :buffer buffer :beg beg :end end :type type :text text)) @@ -231,23 +223,22 @@ description of the problem detected in this region." If BEG is non-nil and END is nil, consider only `overlays-at' BEG. Otherwise consider `overlays-in' the region comprised by BEG and END, defaulting to the whole buffer. Remove all that do not -verify FILTER, sort them by COMPARE (using KEY)." - (cl-remove-if-not - (lambda (ov) - (and (overlay-get ov 'flymake-overlay) - (or (not filter) - (cond ((functionp filter) (funcall filter ov)) - ((symbolp filter) (overlay-get ov filter)))))) - (save-restriction - (widen) - (let ((ovs (if (and beg (null end)) +verify FILTER, a function, and sort them by COMPARE (using KEY)." + (save-restriction + (widen) + (let ((ovs (cl-remove-if-not + (lambda (ov) + (and (overlay-get ov 'flymake) + (or (not filter) + (funcall filter ov)))) + (if (and beg (null end)) (overlays-at beg t) (overlays-in (or beg (point-min)) - (or end (point-max)))))) - (if compare - (cl-sort ovs compare :key (or key - #'identity)) - ovs))))) + (or end (point-max))))))) + (if compare + (cl-sort ovs compare :key (or key + #'identity)) + ovs)))) (defun flymake-delete-own-overlays (&optional filter) "Delete all flymake overlays in BUFFER." @@ -259,8 +250,7 @@ verify FILTER, sort them by COMPARE (using KEY)." (t :inherit error)) "Face used for marking error regions." - :version "24.4" - :group 'flymake) + :version "24.4") (defface flymake-warning '((((supports :underline (:style wave))) @@ -268,8 +258,7 @@ verify FILTER, sort them by COMPARE (using KEY)." (t :inherit warning)) "Face used for marking warning regions." - :version "24.4" - :group 'flymake) + :version "24.4") (defface flymake-note '((((supports :underline (:style wave))) @@ -277,8 +266,7 @@ verify FILTER, sort them by COMPARE (using KEY)." (t :inherit warning)) "Face used for marking note regions." - :version "26.1" - :group 'flymake) + :version "26.1") (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") @@ -324,7 +312,7 @@ Return nil if the region is invalid." "List of flymake backends i.e. sources of flymake diagnostics. This variable holds an arbitrary number of \"backends\" or -\"checkers\" providing the flymake UI's \"frontend\" with +\"checkers\" providing the flymake user interface with information about where and how to annotate problems diagnosed in a buffer. @@ -340,19 +328,23 @@ start checking the buffer): backend for the current check, but will call it again the next time; -* If the backend function returns non-nil, flymake expects this backend to - check the buffer and call its REPORT-FN callback function. If - the computation involved is inexpensive, the backend function - may do so synchronously before returning. If it is not, it may - do so after retuning, using idle timers, asynchronous - processes or other asynchronous mechanisms. +* If the backend function returns non-nil, flymake expects this + backend to check the buffer and call its REPORT-FN callback + function exactly once. If the computation involved is + inexpensive, the backend function may do so synchronously + before returning. If it is not, it may do so after returning, + using idle timers, asynchronous processes or other asynchronous + mechanisms. -* If the backend function signals an error, it is disabled, i.e. flymake - will not attempt it again for this buffer until `flymake-mode' - is turned off and on again. +* If the backend function signals an error, it is disabled, + i.e. flymake will not attempt it again for this buffer until + `flymake-mode' is turned off and on again. -When calling REPORT-FN, the first argument passed to it decides -how to proceed. Recognized values are: +Backends are required to call REPORT-FN with a single argument +ACTION followed by an optional list of keywords parameters and +their values (:KEY1 VALUE1 :KEY2 VALUE2...). + +The possible values for ACTION are. * A (possibly empty) list of objects created with `flymake-make-diagnostic', causing flymake to annotate the @@ -365,9 +357,13 @@ how to proceed. Recognized values are: * The symbol `:panic', signalling that the backend has encountered an exceptional situation and should be disabled. -In the latter cases, it is also possible to provide REPORT-FN -with a string as the keyword argument `:explanation'. The string -should give human-readable details of the situation.") +The recognized optional keyword arguments are: + +* ‘:explanation’: value should give user-readable details of + the situation encountered, if any. + +* ‘:force’: value should be a boolean forcing the flymake UI + to consider the report even if was somehow unexpected.") (defvar flymake-diagnostic-types-alist `((:error @@ -492,7 +488,7 @@ associated `flymake-category' return DEFAULT." ;; Some properties can't be overriden ;; (overlay-put ov 'evaporate t) - (overlay-put ov 'flymake-overlay t) + (overlay-put ov 'flymake t) (overlay-put ov 'flymake--diagnostic diagnostic))) (defun flymake-on-timer-event (buffer) @@ -589,9 +585,9 @@ sources." "Run the backend BACKEND." (push backend flymake--running-backends) (remhash backend flymake--diagnostics-table) - ;; FIXME: Should use `condition-case-unless-debug' - ;; here, but that won't let me catch errors during - ;; testing where `debug-on-error' is always t + ;; FIXME: Should use `condition-case-unless-debug' here, but that + ;; won't let me catch errors from inside `ert-deftest' where + ;; `debug-on-error' is always t (condition-case err (unless (funcall backend (flymake-make-report-fn backend)) @@ -682,6 +678,9 @@ backends." "Turn flymake mode off." (flymake-mode 0)) +(make-obsolete 'flymake-mode-on 'flymake-mode "26.1") +(make-obsolete 'flymake-mode-off 'flymake-mode "26.1") + (defun flymake-after-change-function (start stop _len) "Start syntax check for current buffer if it isn't already running." (let((new-text (buffer-substring start stop))) @@ -709,14 +708,19 @@ backends." (defun flymake-goto-next-error (&optional n filter interactive) "Go to Nth next flymake error in buffer matching FILTER. + +Interactively, always move to the next error. Interactively, and +with a prefix arg, skip any diagnostics with a severity less than +‘:warning’. + +If ‘flymake-wrap-around’ is non-nil, resumes search from top +at end of buffer. + FILTER is a list of diagnostic types found in `flymake-diagnostic-types-alist', or nil, if no filter is to be -applied. - -Interactively, always goes to the next error. Also -interactively, FILTER is determined by the prefix arg. With no -prefix arg, don't use a filter, otherwise only consider -diagnostics of type `:error' and `:warning'." +applied." + ;; TODO: let filter be a number, a severity below which diags are + ;; skipped. (interactive (list 1 (if current-prefix-arg '(:error :warning)) @@ -760,14 +764,17 @@ diagnostics of type `:error' and `:warning'." (defun flymake-goto-prev-error (&optional n filter interactive) "Go to Nth previous flymake error in buffer matching FILTER. + +Interactively, always move to the previous error. Interactively, +and with a prefix arg, skip any diagnostics with a severity less +than ‘:warning’. + +If ‘flymake-wrap-around’ is non-nil, resumes search from top +at end of buffer. + FILTER is a list of diagnostic types found in `flymake-diagnostic-types-alist', or nil, if no filter is to be -applied. - -Interactively, always goes to the previous error. Also -interactively, FILTER is determined by the prefix arg. With no -prefix arg, don't use a filter, otherwise only consider -diagnostics of type `:error' and `:warning'." +applied." (interactive (list 1 (if current-prefix-arg '(:error :warning)) t)) @@ -783,7 +790,8 @@ diagnostics of type `:error' and `:warning'." (defun flymake--mode-line-format () "Produce a pretty minor mode indicator." (let ((running flymake--running-backends) - (reported (hash-table-keys flymake--diagnostics-table))) + (reported (cl-plusp + (hash-table-count flymake--diagnostics-table)))) `((:propertize " Flymake" mouse-face mode-line-highlight ,@(when (not reported) commit 87993cdcc69d3cc21cfe3bf2ed9e0ffbd3cfe5f0 Author: João Távora Date: Thu Sep 28 14:57:01 2017 +0100 Explicitly add a(n empty) keymap for Flymake Too early to decide what will be in it, if anything. Though "M-n" and "M-p" would be great. * lisp/progmodes/flymake-ui.el (flymake-mode-map): New variable diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 00bea3f55b..c628b4e824 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -631,9 +631,14 @@ backends." (add-hook 'post-command-hook #'start 'append 'local) (start)))) +(defvar flymake-mode-map + (let ((map (make-sparse-keymap))) + map) + "Keymap for `flymake-mode'.") + ;;;###autoload (define-minor-mode flymake-mode nil - :group 'flymake :lighter flymake--mode-line-format + :group 'flymake :lighter flymake--mode-line-format :keymap flymake-mode-map (setq flymake--running-backends nil flymake--disabled-backends nil) (cond commit 1b271ad76eff4bd570c292dd7a8c696c19361056 Author: João Távora Date: Thu Sep 28 14:17:27 2017 +0100 Flymake uses some new fringe bitmaps Also fix behaviour whereby flymake wouldn't react to a change in the variable. * lisp/progmodes/flymake-ui.el (flymake-error-bitmap) (flymake-warning-bitmap): Update bitmaps. (flymake-note-bitmap): New defcustom. (flymake-double-exclamation-mark): New bitmap. (flymake-error, flymake-warning, flymake-note) (flymake--highlight-line): 'bitmap property must be a symbol. Also set default face to flymake-error. (flymake--fringe-overlay-spec): Bitmap property can be a variable symbol. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 20c94d20d8..00bea3f55b 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -44,7 +44,8 @@ :link '(custom-manual "(flymake) Top") :group 'tools) -(defcustom flymake-error-bitmap '(exclamation-mark error) +(defcustom flymake-error-bitmap '(flymake-double-exclamation-mark + compilation-error) "Bitmap (a symbol) used in the fringe for indicating errors. The value may also be a list of two elements where the second element specifies the face for the bitmap. For possible bitmap @@ -59,7 +60,7 @@ this is used." (symbol :tag "Bitmap") (face :tag "Face")))) -(defcustom flymake-warning-bitmap 'question-mark +(defcustom flymake-warning-bitmap '(exclamation-mark compilation-warning) "Bitmap (a symbol) used in the fringe for indicating warnings. The value may also be a list of two elements where the second element specifies the face for the bitmap. For possible bitmap @@ -74,6 +75,21 @@ this is used." (symbol :tag "Bitmap") (face :tag "Face")))) +(defcustom flymake-note-bitmap '(exclamation-mark compilation-info) + "Bitmap (a symbol) used in the fringe for indicating info notes. +The value may also be a list of two elements where the second +element specifies the face for the bitmap. For possible bitmap +symbols, see `fringe-bitmaps'. See also `flymake-error-bitmap'. + +The option `flymake-fringe-indicator-position' controls how and where +this is used." + :group 'flymake + :version "26.1" + :type '(choice (symbol :tag "Bitmap") + (list :tag "Bitmap and face" + (symbol :tag "Bitmap") + (face :tag "Face")))) + (defcustom flymake-fringe-indicator-position 'left-fringe "The position to put flymake fringe indicator. The value can be nil (do not use indicators), `left-fringe' or `right-fringe'. @@ -117,6 +133,25 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." "If non-nil, moving to errors wraps around buffer boundaries." :group 'flymake :type 'boolean) +(define-fringe-bitmap 'flymake-double-exclamation-mark + (vector #b00000000 + #b00000000 + #b00000000 + #b00000000 + #b01100110 + #b01100110 + #b01100110 + #b01100110 + #b01100110 + #b01100110 + #b01100110 + #b01100110 + #b00000000 + #b01100110 + #b00000000 + #b00000000 + #b00000000)) + (defvar-local flymake-timer nil "Timer for starting syntax check.") @@ -367,17 +402,17 @@ the diagnostics of each type. The recognized properties are: that differ from an existing type by only a few properties.") (put 'flymake-error 'face 'flymake-error) -(put 'flymake-error 'bitmap flymake-error-bitmap) +(put 'flymake-error 'bitmap 'flymake-error-bitmap) (put 'flymake-error 'severity (warning-numeric-level :error)) (put 'flymake-error 'mode-line-face 'compilation-error) (put 'flymake-warning 'face 'flymake-warning) -(put 'flymake-warning 'bitmap flymake-warning-bitmap) +(put 'flymake-warning 'bitmap 'flymake-warning-bitmap) (put 'flymake-warning 'severity (warning-numeric-level :warning)) (put 'flymake-warning 'mode-line-face 'compilation-warning) (put 'flymake-note 'face 'flymake-note) -(put 'flymake-note 'bitmap nil) +(put 'flymake-note 'bitmap 'flymake-note-bitmap) (put 'flymake-note 'severity (warning-numeric-level :debug)) (put 'flymake-note 'mode-line-face 'compilation-info) @@ -401,14 +436,19 @@ associated `flymake-category' return DEFAULT." (t default)))) -(defun flymake--fringe-overlay-spec (bitmap) - (and flymake-fringe-indicator-position - bitmap - (propertize "!" 'display - (cons flymake-fringe-indicator-position - (if (listp bitmap) - bitmap - (list bitmap)))))) +(defun flymake--fringe-overlay-spec (bitmap &optional recursed) + (if (and (symbolp bitmap) + (boundp bitmap) + (not recursed)) + (flymake--fringe-overlay-spec + (symbol-value bitmap) t) + (and flymake-fringe-indicator-position + bitmap + (propertize "!" 'display + (cons flymake-fringe-indicator-position + (if (listp bitmap) + bitmap + (list bitmap))))))) (defun flymake--highlight-line (diagnostic) "Highlight buffer with info in DIAGNOSTIC." @@ -434,7 +474,8 @@ associated `flymake-category' return DEFAULT." (and cat (plist-member (symbol-plist cat) prop)))) (overlay-put ov prop value)))) - (default-maybe 'bitmap flymake-error-bitmap) + (default-maybe 'bitmap 'flymake-error-bitmap) + (default-maybe 'face 'flymake-error) (default-maybe 'before-string (flymake--fringe-overlay-spec (overlay-get ov 'bitmap))) commit cd39edb5d641f82accad21cd85ff26b3995f1c85 Author: João Távora Date: Thu Sep 28 12:24:44 2017 +0100 Remove old flymake-display-err-menu-for-current-line, it's useless See https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00949.html * lisp/progmodes/flymake-ui.el (flymake-popup-current-error-menu): Remove. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 0071ba93ea..20c94d20d8 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -467,38 +467,6 @@ associated `flymake-category' return DEFAULT." (flymake-log :debug "starting syntax check after no changes for some time") (flymake-start))))) -(define-obsolete-function-alias 'flymake-display-err-menu-for-current-line - 'flymake-popup-current-error-menu "24.4") - -(defun flymake-popup-current-error-menu (&optional event) - "Pop up a menu with errors/warnings for current line." - (interactive (list last-nonmenu-event)) - (let* ((diag-overlays (or - (flymake--overlays :filter 'flymake--diagnostic - :beg (line-beginning-position) - :end (line-end-position)) - (user-error "No flymake problem for current line"))) - (menu (mapcar (lambda (ov) - (let ((diag (overlay-get ov 'flymake--diagnostic))) - (cons (flymake--diag-text diag) - ov))) - diag-overlays)) - (event (if (mouse-event-p event) - event - (list 'mouse-1 (posn-at-point)))) - (diagnostics (mapcar (lambda (ov) (overlay-get ov 'flymake--diagnostic)) - diag-overlays)) - (title (format "Line %d: %d diagnostics(s)" - (line-number-at-pos) - (length diagnostics))) - (choice (x-popup-menu event (list title (cons "" menu))))) - (flymake-log :debug "choice=%s" choice) - ;; FIXME: What is the point of going to the problem locus if we're - ;; certainly already there? - ;; - (when choice (goto-char (overlay-start choice))))) - - ;; Nothing in flymake uses this at all any more, so this is just for ;; third-party compatibility. (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") commit 5235eaf7816167143b5fef66e5c1acce569c4f61 Author: João Távora Date: Thu Sep 28 12:21:43 2017 +0100 Treat Flymake errors as just another type of diagnostic * lisp/progmodes/flymake.el (flymake--diag-errorp): Remove. (flymake--handle-report, flymake-popup-current-error-menu): Don't use it. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 285ef93db6..0071ba93ea 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -401,13 +401,6 @@ associated `flymake-category' return DEFAULT." (t default)))) -(defun flymake--diag-errorp (diag) - "Tell if DIAG is a flymake error or something else" - (let ((sev (flymake--lookup-type-property 'severity - (flymake--diag-type diag) - (warning-numeric-level :error)))) - (>= sev (warning-numeric-level :error)))) - (defun flymake--fringe-overlay-spec (bitmap) (and flymake-fringe-indicator-position bitmap @@ -495,10 +488,9 @@ associated `flymake-category' return DEFAULT." (list 'mouse-1 (posn-at-point)))) (diagnostics (mapcar (lambda (ov) (overlay-get ov 'flymake--diagnostic)) diag-overlays)) - (title (format "Line %d: %d error(s), %d other(s)" + (title (format "Line %d: %d diagnostics(s)" (line-number-at-pos) - (cl-count-if #'flymake--diag-errorp diagnostics) - (cl-count-if-not #'flymake--diag-errorp diagnostics))) + (length diagnostics))) (choice (x-popup-menu event (list title (cons "" menu))))) (flymake-log :debug "choice=%s" choice) ;; FIXME: What is the point of going to the problem locus if we're @@ -560,13 +552,11 @@ FORCE says to handle a report even if it was not expected." (flymake--highlight-line diag) (setf (flymake--diag-backend diag) backend)) diagnostics) - (let ((err-count (cl-count-if #'flymake--diag-errorp diagnostics)) - (warn-count (cl-count-if-not #'flymake--diag-errorp - diagnostics))) - (when flymake-check-start-time - (flymake-log 2 "%d error(s), %d other(s) in %.2f second(s)" - err-count warn-count - (- (float-time) flymake-check-start-time))))))) + (when flymake-check-start-time + (flymake-log 2 "backend %s reported %d diagnostics in %.2f second(s)" + backend + (length diagnostics) + (- (float-time) flymake-check-start-time)))))) (t (flymake--disable-backend "?" :strange commit 8118f0f95f993f64f30ab1d48d9e988ab6f58019 Author: João Távora Date: Thu Sep 28 12:06:56 2017 +0100 Fix three Flymake bugs when checking C header files The first of these problems is longstanding: if an error-less B.h is included from error-ridden A.h, flymake's legacy parser will panic (and disable itself) since it sees a non-zero exit for a clean file. To fix this, recommend returning 'true' in the documentation for the check-syntax target. Another problem was introduced by the parser rewrite. For error patterns spanning more than one line, point may be left in the middle of a line and thus render other patterns useless. Those patterns were written for the old line-by-line parser. To make them useful again, move to the beginning of line in those situations. The third problem was also longstanding and happened on newer GCC's: The "In file included from" prefix confused flymake-proc-get-real-file-name. Fix this. Also updated flymake--diag-region to fallback to highlighting a full line less often. Add automatic tests to check this. * lisp/progmodes/flymake-proc.el (flymake-proc--diagnostics-for-pattern): Fix bug when patterns accidentally spans more than one line. Don't create diagnostics without error messages. (flymake-proc-real-file-name-considering-includes): New helper. (flymake-proc-allowed-file-name-masks): Use it. * lisp/progmodes/flymake.el (flymake-diag-region): Make COL argument explicitly optional. Only fall back to full line in extreme cases. * test/lisp/progmodes/flymake-tests.el (included-c-header-files): New test. (different-diagnostic-types): Update. * test/lisp/progmodes/flymake-resources/Makefile (check-syntax): Always return success (0) error code. (CC_OPTS): Add -Wextra * test/lisp/progmodes/flymake-resources/errors-and-warnings.c (main): Rewrite comments. * test/lisp/progmodes/flymake-resources/errors-and-warnings.c: Include some dummy header files. * test/lisp/progmodes/flymake-resources/no-problems.h: New file. * test/lisp/progmodes/flymake-resources/some-problems.h: New file. * doc/misc/flymake.texi (Example---Configuring a tool called via make): Recommend adding "|| true" to the check-syntax target. diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi index 1bc416fd02..01849b7d9a 100644 --- a/doc/misc/flymake.texi +++ b/doc/misc/flymake.texi @@ -492,7 +492,7 @@ our case this target might look like this: @verbatim check-syntax: - gcc -o /dev/null -S ${CHK_SOURCES} + gcc -o /dev/null -S ${CHK_SOURCES} || true @end verbatim @noindent @@ -504,7 +504,7 @@ Automake variable @code{COMPILE}: @verbatim check-syntax: - $(COMPILE) -o /dev/null -S ${CHK_SOURCES} + $(COMPILE) -o /dev/null -S ${CHK_SOURCES} || true @end verbatim @node Flymake Implementation diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 37b7e49dea..48d35598b7 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -66,7 +66,10 @@ :type 'integer) (defcustom flymake-proc-allowed-file-name-masks - '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-proc-simple-make-init) + '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" + flymake-proc-simple-make-init + nil + flymake-proc-real-file-name-considering-includes) ("\\.xml\\'" flymake-proc-xml-init) ("\\.html?\\'" flymake-proc-xml-init) ("\\.cs\\'" flymake-proc-simple-make-init) @@ -419,12 +422,25 @@ Create parent directories as needed." (condition-case-unless-debug err (cl-loop with (regexp file-idx line-idx col-idx message-idx) = pattern - while (search-forward-regexp regexp nil t) + while (and + (search-forward-regexp regexp nil t) + ;; If the preceding search spanned more than one line, + ;; move to the start of the line we ended up in. This + ;; preserves the usefulness of the patterns in + ;; `flymake-proc-err-line-patterns', which were + ;; written primarily for flymake's original + ;; line-by-line parsing and thus never spanned + ;; multiple lines. + (if (/= (line-number-at-pos (match-beginning 0)) + (line-number-at-pos)) + (goto-char (line-beginning-position)) + t)) for fname = (and file-idx (match-string file-idx)) for message = (and message-idx (match-string message-idx)) for line-string = (and line-idx (match-string line-idx)) - for line-number = (and line-string - (string-to-number line-string)) + for line-number = (or (and line-string + (string-to-number line-string)) + 1) for col-string = (and col-idx (match-string col-idx)) for col-number = (and col-string (string-to-number col-string)) @@ -436,7 +452,7 @@ Create parent directories as needed." fname))) for buffer = (and full-file (find-buffer-visiting full-file)) - if (eq buffer (process-buffer proc)) + if (and (eq buffer (process-buffer proc)) message) collect (with-current-buffer buffer (pcase-let ((`(,beg . ,end) (flymake-diag-region line-number col-number))) @@ -1030,6 +1046,13 @@ Use CREATE-TEMP-F for creating temp copy." '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'") "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\"")) +(defun flymake-proc-real-file-name-considering-includes (scraped) + (flymake-proc-get-real-file-name + (let ((case-fold-search t)) + (replace-regexp-in-string "^in file included from[ \t*]" + "" + scraped)))) + ;;;; .java/make specific (defun flymake-proc-simple-make-java-init () (flymake-proc-simple-make-init-impl 'flymake-proc-create-temp-with-folder-structure nil nil "Makefile" 'flymake-proc-get-make-cmdline)) diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 282727e315..285ef93db6 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -248,9 +248,10 @@ verify FILTER, sort them by COMPARE (using KEY)." (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") -(defun flymake-diag-region (line col) +(defun flymake-diag-region (line &optional col) "Compute region (BEG . END) corresponding to LINE and COL. -Or nil if the region is invalid." +If COL is nil, return a region just for LINE. +Return nil if the region is invalid." (condition-case-unless-debug _err (let ((line (min (max line 1) (line-number-at-pos (point-max) 'absolute)))) @@ -267,13 +268,18 @@ Or nil if the region is invalid." (if (eq (point) beg) (line-beginning-position 2) (point))))) - (if col - (let* ((beg (progn (forward-char (1- col)) (point))) + (if (and col (cl-plusp col)) + (let* ((beg (progn (forward-char (1- col)) + (point))) (sexp-end (ignore-errors (end-of-thing 'sexp))) - (end (or sexp-end - (fallback-eol beg)))) - (cons (if sexp-end beg (fallback-bol)) - end)) + (end (or (and sexp-end + (not (= sexp-end beg)) + sexp-end) + (ignore-errors (goto-char (1+ beg))))) + (safe-end (or end + (fallback-eol beg)))) + (cons (if end beg (fallback-bol)) + safe-end)) (let* ((beg (fallback-bol)) (end (fallback-eol beg))) (cons beg end)))))) diff --git a/test/lisp/progmodes/flymake-resources/Makefile b/test/lisp/progmodes/flymake-resources/Makefile index 0f3f39791c..494407567f 100644 --- a/test/lisp/progmodes/flymake-resources/Makefile +++ b/test/lisp/progmodes/flymake-resources/Makefile @@ -1,6 +1,6 @@ # Makefile for flymake tests -CC_OPTS = -Wall +CC_OPTS = -Wall -Wextra ## Recent gcc (e.g. 4.8.2 on RHEL7) can automatically colorize their output, ## which can confuse flymake. Set GCC_COLORS to disable that. @@ -8,6 +8,6 @@ CC_OPTS = -Wall ## normally use flymake, so it seems like just avoiding the issue ## in this test is fine. Set flymake-log-level to 3 to investigate. check-syntax: - GCC_COLORS= $(CC) $(CC_OPTS) ${CHK_SOURCES} + GCC_COLORS= $(CC) $(CC_OPTS) ${CHK_SOURCES} || true # eof diff --git a/test/lisp/progmodes/flymake-resources/errors-and-warnings.c b/test/lisp/progmodes/flymake-resources/errors-and-warnings.c index 6454dd2023..1d38bd6bd2 100644 --- a/test/lisp/progmodes/flymake-resources/errors-and-warnings.c +++ b/test/lisp/progmodes/flymake-resources/errors-and-warnings.c @@ -1,10 +1,13 @@ - int main() +/* Flymake should notice an error on the next line, since + that file has at least one warning.*/ +#include "some-problems.h" +/* But not this one */ +#include "no-problems.h" + +int main() { - char c = 1000; + char c = 1000; /* a note and a warning */ int bla; - /* The following line should have one warning and one error. The - warning spans the full line because gcc (at least 6.3.0) points - places the error at the =, which isn't a sexp.*/ - char c; if (bla == (void*)3); + char c; if (bla == (void*)3); /* an error, and two warnings */ return c; } diff --git a/test/lisp/progmodes/flymake-resources/no-problems.h b/test/lisp/progmodes/flymake-resources/no-problems.h new file mode 100644 index 0000000000..19ddc615b3 --- /dev/null +++ b/test/lisp/progmodes/flymake-resources/no-problems.h @@ -0,0 +1 @@ +typedef int no_problems; diff --git a/test/lisp/progmodes/flymake-resources/some-problems.h b/test/lisp/progmodes/flymake-resources/some-problems.h new file mode 100644 index 0000000000..165d8dd525 --- /dev/null +++ b/test/lisp/progmodes/flymake-resources/some-problems.h @@ -0,0 +1,5 @@ +#include + +strange; + +sint main(); diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index fa77a9a8ae..222c8f1184 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -122,13 +122,33 @@ SEVERITY-PREDICATE is used to setup (flymake-tests--with-flymake ("errors-and-warnings.c") (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) + (flymake-goto-next-error) (should (eq 'flymake-note (face-at-point))) (flymake-goto-next-error) (should (eq 'flymake-warning (face-at-point))) (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) + (let ((flymake-wrap-around nil)) + (should-error (flymake-goto-next-error nil nil t))) )) + +(ert-deftest included-c-header-files () + "Test inclusion of .h header files." + (skip-unless (and (executable-find "gcc") (executable-find "make"))) + (flymake-tests--with-flymake + ("some-problems.h") + (flymake-goto-next-error) (should (eq 'flymake-warning (face-at-point))) (flymake-goto-next-error) (should (eq 'flymake-error (face-at-point))) + (let ((flymake-wrap-around nil)) + (should-error (flymake-goto-next-error nil nil t))) ) + (flymake-tests--with-flymake + ("no-problems.h") (let ((flymake-wrap-around nil)) (should-error (flymake-goto-next-error nil nil t))) )) commit 9a629a73e0eb10589f5befaf0a1a3290d9ba6a47 Author: João Távora Date: Wed Sep 27 17:47:14 2017 +0100 Add interactive flymake-start function * lisp/progmodes/flymake.el (flymake-on-timer-event) (flymake-after-change-function, flymake-mode): Call flymake-start. (flymake-start): Rename from flymake--start-syntax-check. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 8c92dc7e53..282727e315 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -466,7 +466,7 @@ associated `flymake-category' return DEFAULT." (setq flymake-last-change-time nil) (flymake-log :debug "starting syntax check after no changes for some time") - (flymake--start-syntax-check))))) + (flymake-start))))) (define-obsolete-function-alias 'flymake-display-err-menu-for-current-line 'flymake-popup-current-error-menu "24.4") @@ -596,15 +596,22 @@ sources." err) (flymake--stop-backend backend)))) -(defun flymake--start-syntax-check (&optional deferred) +(defun flymake-start (&optional deferred interactive) "Start a syntax check. Start it immediately, or after current command if DEFERRED is -non-nil." +non-nil. With optional INTERACTIVE or interactively, clear any +stale information about running and automatically disabled +backends." + (interactive (list nil t)) (cl-labels ((start () (remove-hook 'post-command-hook #'start 'local) (setq flymake-check-start-time (float-time)) + (when interactive + (setq flymake--diagnostics-table (make-hash-table) + flymake--running-backends nil + flymake--disabled-backends nil)) (dolist (backend flymake-diagnostic-functions) (cond ((memq backend flymake--running-backends) (flymake-log :debug "Backend %s still running, not restarting" @@ -640,7 +647,7 @@ non-nil." (setq flymake--diagnostics-table (make-hash-table)) (when flymake-start-syntax-check-on-find-file - (flymake--start-syntax-check))))) + (flymake-start))))) ;; Turning the mode OFF. (t @@ -670,13 +677,13 @@ non-nil." (let((new-text (buffer-substring start stop))) (when (and flymake-start-syntax-check-on-newline (equal new-text "\n")) (flymake-log :debug "starting syntax check as new-line has been seen") - (flymake--start-syntax-check 'deferred)) + (flymake-start 'deferred)) (setq flymake-last-change-time (float-time)))) (defun flymake-after-save-hook () (when flymake-mode (flymake-log :debug "starting syntax check as buffer was saved") - (flymake--start-syntax-check))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???) + (flymake-start))) (defun flymake-kill-buffer-hook () (when flymake-timer commit 483f1e834d9008e7b48d7abb3afa84be352014b1 Author: João Távora Date: Wed Sep 27 02:44:06 2017 +0100 A couple of Flymake backends for emacs-lisp-mode Loading flymake-elisp.el doesn't setup flymake-mode to turn on automatically, but it affects emacs-lisp-mode-hook so that flymake-diagnostic-functions is setup with a suitable buffer-local value. The variable flymake-diagnostic-funtions in every live emacs-lisp-mode buffer is also adjusted. * lisp/progmodes/flymake.el (top): Require flymake-elisp. * lisp/progmodes/flymake-elisp.el: New file. diff --git a/lisp/progmodes/flymake-elisp.el b/lisp/progmodes/flymake-elisp.el new file mode 100644 index 0000000000..bf60f57c82 --- /dev/null +++ b/lisp/progmodes/flymake-elisp.el @@ -0,0 +1,176 @@ +;;; flymake-elisp.el --- Flymake backends for emacs-lisp-mode -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 João Távora + +;; Author: João Távora +;; Keywords: + +;; 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 . + +;;; Commentary: + +;; Flymake backends for elisp work. + +;;; Code: +(require 'flymake) +(require 'checkdoc) +(eval-when-compile (require 'cl-lib)) +(require 'bytecomp) + +(defun flymake-elisp--checkdoc-1 () + "Do actual work for `flymake-elisp-checkdoc'." + (let (collected) + (cl-letf (((symbol-function 'checkdoc-create-error) + (lambda (text start end &optional unfixable) + (push (list text start end unfixable) collected) + nil))) + (let* ((checkdoc-autofix-flag nil) + (checkdoc-generate-compile-warnings-flag nil) + (buf (generate-new-buffer " *checkdoc-temp*")) + (checkdoc-diagnostic-buffer buf)) + (unwind-protect + (save-excursion + (checkdoc-current-buffer t)) + (kill-buffer buf)))) + collected)) + +(defun flymake-elisp-checkdoc (report-fn) + "A flymake backend for `checkdoc'. +Calls REPORT-FN directly." + (when (derived-mode-p 'emacs-lisp-mode) + (funcall report-fn + (cl-loop for (text start end _unfixable) in + (flymake-elisp--checkdoc-1) + collect + (flymake-make-diagnostic + (current-buffer) + start end :note text))))) + +(defun flymake-elisp--byte-compile-done (report-fn + origin-buffer + output-buffer + temp-file) + (unwind-protect + (with-current-buffer + origin-buffer + (save-excursion + (save-restriction + (widen) + (funcall + report-fn + (ignore-errors + (cl-loop with data = + (with-current-buffer output-buffer + (goto-char (point-min)) + (search-forward ":flymake-elisp-output-start") + (read (point-marker))) + for (string pos _fill level) in data + do (goto-char pos) + for beg = (if (< (point) (point-max)) + (point) + (line-beginning-position)) + for end = (min + (line-end-position) + (or (cdr + (bounds-of-thing-at-point 'sexp)) + (point-max))) + collect (flymake-make-diagnostic + (current-buffer) + (if (= beg end) (1- beg) beg) + end + level + string))))))) + (kill-buffer output-buffer) + (ignore-errors (delete-file temp-file)))) + +(defun flymake-elisp-byte-compile (report-fn) + "A flymake backend for elisp byte compilation. +Spawn an Emacs process that byte-compiles a file representing the +current buffer state and calls REPORT-FN when done." + (interactive (list (lambda (stuff) + (message "aha %s" stuff)))) + (when (derived-mode-p 'emacs-lisp-mode) + (let ((temp-file (make-temp-file "flymake-elisp-byte-compile")) + (origin-buffer (current-buffer))) + (save-restriction + (widen) + (write-region (point-min) (point-max) temp-file nil 'nomessage)) + (let* ((output-buffer (generate-new-buffer " *flymake-elisp-byte-compile*"))) + (make-process + :name "flymake-elisp-byte-compile" + :buffer output-buffer + :command (list (expand-file-name invocation-name invocation-directory) + "-Q" + "--batch" + ;; "--eval" "(setq load-prefer-newer t)" ; for testing + "-L" default-directory + "-l" "flymake-elisp" + "-f" "flymake-elisp--batch-byte-compile" + temp-file) + :connection-type 'pipe + :sentinel + (lambda (proc _event) + (unless (process-live-p proc) + (flymake-elisp--byte-compile-done report-fn + origin-buffer + output-buffer + temp-file)))) + :stderr null-device + :noquery t)))) + +(defun flymake-elisp--batch-byte-compile (&optional file) + "Helper for `flymake-elisp-byte-compile'. +Runs in a batch-mode Emacs. Interactively use variable +`buffer-file-name' for FILE." + (interactive (list buffer-file-name)) + (let* ((file (or file + (car command-line-args-left))) + (dummy-elc-file) + (byte-compile-log-buffer + (generate-new-buffer " *dummy-byte-compile-log-buffer*")) + (byte-compile-dest-file-function + (lambda (source) + (setq dummy-elc-file (make-temp-file (file-name-nondirectory source))))) + (collected)) + (unwind-protect + (cl-letf (((symbol-function 'byte-compile-log-warning) + (lambda (string &optional fill level) + (push (list string byte-compile-last-position fill level) + collected) + t))) + (byte-compile-file file)) + (ignore-errors + (delete-file dummy-elc-file) + (kill-buffer byte-compile-log-buffer))) + (prin1 :flymake-elisp-output-start) + (terpri) + (pp collected))) + +(defun flymake-elisp-setup-backends () + "Setup flymake for elisp work." + (add-to-list (make-local-variable 'flymake-diagnostic-functions) + 'flymake-elisp-checkdoc t) + (add-to-list (make-local-variable 'flymake-diagnostic-functions) + 'flymake-elisp-byte-compile t)) + +(add-hook 'emacs-lisp-mode-hook + 'flymake-elisp-setup-backends) + +(dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (derived-mode-p 'emacs-lisp-mode) + (flymake-elisp-setup-backends)))) + +(provide 'flymake-elisp) +;;; flymake-elisp.el ends here diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 03b319f871..8c92dc7e53 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -526,10 +526,15 @@ A backend is disabled if it reported `:panic'.") (flymake-log :warning "Disabled the backend %s due to reports of %s (%s)" backend action explanation)) -(cl-defun flymake--handle-report (backend action &key explanation) - "Handle reports from flymake backend identified by BACKEND." +(cl-defun flymake--handle-report (backend action &key explanation force) + "Handle reports from flymake backend identified by BACKEND. + +BACKEND, ACTION and EXPLANATION conform to the calling convention +described in `flymake-diagnostic-functions' (which see). Optional +FORCE says to handle a report even if it was not expected." (cond - ((not (memq backend flymake--running-backends)) + ((and (not (memq backend flymake--running-backends)) + (not force)) (flymake-error "Ignoring unexpected report from backend %s" backend)) ((eq action :progress) (flymake-log 3 "Backend %s reports progress: %s" backend explanation)) @@ -851,4 +856,5 @@ diagnostics of type `:error' and `:warning'." (provide 'flymake) (require 'flymake-proc) +(require 'flymake-elisp) ;;; flymake.el ends here commit e0df7b9699539a6831dd7d72d6845d2995fb619e Author: João Távora Date: Wed Sep 27 02:31:58 2017 +0100 Fancy Flymake mode-line construct displays status Imitates compilation-mode's mode-line a bit, and uses its faces. * lisp/progmodes/flymake.el (flymake-error, flymake-warning, flymake-note): Add mode-line-face to these flymake error types. (flymake-note): Notes don't need a noisy fringe bitmap. (flymake-lighter): Delete. (flymake--update-lighter): Delete. (flymake--mode-line-format): New function and variable. (flymake--diagnostics-table): New buffer-local variable. (flymake--handle-report): Don't update "lighters". Affect flymake--diagnostics-table. (flymake--run-backend): Init flymake--diagnostics-table for backend. (flymake-mode): Use flymake--mode-line-format. (flymake-mode): Don't update lighter. (flymake--highlight-line): Be more careful when overriding a nil default overlay property. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index f136e14ec1..03b319f871 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -35,7 +35,8 @@ (require 'cl-lib) (require 'thingatpt) ; end-of-thing (require 'warnings) ; warning-numeric-level, display-warning -(eval-when-compile (require 'subr-x)) ; when-let*, if-let* +(require 'compile) ; for some faces +(eval-when-compile (require 'subr-x)) ; when-let*, if-let*, hash-table-keys (defgroup flymake nil "Universal on-the-fly syntax checker." @@ -362,20 +363,23 @@ the diagnostics of each type. The recognized properties are: (put 'flymake-error 'face 'flymake-error) (put 'flymake-error 'bitmap flymake-error-bitmap) (put 'flymake-error 'severity (warning-numeric-level :error)) +(put 'flymake-error 'mode-line-face 'compilation-error) (put 'flymake-warning 'face 'flymake-warning) (put 'flymake-warning 'bitmap flymake-warning-bitmap) (put 'flymake-warning 'severity (warning-numeric-level :warning)) +(put 'flymake-warning 'mode-line-face 'compilation-warning) (put 'flymake-note 'face 'flymake-note) -(put 'flymake-note 'bitmap flymake-warning-bitmap) +(put 'flymake-note 'bitmap nil) (put 'flymake-note 'severity (warning-numeric-level :debug)) +(put 'flymake-note 'mode-line-face 'compilation-info) (defun flymake--lookup-type-property (type prop &optional default) "Look up PROP for TYPE in `flymake-diagnostic-types-alist'. If TYPE doesn't declare PROP in either -`flymake-diagnostic-types-alist' or its associated -`flymake-category', return DEFAULT." +`flymake-diagnostic-types-alist' or in the symbol of its +associated `flymake-category' return DEFAULT." (let ((alist-probe (assoc type flymake-diagnostic-types-alist))) (cond (alist-probe (let* ((alist (cdr alist-probe)) @@ -496,16 +500,6 @@ If TYPE doesn't declare PROP in either ;; (when choice (goto-char (overlay-start choice))))) -;; flymake minor mode declarations -(defvar-local flymake-lighter nil) - -(defun flymake--update-lighter (info &optional extended) - "Update Flymake’s \"lighter\" with INFO and EXTENDED." - (setq flymake-lighter (format " Flymake(%s%s)" - info - (if extended - (format ",%s" extended) - "")))) ;; Nothing in flymake uses this at all any more, so this is just for ;; third-party compatibility. @@ -520,6 +514,9 @@ that has been invoked but hasn't reported any final status yet.") "List of currently disabled flymake backends. A backend is disabled if it reported `:panic'.") +(defvar-local flymake--diagnostics-table nil + "Hash table of all diagnostics indexed by backend.") + (defun flymake-is-running () "Tell if flymake has running backends in this buffer" flymake--running-backends) @@ -547,6 +544,7 @@ A backend is disabled if it reported `:panic'.") (eq backend (flymake--diag-backend (overlay-get ov 'flymake--diagnostic))))) + (puthash backend diagnostics flymake--diagnostics-table) (mapc (lambda (diag) (flymake--highlight-line diag) (setf (flymake--diag-backend diag) backend)) @@ -557,11 +555,7 @@ A backend is disabled if it reported `:panic'.") (when flymake-check-start-time (flymake-log 2 "%d error(s), %d other(s) in %.2f second(s)" err-count warn-count - (- (float-time) flymake-check-start-time))) - (if (null diagnostics) - (flymake--update-lighter "[ok]") - (flymake--update-lighter - (format "%d/%d" err-count warn-count))))))) + (- (float-time) flymake-check-start-time))))))) (t (flymake--disable-backend "?" :strange @@ -584,6 +578,7 @@ sources." (defun flymake--run-backend (backend) "Run the backend BACKEND." (push backend flymake--running-backends) + (remhash backend flymake--diagnostics-table) ;; FIXME: Should use `condition-case-unless-debug' ;; here, but that won't let me catch errors during ;; testing where `debug-on-error' is always t @@ -621,7 +616,7 @@ non-nil." ;;;###autoload (define-minor-mode flymake-mode nil - :group 'flymake :lighter flymake-lighter + :group 'flymake :lighter flymake--mode-line-format (setq flymake--running-backends nil flymake--disabled-backends nil) (cond @@ -635,10 +630,9 @@ non-nil." (add-hook 'after-save-hook 'flymake-after-save-hook nil t) (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) - (flymake--update-lighter "*" "*") - (setq flymake-timer (run-at-time nil 1 'flymake-on-timer-event (current-buffer))) + (setq flymake--diagnostics-table (make-hash-table)) (when flymake-start-syntax-check-on-find-file (flymake--start-syntax-check))))) @@ -757,6 +751,102 @@ diagnostics of type `:error' and `:warning'." t)) (flymake-goto-next-error (- (or n 1)) filter interactive)) + +;;; Mode-line fanciness +;;; +(defvar flymake--mode-line-format `(:eval (flymake--mode-line-format))) + +(put 'flymake--mode-line-format 'risky-local-variable t) + +(defun flymake--mode-line-format () + "Produce a pretty minor mode indicator." + (let ((running flymake--running-backends) + (reported (hash-table-keys flymake--diagnostics-table))) + `((:propertize " Flymake" + mouse-face mode-line-highlight + ,@(when (not reported) + `(face compilation-mode-line-fail)) + help-echo + ,(concat (format "%s registered backends\n" + (length flymake-diagnostic-functions)) + (format "%s running\n" + (length running)) + (format "%s disabled\n" + (length flymake--disabled-backends)) + "mouse-1: go to log buffer ") + keymap + ,(let ((map (make-sparse-keymap))) + (define-key map [mode-line mouse-1] + (lambda (_event) + (interactive "e") + (switch-to-buffer "*Flymake log*"))) + map)) + ,@(when running + `(":" (:propertize "Run" + face compilation-mode-line-run + help-echo + ,(format "%s running backends" + (length running))))) + ,@(when reported + (let ((by-type (make-hash-table))) + (maphash (lambda (_backend diags) + (mapc (lambda (diag) + (push diag + (gethash (flymake--diag-type diag) + by-type))) + diags)) + flymake--diagnostics-table) + (cl-loop + for (type . severity) + in (cl-sort (mapcar (lambda (type) + (cons type (flymake--lookup-type-property + type + 'severity + (warning-numeric-level :error)))) + (cl-union (hash-table-keys by-type) + '(:error :warning))) + #'> + :key #'cdr) + for diags = (gethash type by-type) + for face = (flymake--lookup-type-property type + 'mode-line-face + 'compilation-error) + when (or diags + (>= severity (warning-numeric-level :warning))) + collect `(:propertize + ,(format "%d" (length diags)) + face ,face + mouse-face mode-line-highlight + keymap + ,(let ((map (make-sparse-keymap)) + (type type)) + (define-key map [mode-line mouse-4] + (lambda (_event) + (interactive "e") + (flymake-goto-prev-error 1 (list type) t))) + (define-key map [mode-line mouse-5] + (lambda (_event) + (interactive "e") + (flymake-goto-next-error 1 (list type) t))) + map) + help-echo + ,(concat (format "%s diagnostics of type %s\n" + (propertize (format "%d" + (length diags)) + 'face face) + (propertize (format "%s" type) + 'face face)) + "mouse-4/mouse-5: previous/next of this type\n")) + into forms + finally return + `((:propertize "[") + ,@(cl-loop for (a . rest) on forms by #'cdr + collect a when rest collect + '(:propertize " ")) + (:propertize "]")))))))) + + + (provide 'flymake) commit 73601787b45d08cdd5026ea36ff680bd49076950 Author: João Távora Date: Wed Sep 27 12:42:20 2017 +0100 Tweak Flymake commands flymake-goto-[next/prev]-error Add filters, useful for backends like the upcoming flymake-elisp-checkdoc backend, for example, which litters everything with low-priority notes. Also re-implement wraparound for flymake-goto-next-error. Manual mentions this, so it's probably a good idea to keep it. Added a new customization variable flymake-wrap-around to control it. * lisp/progmodes/flymake.el (flymake-goto-prev-error) (flymake-goto-next-error): Accept FILTER argument. (flymake-wrap-around): New variable. (flymake-goto-next-error): Wrap around according to flymake-wrap-around. * test/lisp/progmodes/flymake-tests.el (different-diagnostic-types, dummy-backends): Pass FILTER to flymake-goto-prev-error. (different-diagnostic-types) (dummy-backends): Use flymake-wrap-around. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 242c83cf86..f136e14ec1 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -112,6 +112,10 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." "it is superseded by `warning-minimum-log-level.'" "26.1") +(defcustom flymake-wrap-around t + "If non-nil, moving to errors wraps around buffer boundaries." + :group 'flymake :type 'boolean) + (defvar-local flymake-timer nil "Timer for starting syntax check.") @@ -687,20 +691,44 @@ non-nil." (flymake-mode) (flymake-log :warning "Turned on in `flymake-find-file-hook'"))) -(defun flymake-goto-next-error (&optional n interactive) - "Go to next, or Nth next, flymake error in buffer." - (interactive (list 1 t)) +(defun flymake-goto-next-error (&optional n filter interactive) + "Go to Nth next flymake error in buffer matching FILTER. +FILTER is a list of diagnostic types found in +`flymake-diagnostic-types-alist', or nil, if no filter is to be +applied. + +Interactively, always goes to the next error. Also +interactively, FILTER is determined by the prefix arg. With no +prefix arg, don't use a filter, otherwise only consider +diagnostics of type `:error' and `:warning'." + (interactive (list 1 + (if current-prefix-arg + '(:error :warning)) + t)) (let* ((n (or n 1)) - (ovs (flymake--overlays :filter 'flymake--diagnostic + (ovs (flymake--overlays :filter + (lambda (ov) + (let ((diag (overlay-get + ov + 'flymake--diagnostic))) + (and diag + (or (not filter) + (memq (flymake--diag-type diag) + filter))))) :compare (if (cl-plusp n) #'< #'>) :key #'overlay-start)) - (chain (cl-member-if (lambda (ov) - (if (cl-plusp n) - (> (overlay-start ov) - (point)) - (< (overlay-start ov) - (point)))) - ovs)) + (tail (cl-member-if (lambda (ov) + (if (cl-plusp n) + (> (overlay-start ov) + (point)) + (< (overlay-start ov) + (point)))) + ovs)) + (chain (if flymake-wrap-around + (if tail + (progn (setcdr (last tail) ovs) tail) + (and ovs (setcdr (last ovs) ovs))) + tail)) (target (nth (1- n) chain))) (cond (target (goto-char (overlay-start target)) @@ -709,12 +737,26 @@ non-nil." (funcall (overlay-get target 'help-echo) nil nil (point))))) (interactive - (user-error "No more flymake errors"))))) + (user-error "No more flymake errors%s" + (if filter + (format " of types %s" filter) + "")))))) + +(defun flymake-goto-prev-error (&optional n filter interactive) + "Go to Nth previous flymake error in buffer matching FILTER. +FILTER is a list of diagnostic types found in +`flymake-diagnostic-types-alist', or nil, if no filter is to be +applied. + +Interactively, always goes to the previous error. Also +interactively, FILTER is determined by the prefix arg. With no +prefix arg, don't use a filter, otherwise only consider +diagnostics of type `:error' and `:warning'." + (interactive (list 1 (if current-prefix-arg + '(:error :warning)) + t)) + (flymake-goto-next-error (- (or n 1)) filter interactive)) -(defun flymake-goto-prev-error (&optional n interactive) - "Go to previous, or Nth previous, flymake error in buffer." - (interactive (list 1 t)) - (flymake-goto-next-error (- (or n 1)) interactive)) (provide 'flymake) diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index 921c2f648a..fa77a9a8ae 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -129,7 +129,8 @@ SEVERITY-PREDICATE is used to setup (should (eq 'flymake-warning (face-at-point))) (flymake-goto-next-error) (should (eq 'flymake-error (face-at-point))) - (should-error (flymake-goto-next-error nil t)) )) + (let ((flymake-wrap-around nil)) + (should-error (flymake-goto-next-error nil nil t))) )) (defmacro flymake-tests--assert-set (set should @@ -244,7 +245,8 @@ SEVERITY-PREDICATE is used to setup (should (eq 'flymake-warning (face-at-point))) ; dolor (flymake-goto-next-error) (should (eq 'flymake-error (face-at-point))) ; prognata - (should-error (flymake-goto-next-error nil t)))))) + (let ((flymake-wrap-around nil)) + (should-error (flymake-goto-next-error nil nil t))))))) (provide 'flymake-tests) commit 5ec7d738655db209ef7375e340d3d2b0ae5fc3da Author: João Távora Date: Tue Sep 19 14:56:59 2017 +0100 Flymake's flymake-proc.el backend slightly easier to debug Misc cleanup in flymake-proc.el Improve description of what this file contains. Better name for the backend function. Fix the case where it is run interactively. Keep the output buffer alive iff the external process panics. * lisp/progmodes/flymake-proc.el (flymake-proc-legacy-flymake): Rename from flymake-proc-start-syntax-check. Allow running interactively. (flymake-start-syntax-check): Obsolete alias for flymake-proc-legacy-flymake. (flymake-proc-start-syntax-check): Delete. (flymake-diagnostic-functions): Include flymake-proc-legacy-flymake (flymake-proc--process-sentinel): Keep output buffer alive. Clarify with comments. (flymake-proc--diagnostics-for-pattern) (flymake-proc--process-sentinel) (flymake-proc--safe-delete-directory) (flymake-proc--start-syntax-check-process): Use condition-case-unless-debug. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 1028d9ae40..37b7e49dea 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -1,4 +1,4 @@ -;;; flymake-proc.el --- Flymake for external syntax checker processes -*- lexical-binding: t; -*- +;;; flymake-proc.el --- Flymake backend for external tools -*- lexical-binding: t; -*- ;; Copyright (C) 2003-2017 Free Software Foundation, Inc. @@ -26,9 +26,13 @@ ;; ;; Flymake is a minor Emacs mode performing on-the-fly syntax checks. ;; -;; This file contains the most original implementation of flymake's -;; main source of on-the-fly diagnostic info, the external syntax -;; checker backend. +;; This file contains a significant part of the original flymake's +;; implementation, a buffer-checking mechanism that parses the output +;; of an external syntax check tool with regular expressions. +;; +;; That work has been adapted into a flymake "backend" function, +;; `flymake-proc-legacy-flymake' suitable for adding to the +;; `flymake-diagnostic-functions' variable. ;; ;;; Bugs/todo: @@ -412,7 +416,7 @@ Create parent directories as needed." :warning) (t :error))))))) - (condition-case err + (condition-case-unless-debug err (cl-loop with (regexp file-idx line-idx col-idx message-idx) = pattern while (search-forward-regexp regexp nil t) @@ -497,11 +501,13 @@ Create parent directories as needed." (diagnostics (process-get proc 'flymake-proc--collected-diagnostics)) - (interrupted (process-get proc 'flymake-proc--interrupted))) + (interrupted (process-get proc 'flymake-proc--interrupted)) + (panic nil) + (output-buffer (process-get proc 'flymake-proc--output-buffer))) (flymake-log 2 "process %d exited with code %d" (process-id proc) exit-status) - (unwind-protect - (when (buffer-live-p source-buffer) + (condition-case-unless-debug err + (progn (flymake-log 3 "cleaning up using %s" cleanup-f) (with-current-buffer source-buffer (funcall cleanup-f) @@ -509,19 +515,24 @@ Create parent directories as needed." (funcall flymake-proc--report-fn diagnostics)) (interrupted (flymake-proc--panic :stopped interrupted)) + (diagnostics + ;; non-zero exit but some diagnostics is quite + ;; normal... + (funcall flymake-proc--report-fn diagnostics)) ((null diagnostics) - ;; non-zero exit but no errors is strange + ;; ...but no diagnostics is strange, so panic. + (setq panic t) (flymake-proc--panic :configuration-error (format "Command %s errored, but no diagnostics" - command))) - (diagnostics - (funcall flymake-proc--report-fn diagnostics))))) + command)))))) (delete-process proc) (setq flymake-proc--processes (delq proc flymake-proc--processes)) - (unless (> flymake-log-level 2) - (kill-buffer (process-get proc 'flymake-proc--output-buffer))))))) + (if panic + (flymake-log 1 "Output buffer %s kept alive for debugging" + output-buffer) + (kill-buffer output-buffer)))))) (defun flymake-proc--panic (problem explanation) "Tell flymake UI about a fatal PROBLEM with this backend. @@ -679,7 +690,7 @@ expression. A match indicates `:warning' type, otherwise (flymake-log 2 "deleted file %s" file-name))) (defun flymake-proc--safe-delete-directory (dir-name) - (condition-case nil + (condition-case-unless-debug nil (progn (delete-directory dir-name) (flymake-log 2 "deleted dir %s" dir-name)) @@ -687,13 +698,21 @@ expression. A match indicates `:warning' type, otherwise (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) -(defun flymake-proc-start-syntax-check (report-fn &optional interactive) - "Start syntax checking for current buffer." +(defun flymake-proc-legacy-flymake (report-fn &optional interactive) + "Flymake backend based on the original flymake implementation. +This function is suitable for inclusion in +`flymake-dianostic-types-alist'. For backward compatibility, it +can also be executed interactively independently of +`flymake-mode'." ;; Interactively, behave as if flymake had invoked us through its ;; `flymake-diagnostic-functions' with a suitable ID so flymake can ;; clean up consistently - (interactive (list (flymake-make-report-fn 'flymake-proc-start-syntax-check) - t)) + (interactive (list + (lambda (diags &rest args) + (apply (flymake-make-report-fn 'flymake-proc-legacy-flymake) + diags + (append args '(:force t)))) + t)) (cond ((process-live-p flymake-proc--process) (when interactive @@ -728,9 +747,13 @@ expression. A match indicates `:warning' type, otherwise dir) t))))))) +(define-obsolete-function-alias 'flymake-start-syntax-check + 'flymake-proc-legacy-flymake "26.1" + "Flymake backend based on the original flymake implementation.") + (defun flymake-proc--start-syntax-check-process (cmd args dir) "Start syntax check process." - (condition-case err + (condition-case-unless-debug err (let* ((process (let ((default-directory (or dir default-directory))) (when dir @@ -1070,7 +1093,7 @@ Use CREATE-TEMP-F for creating temp copy." ;;;; Hook onto flymake-ui (add-to-list 'flymake-diagnostic-functions - 'flymake-proc-start-syntax-check) + 'flymake-proc-legacy-flymake) ;;;; @@ -1254,9 +1277,6 @@ Return its components if so, nil otherwise.") (define-obsolete-function-alias 'flymake-safe-delete-directory 'flymake-proc--safe-delete-directory "26.1" nil) - (define-obsolete-function-alias 'flymake-start-syntax-check - 'flymake-proc-start-syntax-check "26.1" - "Start syntax checking for current buffer.") (define-obsolete-function-alias 'flymake-stop-all-syntax-checks 'flymake-proc-stop-all-syntax-checks "26.1" "Kill all syntax check processes.") commit f930963dd48e8c912a7623e204315b02433866cd Author: João Távora Date: Tue Sep 26 01:35:43 2017 +0100 Simplify Flymake logging and erroring Use display-warning and a dedicated *Flymake log* buffer. To ease readability, flymake log messages are now prefixed with a common prefix and the buffer that originated them. Some situations of over-zealous logging are fixed. Use byte-compiler info, if available, to determine whence the flymake-related log message is coming. * lisp/progmodes/flymake-proc.el (flymake-proc--diagnostics-for-pattern): Improve log message. (flymake-proc--panic): Always flymake-log an error (flymake-proc--safe-delete-file) (flymake-proc--safe-delete-directory): Downgrade warning (flymake-proc-start-syntax-check): Simplify slightly. (flymake-proc--start-syntax-check-process): Simplify. (flymake-proc--init-find-buildfile-dir) (flymake-proc--init-create-temp-source-and-master-buffer-copy): No need to warn twice. * lisp/progmodes/flymake.el (flymake-log): Convert to macro. (flymake--log-1): New helper. (flymake-log-level): Deprecate. (flymake-error): New helper. (flymake-ler-make-ler, flymake--handle-report, flymake-mode): Use flymake-error. (flymake-on-timer-event) (flymake--handle-report, flymake--disable-backend) (flymake--run-backend, flymake-start, flymake-mode-on) (flymake-mode-off, flymake-after-change-function) (flymake-after-save-hook, flymake-find-file-hook): Adjust flymake-log calls. * test/lisp/progmodes/flymake-tests.el (flymake-tests--call-with-fixture): Only log errors. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 55f0095534..1028d9ae40 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -441,7 +441,7 @@ Create parent directories as needed." (guess-type flymake-proc-diagnostic-type-pred message) message))) else - do (flymake-log 2 "No buffer found for diagnosed file %s" fname)) + do (flymake-log 2 "Reference to file %s is out of scope" fname)) (error (flymake-log 1 "Error parsing process output for pattern %s: %s" pattern err) @@ -532,7 +532,7 @@ May only be called in a dynamic environment where flymake-proc--report-fn) (funcall flymake-proc--report-fn :panic :explanation (format "%s: %s" problem explanation)) - (error "Trouble telling flymake-ui about problem %s(%s)" + (flymake-error "Trouble telling flymake-ui about problem %s(%s)" problem explanation))) (defun flymake-proc-reformat-err-line-patterns-from-compile-el (original-list) @@ -676,13 +676,13 @@ expression. A match indicates `:warning' type, otherwise (defun flymake-proc--safe-delete-file (file-name) (when (and file-name (file-exists-p file-name)) (delete-file file-name) - (flymake-log 1 "deleted file %s" file-name))) + (flymake-log 2 "deleted file %s" file-name))) (defun flymake-proc--safe-delete-directory (dir-name) (condition-case nil (progn (delete-directory dir-name) - (flymake-log 1 "deleted dir %s" dir-name)) + (flymake-log 2 "deleted dir %s" dir-name)) (error (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) @@ -758,15 +758,11 @@ expression. A match indicates `:warning' type, otherwise default-directory) process) (error - (let* ((err-str - (format-message - "Failed to launch syntax check process `%s' with args %s: %s" - cmd args (error-message-string err))) - (source-file-name buffer-file-name) - (cleanup-f (flymake-proc--get-cleanup-function source-file-name))) - (flymake-log 0 err-str) - (funcall cleanup-f) - (flymake-proc--panic :make-process-error err-str))))) + (flymake-proc--panic :make-process-error + (format-message + "Failed to launch syntax check process `%s' with args %s: %s" + cmd args (error-message-string err))) + (funcall (flymake-proc--get-cleanup-function buffer-file-name))))) (defun flymake-proc-stop-all-syntax-checks (&optional reason) "Kill all syntax check processes." @@ -917,7 +913,6 @@ Return full-name. Names are real, not patched." (file-name-directory source-file-name)))) (if buildfile-dir (setq flymake-proc--base-dir buildfile-dir) - (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name) (flymake-proc--panic "NOMK" (format "No buildfile (%s) found for %s" buildfile-name source-file-name))))) @@ -933,8 +928,10 @@ Return full-name. Names are real, not patched." (if (not master-and-temp-master) (progn - (flymake-log 1 "cannot find master file for %s" source-file-name) - (flymake-proc--panic "NOMASTER" "") ; NOMASTER + (flymake-proc--panic + "NOMASTER" + (format-message "cannot find master file for %s" + source-file-name)) nil) (setq flymake-proc--master-file-name (nth 0 master-and-temp-master)) (setq flymake-proc--temp-master-file-name (nth 1 master-and-temp-master))))) diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index a3cec8d3dd..242c83cf86 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -34,7 +34,7 @@ (require 'cl-lib) (require 'thingatpt) ; end-of-thing -(require 'warnings) ; warning-numeric-level +(require 'warnings) ; warning-numeric-level, display-warning (eval-when-compile (require 'subr-x)) ; when-let*, if-let* (defgroup flymake nil @@ -106,10 +106,11 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." :type 'boolean) (defcustom flymake-log-level -1 - "Logging level, only messages with level lower or equal will be logged. --1 = NONE, 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG" - :group 'flymake + "Obsolete and ignored variable." :type 'integer) +(make-obsolete-variable 'flymake-log-level + "it is superseded by `warning-minimum-log-level.'" + "26.1") (defvar-local flymake-timer nil "Timer for starting syntax check.") @@ -120,15 +121,45 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." (defvar-local flymake-check-start-time nil "Time at which syntax check was started.") -(defun flymake-log (level text &rest args) - "Log a message at level LEVEL. -If LEVEL is higher than `flymake-log-level', the message is -ignored. Otherwise, it is printed using `message'. -TEXT is a format control string, and the remaining arguments ARGS -are the string substitutions (see the function `format')." - (if (<= level flymake-log-level) - (let* ((msg (apply #'format-message text args))) - (message "%s" msg)))) +(defun flymake--log-1 (level sublog msg &rest args) + "Do actual work for `flymake-log'." + (let (;; never popup the log buffer + (warning-minimum-level :emergency) + (warning-type-format + (format " [%s %s]" + (or sublog 'flymake) + (current-buffer)))) + (display-warning (list 'flymake sublog) + (apply #'format-message msg args) + (if (numberp level) + (or (nth level + '(:emergency :error :warning :debug :debug) ) + :error) + level) + "*Flymake log*"))) + +;;;###autoload +(defmacro flymake-log (level msg &rest args) + "Log, at level LEVEL, the message MSG formatted with ARGS. +LEVEL is passed to `display-warning', which is used to display +the warning. If this form is included in a byte-compiled file, +the generated warning contains an indication of the file that +generated it." + (let* ((compile-file (and (boundp 'byte-compile-current-file) + (symbol-value 'byte-compile-current-file))) + (sublog (if (and + compile-file + (not load-file-name)) + (intern + (file-name-nondirectory + (file-name-sans-extension compile-file)))))) + `(flymake--log-1 ,level ',sublog ,msg ,@args))) + +(defun flymake-error (text &rest args) + "Format TEXT with ARGS and signal an error for flymake." + (let ((msg (apply #'format-message text args))) + (flymake-log :error msg) + (error (concat "[Flymake] " msg)))) (cl-defstruct (flymake--diag (:constructor flymake--diag-make)) @@ -147,7 +178,7 @@ description of the problem detected in this region." (defun flymake-ler-make-ler (file line type text &optional full-file) (let* ((file (or full-file file)) (buf (find-buffer-visiting file))) - (unless buf (error "No buffer visiting %s" file)) + (unless buf (flymake-error "No buffer visiting %s" file)) (pcase-let* ((`(,beg . ,end) (with-current-buffer buf (flymake-diag-region line nil)))) @@ -241,8 +272,7 @@ Or nil if the region is invalid." (let* ((beg (fallback-bol)) (end (fallback-eol beg))) (cons beg end)))))) - (error (flymake-log 4 "Invalid region for diagnostic %s") - nil))) + (error (flymake-error "Invalid region line=%s col=%s" line col)))) (defvar flymake-diagnostic-functions nil "List of flymake backends i.e. sources of flymake diagnostics. @@ -427,7 +457,7 @@ If TYPE doesn't declare PROP in either flymake-no-changes-timeout)) (setq flymake-last-change-time nil) - (flymake-log 3 "starting syntax check as more than 1 second passed since last change") + (flymake-log :debug "starting syntax check after no changes for some time") (flymake--start-syntax-check))))) (define-obsolete-function-alias 'flymake-display-err-menu-for-current-line @@ -456,7 +486,7 @@ If TYPE doesn't declare PROP in either (cl-count-if #'flymake--diag-errorp diagnostics) (cl-count-if-not #'flymake--diag-errorp diagnostics))) (choice (x-popup-menu event (list title (cons "" menu))))) - (flymake-log 3 "choice=%s" choice) + (flymake-log :debug "choice=%s" choice) ;; FIXME: What is the point of going to the problem locus if we're ;; certainly already there? ;; @@ -492,14 +522,14 @@ A backend is disabled if it reported `:panic'.") (defun flymake--disable-backend (backend action &optional explanation) (cl-pushnew backend flymake--disabled-backends) - (flymake-log 0 "Disabled the backend %s due to reports of %s (%s)" + (flymake-log :warning "Disabled the backend %s due to reports of %s (%s)" backend action explanation)) (cl-defun flymake--handle-report (backend action &key explanation) "Handle reports from flymake backend identified by BACKEND." (cond ((not (memq backend flymake--running-backends)) - (error "Ignoring unexpected report from backend %s" backend)) + (flymake-error "Ignoring unexpected report from backend %s" backend)) ((eq action :progress) (flymake-log 3 "Backend %s reports progress: %s" backend explanation)) ((eq :panic action) @@ -573,10 +603,10 @@ non-nil." (setq flymake-check-start-time (float-time)) (dolist (backend flymake-diagnostic-functions) (cond ((memq backend flymake--running-backends) - (flymake-log 2 "Backend %s still running, not restarting" + (flymake-log :debug "Backend %s still running, not restarting" backend)) ((memq backend flymake--disabled-backends) - (flymake-log 2 "Backend %s is disabled, not starting" + (flymake-log :debug "Backend %s is disabled, not starting" backend)) (t (flymake--run-backend backend)))))) @@ -595,7 +625,7 @@ non-nil." (flymake-mode (cond ((not flymake-diagnostic-functions) - (error "flymake cannot check syntax in buffer %s" (buffer-name))) + (flymake-error "No backends to check buffer %s" (buffer-name))) (t (add-hook 'after-change-functions 'flymake-after-change-function nil t) (add-hook 'after-save-hook 'flymake-after-save-hook nil t) @@ -625,29 +655,25 @@ non-nil." ;;;###autoload (defun flymake-mode-on () "Turn flymake mode on." - (flymake-mode 1) - (flymake-log 1 "flymake mode turned ON for buffer %s" (buffer-name))) + (flymake-mode 1)) ;;;###autoload (defun flymake-mode-off () "Turn flymake mode off." - (flymake-mode 0) - (flymake-log 1 "flymake mode turned OFF for buffer %s" (buffer-name))) + (flymake-mode 0)) (defun flymake-after-change-function (start stop _len) "Start syntax check for current buffer if it isn't already running." - ;;+(flymake-log 0 "setting change time to %s" (float-time)) (let((new-text (buffer-substring start stop))) (when (and flymake-start-syntax-check-on-newline (equal new-text "\n")) - (flymake-log 3 "starting syntax check as new-line has been seen") + (flymake-log :debug "starting syntax check as new-line has been seen") (flymake--start-syntax-check 'deferred)) (setq flymake-last-change-time (float-time)))) (defun flymake-after-save-hook () - (if (local-variable-p 'flymake-mode (current-buffer)) ; (???) other way to determine whether flymake is active in buffer being saved? - (progn - (flymake-log 3 "starting syntax check as buffer was saved") - (flymake--start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???) + (when flymake-mode + (flymake-log :debug "starting syntax check as buffer was saved") + (flymake--start-syntax-check))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???) (defun flymake-kill-buffer-hook () (when flymake-timer @@ -657,9 +683,9 @@ non-nil." ;;;###autoload (defun flymake-find-file-hook () (unless (or flymake-mode - (null flymake-diagnostic-functions)) + (null flymake-diagnostic-functions)) (flymake-mode) - (flymake-log 3 "automatically turned ON flymake mode"))) + (flymake-log :warning "Turned on in `flymake-find-file-hook'"))) (defun flymake-goto-next-error (&optional n interactive) "Go to next, or Nth next, flymake error in buffer." diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index c2deb1dc5c..921c2f648a 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -46,7 +46,8 @@ SEVERITY-PREDICATE is used to setup (visiting (find-buffer-visiting file)) (buffer (or visiting (find-file-noselect file))) (process-environment (cons "LC_ALL=C" process-environment)) - (i 0)) + (i 0) + (warning-minimum-log-level :error)) (unwind-protect (with-current-buffer buffer (save-excursion commit 0fff900c18c3ae497294f26fd42b70990638d3ed Author: Philipp Stephani Date: Fri Sep 29 23:55:57 2017 +0200 Work around deprecation of gtk_style_context_get_background_color * src/gtkutil.c (xg_check_special_colors): Replace call to gtk_style_context_get_background_color with its definition. diff --git a/src/gtkutil.c b/src/gtkutil.c index a07ee4b1b0..c7d8f92829 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -577,11 +577,18 @@ xg_check_special_colors (struct frame *f, if (get_fg) gtk_style_context_get_color (gsty, state, &col); else - /* FIXME: gtk_style_context_get_background_color is deprecated - in GTK+ 3.16. New versions of GTK+ don’t use the concept of - a single background color any more, so we shouldn’t query for - it. */ - gtk_style_context_get_background_color (gsty, state, &col); + { + GdkRGBA *c; + /* FIXME: Retrieving the background color is deprecated in + GTK+ 3.16. New versions of GTK+ don’t use the concept of a + single background color any more, so we shouldn’t query for + it. */ + gtk_style_context_get (gsty, state, + GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &c, + NULL); + col = *c; + gdk_rgba_free (c); + } unsigned short r = col.red * 65535, commit 94a88c1ae98b1bf7ab80ae9cdd4d6e16b36597ef Author: João Távora Date: Tue Sep 26 00:45:46 2017 +0100 New Flymake API variable flymake-diagnostic-functions Lay groundwork for multiple active backends in the same buffer. Backends are lisp functions called when flymake-mode sees fit. They are responsible for examining the current buffer and telling flymake.el, via return value, if they can syntax check it. Backends should return quickly and inexpensively, but they are also passed a REPORT-FN argument which they may or may not call asynchronously after performing more expensive work. REPORT-FN's calling convention stipulates that a backend calls it with a list of diagnostics as argument, or, alternatively, with a symbol denoting an exceptional situation, usually some panic resulting from a misconfigured backend. In keeping with legacy behaviour, flymake.el's response to a panic is to disable the issuing backend. The flymake--diag object representing a diagnostic now also keeps information about its source backend. Among other uses, this allows flymake to selectively cleanup overlays based on which backend is updating its diagnostics. * lisp/progmodes/flymake-proc.el (flymake-proc--report-fn): New dynamic variable. (flymake-proc--process): New variable. (flymake-can-syntax-check-buffer): Remove. (flymake-proc--process-sentinel): Simplify. Use unwind-protect. Affect flymake-proc--processes here. Bind flymake-proc--report-fn. (flymake-proc--process-filter): Bind flymake-proc--report-fn. (flymake-proc--post-syntax-check): Delete (flymake-proc-start-syntax-check): Take mandatory report-fn. Rewrite. Bind flymake-proc--report-fn. (flymake-proc--process-sentinel): Rewrite and simplify. (flymake-proc--panic): New helper. (flymake-proc--start-syntax-check-process): Record report-fn in process. Use flymake-proc--panic. (flymake-proc-stop-all-syntax-checks): Use mapc. Don't affect flymake-proc--processes here. Record interruption reason. (flymake-proc--init-find-buildfile-dir) (flymake-proc--init-create-temp-source-and-master-buffer-copy): Use flymake-proc--panic. (flymake-diagnostic-functions): Add flymake-proc-start-syntax-check. (flymake-proc-compile): Call flymake-proc-stop-all-syntax-checks with a reason. * lisp/progmodes/flymake.el (flymake-backends): Delete. (flymake-check-was-interrupted): Delete. (flymake--diag): Add backend slot. (flymake-delete-own-overlays): Take optional filter arg. (flymake-diagnostic-functions): New user-visible variable. (flymake--running-backends, flymake--disabled-backends): New buffer-local variables. (flymake-is-running): Now a function, not a variable. (flymake-mode-line, flymake-mode-line-e-w) (flymake-mode-line-status): Delete. (flymake-lighter): flymake's minor-mode "lighter". (flymake-report): Delete. (flymake--backend): Delete. (flymake--can-syntax-check-buffer): Delete. (flymake--handle-report, flymake--disable-backend) (flymake--run-backend, flymake--run-backend): New helpers. (flymake-make-report-fn): Make a lambda. (flymake--start-syntax-check): Iterate flymake-diagnostic-functions. (flymake-mode): Use flymake-lighter. Simplify. Initialize flymake--running-backends and flymake--disabled-backends. (flymake-find-file-hook): Simplify. * test/lisp/progmodes/flymake-tests.el (flymake-tests--call-with-fixture): Use flymake-is-running the function. Check if flymake-mode already active before activating it. Add a thorough test for flymake multiple backends * lisp/progmodes/flymake.el (flymake--start-syntax-check): Don't use condition-case-unless-debug, use condition-case * test/lisp/progmodes/flymake-tests.el (flymake-tests--assert-set): New helper macro. (dummy-backends): New test. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 05f2cab1af..55f0095534 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -102,9 +102,15 @@ NAME is the file name function to use, default `flymake-proc-get-real-file-name' (const :tag "flymake-proc-get-real-file-name" nil) function)))) +(defvar-local flymake-proc--process nil + "Currently active flymake process for a buffer, if any.") + (defvar flymake-proc--processes nil "List of currently active flymake processes.") +(defvar flymake-proc--report-fn nil + "If bound, function used to report back to flymake's UI.") + (defun flymake-proc--get-file-name-mode-and-masks (file-name) "Return the corresponding entry from `flymake-proc-allowed-file-name-masks'." (unless (stringp file-name) @@ -118,11 +124,6 @@ NAME is the file name function to use, default `flymake-proc-get-real-file-name' (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks)) mode-and-masks)) -(defun flymake-proc-can-syntax-check-file (file-name) - "Determine whether we can syntax check FILE-NAME. -Return nil if we cannot, non-nil if we can." - (if (flymake-proc-get-init-function file-name) t nil)) - (defun flymake-proc--get-init-function (file-name) "Return init function to be used for the file." (let* ((init-f (nth 0 (flymake-proc--get-file-name-mode-and-masks file-name)))) @@ -450,7 +451,9 @@ Create parent directories as needed." "Parse STRING and collect diagnostics info." (flymake-log 3 "received %d byte(s) of output from process %d" (length string) (process-id proc)) - (let ((output-buffer (process-get proc 'flymake-proc--output-buffer))) + (let ((output-buffer (process-get proc 'flymake-proc--output-buffer)) + (flymake-proc--report-fn + (process-get proc 'flymake-proc--report-fn))) (when (and (buffer-live-p (process-buffer proc)) output-buffer) (with-current-buffer output-buffer @@ -481,52 +484,56 @@ Create parent directories as needed." (process-put proc 'flymake-proc--unprocessed-mark (point-marker)))))))) -(defun flymake-proc--process-sentinel (process _event) +(defun flymake-proc--process-sentinel (proc _event) "Sentinel for syntax check buffers." - (when (memq (process-status process) '(signal exit)) - (let* ((exit-status (process-exit-status process)) - (command (process-command process)) - (source-buffer (process-buffer process)) - (cleanup-f (flymake-proc--get-cleanup-function - (buffer-file-name source-buffer)))) - + (when (memq (process-status proc) '(signal exit)) + (let* ((exit-status (process-exit-status proc)) + (command (process-command proc)) + (source-buffer (process-buffer proc)) + (flymake-proc--report-fn (process-get proc + 'flymake-proc--report-fn)) + (cleanup-f (flymake-proc--get-cleanup-function + (buffer-file-name source-buffer))) + (diagnostics (process-get + proc + 'flymake-proc--collected-diagnostics)) + (interrupted (process-get proc 'flymake-proc--interrupted))) (flymake-log 2 "process %d exited with code %d" - (process-id process) exit-status) - (unless (> flymake-log-level 2) - (kill-buffer (process-get process 'flymake-proc--output-buffer))) - (condition-case err - (progn + (process-id proc) exit-status) + (unwind-protect + (when (buffer-live-p source-buffer) (flymake-log 3 "cleaning up using %s" cleanup-f) - (when (buffer-live-p source-buffer) - (with-current-buffer source-buffer - (funcall cleanup-f))) - - (delete-process process) - (setq flymake-proc--processes (delq process flymake-proc--processes)) - - (when (buffer-live-p source-buffer) - (with-current-buffer source-buffer - (flymake-proc--post-syntax-check - exit-status command - (process-get process 'flymake-proc--collected-diagnostics)) - (setq flymake-is-running nil)))) - (error - (let ((err-str (format "Error in process sentinel for buffer %s: %s" - source-buffer (error-message-string err)))) - (flymake-log 0 err-str) - (with-current-buffer source-buffer - (setq flymake-is-running nil)))))))) - -(defun flymake-proc--post-syntax-check (exit-status command diagnostics) - (if (equal 0 exit-status) - (flymake-report diagnostics) - (if flymake-check-was-interrupted - (flymake-report-status nil "") ;; STOPPED - (if (null diagnostics) - (flymake-report-fatal-status - "CFGERR" - (format "Configuration error has occurred while running %s" command)) - (flymake-report diagnostics))))) + (with-current-buffer source-buffer + (funcall cleanup-f) + (cond ((equal 0 exit-status) + (funcall flymake-proc--report-fn diagnostics)) + (interrupted + (flymake-proc--panic :stopped interrupted)) + ((null diagnostics) + ;; non-zero exit but no errors is strange + (flymake-proc--panic + :configuration-error + (format "Command %s errored, but no diagnostics" + command))) + (diagnostics + (funcall flymake-proc--report-fn diagnostics))))) + (delete-process proc) + (setq flymake-proc--processes + (delq proc flymake-proc--processes)) + (unless (> flymake-log-level 2) + (kill-buffer (process-get proc 'flymake-proc--output-buffer))))))) + +(defun flymake-proc--panic (problem explanation) + "Tell flymake UI about a fatal PROBLEM with this backend. +May only be called in a dynamic environment where +`flymake-proc--dynamic-report-fn' is bound" + (flymake-log 0 "%s: %s" problem explanation) + (if (and (boundp 'flymake-proc--report-fn) + flymake-proc--report-fn) + (funcall flymake-proc--report-fn :panic + :explanation (format "%s: %s" problem explanation)) + (error "Trouble telling flymake-ui about problem %s(%s)" + problem explanation))) (defun flymake-proc-reformat-err-line-patterns-from-compile-el (original-list) "Grab error line patterns from ORIGINAL-LIST in compile.el format. @@ -679,34 +686,47 @@ expression. A match indicates `:warning' type, otherwise (error (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) -(defun flymake-proc-start-syntax-check () + +(defun flymake-proc-start-syntax-check (report-fn &optional interactive) "Start syntax checking for current buffer." - (interactive) - (flymake-log 3 "flymake is running: %s" flymake-is-running) - (when (not (and flymake-is-running - (flymake-proc-can-syntax-check-file buffer-file-name))) - (when (or (not flymake-proc-compilation-prevents-syntax-check) - (not (flymake-proc--compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP") + ;; Interactively, behave as if flymake had invoked us through its + ;; `flymake-diagnostic-functions' with a suitable ID so flymake can + ;; clean up consistently + (interactive (list (flymake-make-report-fn 'flymake-proc-start-syntax-check) + t)) + (cond + ((process-live-p flymake-proc--process) + (when interactive + (user-error + "There's already a flymake process running in this buffer"))) + ((and buffer-file-name + ;; Since we write temp files in current dir, there's no point + ;; trying if the directory is read-only (bug#8954). + (file-writable-p (file-name-directory buffer-file-name)) + (or (not flymake-proc-compilation-prevents-syntax-check) + (not (flymake-proc--compilation-is-running)))) + (let ((init-f (flymake-proc--get-init-function buffer-file-name))) + (unless init-f (error "Can find a suitable init function")) (flymake-proc--clear-buildfile-cache) (flymake-proc--clear-project-include-dirs-cache) - (setq flymake-check-was-interrupted nil) - (setq flymake-check-start-time (float-time)) - - (let* ((source-file-name buffer-file-name) - (init-f (flymake-proc--get-init-function source-file-name)) - (cleanup-f (flymake-proc--get-cleanup-function source-file-name)) + (let* ((flymake-proc--report-fn report-fn) + (cleanup-f (flymake-proc--get-cleanup-function buffer-file-name)) (cmd-and-args (funcall init-f)) (cmd (nth 0 cmd-and-args)) (args (nth 1 cmd-and-args)) (dir (nth 2 cmd-and-args))) - (if (not cmd-and-args) - (progn - (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name) - (funcall cleanup-f)) - (progn - (setq flymake-last-change-time nil) - (flymake-proc--start-syntax-check-process cmd args dir))))))) + (cond ((not cmd-and-args) + (progn + (flymake-log 0 "init function %s for %s failed, cleaning up" + init-f buffer-file-name) + (funcall cleanup-f))) + (t + (setq flymake-last-change-time nil) + (flymake-proc--start-syntax-check-process cmd + args + dir) + t))))))) (defun flymake-proc--start-syntax-check-process (cmd args dir) "Start syntax check process." @@ -721,15 +741,18 @@ expression. A match indicates `:warning' type, otherwise :noquery t :filter 'flymake-proc--process-filter :sentinel 'flymake-proc--process-sentinel)))) - (setf (process-get process 'flymake-proc--output-buffer) - (generate-new-buffer - (format " *flymake output for %s*" (current-buffer)))) + (process-put process 'flymake-proc--output-buffer + (generate-new-buffer + (format " *flymake output for %s*" (current-buffer)))) + (process-put process 'flymake-proc--report-fn + flymake-proc--report-fn) + + (setq-local flymake-proc--process process) (push process flymake-proc--processes) (setq flymake-is-running t) (setq flymake-last-change-time nil) - (flymake-report-status nil "*") (flymake-log 2 "started process %d, command=%s, dir=%s" (process-id process) (process-command process) default-directory) @@ -743,22 +766,16 @@ expression. A match indicates `:warning' type, otherwise (cleanup-f (flymake-proc--get-cleanup-function source-file-name))) (flymake-log 0 err-str) (funcall cleanup-f) - (flymake-report-fatal-status "PROCERR" err-str))))) - -(defun flymake-proc--kill-process (proc) - "Kill process PROC." - (kill-process proc) - (let* ((buf (process-buffer proc))) - (when (buffer-live-p buf) - (with-current-buffer buf - (setq flymake-check-was-interrupted t)))) - (flymake-log 1 "killed process %d" (process-id proc))) - -(defun flymake-proc-stop-all-syntax-checks () + (flymake-proc--panic :make-process-error err-str))))) + +(defun flymake-proc-stop-all-syntax-checks (&optional reason) "Kill all syntax check processes." - (interactive) - (while flymake-proc--processes - (flymake-proc--kill-process (pop flymake-proc--processes)))) + (interactive (list "Interrupted by user")) + (mapc (lambda (proc) + (kill-process proc) + (process-put proc 'flymake-proc--interrupted reason) + (flymake-log 2 "killed process %d" (process-id proc))) + flymake-proc--processes)) (defun flymake-proc--compilation-is-running () (and (boundp 'compilation-in-progress) @@ -767,7 +784,7 @@ expression. A match indicates `:warning' type, otherwise (defun flymake-proc-compile () "Kill all flymake syntax checks, start compilation." (interactive) - (flymake-proc-stop-all-syntax-checks) + (flymake-proc-stop-all-syntax-checks "Stopping for proper compilation") (call-interactively 'compile)) ;;;; general init-cleanup and helper routines @@ -897,11 +914,11 @@ Return full-name. Names are real, not patched." "Find buildfile, store its dir in buffer data and return its dir, if found." (let* ((buildfile-dir (flymake-proc--find-buildfile buildfile-name - (file-name-directory source-file-name)))) + (file-name-directory source-file-name)))) (if buildfile-dir (setq flymake-proc--base-dir buildfile-dir) (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name) - (flymake-report-fatal-status + (flymake-proc--panic "NOMK" (format "No buildfile (%s) found for %s" buildfile-name source-file-name))))) @@ -917,7 +934,7 @@ Return full-name. Names are real, not patched." (if (not master-and-temp-master) (progn (flymake-log 1 "cannot find master file for %s" source-file-name) - (flymake-report-status "!" "") ; NOMASTER + (flymake-proc--panic "NOMASTER" "") ; NOMASTER nil) (setq flymake-proc--master-file-name (nth 0 master-and-temp-master)) (setq flymake-proc--temp-master-file-name (nth 1 master-and-temp-master))))) @@ -1054,6 +1071,11 @@ Use CREATE-TEMP-F for creating temp copy." 'flymake-proc-create-temp-inplace)))) +;;;; Hook onto flymake-ui +(add-to-list 'flymake-diagnostic-functions + 'flymake-proc-start-syntax-check) + + ;;;; (progn @@ -1238,9 +1260,6 @@ Return its components if so, nil otherwise.") (define-obsolete-function-alias 'flymake-start-syntax-check 'flymake-proc-start-syntax-check "26.1" "Start syntax checking for current buffer.") - (define-obsolete-function-alias 'flymake-kill-process - 'flymake-proc--kill-process "26.1" - "Kill process PROC.") (define-obsolete-function-alias 'flymake-stop-all-syntax-checks 'flymake-proc-stop-all-syntax-checks "26.1" "Kill all syntax check processes.") diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index ea9e7c92ea..a3cec8d3dd 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -35,6 +35,7 @@ (require 'cl-lib) (require 'thingatpt) ; end-of-thing (require 'warnings) ; warning-numeric-level +(eval-when-compile (require 'subr-x)) ; when-let*, if-let* (defgroup flymake nil "Universal on-the-fly syntax checker." @@ -119,15 +120,6 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." (defvar-local flymake-check-start-time nil "Time at which syntax check was started.") -(defvar-local flymake-check-was-interrupted nil - "Non-nil if syntax check was killed by `flymake-compile'.") - -(defvar-local flymake-err-info nil - "Sorted list of line numbers and lists of err info in the form (file, err-text).") - -(defvar-local flymake-new-err-info nil - "Same as `flymake-err-info', effective when a syntax check is in progress.") - (defun flymake-log (level text &rest args) "Log a message at level LEVEL. If LEVEL is higher than `flymake-log-level', the message is @@ -140,7 +132,7 @@ are the string substitutions (see the function `format')." (cl-defstruct (flymake--diag (:constructor flymake--diag-make)) - buffer beg end type text) + buffer beg end type text backend) (defun flymake-make-diagnostic (buffer beg @@ -186,9 +178,9 @@ verify FILTER, sort them by COMPARE (using KEY)." #'identity)) ovs))))) -(defun flymake-delete-own-overlays () +(defun flymake-delete-own-overlays (&optional filter) "Delete all flymake overlays in BUFFER." - (mapc #'delete-overlay (flymake--overlays))) + (mapc #'delete-overlay (flymake--overlays :filter filter))) (defface flymake-error '((((supports :underline (:style wave))) @@ -252,6 +244,55 @@ Or nil if the region is invalid." (error (flymake-log 4 "Invalid region for diagnostic %s") nil))) +(defvar flymake-diagnostic-functions nil + "List of flymake backends i.e. sources of flymake diagnostics. + +This variable holds an arbitrary number of \"backends\" or +\"checkers\" providing the flymake UI's \"frontend\" with +information about where and how to annotate problems diagnosed in +a buffer. + +Backends are lisp functions sharing a common calling +convention. Whenever flymake decides it is time to re-check the +buffer, each backend is called with a single argument, a +REPORT-FN callback, detailed below. Backend functions are first +expected to quickly and inexpensively announce the feasibility of +checking the buffer (i.e. they aren't expected to immediately +start checking the buffer): + +* If the backend function returns nil, flymake forgets about this + backend for the current check, but will call it again the next + time; + +* If the backend function returns non-nil, flymake expects this backend to + check the buffer and call its REPORT-FN callback function. If + the computation involved is inexpensive, the backend function + may do so synchronously before returning. If it is not, it may + do so after retuning, using idle timers, asynchronous + processes or other asynchronous mechanisms. + +* If the backend function signals an error, it is disabled, i.e. flymake + will not attempt it again for this buffer until `flymake-mode' + is turned off and on again. + +When calling REPORT-FN, the first argument passed to it decides +how to proceed. Recognized values are: + +* A (possibly empty) list of objects created with + `flymake-make-diagnostic', causing flymake to annotate the + buffer with this information and consider the backend has + having finished its check normally. + +* The symbol `:progress', signalling that the backend is still + working and will call REPORT-FN again in the future. + +* The symbol `:panic', signalling that the backend has + encountered an exceptional situation and should be disabled. + +In the latter cases, it is also possible to provide REPORT-FN +with a string as the keyword argument `:explanation'. The string +should give human-readable details of the situation.") + (defvar flymake-diagnostic-types-alist `((:error . ((flymake-category . flymake-error))) @@ -376,15 +417,11 @@ If TYPE doesn't declare PROP in either (overlay-put ov 'flymake-overlay t) (overlay-put ov 'flymake--diagnostic diagnostic))) - -(defvar-local flymake-is-running nil - "If t, flymake syntax check process is running for the current buffer.") - (defun flymake-on-timer-event (buffer) "Start a syntax check for buffer BUFFER if necessary." (when (buffer-live-p buffer) (with-current-buffer buffer - (when (and (not flymake-is-running) + (when (and (not (flymake-is-running)) flymake-last-change-time (> (- (float-time) flymake-last-change-time) flymake-no-changes-timeout)) @@ -426,59 +463,123 @@ If TYPE doesn't declare PROP in either (when choice (goto-char (overlay-start choice))))) ;; flymake minor mode declarations -(defvar-local flymake-mode-line nil) -(defvar-local flymake-mode-line-e-w nil) -(defvar-local flymake-mode-line-status nil) - -(defun flymake-report-status (e-w &optional status) - "Show status in mode line." - (when e-w - (setq flymake-mode-line-e-w e-w)) - (when status - (setq flymake-mode-line-status status)) - (let* ((mode-line " Flymake")) - (when (> (length flymake-mode-line-e-w) 0) - (setq mode-line (concat mode-line ":" flymake-mode-line-e-w))) - (setq mode-line (concat mode-line flymake-mode-line-status)) - (setq flymake-mode-line mode-line) - (force-mode-line-update))) +(defvar-local flymake-lighter nil) + +(defun flymake--update-lighter (info &optional extended) + "Update Flymake’s \"lighter\" with INFO and EXTENDED." + (setq flymake-lighter (format " Flymake(%s%s)" + info + (if extended + (format ",%s" extended) + "")))) ;; Nothing in flymake uses this at all any more, so this is just for ;; third-party compatibility. (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") -(defun flymake-report-fatal-status (status warning) - "Display a warning and switch flymake mode off." - ;; This first message was always shown by default, and flymake-log - ;; does nothing by default, hence the use of message. - ;; Another option is display-warning. - (if (< flymake-log-level 0) - (message "Flymake: %s. Flymake will be switched OFF" warning)) - (flymake-mode 0) - (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s" - (buffer-name) status warning)) +(defvar-local flymake--running-backends nil + "List of currently active flymake backends. +An active backend is a member of `flymake-diagnostic-functions' +that has been invoked but hasn't reported any final status yet.") -(defun flymake-report (diagnostics) - (save-restriction - (widen) - (flymake-delete-own-overlays) - (mapc #'flymake--highlight-line diagnostics) - (let ((err-count (cl-count-if #'flymake--diag-errorp diagnostics)) - (warn-count (cl-count-if-not #'flymake--diag-errorp diagnostics))) - (when flymake-check-start-time - (flymake-log 2 "%s: %d error(s), %d other(s) in %.2f second(s)" - (buffer-name) err-count warn-count - (- (float-time) flymake-check-start-time))) - (if (null diagnostics) - (flymake-report-status "" "") - (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) +(defvar-local flymake--disabled-backends nil + "List of currently disabled flymake backends. +A backend is disabled if it reported `:panic'.") + +(defun flymake-is-running () + "Tell if flymake has running backends in this buffer" + flymake--running-backends) + +(defun flymake--disable-backend (backend action &optional explanation) + (cl-pushnew backend flymake--disabled-backends) + (flymake-log 0 "Disabled the backend %s due to reports of %s (%s)" + backend action explanation)) + +(cl-defun flymake--handle-report (backend action &key explanation) + "Handle reports from flymake backend identified by BACKEND." + (cond + ((not (memq backend flymake--running-backends)) + (error "Ignoring unexpected report from backend %s" backend)) + ((eq action :progress) + (flymake-log 3 "Backend %s reports progress: %s" backend explanation)) + ((eq :panic action) + (flymake--disable-backend backend action explanation)) + ((listp action) + (let ((diagnostics action)) + (save-restriction + (widen) + (flymake-delete-own-overlays + (lambda (ov) + (eq backend + (flymake--diag-backend + (overlay-get ov 'flymake--diagnostic))))) + (mapc (lambda (diag) + (flymake--highlight-line diag) + (setf (flymake--diag-backend diag) backend)) + diagnostics) + (let ((err-count (cl-count-if #'flymake--diag-errorp diagnostics)) + (warn-count (cl-count-if-not #'flymake--diag-errorp + diagnostics))) + (when flymake-check-start-time + (flymake-log 2 "%d error(s), %d other(s) in %.2f second(s)" + err-count warn-count + (- (float-time) flymake-check-start-time))) + (if (null diagnostics) + (flymake--update-lighter "[ok]") + (flymake--update-lighter + (format "%d/%d" err-count warn-count))))))) + (t + (flymake--disable-backend "?" + :strange + (format "unknown action %s (%s)" + action explanation)))) + (unless (eq action :progress) + (flymake--stop-backend backend))) + +(defun flymake-make-report-fn (backend) + "Make a suitable anonymous report function for BACKEND. +BACKEND is used to help flymake distinguish diagnostic +sources." + (lambda (&rest args) + (apply #'flymake--handle-report backend args))) + +(defun flymake--stop-backend (backend) + "Stop the backend BACKEND." + (setq flymake--running-backends (delq backend flymake--running-backends))) + +(defun flymake--run-backend (backend) + "Run the backend BACKEND." + (push backend flymake--running-backends) + ;; FIXME: Should use `condition-case-unless-debug' + ;; here, but that won't let me catch errors during + ;; testing where `debug-on-error' is always t + (condition-case err + (unless (funcall backend + (flymake-make-report-fn backend)) + (flymake--stop-backend backend)) + (error + (flymake--disable-backend backend :error + err) + (flymake--stop-backend backend)))) (defun flymake--start-syntax-check (&optional deferred) - (cl-labels ((start - () - (remove-hook 'post-command-hook #'start 'local) - (setq flymake-check-start-time (float-time)) - (flymake-proc-start-syntax-check))) + "Start a syntax check. +Start it immediately, or after current command if DEFERRED is +non-nil." + (cl-labels + ((start + () + (remove-hook 'post-command-hook #'start 'local) + (setq flymake-check-start-time (float-time)) + (dolist (backend flymake-diagnostic-functions) + (cond ((memq backend flymake--running-backends) + (flymake-log 2 "Backend %s still running, not restarting" + backend)) + ((memq backend flymake--disabled-backends) + (flymake-log 2 "Backend %s is disabled, not starting" + backend)) + (t + (flymake--run-backend backend)))))) (if (and deferred this-command) (add-hook 'post-command-hook #'start 'append 'local) @@ -486,33 +587,27 @@ If TYPE doesn't declare PROP in either ;;;###autoload (define-minor-mode flymake-mode nil - :group 'flymake :lighter flymake-mode-line + :group 'flymake :lighter flymake-lighter + (setq flymake--running-backends nil + flymake--disabled-backends nil) (cond - ;; Turning the mode ON. (flymake-mode (cond - ((not buffer-file-name) - (message "Flymake unable to run without a buffer file name")) - ((not (flymake-can-syntax-check-file buffer-file-name)) - (flymake-log 2 "flymake cannot check syntax in buffer %s" (buffer-name))) + ((not flymake-diagnostic-functions) + (error "flymake cannot check syntax in buffer %s" (buffer-name))) (t (add-hook 'after-change-functions 'flymake-after-change-function nil t) (add-hook 'after-save-hook 'flymake-after-save-hook nil t) (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) - ;;+(add-hook 'find-file-hook 'flymake-find-file-hook) - (flymake-report-status "" "") + (flymake--update-lighter "*" "*") (setq flymake-timer (run-at-time nil 1 'flymake-on-timer-event (current-buffer))) - (when (and flymake-start-syntax-check-on-find-file - ;; Since we write temp files in current dir, there's no point - ;; trying if the directory is read-only (bug#8954). - (file-writable-p (file-name-directory buffer-file-name))) - (with-demoted-errors - (flymake--start-syntax-check)))))) + (when flymake-start-syntax-check-on-find-file + (flymake--start-syntax-check))))) ;; Turning the mode OFF. (t @@ -525,9 +620,7 @@ If TYPE doesn't declare PROP in either (when flymake-timer (cancel-timer flymake-timer) - (setq flymake-timer nil)) - - (setq flymake-is-running nil)))) + (setq flymake-timer nil))))) ;;;###autoload (defun flymake-mode-on () @@ -563,8 +656,8 @@ If TYPE doesn't declare PROP in either ;;;###autoload (defun flymake-find-file-hook () - (when (and (not (local-variable-p 'flymake-mode (current-buffer))) - (flymake-can-syntax-check-file buffer-file-name)) + (unless (or flymake-mode + (null flymake-diagnostic-functions)) (flymake-mode) (flymake-log 3 "automatically turned ON flymake mode"))) @@ -599,8 +692,5 @@ If TYPE doesn't declare PROP in either (provide 'flymake) -(declare-function flymake-proc-start-syntax-check "flymake-proc") -(declare-function flymake-can-syntax-check-file "flymake-proc") - (require 'flymake-proc) ;;; flymake.el ends here diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index 5ecc87fc7e..c2deb1dc5c 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -1,4 +1,4 @@ -;;; flymake-tests.el --- Test suite for flymake +;;; flymake-tests.el --- Test suite for flymake -*- lexical-binding: t -*- ;; Copyright (C) 2011-2017 Free Software Foundation, Inc. @@ -53,7 +53,7 @@ SEVERITY-PREDICATE is used to setup (when sev-pred-supplied-p (setq-local flymake-proc-diagnostic-type-pred severity-predicate)) (goto-char (point-min)) - (flymake-mode 1) + (unless flymake-mode (flymake-mode 1)) ;; Weirdness here... http://debbugs.gnu.org/17647#25 ;; ... meaning `sleep-for', and even ;; `accept-process-output', won't suffice as ways to get @@ -63,7 +63,7 @@ SEVERITY-PREDICATE is used to setup ;; reading an input event, so, as a workaround, use a dummy ;; `read-event' with a very short timeout. (unless noninteractive (read-event "" nil 0.1)) - (while (and flymake-is-running (< (setq i (1+ i)) 10)) + (while (and (flymake-is-running) (< (setq i (1+ i)) 10)) (unless noninteractive (read-event "" nil 0.1)) (sleep-for (+ 0.5 flymake-no-changes-timeout))) (funcall fn))) @@ -130,6 +130,121 @@ SEVERITY-PREDICATE is used to setup (should (eq 'flymake-error (face-at-point))) (should-error (flymake-goto-next-error nil t)) )) +(defmacro flymake-tests--assert-set (set + should + should-not) + (declare (indent 1)) + `(progn + ,@(cl-loop + for s in should + collect `(should (memq ,s ,set))) + ,@(cl-loop + for s in should-not + collect `(should-not (memq ,s ,set))))) + +(ert-deftest dummy-backends () + "Test GCC warning via function predicate." + (with-temp-buffer + (cl-labels + ((diagnose + (report-fn type words) + (funcall + report-fn + (cl-loop + for word in words + append + (save-excursion + (goto-char (point-min)) + (cl-loop while (word-search-forward word nil t) + collect (flymake-make-diagnostic + (current-buffer) + (match-beginning 0) + (match-end 0) + type + (concat word " is wrong"))))))) + (error-backend + (report-fn) + (run-with-timer + 0.5 nil + #'diagnose report-fn :error '("manha" "prognata"))) + (warning-backend + (report-fn) + (run-with-timer + 0.5 nil + #'diagnose report-fn :warning '("ut" "dolor"))) + (sync-backend + (report-fn) + (diagnose report-fn :note '("quis" "commodo"))) + (refusing-backend + (_report-fn) + nil) + (panicking-backend + (report-fn) + (run-with-timer + 0.5 nil + report-fn :panic :explanation "The spanish inquisition!")) + (crashing-backend + (_report-fn) + ;; HACK: Shoosh log during tests + (setq-local warning-minimum-log-level :emergency) + (error "crashed"))) + (insert "Lorem ipsum dolor sit amet, consectetur adipiscing + elit, sed do eiusmod tempor incididunt ut labore et dolore + manha aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in + voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non prognata + sunt in culpa qui officia deserunt mollit anim id est + laborum.") + (let ((flymake-diagnostic-functions + (list #'error-backend #'warning-backend #'sync-backend + #'refusing-backend #'panicking-backend + #'crashing-backend + ))) + (flymake-mode) + ;; FIXME: accessing some flymake-ui's internals here... + (flymake-tests--assert-set flymake--running-backends + (#'error-backend #'warning-backend #'panicking-backend) + (#'sync-backend #'crashing-backend #'refusing-backend)) + + (flymake-tests--assert-set flymake--disabled-backends + (#'crashing-backend) + (#'error-backend #'warning-backend #'sync-backend + #'panicking-backend #'refusing-backend)) + + (cl-loop repeat 10 while (flymake-is-running) + unless noninteractive do (read-event "" nil 0.1) + do (sleep-for (+ 0.5 flymake-no-changes-timeout))) + + (should (eq flymake--running-backends '())) + + (flymake-tests--assert-set flymake--disabled-backends + (#'crashing-backend #'panicking-backend) + (#'error-backend #'warning-backend #'sync-backend + #'refusing-backend)) + + (goto-char (point-min)) + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) ; dolor + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) ; ut + (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) ; manha + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) ; Ut + (flymake-goto-next-error) + (should (eq 'flymake-note (face-at-point))) ; quis + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) ; ut + (flymake-goto-next-error) + (should (eq 'flymake-note (face-at-point))) ; commodo + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) ; dolor + (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) ; prognata + (should-error (flymake-goto-next-error nil t)))))) + (provide 'flymake-tests) ;;; flymake.el ends here commit b2f8b8b47ad6fe1550a183c5e96896ff587cd5f0 Author: João Távora Date: Sat Sep 23 18:15:40 2017 +0100 More Flymake cleanup before advancing to backend redesign Diagnostics are reported for buffers, not necessarily files. It’s the backend’s responsibility to compute the buffer where the diagnostic is applicable. For now, this has to match the buffer where flymake-mode is active and which is at the origin of the backend call. flymake.el knows nothing about line/column diagnostics (except for backward-compatible flymake-ler-make-ler, which must yet be tested). It’s also the backend’s reponsibility to compute a BEG and END positions for the diagnostic in the relevant buffer. * lisp/progmodes/flymake-proc.el (flymake-proc--diagnostics-for-pattern): Convert LINE/COL to region here. Check file buffer here. (flymake-proc--process-sentinel): Don’t kill output buffer if high enough log level. * lisp/progmodes/flymake.el (flymake-diag-region): Make this a utility function. (flymake--highlight-line): Diagnostic has region now. (flymake-popup-current-error-menu): Don’t add file and line numbers to already this silly menu. (flymake--fix-line-numbers): Remove. (flymake-report): No need to fix diagnostics here. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 2e593bd758..05f2cab1af 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -423,18 +423,24 @@ Create parent directories as needed." for col-string = (and col-idx (match-string col-idx)) for col-number = (and col-string (string-to-number col-string)) - collect (with-current-buffer (process-buffer proc) - (flymake-make-diagnostic - :file fname - :line line-number - :col col-number - :type (guess-type flymake-proc-diagnostic-type-pred message) - :text message - :full-file (and fname - (funcall - (flymake-proc--get-real-file-name-function - fname) - fname))))) + for full-file = (with-current-buffer (process-buffer proc) + (and fname + (funcall + (flymake-proc--get-real-file-name-function + fname) + fname))) + for buffer = (and full-file + (find-buffer-visiting full-file)) + if (eq buffer (process-buffer proc)) + collect (with-current-buffer buffer + (pcase-let ((`(,beg . ,end) + (flymake-diag-region line-number col-number))) + (flymake-make-diagnostic + buffer beg end + (guess-type flymake-proc-diagnostic-type-pred message) + message))) + else + do (flymake-log 2 "No buffer found for diagnosed file %s" fname)) (error (flymake-log 1 "Error parsing process output for pattern %s: %s" pattern err) @@ -486,7 +492,8 @@ Create parent directories as needed." (flymake-log 2 "process %d exited with code %d" (process-id process) exit-status) - (kill-buffer (process-get process 'flymake-proc--output-buffer)) + (unless (> flymake-log-level 2) + (kill-buffer (process-get process 'flymake-proc--output-buffer))) (condition-case err (progn (flymake-log 3 "cleaning up using %s" cleanup-f) diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index b5abf5c6ff..ea9e7c92ea 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -139,10 +139,29 @@ are the string substitutions (see the function `format')." (message "%s" msg)))) (cl-defstruct (flymake--diag - (:constructor flymake-make-diagnostic)) - file line col type text full-file) -(define-obsolete-function-alias 'flymake-ler-make 'flymake-make-diagnostic "26.1" - "Constructor for objects of type `flymake--diag'") + (:constructor flymake--diag-make)) + buffer beg end type text) + +(defun flymake-make-diagnostic (buffer + beg + end + type + text) + "Mark BUFFER's region from BEG to END with a flymake diagnostic. +TYPE is a key to `flymake-diagnostic-types-alist' and TEXT is a +description of the problem detected in this region." + (flymake--diag-make :buffer buffer :beg beg :end end :type type :text text)) + +(defun flymake-ler-make-ler (file line type text &optional full-file) + (let* ((file (or full-file file)) + (buf (find-buffer-visiting file))) + (unless buf (error "No buffer visiting %s" file)) + (pcase-let* ((`(,beg . ,end) + (with-current-buffer buf + (flymake-diag-region line nil)))) + (flymake-make-diagnostic buf beg end type text)))) + +(make-obsolete 'flymake-ler-make-ler 'flymake-make-diagnostic "26.1") (cl-defun flymake--overlays (&key beg end filter compare key) "Get flymake-related overlays. @@ -201,15 +220,14 @@ verify FILTER, sort them by COMPARE (using KEY)." (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") -(defun flymake--diag-region (diagnostic) - "Return the region (BEG . END) for DIAGNOSTIC. +(defun flymake-diag-region (line col) + "Compute region (BEG . END) corresponding to LINE and COL. Or nil if the region is invalid." - ;; FIXME: make this a generic function (condition-case-unless-debug _err - (save-excursion - (goto-char (point-min)) - (let ((line (flymake--diag-line diagnostic)) - (col (flymake--diag-col diagnostic))) + (let ((line (min (max line 1) + (line-number-at-pos (point-max) 'absolute)))) + (save-excursion + (goto-char (point-min)) (forward-line (1- line)) (cl-flet ((fallback-bol () (progn (back-to-indentation) (point))) @@ -316,8 +334,9 @@ If TYPE doesn't declare PROP in either (defun flymake--highlight-line (diagnostic) "Highlight buffer with info in DIAGNOSTIC." - (when-let* ((region (flymake--diag-region diagnostic)) - (ov (make-overlay (car region) (cdr region)))) + (when-let* ((ov (make-overlay + (flymake--diag-beg diagnostic) + (flymake--diag-end diagnostic)))) ;; First set `category' in the overlay, then copy over every other ;; property. ;; @@ -387,12 +406,7 @@ If TYPE doesn't declare PROP in either (user-error "No flymake problem for current line"))) (menu (mapcar (lambda (ov) (let ((diag (overlay-get ov 'flymake--diagnostic))) - (cons (format "%s - %s(%s)" - (flymake--diag-text diag) - (or (flymake--diag-file diag) - "(no file)") - (or (flymake--diag-line diag) - "?")) + (cons (flymake--diag-text diag) ov))) diag-overlays)) (event (if (mouse-event-p event) @@ -444,26 +458,10 @@ If TYPE doesn't declare PROP in either (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s" (buffer-name) status warning)) -(defun flymake--fix-line-numbers (diagnostic) - "Ensure DIAGNOSTIC has sensible error lines" - (setf (flymake--diag-line diagnostic) - (min (max (flymake--diag-line diagnostic) - 1) - (line-number-at-pos (point-max) 'absolute)))) - (defun flymake-report (diagnostics) (save-restriction (widen) (flymake-delete-own-overlays) - (setq diagnostics - (cl-remove-if-not - (lambda (diag) - (let ((ff (flymake--diag-full-file diag))) - (and ff - (equal (expand-file-name ff) - (expand-file-name (buffer-file-name)))))) - diagnostics)) - (mapc #'flymake--fix-line-numbers diagnostics) (mapc #'flymake--highlight-line diagnostics) (let ((err-count (cl-count-if #'flymake--diag-errorp diagnostics)) (warn-count (cl-count-if-not #'flymake--diag-errorp diagnostics))) commit b0bb181f9359aff07b09b919b8af397ef39d6784 Author: João Távora Date: Fri Sep 22 01:31:23 2017 +0100 Protect Flymake's eager checks against commands like fill-paragraph If flymake-start-syntax-check-on-newline is t, check should start as soon as a newline is seen by after-change-functions. But don't rush it: since the buffer state might not be final, we might end up with invalid diagnostic regions after some commands silently insert and delete newlines (looking at you, fill-paragraph). * lisp/progmodes/flymake.el (flymake-after-change-function): Pass `deferred' to flymake--start-syntax-check. (flymake--start-syntax-check): Take optional `deferred' arg. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index b32e799e67..b5abf5c6ff 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -475,6 +475,17 @@ If TYPE doesn't declare PROP in either (flymake-report-status "" "") (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) +(defun flymake--start-syntax-check (&optional deferred) + (cl-labels ((start + () + (remove-hook 'post-command-hook #'start 'local) + (setq flymake-check-start-time (float-time)) + (flymake-proc-start-syntax-check))) + (if (and deferred + this-command) + (add-hook 'post-command-hook #'start 'append 'local) + (start)))) + ;;;###autoload (define-minor-mode flymake-mode nil :group 'flymake :lighter flymake-mode-line @@ -538,7 +549,7 @@ If TYPE doesn't declare PROP in either (let((new-text (buffer-substring start stop))) (when (and flymake-start-syntax-check-on-newline (equal new-text "\n")) (flymake-log 3 "starting syntax check as new-line has been seen") - (flymake--start-syntax-check)) + (flymake--start-syntax-check 'deferred)) (setq flymake-last-change-time (float-time)))) (defun flymake-after-save-hook () @@ -590,9 +601,6 @@ If TYPE doesn't declare PROP in either (provide 'flymake) -(defun flymake--start-syntax-check () - (flymake-proc-start-syntax-check)) - (declare-function flymake-proc-start-syntax-check "flymake-proc") (declare-function flymake-can-syntax-check-file "flymake-proc") commit 54beebb4e0d919c7ee6dcdd7d774d851c35f85b7 Author: João Távora Date: Thu Sep 21 14:44:13 2017 +0100 Flymake highlights GCC info/notes as detected by flymake-proc.el * lisp/progmodes/flymake-proc.el (flymake-proc--diagnostics-for-pattern): Rewrite (using cl-loop) to honour more sophisticated flymake-proc-diagnostic-type-pred. (flymake-warning-re): Is now an obsolete alias for flymake-proc-diagnostic-type-pred. (flymake-proc-diagnostic-type-pred): Rename and augment from flymake-proc-warning-predicate. (flymake-proc-warning-predicate): Delete. * lisp/progmodes/flymake.el (flymake-note): New face. (flymake-diagnostic-types-alist): Simplify. (flymake-note): New overlay category. (flymake--lookup-type-property): Only lookup single keys, not lists. (flymake--diag-errorp): Rewrite. (flymake--highlight-line): Use flymake--lookup-type-property. * test/lisp/progmodes/flymake-tests.el (different-diagnostic-types): Rename from errors-and-warnings. Check notes. (flymake-tests--call-with-fixture): Use flymake-proc-diagnostic-type-pred. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index dd6bf50173..2e593bd758 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -394,47 +394,51 @@ Create parent directories as needed." (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name)) (defun flymake-proc--diagnostics-for-pattern (proc pattern) - (condition-case err - (pcase-let ((`(,regexp ,file-idx ,line-idx ,col-idx ,message-idx) - pattern) - (retval)) - (while (search-forward-regexp regexp nil t) - (let* ((fname (and file-idx (match-string file-idx))) - (message (and message-idx (match-string message-idx))) - (line-string (and line-idx (match-string line-idx))) - (line-number (and line-string - (string-to-number line-string))) - (col-string (and col-idx (match-string col-idx))) - (col-number (and col-string - (string-to-number col-string)))) - (with-current-buffer (process-buffer proc) - (push - (flymake-make-diagnostic - :file fname - :line line-number - :col col-number - :type (if (and - message - (cond ((stringp flymake-proc-warning-predicate) - (string-match flymake-proc-warning-predicate - message)) - ((functionp flymake-proc-warning-predicate) - (funcall flymake-proc-warning-predicate - message)))) - "w" - "e") - :text message - :full-file (and fname - (funcall - (flymake-proc--get-real-file-name-function - fname) - fname))) - retval)))) - retval) - (error - (flymake-log 1 "Error parsing process output for pattern %s: %s" - pattern err) - nil))) + (cl-flet ((guess-type + (pred message) + (cond ((null message) + :error) + ((stringp pred) + (if (string-match pred message) + :warning + :error)) + ((functionp pred) + (let ((probe (funcall pred message))) + (cond ((assoc-default probe + flymake-diagnostic-types-alist) + probe) + (probe + :warning) + (t + :error))))))) + (condition-case err + (cl-loop + with (regexp file-idx line-idx col-idx message-idx) = pattern + while (search-forward-regexp regexp nil t) + for fname = (and file-idx (match-string file-idx)) + for message = (and message-idx (match-string message-idx)) + for line-string = (and line-idx (match-string line-idx)) + for line-number = (and line-string + (string-to-number line-string)) + for col-string = (and col-idx (match-string col-idx)) + for col-number = (and col-string + (string-to-number col-string)) + collect (with-current-buffer (process-buffer proc) + (flymake-make-diagnostic + :file fname + :line line-number + :col col-number + :type (guess-type flymake-proc-diagnostic-type-pred message) + :text message + :full-file (and fname + (funcall + (flymake-proc--get-real-file-name-function + fname) + fname))))) + (error + (flymake-log 1 "Error parsing process output for pattern %s: %s" + pattern err) + nil)))) (defun flymake-proc--process-filter (proc string) "Parse STRING and collect diagnostics info." @@ -567,12 +571,29 @@ Convert it to flymake internal format." Use `flymake-proc-reformat-err-line-patterns-from-compile-el' to add patterns from compile.el") -(define-obsolete-variable-alias 'flymake-warning-re 'flymake-proc-warning-predicate "24.4") -(defvar flymake-proc-warning-predicate "^[wW]arning" - "Predicate matching against error text to detect a warning. -Takes a single argument, the error's text and should return non-nil -if it's a warning. -Instead of a function, it can also be a regular expression.") +(define-obsolete-variable-alias 'flymake-warning-re 'flymake-proc-diagnostic-type-pred "26.1") +(defvar flymake-proc-diagnostic-type-pred + 'flymake-proc-default-guess + "Predicate matching against diagnostic text to detect its type. +Takes a single argument, the diagnostic's text and should return +a value suitable for indexing +`flymake-diagnostic-types-alist' (which see). If the returned +value is nil, a type of `error' is assumed. For some backward +compatibility, if a non-nil value is returned that that doesn't +index that alist, a type of `:warning' is assumed. + +Instead of a function, it can also be a string, a regular +expression. A match indicates `:warning' type, otherwise +`:error'") + +(defun flymake-proc-default-guess (text) + "Guess if TEXT means a warning, a note or an error." + (cond ((string-match "^[wW]arning" text) + :warning) + ((string-match "^[nN]ote" text) + :note) + (t + :error))) (defun flymake-proc-get-project-include-dirs-imp (basedir) "Include dirs for the project current file belongs to." @@ -1167,12 +1188,6 @@ Convert it to flymake internal format.") (REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns from compile.el") - (define-obsolete-variable-alias 'flymake-warning-predicate - 'flymake-proc-warning-predicate "26.1" - "Predicate matching against error text to detect a warning. -Takes a single argument, the error's text and should return non-nil -if it's a warning. -Instead of a function, it can also be a regular expression.") (define-obsolete-function-alias 'flymake-parse-line 'flymake-proc-parse-line "26.1" "Parse LINE to see if it is an error or warning. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 827bce4b63..b32e799e67 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -189,6 +189,15 @@ verify FILTER, sort them by COMPARE (using KEY)." :version "24.4" :group 'flymake) +(defface flymake-note + '((((supports :underline (:style wave))) + :underline (:style wave :color "yellow green")) + (t + :inherit warning)) + "Face used for marking note regions." + :version "26.1" + :group 'flymake) + (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") @@ -226,13 +235,14 @@ Or nil if the region is invalid." nil))) (defvar flymake-diagnostic-types-alist - `((("e" :error error) + `((:error . ((flymake-category . flymake-error))) - (("w" :warning warning) - . ((flymake-category . flymake-warning)))) + (:warning + . ((flymake-category . flymake-warning))) + (:note + . ((flymake-category . flymake-note)))) "Alist ((KEY . PROPS)*) of properties of flymake error types. -KEY can be anything passed as `:type' to `flymake-diag-make', or -a list of these objects. +KEY can be anything passed as `:type' to `flymake-diag-make'. PROPS is an alist of properties that are applied, in order, to the diagnostics of each type. The recognized properties are: @@ -259,27 +269,21 @@ the diagnostics of each type. The recognized properties are: (put 'flymake-error 'face 'flymake-error) (put 'flymake-error 'bitmap flymake-error-bitmap) (put 'flymake-error 'severity (warning-numeric-level :error)) -(put 'flymake-error 'mode-line-face 'compilation-error) (put 'flymake-warning 'face 'flymake-warning) (put 'flymake-warning 'bitmap flymake-warning-bitmap) (put 'flymake-warning 'severity (warning-numeric-level :warning)) -(put 'flymake-warning 'mode-line-face 'compilation-warning) (put 'flymake-note 'face 'flymake-note) (put 'flymake-note 'bitmap flymake-warning-bitmap) (put 'flymake-note 'severity (warning-numeric-level :debug)) -(put 'flymake-note 'mode-line-face 'compilation-info) (defun flymake--lookup-type-property (type prop &optional default) "Look up PROP for TYPE in `flymake-diagnostic-types-alist'. If TYPE doesn't declare PROP in either -`flymake-diagnostic-types-alist' or its associated category, -return DEFAULT." - (let ((alist-probe (assoc type flymake-diagnostic-types-alist - (lambda (entry key) - (or (equal key entry) - (member key entry)))))) +`flymake-diagnostic-types-alist' or its associated +`flymake-category', return DEFAULT." + (let ((alist-probe (assoc type flymake-diagnostic-types-alist))) (cond (alist-probe (let* ((alist (cdr alist-probe)) (prop-probe (assoc prop alist))) diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index 5e76f3136e..5ecc87fc7e 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -41,7 +41,7 @@ nil sev-pred-supplied-p)) "Call FN after flymake setup in FILE, using `flymake-proc`. SEVERITY-PREDICATE is used to setup -`flymake-proc-warning-predicate'." +`flymake-proc-diagnostic-type-pred'" (let* ((file (expand-file-name file flymake-tests-data-directory)) (visiting (find-buffer-visiting file)) (buffer (or visiting (find-file-noselect file))) @@ -51,7 +51,7 @@ SEVERITY-PREDICATE is used to setup (with-current-buffer buffer (save-excursion (when sev-pred-supplied-p - (setq-local flymake-proc-warning-predicate severity-predicate)) + (setq-local flymake-proc-diagnostic-type-pred severity-predicate)) (goto-char (point-min)) (flymake-mode 1) ;; Weirdness here... http://debbugs.gnu.org/17647#25 @@ -115,13 +115,13 @@ SEVERITY-PREDICATE is used to setup (should (eq 'flymake-warning (face-at-point))))) -(ert-deftest errors-and-warnings () +(ert-deftest different-diagnostic-types () "Test GCC warning via function predicate." (skip-unless (and (executable-find "gcc") (executable-find "make"))) (flymake-tests--with-flymake ("errors-and-warnings.c") (flymake-goto-next-error) - (should (eq 'flymake-error (face-at-point))) + (should (eq 'flymake-note (face-at-point))) (flymake-goto-next-error) (should (eq 'flymake-warning (face-at-point))) (flymake-goto-next-error) commit 491cc4a1bd68c2f651027982e3dfb7545d3e57ab Author: João Távora Date: Thu Sep 21 14:45:21 2017 +0100 Flymake checks file names before considering diagnostics The error patterns for gcc picked up errors for the Makefile itself, for example. These shouldn't count as actual errors. * lisp/progmodes/flymake.el (flymake-report): Check matching file names. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index f9aa098071..827bce4b63 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -451,6 +451,14 @@ return DEFAULT." (save-restriction (widen) (flymake-delete-own-overlays) + (setq diagnostics + (cl-remove-if-not + (lambda (diag) + (let ((ff (flymake--diag-full-file diag))) + (and ff + (equal (expand-file-name ff) + (expand-file-name (buffer-file-name)))))) + diagnostics)) (mapc #'flymake--fix-line-numbers diagnostics) (mapc #'flymake--highlight-line diagnostics) (let ((err-count (cl-count-if #'flymake--diag-errorp diagnostics)) commit 8c85ab027eefb352a39d68433f64bfaa1c446856 Author: João Távora Date: Thu Sep 21 14:20:22 2017 +0100 Echo Flymake error messages when navigating errors interactively Perhaps binding M-n and M-p to flymake-goto-next-error and flymake-goto-prev-error also wouldn't be a bad idea. * lisp/progmodes/flymake.el (flymake-goto-next-error): Use target overlay's help-echo. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index e8d5de6023..f9aa098071 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -557,15 +557,19 @@ return DEFAULT." (chain (cl-member-if (lambda (ov) (if (cl-plusp n) (> (overlay-start ov) - (point)) - (< (overlay-start ov) - (point)))) - ovs)) - (target (nth (1- n) chain))) - (if target - (goto-char (overlay-start target)) - (when interactive - (user-error "No more flymake errors"))))) + (point)) + (< (overlay-start ov) + (point)))) + ovs)) + (target (nth (1- n) chain))) + (cond (target + (goto-char (overlay-start target)) + (when interactive + (message + (funcall (overlay-get target 'help-echo) + nil nil (point))))) + (interactive + (user-error "No more flymake errors"))))) (defun flymake-goto-prev-error (&optional n interactive) "Go to previous, or Nth previous, flymake error in buffer." commit 0d26e45ab47d781e561bb415e28aab8cb166ced2 Author: João Távora Date: Thu Sep 21 14:57:20 2017 +0100 Add a new Flymake test for multiple errors and warnings * test/lisp/progmodes/flymake-tests.el (flymake-tests--call-with-fixture): Save excursion. (errors-and-warnings): New test. * test/lisp/progmodes/flymake-resources/errors-and-warnings.c: New test fixture. diff --git a/test/lisp/progmodes/flymake-resources/errors-and-warnings.c b/test/lisp/progmodes/flymake-resources/errors-and-warnings.c new file mode 100644 index 0000000000..6454dd2023 --- /dev/null +++ b/test/lisp/progmodes/flymake-resources/errors-and-warnings.c @@ -0,0 +1,10 @@ + int main() +{ + char c = 1000; + int bla; + /* The following line should have one warning and one error. The + warning spans the full line because gcc (at least 6.3.0) points + places the error at the =, which isn't a sexp.*/ + char c; if (bla == (void*)3); + return c; +} diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index c626aec04b..5e76f3136e 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -49,23 +49,24 @@ SEVERITY-PREDICATE is used to setup (i 0)) (unwind-protect (with-current-buffer buffer - (when sev-pred-supplied-p - (setq-local flymake-proc-warning-predicate severity-predicate)) - (goto-char (point-min)) - (flymake-mode 1) - ;; Weirdness here... http://debbugs.gnu.org/17647#25 - ;; ... meaning `sleep-for', and even - ;; `accept-process-output', won't suffice as ways to get - ;; process filters and sentinels to run, though they do work - ;; fine in a non-interactive batch session. The only thing - ;; that will indeed unblock pending process output is - ;; reading an input event, so, as a workaround, use a dummy - ;; `read-event' with a very short timeout. - (unless noninteractive (read-event "" nil 0.1)) - (while (and flymake-is-running (< (setq i (1+ i)) 10)) + (save-excursion + (when sev-pred-supplied-p + (setq-local flymake-proc-warning-predicate severity-predicate)) + (goto-char (point-min)) + (flymake-mode 1) + ;; Weirdness here... http://debbugs.gnu.org/17647#25 + ;; ... meaning `sleep-for', and even + ;; `accept-process-output', won't suffice as ways to get + ;; process filters and sentinels to run, though they do work + ;; fine in a non-interactive batch session. The only thing + ;; that will indeed unblock pending process output is + ;; reading an input event, so, as a workaround, use a dummy + ;; `read-event' with a very short timeout. (unless noninteractive (read-event "" nil 0.1)) - (sleep-for (+ 0.5 flymake-no-changes-timeout))) - (funcall fn)) + (while (and flymake-is-running (< (setq i (1+ i)) 10)) + (unless noninteractive (read-event "" nil 0.1)) + (sleep-for (+ 0.5 flymake-no-changes-timeout))) + (funcall fn))) (and buffer (not visiting) (let (kill-buffer-query-functions) (kill-buffer buffer)))))) @@ -114,6 +115,21 @@ SEVERITY-PREDICATE is used to setup (should (eq 'flymake-warning (face-at-point))))) +(ert-deftest errors-and-warnings () + "Test GCC warning via function predicate." + (skip-unless (and (executable-find "gcc") (executable-find "make"))) + (flymake-tests--with-flymake + ("errors-and-warnings.c") + (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-warning (face-at-point))) + (flymake-goto-next-error) + (should (eq 'flymake-error (face-at-point))) + (should-error (flymake-goto-next-error nil t)) )) + (provide 'flymake-tests) ;;; flymake.el ends here commit 7787988587eacb9aa77bbfb830a885fab8edd828 Author: João Távora Date: Wed Sep 20 19:09:10 2017 +0100 Flymake warning face easier to distinguish A orange wavy underline is very hard to tell from a red wavy underline. * lisp/progmodes/flymake.el (flymake-warning): Change color to "deep sky blue" diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 72acc3a920..e8d5de6023 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -182,7 +182,7 @@ verify FILTER, sort them by COMPARE (using KEY)." (defface flymake-warning '((((supports :underline (:style wave))) - :underline (:style wave :color "DarkOrange")) + :underline (:style wave :color "deep sky blue")) (t :inherit warning)) "Face used for marking warning regions." commit 734aa8818d2916f2fe2eb9fe4add66255379b938 Author: João Távora Date: Tue Sep 19 14:25:34 2017 +0100 Flymake's flymake-proc.el parses column numbers from gcc/javac errors Column numbers are not a great way of marking diagnostic regions, but that's probably all that can be expected from the flymake-proc.el backend. For now, try (end-of-thing 'sexp) to discover the diagnostic's end position. * lisp/progmodes/flymake-proc.el () (flymake-proc-err-line-patterns): Also parse column numbers, if available, for gcc/javac warnings. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index abda259e89..dd6bf50173 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -558,8 +558,8 @@ Convert it to flymake internal format." ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1) ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1) ;; ant/javac. Note this also matches gcc warnings! - (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)" - 2 4 nil 5)) + (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?:[ \t\n]*\\(.+\\)" + 2 4 5 6)) ;; compilation-error-regexp-alist) (flymake-proc-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist)) "Patterns for matching error/warning lines. Each pattern has the form commit ae64bf12a865d5d40b7541af0da9b971936994ec Author: João Távora Date: Thu Sep 7 15:13:39 2017 +0100 New Flymake variable flymake-diagnostic-types-alist and much cleanup A new user-visible variable is introduced where different diagnostic types can be categorized. Flymake backends can also contribute to this variable. Anything that doesn’t match an existing error type is considered. The variable’s alists are used to propertize the overlays pertaining to each error type. The user can override the built-in properties by either by modifying the alist, or by modifying the properties of a special "category" symbol, named by the `flymake-category' entry in the alist. The `flymake-category' entry is especially useful for, say, the author of foo-flymake-backend, who issues diagnostics of type :foo-note, that should behave like notes, except with no fringe bitmap: (add-to-list 'flymake-diagnostic-types-alist '(:foo-note . ((flymake-category . flymake-note) (bitmap . nil)))) For essential properties like `severity', `priority', etc, a default value is produced. Some properties like `evaporate' cannot be overriden. * lisp/progmodes/flymake.el (flymake--diag): Rename from flymake-ler. (flymake-ler-make): Obsolete alias for flymake-diagnostic-make (flymake-ler-errorp): Rewrite using flymake--severity. (flymake--place-overlay): Delete. (flymake--overlays): Now a cl-defun with &key args. Document. Use `overlays-at' if BEG is non-nil and END is nil. (flymake--lookup-type-property): New helper. (flymake--highlight-line): Rewrite. (flymake-diagnostic-types-alist): New API variable. (flymake--diag-region) (flymake--severity, flymake--face) (flymake--fringe-overlay-spec): New helper. (flymake-popup-current-error-menu): Use new flymake-overlays. (flymake-popup-current-error-menu, flymake-report): Use flymake--diag-errorp. (flymake--fix-line-numbers): Use flymake--diag-line. (flymake-goto-next-error): Pass :key to flymake-overlays * lisp/progmodes/flymake-proc.el (flymake-proc--diagnostics-for-pattern): Use flymake-diagnostic-make. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 0395fff322..abda259e89 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -409,7 +409,7 @@ Create parent directories as needed." (string-to-number col-string)))) (with-current-buffer (process-buffer proc) (push - (flymake-ler-make + (flymake-make-diagnostic :file fname :line line-number :col col-number diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index f00915a684..72acc3a920 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -33,6 +33,8 @@ ;;; Code: (require 'cl-lib) +(require 'thingatpt) ; end-of-thing +(require 'warnings) ; warning-numeric-level (defgroup flymake nil "Universal on-the-fly syntax checker." @@ -136,57 +138,18 @@ are the string substitutions (see the function `format')." (let* ((msg (apply #'format-message text args))) (message "%s" msg)))) -(cl-defstruct (flymake-ler - (:constructor flymake-ler-make)) +(cl-defstruct (flymake--diag + (:constructor flymake-make-diagnostic)) file line col type text full-file) - -(defun flymake-ler-errorp (diag) - "Tell if DIAG is a flymake error or something else" - (string= "e" (flymake-ler-type diag))) - -(defun flymake--place-overlay (beg end tooltip-text face bitmap diag) - "Place a flymake overlay in range BEG and END. -Make a flymake fringe overlay for the line at BEG, if needed." - (let* ((fringe-overlay - (or (cl-find-if (lambda (ov) - (overlay-get ov 'flymake--fringe-overlay)) - (overlays-at beg)) - (make-overlay beg (1+ beg))))) - (let ((ov fringe-overlay)) - (overlay-put ov 'help-echo - (concat tooltip-text "\n" - (overlay-get ov 'help-echo))) - (overlay-put ov 'before-string - (and flymake-fringe-indicator-position - (propertize "!" 'display - (cons flymake-fringe-indicator-position - (if (listp bitmap) - bitmap - (list bitmap)) - )))) - (overlay-put ov 'evaporate t) - (overlay-put ov 'flymake-overlay t) - (overlay-put ov 'priority 100) - ov) - (let ((ov (make-overlay beg end))) - (overlay-put ov 'face face) - (overlay-put ov 'help-echo - (concat tooltip-text "\n" - (overlay-get ov 'help-echo))) - (overlay-put ov 'evaporate t) - (overlay-put ov 'flymake-overlay t) - (overlay-put ov 'flymake--diagnostic diag)) - (cl-loop for i from 0 - for overlay in - (flymake--overlays - 'flymake--diagnostic - (lambda (_ov1 ov2) - (flymake-ler-errorp - (overlay-get ov2 'flymake--diagnostic))) - beg end) - do (overlay-put overlay 'priority (+ 100 i))))) - -(defun flymake--overlays (&optional filter compare beg end) +(define-obsolete-function-alias 'flymake-ler-make 'flymake-make-diagnostic "26.1" + "Constructor for objects of type `flymake--diag'") + +(cl-defun flymake--overlays (&key beg end filter compare key) + "Get flymake-related overlays. +If BEG is non-nil and END is nil, consider only `overlays-at' +BEG. Otherwise consider `overlays-in' the region comprised by BEG +and END, defaulting to the whole buffer. Remove all that do not +verify FILTER, sort them by COMPARE (using KEY)." (cl-remove-if-not (lambda (ov) (and (overlay-get ov 'flymake-overlay) @@ -195,12 +158,13 @@ Make a flymake fringe overlay for the line at BEG, if needed." ((symbolp filter) (overlay-get ov filter)))))) (save-restriction (widen) - (let ((ovs (overlays-in (or beg (point-min)) - (or end (point-max))))) + (let ((ovs (if (and beg (null end)) + (overlays-at beg t) + (overlays-in (or beg (point-min)) + (or end (point-max)))))) (if compare - (cl-sort ovs - compare - :key #'overlay-start) + (cl-sort ovs compare :key (or key + #'identity)) ovs))))) (defun flymake-delete-own-overlays () @@ -228,27 +192,167 @@ Make a flymake fringe overlay for the line at BEG, if needed." (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") +(defun flymake--diag-region (diagnostic) + "Return the region (BEG . END) for DIAGNOSTIC. +Or nil if the region is invalid." + ;; FIXME: make this a generic function + (condition-case-unless-debug _err + (save-excursion + (goto-char (point-min)) + (let ((line (flymake--diag-line diagnostic)) + (col (flymake--diag-col diagnostic))) + (forward-line (1- line)) + (cl-flet ((fallback-bol + () (progn (back-to-indentation) (point))) + (fallback-eol + (beg) + (progn + (end-of-line) + (skip-chars-backward " \t\f\t\n" beg) + (if (eq (point) beg) + (line-beginning-position 2) + (point))))) + (if col + (let* ((beg (progn (forward-char (1- col)) (point))) + (sexp-end (ignore-errors (end-of-thing 'sexp))) + (end (or sexp-end + (fallback-eol beg)))) + (cons (if sexp-end beg (fallback-bol)) + end)) + (let* ((beg (fallback-bol)) + (end (fallback-eol beg))) + (cons beg end)))))) + (error (flymake-log 4 "Invalid region for diagnostic %s") + nil))) + +(defvar flymake-diagnostic-types-alist + `((("e" :error error) + . ((flymake-category . flymake-error))) + (("w" :warning warning) + . ((flymake-category . flymake-warning)))) + "Alist ((KEY . PROPS)*) of properties of flymake error types. +KEY can be anything passed as `:type' to `flymake-diag-make', or +a list of these objects. + +PROPS is an alist of properties that are applied, in order, to +the diagnostics of each type. The recognized properties are: + +* Every property pertaining to overlays, except `category' and + `evaporate' (see Info Node `(elisp)Overlay Properties'), used + affect the appearance of Flymake annotations. + +* `bitmap', an image displayed in the fringe according to + `flymake-fringe-indicator-position'. The value actually + follows the syntax of `flymake-error-bitmap' (which see). It + is overriden by any `before-string' overlay property. + +* `severity', a non-negative integer specifying the diagnostic's + severity. The higher, the more serious. If the overlay + priority `priority' is not specified, `severity' is used to set + it and help sort overlapping overlays. + +* `flymake-category', a symbol whose property list is considered + as a default for missing values of any other properties. This + is useful to backend authors when creating new diagnostic types + that differ from an existing type by only a few properties.") + +(put 'flymake-error 'face 'flymake-error) +(put 'flymake-error 'bitmap flymake-error-bitmap) +(put 'flymake-error 'severity (warning-numeric-level :error)) +(put 'flymake-error 'mode-line-face 'compilation-error) + +(put 'flymake-warning 'face 'flymake-warning) +(put 'flymake-warning 'bitmap flymake-warning-bitmap) +(put 'flymake-warning 'severity (warning-numeric-level :warning)) +(put 'flymake-warning 'mode-line-face 'compilation-warning) + +(put 'flymake-note 'face 'flymake-note) +(put 'flymake-note 'bitmap flymake-warning-bitmap) +(put 'flymake-note 'severity (warning-numeric-level :debug)) +(put 'flymake-note 'mode-line-face 'compilation-info) + +(defun flymake--lookup-type-property (type prop &optional default) + "Look up PROP for TYPE in `flymake-diagnostic-types-alist'. +If TYPE doesn't declare PROP in either +`flymake-diagnostic-types-alist' or its associated category, +return DEFAULT." + (let ((alist-probe (assoc type flymake-diagnostic-types-alist + (lambda (entry key) + (or (equal key entry) + (member key entry)))))) + (cond (alist-probe + (let* ((alist (cdr alist-probe)) + (prop-probe (assoc prop alist))) + (if prop-probe + (cdr prop-probe) + (if-let* ((cat (assoc-default 'flymake-category alist)) + (plist (and (symbolp cat) + (symbol-plist cat))) + (cat-probe (plist-member plist prop))) + (cadr cat-probe) + default)))) + (t + default)))) + +(defun flymake--diag-errorp (diag) + "Tell if DIAG is a flymake error or something else" + (let ((sev (flymake--lookup-type-property 'severity + (flymake--diag-type diag) + (warning-numeric-level :error)))) + (>= sev (warning-numeric-level :error)))) + +(defun flymake--fringe-overlay-spec (bitmap) + (and flymake-fringe-indicator-position + bitmap + (propertize "!" 'display + (cons flymake-fringe-indicator-position + (if (listp bitmap) + bitmap + (list bitmap)))))) + (defun flymake--highlight-line (diagnostic) - "Highlight buffer with info in DIAGNOSTIC. -Reuse overlays if necessary -Perhaps use the message text as a hint to enhance highlighting." - (save-excursion - (goto-char (point-min)) - (let ((line-no (flymake-ler-line diagnostic))) - (forward-line (1- line-no)) - (pcase-let* ((beg (progn (back-to-indentation) (point))) - (end (progn - (end-of-line) - (skip-chars-backward " \t\f\t\n" beg) - (if (eq (point) beg) - (line-beginning-position 2) - (point)))) - (tooltip-text (flymake-ler-text diagnostic)) - (`(,face ,bitmap) - (if (equal "e" (flymake-ler-type diagnostic)) - (list 'flymake-errline flymake-error-bitmap) - (list 'flymake-warnline flymake-warning-bitmap)))) - (flymake--place-overlay beg end tooltip-text face bitmap diagnostic))))) + "Highlight buffer with info in DIAGNOSTIC." + (when-let* ((region (flymake--diag-region diagnostic)) + (ov (make-overlay (car region) (cdr region)))) + ;; First set `category' in the overlay, then copy over every other + ;; property. + ;; + (let ((alist (assoc-default (flymake--diag-type diagnostic) + flymake-diagnostic-types-alist))) + (overlay-put ov 'category (assoc-default 'flymake-category alist)) + (cl-loop for (k . v) in alist + unless (eq k 'category) + do (overlay-put ov k v))) + ;; Now ensure some essential defaults are set + ;; + (cl-flet ((default-maybe + (prop value) + (unless (or (plist-member (overlay-properties ov) prop) + (let ((cat (overlay-get ov + 'flymake-category))) + (and cat + (plist-member (symbol-plist cat) prop)))) + (overlay-put ov prop value)))) + (default-maybe 'bitmap flymake-error-bitmap) + (default-maybe 'before-string + (flymake--fringe-overlay-spec + (overlay-get ov 'bitmap))) + (default-maybe 'help-echo + (lambda (_window _ov pos) + (mapconcat + (lambda (ov) + (let ((diag (overlay-get ov 'flymake--diagnostic))) + (flymake--diag-text diag))) + (flymake--overlays :beg pos) + "\n"))) + (default-maybe 'severity (warning-numeric-level :error)) + (default-maybe 'priority (+ 100 (overlay-get ov 'severity)))) + ;; Some properties can't be overriden + ;; + (overlay-put ov 'evaporate t) + (overlay-put ov 'flymake-overlay t) + (overlay-put ov 'flymake--diagnostic diagnostic))) + (defvar-local flymake-is-running nil "If t, flymake syntax check process is running for the current buffer.") @@ -273,17 +377,17 @@ Perhaps use the message text as a hint to enhance highlighting." "Pop up a menu with errors/warnings for current line." (interactive (list last-nonmenu-event)) (let* ((diag-overlays (or - (flymake--overlays 'flymake--diagnostic nil - (line-beginning-position) - (line-end-position)) + (flymake--overlays :filter 'flymake--diagnostic + :beg (line-beginning-position) + :end (line-end-position)) (user-error "No flymake problem for current line"))) (menu (mapcar (lambda (ov) (let ((diag (overlay-get ov 'flymake--diagnostic))) (cons (format "%s - %s(%s)" - (flymake-ler-text diag) - (or (flymake-ler-file diag) + (flymake--diag-text diag) + (or (flymake--diag-file diag) "(no file)") - (or (flymake-ler-line diag) + (or (flymake--diag-line diag) "?")) ov))) diag-overlays)) @@ -294,8 +398,8 @@ Perhaps use the message text as a hint to enhance highlighting." diag-overlays)) (title (format "Line %d: %d error(s), %d other(s)" (line-number-at-pos) - (cl-count-if #'flymake-ler-errorp diagnostics) - (cl-count-if-not #'flymake-ler-errorp diagnostics))) + (cl-count-if #'flymake--diag-errorp diagnostics) + (cl-count-if-not #'flymake--diag-errorp diagnostics))) (choice (x-popup-menu event (list title (cons "" menu))))) (flymake-log 3 "choice=%s" choice) ;; FIXME: What is the point of going to the problem locus if we're @@ -338,8 +442,8 @@ Perhaps use the message text as a hint to enhance highlighting." (defun flymake--fix-line-numbers (diagnostic) "Ensure DIAGNOSTIC has sensible error lines" - (setf (flymake-ler-line diagnostic) - (min (max (flymake-ler-line diagnostic) + (setf (flymake--diag-line diagnostic) + (min (max (flymake--diag-line diagnostic) 1) (line-number-at-pos (point-max) 'absolute)))) @@ -349,8 +453,8 @@ Perhaps use the message text as a hint to enhance highlighting." (flymake-delete-own-overlays) (mapc #'flymake--fix-line-numbers diagnostics) (mapc #'flymake--highlight-line diagnostics) - (let ((err-count (cl-count-if #'flymake-ler-errorp diagnostics)) - (warn-count (cl-count-if-not #'flymake-ler-errorp diagnostics))) + (let ((err-count (cl-count-if #'flymake--diag-errorp diagnostics)) + (warn-count (cl-count-if-not #'flymake--diag-errorp diagnostics))) (when flymake-check-start-time (flymake-log 2 "%s: %d error(s), %d other(s) in %.2f second(s)" (buffer-name) err-count warn-count @@ -447,11 +551,12 @@ Perhaps use the message text as a hint to enhance highlighting." "Go to next, or Nth next, flymake error in buffer." (interactive (list 1 t)) (let* ((n (or n 1)) - (ovs (flymake--overlays 'flymake--diagnostic - (if (cl-plusp n) #'< #'>))) - (chain (cl-member-if (lambda (ov) - (if (cl-plusp n) - (> (overlay-start ov) + (ovs (flymake--overlays :filter 'flymake--diagnostic + :compare (if (cl-plusp n) #'< #'>) + :key #'overlay-start)) + (chain (cl-member-if (lambda (ov) + (if (cl-plusp n) + (> (overlay-start ov) (point)) (< (overlay-start ov) (point)))) commit 9f8ad133eb66ffb0a1985a35a3c05bc52e6f6361 Author: João Távora Date: Thu Sep 7 14:19:33 2017 +0100 Refactor Flymake tests in preparation for more tests Introduce a slightly more generic fixture macro. Also make flymake-tests.el friendlier to interactive runs, by not killing buffers visited by the user. * test/lisp/progmodes/flymake-tests.el (flymake-tests--call-with-fixture): New helper from flymake-tests--current-face. Don't kill file buffers already being visited before the test starts. (flymake-tests--with-flymake): New macro. (flymake-tests--current-face): Delete. (warning-predicate-rx-gcc, warning-predicate-function-gcc) (warning-predicate-rx-perl, warning-predicate-function-perl): Use flymake-test--with-flymake. diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index 338e8e0f07..c626aec04b 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -34,15 +34,23 @@ "Directory containing flymake test data.") -;; Warning predicate -(defun flymake-tests--current-face (file predicate) - (let ((buffer (find-file-noselect - (expand-file-name file flymake-tests-data-directory))) - (process-environment (cons "LC_ALL=C" process-environment)) - (i 0)) +;; +;; +(cl-defun flymake-tests--call-with-fixture (fn file + &key (severity-predicate + nil sev-pred-supplied-p)) + "Call FN after flymake setup in FILE, using `flymake-proc`. +SEVERITY-PREDICATE is used to setup +`flymake-proc-warning-predicate'." + (let* ((file (expand-file-name file flymake-tests-data-directory)) + (visiting (find-buffer-visiting file)) + (buffer (or visiting (find-file-noselect file))) + (process-environment (cons "LC_ALL=C" process-environment)) + (i 0)) (unwind-protect (with-current-buffer buffer - (setq-local flymake-proc-warning-predicate predicate) + (when sev-pred-supplied-p + (setq-local flymake-proc-warning-predicate severity-predicate)) (goto-char (point-min)) (flymake-mode 1) ;; Weirdness here... http://debbugs.gnu.org/17647#25 @@ -57,36 +65,54 @@ (while (and flymake-is-running (< (setq i (1+ i)) 10)) (unless noninteractive (read-event "" nil 0.1)) (sleep-for (+ 0.5 flymake-no-changes-timeout))) - (flymake-goto-next-error) - (face-at-point)) - (and buffer (let (kill-buffer-query-functions) (kill-buffer buffer)))))) + (funcall fn)) + (and buffer + (not visiting) + (let (kill-buffer-query-functions) (kill-buffer buffer)))))) + +(cl-defmacro flymake-tests--with-flymake ((file &rest args) + &body body) + (declare (indent 1) + (debug (sexp &rest form))) + `(flymake-tests--call-with-fixture (lambda () ,@body) ,file ,@args)) (ert-deftest warning-predicate-rx-gcc () "Test GCC warning via regexp predicate." (skip-unless (and (executable-find "gcc") (executable-find "make"))) - (should (eq 'flymake-warning - (flymake-tests--current-face "test.c" "^[Ww]arning")))) + (flymake-tests--with-flymake + ("test.c" :severity-predicate "^[Ww]arning") + (flymake-goto-next-error) + (should (eq 'flymake-warning + (face-at-point))))) (ert-deftest warning-predicate-function-gcc () "Test GCC warning via function predicate." (skip-unless (and (executable-find "gcc") (executable-find "make"))) - (should (eq 'flymake-warning - (flymake-tests--current-face "test.c" - (lambda (msg) (string-match "^[Ww]arning" msg)))))) + (flymake-tests--with-flymake + ("test.c" :severity-predicate + (lambda (msg) (string-match "^[Ww]arning" msg))) + (flymake-goto-next-error) + (should (eq 'flymake-warning + (face-at-point))))) (ert-deftest warning-predicate-rx-perl () "Test perl warning via regular expression predicate." (skip-unless (executable-find "perl")) - (should (eq 'flymake-warning - (flymake-tests--current-face "test.pl" "^Scalar value")))) + (flymake-tests--with-flymake + ("test.pl" :severity-predicate "^Scalar value") + (flymake-goto-next-error) + (should (eq 'flymake-warning + (face-at-point))))) (ert-deftest warning-predicate-function-perl () "Test perl warning via function predicate." (skip-unless (executable-find "perl")) - (should (eq 'flymake-warning - (flymake-tests--current-face - "test.pl" - (lambda (msg) (string-match "^Scalar value" msg)))))) + (flymake-tests--with-flymake + ("test.pl" :severity-predicate + (lambda (msg) (string-match "^Scalar value" msg))) + (flymake-goto-next-error) + (should (eq 'flymake-warning + (face-at-point))))) (provide 'flymake-tests) commit bbcb079522e12d8b9d416a8472a2966cc6d83a5a Author: João Távora Date: Sun Aug 20 12:19:45 2017 +0100 Allow running Flymake tests from interactive sessions * test/lisp/progmodes/flymake-tests.el (flymake-tests-data-directory): Expand to reasonable value if no EMACS_TEST_DIRECTORY. (flymake-tests--current-face): Work around "weirdness" of bug 17647 with read-event. diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index a018f11efd..338e8e0f07 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -26,7 +26,11 @@ (require 'flymake) (defvar flymake-tests-data-directory - (expand-file-name "lisp/progmodes/flymake-resources" (getenv "EMACS_TEST_DIRECTORY")) + (expand-file-name "lisp/progmodes/flymake-resources" + (or (getenv "EMACS_TEST_DIRECTORY") + (expand-file-name "../../.." + (or load-file-name + buffer-file-name)))) "Directory containing flymake test data.") @@ -41,8 +45,17 @@ (setq-local flymake-proc-warning-predicate predicate) (goto-char (point-min)) (flymake-mode 1) - ;; Weirdness here... https://debbugs.gnu.org/17647#25 + ;; Weirdness here... http://debbugs.gnu.org/17647#25 + ;; ... meaning `sleep-for', and even + ;; `accept-process-output', won't suffice as ways to get + ;; process filters and sentinels to run, though they do work + ;; fine in a non-interactive batch session. The only thing + ;; that will indeed unblock pending process output is + ;; reading an input event, so, as a workaround, use a dummy + ;; `read-event' with a very short timeout. + (unless noninteractive (read-event "" nil 0.1)) (while (and flymake-is-running (< (setq i (1+ i)) 10)) + (unless noninteractive (read-event "" nil 0.1)) (sleep-for (+ 0.5 flymake-no-changes-timeout))) (flymake-goto-next-error) (face-at-point)) @@ -59,7 +72,7 @@ (skip-unless (and (executable-find "gcc") (executable-find "make"))) (should (eq 'flymake-warning (flymake-tests--current-face "test.c" - (lambda (msg) (string-match "^[Ww]arning" msg)))))) + (lambda (msg) (string-match "^[Ww]arning" msg)))))) (ert-deftest warning-predicate-rx-perl () "Test perl warning via regular expression predicate." commit 1c30f9fc08649649676ed812f1579f0948558ceb Author: João Távora Date: Wed Sep 6 16:03:24 2017 +0100 Flymake diagnostics now apply to arbitrary buffer regions Make Flymake UI some 150 lines lighter Strip away much of the original implementation's complexity in manipulating objects representing diagnostics as well as creating and navigating overlays. Lay some groundwork for a more flexible approach that allows for different classes of diagnostics, not necessarily line-based. Importantly, one overlay per diagnostic is created, whereas the original implementation had one per line, and on it it concatenated the results of errors and warnings. This means that currently, an error and warning on the same line are problematic and the warning might be overlooked but this will soon be fixed by setting appropriate priorities. Since diagnostics can highlight arbitrary regions, not just lines, the faces were renamed. Tests pass and backward compatibility with interactive functions is maintained, but probably any third-party extension or customization relying on more than a trivial set of flymake.el internals has stopped working. * lisp/progmodes/flymake-proc.el (flymake-proc--diagnostics-for-pattern): Use new flymake-ler-make constructor syntax. * lisp/progmodes/flymake.el (flymake-ins-after) (flymake-set-at, flymake-er-make-er, flymake-er-get-line) (flymake-er-get-line-err-info-list, flymake-ler-set-file) (flymake-ler-set-full-file, flymake-ler-set-line) (flymake-get-line-err-count, flymake-get-err-count) (flymake-highlight-err-lines, flymake-overlay-p) (flymake-make-overlay, flymake-region-has-flymake-overlays) (flymake-find-err-info) (flymake-line-err-info-is-less-or-equal) (flymake-add-line-err-info, flymake-add-err-info) (flymake-get-first-err-line-no) (flymake-get-last-err-line-no, flymake-get-next-err-line-no) (flymake-get-prev-err-line-no, flymake-skip-whitespace) (flymake-goto-line, flymake-goto-next-error) (flymake-goto-prev-error, flymake-patch-err-text): Delete functions no longer used. (flymake-goto-next-error, flymake-goto-prev-error): Rewrite. (flymake-report): Rewrite. (flymake-popup-current-error-menu): Rewrite. (flymake--highlight-line): Rename from flymake-highlight-line. Call `flymake--place-overlay. (flymake--place-overlay): New function. (flymake-ler-errorp): New predicate. (flymake-ler): Simplify. (flymake-error): Rename from flymake-errline. (flymake-warning): Rename from flymake-warnline. (flymake-warnline, flymake-errline): Obsoletion aliases. * test/lisp/progmodes/flymake-tests.el (warning-predicate-rx-gcc) (warning-predicate-function-gcc, warning-predicate-rx-perl) (warning-predicate-function-perl): Use face `flymake-warning'. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index bdfdf9afcb..0395fff322 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -395,33 +395,41 @@ Create parent directories as needed." (defun flymake-proc--diagnostics-for-pattern (proc pattern) (condition-case err - (pcase-let ((`(,regexp ,file-idx ,line-idx ,_col-idx ,message-idx) + (pcase-let ((`(,regexp ,file-idx ,line-idx ,col-idx ,message-idx) pattern) (retval)) (while (search-forward-regexp regexp nil t) - (let ((fname (and file-idx (match-string file-idx))) - (message (and message-idx (match-string message-idx))) - (line-number (and line-idx (string-to-number - (match-string line-idx))))) + (let* ((fname (and file-idx (match-string file-idx))) + (message (and message-idx (match-string message-idx))) + (line-string (and line-idx (match-string line-idx))) + (line-number (and line-string + (string-to-number line-string))) + (col-string (and col-idx (match-string col-idx))) + (col-number (and col-string + (string-to-number col-string)))) (with-current-buffer (process-buffer proc) - (push (flymake-ler-make-ler - fname - line-number - (if (and message - (cond ((stringp flymake-proc-warning-predicate) - (string-match flymake-proc-warning-predicate - message)) - ((functionp flymake-proc-warning-predicate) - (funcall flymake-proc-warning-predicate - message)))) - "w" - "e") - message - (and fname - (funcall (flymake-proc--get-real-file-name-function - fname) - fname))) - retval)))) + (push + (flymake-ler-make + :file fname + :line line-number + :col col-number + :type (if (and + message + (cond ((stringp flymake-proc-warning-predicate) + (string-match flymake-proc-warning-predicate + message)) + ((functionp flymake-proc-warning-predicate) + (funcall flymake-proc-warning-predicate + message)))) + "w" + "e") + :text message + :full-file (and fname + (funcall + (flymake-proc--get-real-file-name-function + fname) + fname))) + retval)))) retval) (error (flymake-log 1 "Error parsing process output for pattern %s: %s" diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index a360306503..f00915a684 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -136,223 +136,119 @@ are the string substitutions (see the function `format')." (let* ((msg (apply #'format-message text args))) (message "%s" msg)))) -(defun flymake-ins-after (list pos val) - "Insert VAL into LIST after position POS. -POS counts from zero." - (let ((tmp (copy-sequence list))) - (setcdr (nthcdr pos tmp) (cons val (nthcdr (1+ pos) tmp))) - tmp)) - -(defun flymake-set-at (list pos val) - "Set VAL at position POS in LIST. -POS counts from zero." - (let ((tmp (copy-sequence list))) - (setcar (nthcdr pos tmp) val) - tmp)) - -(defun flymake-er-make-er (line-no line-err-info-list) - (list line-no line-err-info-list)) - -(defun flymake-er-get-line (err-info) - (nth 0 err-info)) - -(defun flymake-er-get-line-err-info-list (err-info) - (nth 1 err-info)) - (cl-defstruct (flymake-ler - (:constructor nil) - (:constructor flymake-ler-make-ler (file line type text &optional full-file))) - file line type text full-file) - -(defun flymake-ler-set-file (line-err-info file) - (flymake-ler-make-ler file - (flymake-ler-line line-err-info) - (flymake-ler-type line-err-info) - (flymake-ler-text line-err-info) - (flymake-ler-full-file line-err-info))) - -(defun flymake-ler-set-full-file (line-err-info full-file) - (flymake-ler-make-ler (flymake-ler-file line-err-info) - (flymake-ler-line line-err-info) - (flymake-ler-type line-err-info) - (flymake-ler-text line-err-info) - full-file)) - -(defun flymake-ler-set-line (line-err-info line) - (flymake-ler-make-ler (flymake-ler-file line-err-info) - line - (flymake-ler-type line-err-info) - (flymake-ler-text line-err-info) - (flymake-ler-full-file line-err-info))) - -(defun flymake-get-line-err-count (line-err-info-list type) - "Return number of errors of specified TYPE. -Value of TYPE is either \"e\" or \"w\"." - (let* ((idx 0) - (count (length line-err-info-list)) - (err-count 0)) - - (while (< idx count) - (when (equal type (flymake-ler-type (nth idx line-err-info-list))) - (setq err-count (1+ err-count))) - (setq idx (1+ idx))) - err-count)) - -(defun flymake-get-err-count (err-info-list type) - "Return number of errors of specified TYPE for ERR-INFO-LIST." - (let* ((idx 0) - (count (length err-info-list)) - (err-count 0)) - (while (< idx count) - (setq err-count (+ err-count (flymake-get-line-err-count (nth 1 (nth idx err-info-list)) type))) - (setq idx (1+ idx))) - err-count)) - -(defun flymake-highlight-err-lines (err-info-list) - "Highlight error lines in BUFFER using info from ERR-INFO-LIST." - (save-excursion - (dolist (err err-info-list) - (flymake-highlight-line (car err) (nth 1 err))))) - -(defun flymake-overlay-p (ov) - "Determine whether overlay OV was created by flymake." - (and (overlayp ov) (overlay-get ov 'flymake-overlay))) - -(defun flymake-make-overlay (beg end tooltip-text face bitmap) - "Allocate a flymake overlay in range BEG and END." - (when (not (flymake-region-has-flymake-overlays beg end)) - (let ((ov (make-overlay beg end nil t)) - (fringe (and flymake-fringe-indicator-position - (propertize "!" 'display - (cons flymake-fringe-indicator-position - (if (listp bitmap) - bitmap - (list bitmap))))))) - (overlay-put ov 'face face) - (overlay-put ov 'help-echo tooltip-text) + (:constructor flymake-ler-make)) + file line col type text full-file) + +(defun flymake-ler-errorp (diag) + "Tell if DIAG is a flymake error or something else" + (string= "e" (flymake-ler-type diag))) + +(defun flymake--place-overlay (beg end tooltip-text face bitmap diag) + "Place a flymake overlay in range BEG and END. +Make a flymake fringe overlay for the line at BEG, if needed." + (let* ((fringe-overlay + (or (cl-find-if (lambda (ov) + (overlay-get ov 'flymake--fringe-overlay)) + (overlays-at beg)) + (make-overlay beg (1+ beg))))) + (let ((ov fringe-overlay)) + (overlay-put ov 'help-echo + (concat tooltip-text "\n" + (overlay-get ov 'help-echo))) + (overlay-put ov 'before-string + (and flymake-fringe-indicator-position + (propertize "!" 'display + (cons flymake-fringe-indicator-position + (if (listp bitmap) + bitmap + (list bitmap)) + )))) + (overlay-put ov 'evaporate t) (overlay-put ov 'flymake-overlay t) (overlay-put ov 'priority 100) - (overlay-put ov 'evaporate t) - (overlay-put ov 'before-string fringe) - ;;+(flymake-log 3 "created overlay %s" ov) ov) - (flymake-log 3 "created an overlay at (%d-%d)" beg end))) + (let ((ov (make-overlay beg end))) + (overlay-put ov 'face face) + (overlay-put ov 'help-echo + (concat tooltip-text "\n" + (overlay-get ov 'help-echo))) + (overlay-put ov 'evaporate t) + (overlay-put ov 'flymake-overlay t) + (overlay-put ov 'flymake--diagnostic diag)) + (cl-loop for i from 0 + for overlay in + (flymake--overlays + 'flymake--diagnostic + (lambda (_ov1 ov2) + (flymake-ler-errorp + (overlay-get ov2 'flymake--diagnostic))) + beg end) + do (overlay-put overlay 'priority (+ 100 i))))) + +(defun flymake--overlays (&optional filter compare beg end) + (cl-remove-if-not + (lambda (ov) + (and (overlay-get ov 'flymake-overlay) + (or (not filter) + (cond ((functionp filter) (funcall filter ov)) + ((symbolp filter) (overlay-get ov filter)))))) + (save-restriction + (widen) + (let ((ovs (overlays-in (or beg (point-min)) + (or end (point-max))))) + (if compare + (cl-sort ovs + compare + :key #'overlay-start) + ovs))))) (defun flymake-delete-own-overlays () "Delete all flymake overlays in BUFFER." - (dolist (ol (overlays-in (point-min) (point-max))) - (when (flymake-overlay-p ol) - (delete-overlay ol) - ;;+(flymake-log 3 "deleted overlay %s" ol) - ))) - -(defun flymake-region-has-flymake-overlays (beg end) - "Check if region specified by BEG and END has overlay. -Return t if it has at least one flymake overlay, nil if no overlay." - (let ((ov (overlays-in beg end)) - (has-flymake-overlays nil)) - (while (consp ov) - (when (flymake-overlay-p (car ov)) - (setq has-flymake-overlays t)) - (setq ov (cdr ov))) - has-flymake-overlays)) - -(defface flymake-errline + (mapc #'delete-overlay (flymake--overlays))) + +(defface flymake-error '((((supports :underline (:style wave))) :underline (:style wave :color "Red1")) (t :inherit error)) - "Face used for marking error lines." + "Face used for marking error regions." :version "24.4" :group 'flymake) -(defface flymake-warnline +(defface flymake-warning '((((supports :underline (:style wave))) :underline (:style wave :color "DarkOrange")) (t :inherit warning)) - "Face used for marking warning lines." + "Face used for marking warning regions." :version "24.4" :group 'flymake) -(defun flymake-highlight-line (line-no line-err-info-list) - "Highlight line LINE-NO in current buffer. -Perhaps use text from LINE-ERR-INFO-LIST to enhance highlighting." - (goto-char (point-min)) - (forward-line (1- line-no)) - (pcase-let* ((beg (progn (back-to-indentation) (point))) - (end (progn - (end-of-line) - (skip-chars-backward " \t\f\t\n" beg) - (if (eq (point) beg) - (line-beginning-position 2) - (point)))) - (tooltip-text (mapconcat #'flymake-ler-text line-err-info-list "\n")) - (`(,face ,bitmap) - (if (> (flymake-get-line-err-count line-err-info-list "e") 0) - (list 'flymake-errline flymake-error-bitmap) - (list 'flymake-warnline flymake-warning-bitmap)))) - (flymake-make-overlay beg end tooltip-text face bitmap))) - -(defun flymake-find-err-info (err-info-list line-no) - "Find (line-err-info-list pos) for specified LINE-NO." - (if err-info-list - (let* ((line-err-info-list nil) - (pos 0) - (count (length err-info-list))) - - (while (and (< pos count) (< (car (nth pos err-info-list)) line-no)) - (setq pos (1+ pos))) - (when (and (< pos count) (equal (car (nth pos err-info-list)) line-no)) - (setq line-err-info-list (flymake-er-get-line-err-info-list (nth pos err-info-list)))) - (list line-err-info-list pos)) - '(nil 0))) - -(defun flymake-line-err-info-is-less-or-equal (line-one line-two) - (or (string< (flymake-ler-type line-one) (flymake-ler-type line-two)) - (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two)) - (not (flymake-ler-file line-one)) (flymake-ler-file line-two)) - (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two)) - (or (and (flymake-ler-file line-one) (flymake-ler-file line-two)) - (and (not (flymake-ler-file line-one)) (not (flymake-ler-file line-two))))))) - -(defun flymake-add-line-err-info (line-err-info-list line-err-info) - "Update LINE-ERR-INFO-LIST with the error LINE-ERR-INFO. -For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'. -The new element is inserted in the proper position, according to -the predicate `flymake-line-err-info-is-less-or-equal'. -The updated value of LINE-ERR-INFO-LIST is returned." - (if (not line-err-info-list) - (list line-err-info) - (let* ((count (length line-err-info-list)) - (idx 0)) - (while (and (< idx count) (flymake-line-err-info-is-less-or-equal (nth idx line-err-info-list) line-err-info)) - (setq idx (1+ idx))) - (cond ((equal 0 idx) (setq line-err-info-list (cons line-err-info line-err-info-list))) - (t (setq line-err-info-list (flymake-ins-after line-err-info-list (1- idx) line-err-info)))) - line-err-info-list))) - -(defun flymake-add-err-info (err-info-list line-err-info) - "Update ERR-INFO-LIST with the error LINE-ERR-INFO, preserving sort order. -Returns the updated value of ERR-INFO-LIST. -For the format of ERR-INFO-LIST, see `flymake-err-info'. -For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." - (let* ((line-no (if (flymake-ler-file line-err-info) 1 (flymake-ler-line line-err-info))) - (info-and-pos (flymake-find-err-info err-info-list line-no)) - (exists (car info-and-pos)) - (pos (nth 1 info-and-pos)) - (line-err-info-list nil) - (err-info nil)) - - (if exists - (setq line-err-info-list (flymake-er-get-line-err-info-list (car (nthcdr pos err-info-list))))) - (setq line-err-info-list (flymake-add-line-err-info line-err-info-list line-err-info)) - - (setq err-info (flymake-er-make-er line-no line-err-info-list)) - (cond (exists (setq err-info-list (flymake-set-at err-info-list pos err-info))) - ((equal 0 pos) (setq err-info-list (cons err-info err-info-list))) - (t (setq err-info-list (flymake-ins-after err-info-list (1- pos) err-info)))) - err-info-list)) +(define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1") +(define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1") + +(defun flymake--highlight-line (diagnostic) + "Highlight buffer with info in DIAGNOSTIC. +Reuse overlays if necessary +Perhaps use the message text as a hint to enhance highlighting." + (save-excursion + (goto-char (point-min)) + (let ((line-no (flymake-ler-line diagnostic))) + (forward-line (1- line-no)) + (pcase-let* ((beg (progn (back-to-indentation) (point))) + (end (progn + (end-of-line) + (skip-chars-backward " \t\f\t\n" beg) + (if (eq (point) beg) + (line-beginning-position 2) + (point)))) + (tooltip-text (flymake-ler-text diagnostic)) + (`(,face ,bitmap) + (if (equal "e" (flymake-ler-type diagnostic)) + (list 'flymake-errline flymake-error-bitmap) + (list 'flymake-warnline flymake-warning-bitmap)))) + (flymake--place-overlay beg end tooltip-text face bitmap diagnostic))))) (defvar-local flymake-is-running nil "If t, flymake syntax check process is running for the current buffer.") @@ -368,7 +264,7 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (setq flymake-last-change-time nil) (flymake-log 3 "starting syntax check as more than 1 second passed since last change") - (flymake-start-syntax-check))))) + (flymake--start-syntax-check))))) (define-obsolete-function-alias 'flymake-display-err-menu-for-current-line 'flymake-popup-current-error-menu "24.4") @@ -376,38 +272,36 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (defun flymake-popup-current-error-menu (&optional event) "Pop up a menu with errors/warnings for current line." (interactive (list last-nonmenu-event)) - (let* ((line-no (line-number-at-pos)) - (errors (or (car (flymake-find-err-info flymake-err-info line-no)) - (user-error "No errors for current line"))) - (menu (mapcar (lambda (x) - (if (flymake-ler-file x) - (cons (format "%s - %s(%d)" - (flymake-ler-text x) - (flymake-ler-file x) - (flymake-ler-line x)) - x) - (list (flymake-ler-text x)))) - errors)) + (let* ((diag-overlays (or + (flymake--overlays 'flymake--diagnostic nil + (line-beginning-position) + (line-end-position)) + (user-error "No flymake problem for current line"))) + (menu (mapcar (lambda (ov) + (let ((diag (overlay-get ov 'flymake--diagnostic))) + (cons (format "%s - %s(%s)" + (flymake-ler-text diag) + (or (flymake-ler-file diag) + "(no file)") + (or (flymake-ler-line diag) + "?")) + ov))) + diag-overlays)) (event (if (mouse-event-p event) event (list 'mouse-1 (posn-at-point)))) - (title (format "Line %d: %d error(s), %d warning(s)" - line-no - (flymake-get-line-err-count errors "e") - (flymake-get-line-err-count errors "w"))) + (diagnostics (mapcar (lambda (ov) (overlay-get ov 'flymake--diagnostic)) + diag-overlays)) + (title (format "Line %d: %d error(s), %d other(s)" + (line-number-at-pos) + (cl-count-if #'flymake-ler-errorp diagnostics) + (cl-count-if-not #'flymake-ler-errorp diagnostics))) (choice (x-popup-menu event (list title (cons "" menu))))) (flymake-log 3 "choice=%s" choice) - (when choice - (flymake-goto-file-and-line (flymake-ler-full-file choice) - (flymake-ler-line choice))))) - -(defun flymake-goto-file-and-line (file line) - "Try to get buffer for FILE and goto line LINE in it." - (if (not (file-exists-p file)) - (flymake-log 1 "File %s does not exist" file) - (find-file file) - (goto-char (point-min)) - (forward-line (1- line)))) + ;; FIXME: What is the point of going to the problem locus if we're + ;; certainly already there? + ;; + (when choice (goto-char (overlay-start choice))))) ;; flymake minor mode declarations (defvar-local flymake-mode-line nil) @@ -452,33 +346,19 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (defun flymake-report (diagnostics) (save-restriction (widen) - (mapc #'flymake--fix-line-numbers diagnostics) (flymake-delete-own-overlays) - (setq flymake-err-info - (cl-loop with grouped - for diag in diagnostics - for line = (flymake-ler-line diag) - for existing = (assoc line grouped) - if existing - do (setcdr existing - (list diag (cl-second existing))) - else - do (push (list line (list diag)) grouped) - finally (return grouped))) - (flymake-highlight-err-lines flymake-err-info) - (let ((err-count (flymake-get-err-count flymake-err-info "e")) - (warn-count (flymake-get-err-count flymake-err-info "w"))) + (mapc #'flymake--fix-line-numbers diagnostics) + (mapc #'flymake--highlight-line diagnostics) + (let ((err-count (cl-count-if #'flymake-ler-errorp diagnostics)) + (warn-count (cl-count-if-not #'flymake-ler-errorp diagnostics))) (when flymake-check-start-time - (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)" + (flymake-log 2 "%s: %d error(s), %d other(s) in %.2f second(s)" (buffer-name) err-count warn-count (- (float-time) flymake-check-start-time))) - (if (and (equal 0 err-count) (equal 0 warn-count)) + (if (null diagnostics) (flymake-report-status "" "") (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) -(defvar-local flymake--backend nil - "The currently active backend selected by `flymake-mode'") - ;;;###autoload (define-minor-mode flymake-mode nil :group 'flymake :lighter flymake-mode-line @@ -507,7 +387,7 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." ;; trying if the directory is read-only (bug#8954). (file-writable-p (file-name-directory buffer-file-name))) (with-demoted-errors - (flymake-start-syntax-check)))))) + (flymake--start-syntax-check)))))) ;; Turning the mode OFF. (t @@ -542,14 +422,14 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (let((new-text (buffer-substring start stop))) (when (and flymake-start-syntax-check-on-newline (equal new-text "\n")) (flymake-log 3 "starting syntax check as new-line has been seen") - (flymake-start-syntax-check)) + (flymake--start-syntax-check)) (setq flymake-last-change-time (float-time)))) (defun flymake-after-save-hook () (if (local-variable-p 'flymake-mode (current-buffer)) ; (???) other way to determine whether flymake is active in buffer being saved? (progn (flymake-log 3 "starting syntax check as buffer was saved") - (flymake-start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???) + (flymake--start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???) (defun flymake-kill-buffer-hook () (when flymake-timer @@ -558,85 +438,41 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." ;;;###autoload (defun flymake-find-file-hook () - ;;+(when flymake-start-syntax-check-on-find-file - ;;+ (flymake-log 3 "starting syntax check on file open") - ;;+ (flymake-start-syntax-check) - ;;+) (when (and (not (local-variable-p 'flymake-mode (current-buffer))) (flymake-can-syntax-check-file buffer-file-name)) (flymake-mode) (flymake-log 3 "automatically turned ON flymake mode"))) -(defun flymake-get-first-err-line-no (err-info-list) - "Return first line with error." - (when err-info-list - (flymake-er-get-line (car err-info-list)))) - -(defun flymake-get-last-err-line-no (err-info-list) - "Return last line with error." - (when err-info-list - (flymake-er-get-line (nth (1- (length err-info-list)) err-info-list)))) - -(defun flymake-get-next-err-line-no (err-info-list line-no) - "Return next line with error." - (when err-info-list - (let* ((count (length err-info-list)) - (idx 0)) - (while (and (< idx count) (>= line-no (flymake-er-get-line (nth idx err-info-list)))) - (setq idx (1+ idx))) - (if (< idx count) - (flymake-er-get-line (nth idx err-info-list)))))) - -(defun flymake-get-prev-err-line-no (err-info-list line-no) - "Return previous line with error." - (when err-info-list - (let* ((count (length err-info-list))) - (while (and (> count 0) (<= line-no (flymake-er-get-line (nth (1- count) err-info-list)))) - (setq count (1- count))) - (if (> count 0) - (flymake-er-get-line (nth (1- count) err-info-list)))))) - -(defun flymake-skip-whitespace () - "Move forward until non-whitespace is reached." - (while (looking-at "[ \t]") - (forward-char))) - -(defun flymake-goto-line (line-no) - "Go to line LINE-NO, then skip whitespace." - (goto-char (point-min)) - (forward-line (1- line-no)) - (flymake-skip-whitespace)) - -(defun flymake-goto-next-error () - "Go to next error in err ring." - (interactive) - (let ((line-no (flymake-get-next-err-line-no flymake-err-info (line-number-at-pos)))) - (when (not line-no) - (setq line-no (flymake-get-first-err-line-no flymake-err-info)) - (flymake-log 1 "passed end of file")) - (if line-no - (flymake-goto-line line-no) - (flymake-log 1 "no errors in current buffer")))) - -(defun flymake-goto-prev-error () - "Go to previous error in err ring." - (interactive) - (let ((line-no (flymake-get-prev-err-line-no flymake-err-info (line-number-at-pos)))) - (when (not line-no) - (setq line-no (flymake-get-last-err-line-no flymake-err-info)) - (flymake-log 1 "passed beginning of file")) - (if line-no - (flymake-goto-line line-no) - (flymake-log 1 "no errors in current buffer")))) - -(defun flymake-patch-err-text (string) - (if (string-match "^[\n\t :0-9]*\\(.*\\)$" string) - (match-string 1 string) - string)) +(defun flymake-goto-next-error (&optional n interactive) + "Go to next, or Nth next, flymake error in buffer." + (interactive (list 1 t)) + (let* ((n (or n 1)) + (ovs (flymake--overlays 'flymake--diagnostic + (if (cl-plusp n) #'< #'>))) + (chain (cl-member-if (lambda (ov) + (if (cl-plusp n) + (> (overlay-start ov) + (point)) + (< (overlay-start ov) + (point)))) + ovs)) + (target (nth (1- n) chain))) + (if target + (goto-char (overlay-start target)) + (when interactive + (user-error "No more flymake errors"))))) + +(defun flymake-goto-prev-error (&optional n interactive) + "Go to previous, or Nth previous, flymake error in buffer." + (interactive (list 1 t)) + (flymake-goto-next-error (- (or n 1)) interactive)) (provide 'flymake) -(declare-function flymake-start-syntax-check "flymake-proc") +(defun flymake--start-syntax-check () + (flymake-proc-start-syntax-check)) + +(declare-function flymake-proc-start-syntax-check "flymake-proc") (declare-function flymake-can-syntax-check-file "flymake-proc") (require 'flymake-proc) diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index dda72a35d2..a018f11efd 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -51,26 +51,26 @@ (ert-deftest warning-predicate-rx-gcc () "Test GCC warning via regexp predicate." (skip-unless (and (executable-find "gcc") (executable-find "make"))) - (should (eq 'flymake-warnline + (should (eq 'flymake-warning (flymake-tests--current-face "test.c" "^[Ww]arning")))) (ert-deftest warning-predicate-function-gcc () "Test GCC warning via function predicate." (skip-unless (and (executable-find "gcc") (executable-find "make"))) - (should (eq 'flymake-warnline + (should (eq 'flymake-warning (flymake-tests--current-face "test.c" (lambda (msg) (string-match "^[Ww]arning" msg)))))) (ert-deftest warning-predicate-rx-perl () "Test perl warning via regular expression predicate." (skip-unless (executable-find "perl")) - (should (eq 'flymake-warnline + (should (eq 'flymake-warning (flymake-tests--current-face "test.pl" "^Scalar value")))) (ert-deftest warning-predicate-function-perl () "Test perl warning via function predicate." (skip-unless (executable-find "perl")) - (should (eq 'flymake-warnline + (should (eq 'flymake-warning (flymake-tests--current-face "test.pl" (lambda (msg) (string-match "^Scalar value" msg)))))) commit bb8b663d327e79eb271b467317c283cc884106ac Author: João Távora Date: Wed Aug 23 23:18:05 2017 +0100 Move symbols in flymake-proc.el to separate namespace Every symbol in this flymake now starts with the prefix flymake-proc-. Make obsolete aliases for (almost?) every symbol. Furthermore, many flymake-proc.el symbols are prefixed with "flymake-proc--", that is they were considered internal. Some customization variables, interactive functions, and other symbols considered useful to user customizations or third-party libraries are considered "public" or "external" and so use a "flymake-proc-" prefix. * lisp/progmodes/flymake-proc.el: Every symbol renamed. * test/lisp/progmodes/flymake-tests.el (flymake-tests--current-face): Use flymake-proc-warning-predicate, not flymake-warning-predicate. * lisp/progmodes/flymake-proc.el (flymake-proc--get-project-include-dirs-function) (flymake-proc--get-project-include-dirs-imp) (flymake-proc--get-include-dirs-dot) (flymake-proc--get-tex-args) (flymake-proc--find-make-buildfile) (flymake-proc--get-syntax-check-program-args) (flymake-proc--init-create-temp-source-and-master-buffer-copy) (flymake-proc--init-find-buildfile-dir) (flymake-proc--get-full-nonpatched-file-name) (flymake-proc--get-full-patched-file-name) (flymake-proc--base-dir, flymake-proc--temp-master-file-name) (flymake-proc--master-file-name) (flymake-proc--temp-source-file-name) (flymake-proc--delete-temp-directory) (flymake-proc--kill-process) (flymake-proc--start-syntax-check-process) (flymake-proc--compilation-is-running) (flymake-proc--safe-delete-directory) (flymake-proc--safe-delete-file) (flymake-proc--get-program-dir) (flymake-proc--restore-formatting) (flymake-proc--clear-project-include-dirs-cache) (flymake-proc--project-include-dirs-cache) (flymake-proc--get-system-include-dirs) (flymake-proc--get-project-include-dirs) (flymake-proc--add-project-include-dirs-to-cache) (flymake-proc--get-project-include-dirs-from-cache) (flymake-proc--post-syntax-check) (flymake-proc--process-sentinel) (flymake-proc--process-filter) (flymake-proc--create-master-file) (flymake-proc--find-buffer-for-file) (flymake-proc--copy-buffer-to-temp-buffer) (flymake-proc--read-file-to-temp-buffer) (flymake-proc--save-buffer-in-file) (flymake-proc--replace-region, flymake-proc--check-include) (flymake-proc--check-patch-master-file-buffer) (flymake-proc--master-file-compare) (flymake-proc--find-possible-master-files) (flymake-proc--included-file-name, flymake-proc--same-files) (flymake-proc--fix-file-name, flymake-proc--find-buildfile) (flymake-proc--clear-buildfile-cache) (flymake-proc--add-buildfile-to-cache) (flymake-proc--get-buildfile-from-cache) (flymake-proc--find-buildfile-cache) (flymake-proc--get-real-file-name-function) (flymake-proc--get-cleanup-function) (flymake-proc--get-init-function) (flymake-proc--get-file-name-mode-and-masks) (flymake-proc--processes): Rename to internal symbol from flymake-proc- version. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index dae118eb4f..bdfdf9afcb 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -39,40 +39,40 @@ (require 'flymake) -(defcustom flymake-compilation-prevents-syntax-check t +(defcustom flymake-proc-compilation-prevents-syntax-check t "If non-nil, don't start syntax check if compilation is running." :group 'flymake :type 'boolean) -(defcustom flymake-xml-program +(defcustom flymake-proc-xml-program (if (executable-find "xmlstarlet") "xmlstarlet" "xml") "Program to use for XML validation." :type 'file :group 'flymake :version "24.4") -(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest") +(defcustom flymake-proc-master-file-dirs '("." "./src" "./UnitTest") "Dirs where to look for master files." :group 'flymake :type '(repeat (string))) -(defcustom flymake-master-file-count-limit 32 +(defcustom flymake-proc-master-file-count-limit 32 "Max number of master files to check." :group 'flymake :type 'integer) -(defcustom flymake-allowed-file-name-masks - '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-simple-make-init) - ("\\.xml\\'" flymake-xml-init) - ("\\.html?\\'" flymake-xml-init) - ("\\.cs\\'" flymake-simple-make-init) - ("\\.p[ml]\\'" flymake-perl-init) - ("\\.php[345]?\\'" flymake-php-init) - ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup) - ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup) - ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup) - ("\\.tex\\'" flymake-simple-tex-init) - ("\\.idl\\'" flymake-simple-make-init) +(defcustom flymake-proc-allowed-file-name-masks + '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-proc-simple-make-init) + ("\\.xml\\'" flymake-proc-xml-init) + ("\\.html?\\'" flymake-proc-xml-init) + ("\\.cs\\'" flymake-proc-simple-make-init) + ("\\.p[ml]\\'" flymake-proc-perl-init) + ("\\.php[345]?\\'" flymake-proc-php-init) + ("\\.h\\'" flymake-proc-master-make-header-init flymake-proc-master-cleanup) + ("\\.java\\'" flymake-proc-simple-make-java-init flymake-proc-simple-java-cleanup) + ("[0-9]+\\.tex\\'" flymake-proc-master-tex-init flymake-proc-master-cleanup) + ("\\.tex\\'" flymake-proc-simple-tex-init) + ("\\.idl\\'" flymake-proc-simple-make-init) ;; ("\\.cpp\\'" 1) ;; ("\\.java\\'" 3) ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'") @@ -88,28 +88,28 @@ This is an alist with elements of the form: REGEXP INIT [CLEANUP [NAME]] REGEXP is a regular expression that matches a file name. INIT is the init function to use. -CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'. -NAME is the file name function to use, default `flymake-get-real-file-name'." +CLEANUP is the cleanup function to use, default `flymake-proc-simple-cleanup'. +NAME is the file name function to use, default `flymake-proc-get-real-file-name'." :group 'flymake :type '(alist :key-type (regexp :tag "File regexp") :value-type (list :tag "Handler functions" (function :tag "Init function") (choice :tag "Cleanup function" - (const :tag "flymake-simple-cleanup" nil) + (const :tag "flymake-proc-simple-cleanup" nil) function) (choice :tag "Name function" - (const :tag "flymake-get-real-file-name" nil) + (const :tag "flymake-proc-get-real-file-name" nil) function)))) -(defvar flymake-processes nil +(defvar flymake-proc--processes nil "List of currently active flymake processes.") -(defun flymake-get-file-name-mode-and-masks (file-name) - "Return the corresponding entry from `flymake-allowed-file-name-masks'." +(defun flymake-proc--get-file-name-mode-and-masks (file-name) + "Return the corresponding entry from `flymake-proc-allowed-file-name-masks'." (unless (stringp file-name) (error "Invalid file-name")) - (let ((fnm flymake-allowed-file-name-masks) + (let ((fnm flymake-proc-allowed-file-name-masks) (mode-and-masks nil)) (while (and (not mode-and-masks) fnm) (if (string-match (car (car fnm)) file-name) @@ -118,58 +118,58 @@ NAME is the file name function to use, default `flymake-get-real-file-name'." (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks)) mode-and-masks)) -(defun flymake-can-syntax-check-file (file-name) +(defun flymake-proc-can-syntax-check-file (file-name) "Determine whether we can syntax check FILE-NAME. Return nil if we cannot, non-nil if we can." - (if (flymake-get-init-function file-name) t nil)) + (if (flymake-proc-get-init-function file-name) t nil)) -(defun flymake-get-init-function (file-name) +(defun flymake-proc--get-init-function (file-name) "Return init function to be used for the file." - (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name)))) + (let* ((init-f (nth 0 (flymake-proc--get-file-name-mode-and-masks file-name)))) ;;(flymake-log 0 "calling %s" init-f) ;;(funcall init-f (current-buffer)) init-f)) -(defun flymake-get-cleanup-function (file-name) +(defun flymake-proc--get-cleanup-function (file-name) "Return cleanup function to be used for the file." - (or (nth 1 (flymake-get-file-name-mode-and-masks file-name)) - 'flymake-simple-cleanup)) + (or (nth 1 (flymake-proc--get-file-name-mode-and-masks file-name)) + 'flymake-proc-simple-cleanup)) -(defun flymake-get-real-file-name-function (file-name) - (or (nth 2 (flymake-get-file-name-mode-and-masks file-name)) - 'flymake-get-real-file-name)) +(defun flymake-proc--get-real-file-name-function (file-name) + (or (nth 2 (flymake-proc--get-file-name-mode-and-masks file-name)) + 'flymake-proc-get-real-file-name)) -(defvar flymake-find-buildfile-cache (make-hash-table :test #'equal)) +(defvar flymake-proc--find-buildfile-cache (make-hash-table :test #'equal)) -(defun flymake-get-buildfile-from-cache (dir-name) +(defun flymake-proc--get-buildfile-from-cache (dir-name) "Look up DIR-NAME in cache and return its associated value. If DIR-NAME is not found, return nil." - (gethash dir-name flymake-find-buildfile-cache)) + (gethash dir-name flymake-proc--find-buildfile-cache)) -(defun flymake-add-buildfile-to-cache (dir-name buildfile) +(defun flymake-proc--add-buildfile-to-cache (dir-name buildfile) "Associate DIR-NAME with BUILDFILE in the buildfile cache." - (puthash dir-name buildfile flymake-find-buildfile-cache)) + (puthash dir-name buildfile flymake-proc--find-buildfile-cache)) -(defun flymake-clear-buildfile-cache () +(defun flymake-proc--clear-buildfile-cache () "Clear the buildfile cache." - (clrhash flymake-find-buildfile-cache)) + (clrhash flymake-proc--find-buildfile-cache)) -(defun flymake-find-buildfile (buildfile-name source-dir-name) +(defun flymake-proc--find-buildfile (buildfile-name source-dir-name) "Find buildfile starting from current directory. Buildfile includes Makefile, build.xml etc. Return its file name if found, or nil if not found." - (or (flymake-get-buildfile-from-cache source-dir-name) + (or (flymake-proc--get-buildfile-from-cache source-dir-name) (let* ((file (locate-dominating-file source-dir-name buildfile-name))) (if file (progn (flymake-log 3 "found buildfile at %s" file) - (flymake-add-buildfile-to-cache source-dir-name file) + (flymake-proc--add-buildfile-to-cache source-dir-name file) file) (progn (flymake-log 3 "buildfile for %s not found" source-dir-name) nil))))) -(defun flymake-fix-file-name (name) +(defun flymake-proc--fix-file-name (name) "Replace all occurrences of `\\' with `/'." (when name (setq name (expand-file-name name)) @@ -177,16 +177,16 @@ Return its file name if found, or nil if not found." (setq name (directory-file-name name)) name)) -(defun flymake-same-files (file-name-one file-name-two) +(defun flymake-proc--same-files (file-name-one file-name-two) "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file. Return t if so, nil if not." - (equal (flymake-fix-file-name file-name-one) - (flymake-fix-file-name file-name-two))) + (equal (flymake-proc--fix-file-name file-name-one) + (flymake-proc--fix-file-name file-name-two))) ;; This is bound dynamically to pass a parameter to a sort predicate below -(defvar flymake-included-file-name) +(defvar flymake-proc--included-file-name) -(defun flymake-find-possible-master-files (file-name master-file-dirs masks) +(defun flymake-proc--find-possible-master-files (file-name master-file-dirs masks) "Find (by name and location) all possible master files. Name is specified by FILE-NAME and location is specified by @@ -209,35 +209,35 @@ max-level parent dirs. File contents are not checked." (while (and (not done) dir-files) (when (not (file-directory-p (car dir-files))) (setq files (cons (car dir-files) files)) - (when (>= (length files) flymake-master-file-count-limit) - (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit) + (when (>= (length files) flymake-proc-master-file-count-limit) + (flymake-log 3 "master file count limit (%d) reached" flymake-proc-master-file-count-limit) (setq done t))) (setq dir-files (cdr dir-files)))) (setq masks (cdr masks)))) (setq dirs (cdr dirs))) (when files - (let ((flymake-included-file-name (file-name-nondirectory file-name))) - (setq files (sort files 'flymake-master-file-compare)))) + (let ((flymake-proc--included-file-name (file-name-nondirectory file-name))) + (setq files (sort files 'flymake-proc--master-file-compare)))) (flymake-log 3 "found %d possible master file(s)" (length files)) files)) -(defun flymake-master-file-compare (file-one file-two) +(defun flymake-proc--master-file-compare (file-one file-two) "Compare two files specified by FILE-ONE and FILE-TWO. This function is used in sort to move most possible file names to the beginning of the list (File.h -> File.cpp moved to top)." - (and (equal (file-name-sans-extension flymake-included-file-name) + (and (equal (file-name-sans-extension flymake-proc--included-file-name) (file-name-base file-one)) (not (equal file-one file-two)))) -(defvar flymake-check-file-limit 8192 +(defvar flymake-proc-check-file-limit 8192 "Maximum number of chars to look at when checking possible master file. Nil means search the entire file.") -(defun flymake-check-patch-master-file-buffer - (master-file-temp-buffer - master-file-name patched-master-file-name - source-file-name patched-source-file-name - include-dirs regexp) +(defun flymake-proc--check-patch-master-file-buffer + (master-file-temp-buffer + master-file-name patched-master-file-name + source-file-name patched-source-file-name + include-dirs regexp) "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME. If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME instead of SOURCE-FILE-NAME. @@ -251,7 +251,7 @@ instead of reading master file from disk." (source-file-nonext (file-name-sans-extension source-file-nondir)) (found nil) (inc-name nil) - (search-limit flymake-check-file-limit)) + (search-limit flymake-proc-check-file-limit)) (setq regexp (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\"" ;; Hack for tex files, where \include often excludes .tex. @@ -287,18 +287,18 @@ instead of reading master file from disk." inc-name (- (length inc-name) (length source-file-nondir)) nil)) (flymake-log 3 "inc-name=%s" inc-name) - (when (flymake-check-include source-file-name inc-name + (when (flymake-proc--check-include source-file-name inc-name include-dirs) (setq found t) ;; replace-match is not used here as it fails in ;; XEmacs with 'last match not a buffer' error as ;; check-includes calls replace-in-string - (flymake-replace-region + (flymake-proc--replace-region match-beg match-end (file-name-nondirectory patched-source-file-name)))) (forward-line 1))) (when found - (flymake-save-buffer-in-file patched-master-file-name))) + (flymake-proc--save-buffer-in-file patched-master-file-name))) ;;+(flymake-log 3 "killing buffer %s" ;; (buffer-name master-file-temp-buffer)) (kill-buffer master-file-temp-buffer)) @@ -308,7 +308,7 @@ instead of reading master file from disk." found)) ;;; XXX: remove -(defun flymake-replace-region (beg end rep) +(defun flymake-proc--replace-region (beg end rep) "Replace text in BUFFER in region (BEG END) with REP." (save-excursion (goto-char end) @@ -316,14 +316,14 @@ instead of reading master file from disk." (insert rep) (delete-region beg end))) -(defun flymake-read-file-to-temp-buffer (file-name) +(defun flymake-proc--read-file-to-temp-buffer (file-name) "Insert contents of FILE-NAME into newly created temp buffer." (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name)))))) (with-current-buffer temp-buffer (insert-file-contents file-name)) temp-buffer)) -(defun flymake-copy-buffer-to-temp-buffer (buffer) +(defun flymake-proc--copy-buffer-to-temp-buffer (buffer) "Copy contents of BUFFER into newly created temp buffer." (with-current-buffer (get-buffer-create (generate-new-buffer-name @@ -331,13 +331,13 @@ instead of reading master file from disk." (insert-buffer-substring buffer) (current-buffer))) -(defun flymake-check-include (source-file-name inc-name include-dirs) +(defun flymake-proc--check-include (source-file-name inc-name include-dirs) "Check if SOURCE-FILE-NAME can be found in include path. Return t if it can be found via include path using INC-NAME." (if (file-name-absolute-p inc-name) - (flymake-same-files source-file-name inc-name) + (flymake-proc--same-files source-file-name inc-name) (while (and include-dirs - (not (flymake-same-files + (not (flymake-proc--same-files source-file-name (concat (file-name-directory source-file-name) "/" (car include-dirs) @@ -345,17 +345,17 @@ Return t if it can be found via include path using INC-NAME." (setq include-dirs (cdr include-dirs))) include-dirs)) -(defun flymake-find-buffer-for-file (file-name) +(defun flymake-proc--find-buffer-for-file (file-name) "Check if there exists a buffer visiting FILE-NAME. Return t if so, nil if not." (let ((buffer-name (get-file-buffer file-name))) (if buffer-name (get-buffer buffer-name)))) -(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp) +(defun flymake-proc--create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp) "Save SOURCE-FILE-NAME with a different name. Find master file, patch and save it." - (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks)) + (let* ((possible-master-files (flymake-proc--find-possible-master-files source-file-name flymake-proc-master-file-dirs masks)) (master-file-count (length possible-master-files)) (idx 0) (temp-buffer nil) @@ -366,11 +366,11 @@ Find master file, patch and save it." (while (and (not found) (< idx master-file-count)) (setq master-file-name (nth idx possible-master-files)) (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master")) - (if (flymake-find-buffer-for-file master-file-name) - (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name))) - (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name))) + (if (flymake-proc--find-buffer-for-file master-file-name) + (setq temp-buffer (flymake-proc--copy-buffer-to-temp-buffer (flymake-proc--find-buffer-for-file master-file-name))) + (setq temp-buffer (flymake-proc--read-file-to-temp-buffer master-file-name))) (setq found - (flymake-check-patch-master-file-buffer + (flymake-proc--check-patch-master-file-buffer temp-buffer master-file-name patched-master-file-name @@ -386,7 +386,7 @@ Find master file, patch and save it." (file-name-nondirectory source-file-name)) nil)))) -(defun flymake-save-buffer-in-file (file-name) +(defun flymake-proc--save-buffer-in-file (file-name) "Save the entire buffer contents into file FILE-NAME. Create parent directories as needed." (make-directory (file-name-directory file-name) 1) @@ -408,17 +408,17 @@ Create parent directories as needed." fname line-number (if (and message - (cond ((stringp flymake-warning-predicate) - (string-match flymake-warning-predicate + (cond ((stringp flymake-proc-warning-predicate) + (string-match flymake-proc-warning-predicate message)) - ((functionp flymake-warning-predicate) - (funcall flymake-warning-predicate + ((functionp flymake-proc-warning-predicate) + (funcall flymake-proc-warning-predicate message)))) "w" "e") message (and fname - (funcall (flymake-get-real-file-name-function + (funcall (flymake-proc--get-real-file-name-function fname) fname))) retval)))) @@ -428,7 +428,7 @@ Create parent directories as needed." pattern err) nil))) -(defun flymake-process-filter (proc string) +(defun flymake-proc--process-filter (proc string) "Parse STRING and collect diagnostics info." (flymake-log 3 "received %d byte(s) of output from process %d" (length string) (process-id proc)) @@ -452,7 +452,7 @@ Create parent directories as needed." ;; (save-excursion (goto-char unprocessed-mark) - (dolist (pattern flymake-err-line-patterns) + (dolist (pattern flymake-proc-err-line-patterns) (let ((new (flymake-proc--diagnostics-for-pattern proc pattern))) (process-put proc @@ -463,13 +463,13 @@ Create parent directories as needed." (process-put proc 'flymake-proc--unprocessed-mark (point-marker)))))))) -(defun flymake-process-sentinel (process _event) +(defun flymake-proc--process-sentinel (process _event) "Sentinel for syntax check buffers." (when (memq (process-status process) '(signal exit)) (let* ((exit-status (process-exit-status process)) (command (process-command process)) (source-buffer (process-buffer process)) - (cleanup-f (flymake-get-cleanup-function + (cleanup-f (flymake-proc--get-cleanup-function (buffer-file-name source-buffer)))) (flymake-log 2 "process %d exited with code %d" @@ -483,11 +483,11 @@ Create parent directories as needed." (funcall cleanup-f))) (delete-process process) - (setq flymake-processes (delq process flymake-processes)) + (setq flymake-proc--processes (delq process flymake-proc--processes)) (when (buffer-live-p source-buffer) (with-current-buffer source-buffer - (flymake-post-syntax-check + (flymake-proc--post-syntax-check exit-status command (process-get process 'flymake-proc--collected-diagnostics)) (setq flymake-is-running nil)))) @@ -498,7 +498,7 @@ Create parent directories as needed." (with-current-buffer source-buffer (setq flymake-is-running nil)))))))) -(defun flymake-post-syntax-check (exit-status command diagnostics) +(defun flymake-proc--post-syntax-check (exit-status command diagnostics) (if (equal 0 exit-status) (flymake-report diagnostics) (if flymake-check-was-interrupted @@ -509,7 +509,7 @@ Create parent directories as needed." (format "Configuration error has occurred while running %s" command)) (flymake-report diagnostics))))) -(defun flymake-reformat-err-line-patterns-from-compile-el (original-list) +(defun flymake-proc-reformat-err-line-patterns-from-compile-el (original-list) "Grab error line patterns from ORIGINAL-LIST in compile.el format. Convert it to flymake internal format." (let* ((converted-list '())) @@ -529,7 +529,7 @@ Convert it to flymake internal format." (require 'compile) -(defvar flymake-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text +(defvar flymake-proc-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text (append '( ;; MS Visual C++ 6.0 @@ -553,24 +553,24 @@ Convert it to flymake internal format." (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)" 2 4 nil 5)) ;; compilation-error-regexp-alist) - (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist)) + (flymake-proc-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist)) "Patterns for matching error/warning lines. Each pattern has the form \(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). -Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns +Use `flymake-proc-reformat-err-line-patterns-from-compile-el' to add patterns from compile.el") -(define-obsolete-variable-alias 'flymake-warning-re 'flymake-warning-predicate "24.4") -(defvar flymake-warning-predicate "^[wW]arning" +(define-obsolete-variable-alias 'flymake-warning-re 'flymake-proc-warning-predicate "24.4") +(defvar flymake-proc-warning-predicate "^[wW]arning" "Predicate matching against error text to detect a warning. Takes a single argument, the error's text and should return non-nil if it's a warning. Instead of a function, it can also be a regular expression.") -(defun flymake-get-project-include-dirs-imp (basedir) +(defun flymake-proc-get-project-include-dirs-imp (basedir) "Include dirs for the project current file belongs to." - (if (flymake-get-project-include-dirs-from-cache basedir) + (if (flymake-proc--get-project-include-dirs-from-cache basedir) (progn - (flymake-get-project-include-dirs-from-cache basedir)) + (flymake-proc--get-project-include-dirs-from-cache basedir)) ;;else (let* ((command-line (concat "make -C " (shell-quote-argument basedir) @@ -589,53 +589,53 @@ Instead of a function, it can also be a regular expression.") (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines))) (push (replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs)) (setq inc-count (1- inc-count))))) - (flymake-add-project-include-dirs-to-cache basedir inc-dirs) + (flymake-proc--add-project-include-dirs-to-cache basedir inc-dirs) inc-dirs))) -(defvar flymake-get-project-include-dirs-function #'flymake-get-project-include-dirs-imp +(defvar flymake-proc-get-project-include-dirs-function #'flymake-proc-get-project-include-dirs-imp "Function used to get project include dirs, one parameter: basedir name.") -(defun flymake-get-project-include-dirs (basedir) - (funcall flymake-get-project-include-dirs-function basedir)) +(defun flymake-proc--get-project-include-dirs (basedir) + (funcall flymake-proc-get-project-include-dirs-function basedir)) -(defun flymake-get-system-include-dirs () +(defun flymake-proc--get-system-include-dirs () "System include dirs - from the `INCLUDE' env setting." (let* ((includes (getenv "INCLUDE"))) (if includes (split-string includes path-separator t) nil))) -(defvar flymake-project-include-dirs-cache (make-hash-table :test #'equal)) +(defvar flymake-proc--project-include-dirs-cache (make-hash-table :test #'equal)) -(defun flymake-get-project-include-dirs-from-cache (base-dir) - (gethash base-dir flymake-project-include-dirs-cache)) +(defun flymake-proc--get-project-include-dirs-from-cache (base-dir) + (gethash base-dir flymake-proc--project-include-dirs-cache)) -(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs) - (puthash base-dir include-dirs flymake-project-include-dirs-cache)) +(defun flymake-proc--add-project-include-dirs-to-cache (base-dir include-dirs) + (puthash base-dir include-dirs flymake-proc--project-include-dirs-cache)) -(defun flymake-clear-project-include-dirs-cache () - (clrhash flymake-project-include-dirs-cache)) +(defun flymake-proc--clear-project-include-dirs-cache () + (clrhash flymake-proc--project-include-dirs-cache)) -(defun flymake-get-include-dirs (base-dir) +(defun flymake-proc-get-include-dirs (base-dir) "Get dirs to use when resolving local file names." - (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs)))) + (let* ((include-dirs (append '(".") (flymake-proc--get-project-include-dirs base-dir) (flymake-proc--get-system-include-dirs)))) include-dirs)) -;; (defun flymake-restore-formatting () +;; (defun flymake-proc--restore-formatting () ;; "Remove any formatting made by flymake." ;; ) -;; (defun flymake-get-program-dir (buffer) +;; (defun flymake-proc--get-program-dir (buffer) ;; "Get dir to start program in." ;; (unless (bufferp buffer) ;; (error "Invalid buffer")) ;; (with-current-buffer buffer ;; default-directory)) -(defun flymake-safe-delete-file (file-name) +(defun flymake-proc--safe-delete-file (file-name) (when (and file-name (file-exists-p file-name)) (delete-file file-name) (flymake-log 1 "deleted file %s" file-name))) -(defun flymake-safe-delete-directory (dir-name) +(defun flymake-proc--safe-delete-directory (dir-name) (condition-case nil (progn (delete-directory dir-name) @@ -643,23 +643,23 @@ Instead of a function, it can also be a regular expression.") (error (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) -(defun flymake-start-syntax-check () +(defun flymake-proc-start-syntax-check () "Start syntax checking for current buffer." (interactive) (flymake-log 3 "flymake is running: %s" flymake-is-running) - (when (and (not flymake-is-running) - (flymake-can-syntax-check-file buffer-file-name)) - (when (or (not flymake-compilation-prevents-syntax-check) - (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP") - (flymake-clear-buildfile-cache) - (flymake-clear-project-include-dirs-cache) + (when (not (and flymake-is-running + (flymake-proc-can-syntax-check-file buffer-file-name))) + (when (or (not flymake-proc-compilation-prevents-syntax-check) + (not (flymake-proc--compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP") + (flymake-proc--clear-buildfile-cache) + (flymake-proc--clear-project-include-dirs-cache) (setq flymake-check-was-interrupted nil) (setq flymake-check-start-time (float-time)) (let* ((source-file-name buffer-file-name) - (init-f (flymake-get-init-function source-file-name)) - (cleanup-f (flymake-get-cleanup-function source-file-name)) + (init-f (flymake-proc--get-init-function source-file-name)) + (cleanup-f (flymake-proc--get-cleanup-function source-file-name)) (cmd-and-args (funcall init-f)) (cmd (nth 0 cmd-and-args)) (args (nth 1 cmd-and-args)) @@ -670,9 +670,9 @@ Instead of a function, it can also be a regular expression.") (funcall cleanup-f)) (progn (setq flymake-last-change-time nil) - (flymake-start-syntax-check-process cmd args dir))))))) + (flymake-proc--start-syntax-check-process cmd args dir))))))) -(defun flymake-start-syntax-check-process (cmd args dir) +(defun flymake-proc--start-syntax-check-process (cmd args dir) "Start syntax check process." (condition-case err (let* ((process @@ -683,12 +683,12 @@ Instead of a function, it can also be a regular expression.") :buffer (current-buffer) :command (cons cmd args) :noquery t - :filter 'flymake-process-filter - :sentinel 'flymake-process-sentinel)))) + :filter 'flymake-proc--process-filter + :sentinel 'flymake-proc--process-sentinel)))) (setf (process-get process 'flymake-proc--output-buffer) (generate-new-buffer (format " *flymake output for %s*" (current-buffer)))) - (push process flymake-processes) + (push process flymake-proc--processes) (setq flymake-is-running t) (setq flymake-last-change-time nil) @@ -704,12 +704,12 @@ Instead of a function, it can also be a regular expression.") "Failed to launch syntax check process `%s' with args %s: %s" cmd args (error-message-string err))) (source-file-name buffer-file-name) - (cleanup-f (flymake-get-cleanup-function source-file-name))) + (cleanup-f (flymake-proc--get-cleanup-function source-file-name))) (flymake-log 0 err-str) (funcall cleanup-f) (flymake-report-fatal-status "PROCERR" err-str))))) -(defun flymake-kill-process (proc) +(defun flymake-proc--kill-process (proc) "Kill process PROC." (kill-process proc) (let* ((buf (process-buffer proc))) @@ -718,24 +718,24 @@ Instead of a function, it can also be a regular expression.") (setq flymake-check-was-interrupted t)))) (flymake-log 1 "killed process %d" (process-id proc))) -(defun flymake-stop-all-syntax-checks () +(defun flymake-proc-stop-all-syntax-checks () "Kill all syntax check processes." (interactive) - (while flymake-processes - (flymake-kill-process (pop flymake-processes)))) + (while flymake-proc--processes + (flymake-proc--kill-process (pop flymake-proc--processes)))) -(defun flymake-compilation-is-running () +(defun flymake-proc--compilation-is-running () (and (boundp 'compilation-in-progress) compilation-in-progress)) -(defun flymake-compile () +(defun flymake-proc-compile () "Kill all flymake syntax checks, start compilation." (interactive) - (flymake-stop-all-syntax-checks) + (flymake-proc-stop-all-syntax-checks) (call-interactively 'compile)) ;;;; general init-cleanup and helper routines -(defun flymake-create-temp-inplace (file-name prefix) +(defun flymake-proc-create-temp-inplace (file-name prefix) (unless (stringp file-name) (error "Invalid file-name")) (or prefix @@ -748,7 +748,7 @@ Instead of a function, it can also be a regular expression.") (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name) temp-name)) -(defun flymake-create-temp-with-folder-structure (file-name _prefix) +(defun flymake-proc-create-temp-with-folder-structure (file-name _prefix) (unless (stringp file-name) (error "Invalid file-name")) @@ -762,48 +762,48 @@ Instead of a function, it can also be a regular expression.") (file-truename (expand-file-name (file-name-nondirectory file-name) temp-dir)))) -(defun flymake-delete-temp-directory (dir-name) - "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error." +(defun flymake-proc--delete-temp-directory (dir-name) + "Attempt to delete temp dir created by `flymake-proc-create-temp-with-folder-structure', do not fail on error." (let* ((temp-dir temporary-file-directory) (suffix (substring dir-name (1+ (length temp-dir))))) (while (> (length suffix) 0) (setq suffix (directory-file-name suffix)) ;;+(flymake-log 0 "suffix=%s" suffix) - (flymake-safe-delete-directory + (flymake-proc--safe-delete-directory (file-truename (expand-file-name suffix temp-dir))) (setq suffix (file-name-directory suffix))))) -(defvar-local flymake-temp-source-file-name nil) -(defvar-local flymake-master-file-name nil) -(defvar-local flymake-temp-master-file-name nil) -(defvar-local flymake-base-dir nil) +(defvar-local flymake-proc--temp-source-file-name nil) +(defvar-local flymake-proc--master-file-name nil) +(defvar-local flymake-proc--temp-master-file-name nil) +(defvar-local flymake-proc--base-dir nil) -(defun flymake-init-create-temp-buffer-copy (create-temp-f) +(defun flymake-proc-init-create-temp-buffer-copy (create-temp-f) "Make a temporary copy of the current buffer, save its name in buffer data and return the name." (let* ((source-file-name buffer-file-name) (temp-source-file-name (funcall create-temp-f source-file-name "flymake"))) - (flymake-save-buffer-in-file temp-source-file-name) - (setq flymake-temp-source-file-name temp-source-file-name) + (flymake-proc--save-buffer-in-file temp-source-file-name) + (setq flymake-proc--temp-source-file-name temp-source-file-name) temp-source-file-name)) -(defun flymake-simple-cleanup () - "Do cleanup after `flymake-init-create-temp-buffer-copy'. +(defun flymake-proc-simple-cleanup () + "Do cleanup after `flymake-proc-init-create-temp-buffer-copy'. Delete temp file." - (flymake-safe-delete-file flymake-temp-source-file-name) + (flymake-proc--safe-delete-file flymake-proc--temp-source-file-name) (setq flymake-last-change-time nil)) -(defun flymake-get-real-file-name (file-name-from-err-msg) +(defun flymake-proc-get-real-file-name (file-name-from-err-msg) "Translate file name from error message to \"real\" file name. Return full-name. Names are real, not patched." (let* ((real-name nil) (source-file-name buffer-file-name) - (master-file-name flymake-master-file-name) - (temp-source-file-name flymake-temp-source-file-name) - (temp-master-file-name flymake-temp-master-file-name) + (master-file-name flymake-proc--master-file-name) + (temp-source-file-name flymake-proc--temp-source-file-name) + (temp-master-file-name flymake-proc--temp-master-file-name) (base-dirs - (list flymake-base-dir + (list flymake-proc--base-dir (file-name-directory source-file-name) (if master-file-name (file-name-directory master-file-name)))) (files (list (list source-file-name source-file-name) @@ -814,17 +814,17 @@ Return full-name. Names are real, not patched." (when (equal 0 (length file-name-from-err-msg)) (setq file-name-from-err-msg source-file-name)) - (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files)) + (setq real-name (flymake-proc--get-full-patched-file-name file-name-from-err-msg base-dirs files)) ;; if real-name is nil, than file name from err msg is none of the files we've patched (if (not real-name) - (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs))) + (setq real-name (flymake-proc--get-full-nonpatched-file-name file-name-from-err-msg base-dirs))) (if (not real-name) (setq real-name file-name-from-err-msg)) - (setq real-name (flymake-fix-file-name real-name)) + (setq real-name (flymake-proc--fix-file-name real-name)) (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name) real-name)) -(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files) +(defun flymake-proc--get-full-patched-file-name (file-name-from-err-msg base-dirs files) (let* ((base-dirs-count (length base-dirs)) (file-count (length files)) (real-name nil)) @@ -836,7 +836,7 @@ Return full-name. Names are real, not patched." (this-file (nth 0 (nth (1- file-count) files))) (this-real-name (nth 1 (nth (1- file-count) files)))) ;;+(flymake-log 0 "this-dir=%s this-file=%s this-real=%s msg-file=%s" this-dir this-file this-real-name file-name-from-err-msg) - (when (and this-dir this-file (flymake-same-files + (when (and this-dir this-file (flymake-proc--same-files (expand-file-name file-name-from-err-msg this-dir) this-file)) (setq real-name this-real-name))) @@ -844,7 +844,7 @@ Return full-name. Names are real, not patched." (setq base-dirs-count (1- base-dirs-count))) real-name)) -(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs) +(defun flymake-proc--get-full-nonpatched-file-name (file-name-from-err-msg base-dirs) (let* ((real-name nil)) (if (file-name-absolute-p file-name-from-err-msg) (setq real-name file-name-from-err-msg) @@ -857,23 +857,23 @@ Return full-name. Names are real, not patched." (setq base-dirs-count (1- base-dirs-count)))))) real-name)) -(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name) +(defun flymake-proc--init-find-buildfile-dir (source-file-name buildfile-name) "Find buildfile, store its dir in buffer data and return its dir, if found." (let* ((buildfile-dir - (flymake-find-buildfile buildfile-name + (flymake-proc--find-buildfile buildfile-name (file-name-directory source-file-name)))) (if buildfile-dir - (setq flymake-base-dir buildfile-dir) + (setq flymake-proc--base-dir buildfile-dir) (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name) (flymake-report-fatal-status "NOMK" (format "No buildfile (%s) found for %s" buildfile-name source-file-name))))) -(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp) +(defun flymake-proc--init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp) "Find master file (or buffer), create its copy along with a copy of the source file." (let* ((source-file-name buffer-file-name) - (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)) - (master-and-temp-master (flymake-create-master-file + (temp-source-file-name (flymake-proc-init-create-temp-buffer-copy create-temp-f)) + (master-and-temp-master (flymake-proc--create-master-file source-file-name temp-source-file-name get-incl-dirs-f create-temp-f master-file-masks include-regexp))) @@ -883,15 +883,15 @@ Return full-name. Names are real, not patched." (flymake-log 1 "cannot find master file for %s" source-file-name) (flymake-report-status "!" "") ; NOMASTER nil) - (setq flymake-master-file-name (nth 0 master-and-temp-master)) - (setq flymake-temp-master-file-name (nth 1 master-and-temp-master))))) + (setq flymake-proc--master-file-name (nth 0 master-and-temp-master)) + (setq flymake-proc--temp-master-file-name (nth 1 master-and-temp-master))))) -(defun flymake-master-cleanup () - (flymake-simple-cleanup) - (flymake-safe-delete-file flymake-temp-master-file-name)) +(defun flymake-proc-master-cleanup () + (flymake-proc-simple-cleanup) + (flymake-proc--safe-delete-file flymake-proc--temp-master-file-name)) ;;;; make-specific init-cleanup routines -(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f) +(defun flymake-proc--get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f) "Create a command line for syntax check using GET-CMD-LINE-F." (funcall get-cmd-line-f (if use-relative-source @@ -902,7 +902,7 @@ Return full-name. Names are real, not patched." (file-name-directory source-file-name)) base-dir))) -(defun flymake-get-make-cmdline (source base-dir) +(defun flymake-proc-get-make-cmdline (source base-dir) (list "make" (list "-s" "-C" @@ -911,111 +911,416 @@ Return full-name. Names are real, not patched." "SYNTAX_CHECK_MODE=1" "check-syntax"))) -(defun flymake-get-ant-cmdline (source base-dir) +(defun flymake-proc-get-ant-cmdline (source base-dir) (list "ant" (list "-buildfile" (concat base-dir "/" "build.xml") (concat "-DCHK_SOURCES=" source) "check-syntax"))) -(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f) +(defun flymake-proc-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f) "Create syntax check command line for a directly checked source file. Use CREATE-TEMP-F for creating temp copy." (let* ((args nil) (source-file-name buffer-file-name) - (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name))) + (buildfile-dir (flymake-proc--init-find-buildfile-dir source-file-name build-file-name))) (if buildfile-dir - (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))) - (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir + (let* ((temp-source-file-name (flymake-proc-init-create-temp-buffer-copy create-temp-f))) + (setq args (flymake-proc--get-syntax-check-program-args temp-source-file-name buildfile-dir use-relative-base-dir use-relative-source get-cmdline-f)))) args)) -(defun flymake-simple-make-init () - (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline)) +(defun flymake-proc-simple-make-init () + (flymake-proc-simple-make-init-impl 'flymake-proc-create-temp-inplace t t "Makefile" 'flymake-proc-get-make-cmdline)) -(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp) +(defun flymake-proc-master-make-init (get-incl-dirs-f master-file-masks include-regexp) "Create make command line for a source file checked via master file compilation." (let* ((make-args nil) - (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy - get-incl-dirs-f 'flymake-create-temp-inplace + (temp-master-file-name (flymake-proc--init-create-temp-source-and-master-buffer-copy + get-incl-dirs-f 'flymake-proc-create-temp-inplace master-file-masks include-regexp))) (when temp-master-file-name - (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile"))) + (let* ((buildfile-dir (flymake-proc--init-find-buildfile-dir temp-master-file-name "Makefile"))) (if buildfile-dir - (setq make-args (flymake-get-syntax-check-program-args - temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline))))) + (setq make-args (flymake-proc--get-syntax-check-program-args + temp-master-file-name buildfile-dir nil nil 'flymake-proc-get-make-cmdline))))) make-args)) -(defun flymake-find-make-buildfile (source-dir) - (flymake-find-buildfile "Makefile" source-dir)) +(defun flymake-proc--find-make-buildfile (source-dir) + (flymake-proc--find-buildfile "Makefile" source-dir)) ;;;; .h/make specific -(defun flymake-master-make-header-init () - (flymake-master-make-init - 'flymake-get-include-dirs +(defun flymake-proc-master-make-header-init () + (flymake-proc-master-make-init + 'flymake-proc-get-include-dirs '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'") "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\"")) ;;;; .java/make specific -(defun flymake-simple-make-java-init () - (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline)) +(defun flymake-proc-simple-make-java-init () + (flymake-proc-simple-make-init-impl 'flymake-proc-create-temp-with-folder-structure nil nil "Makefile" 'flymake-proc-get-make-cmdline)) -(defun flymake-simple-ant-java-init () - (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline)) +(defun flymake-proc-simple-ant-java-init () + (flymake-proc-simple-make-init-impl 'flymake-proc-create-temp-with-folder-structure nil nil "build.xml" 'flymake-proc-get-ant-cmdline)) -(defun flymake-simple-java-cleanup () - "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs." - (flymake-safe-delete-file flymake-temp-source-file-name) - (when flymake-temp-source-file-name - (flymake-delete-temp-directory - (file-name-directory flymake-temp-source-file-name)))) +(defun flymake-proc-simple-java-cleanup () + "Cleanup after `flymake-proc-simple-make-java-init' -- delete temp file and dirs." + (flymake-proc--safe-delete-file flymake-proc--temp-source-file-name) + (when flymake-proc--temp-source-file-name + (flymake-proc--delete-temp-directory + (file-name-directory flymake-proc--temp-source-file-name)))) ;;;; perl-specific init-cleanup routines -(defun flymake-perl-init () - (let* ((temp-file (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)) +(defun flymake-proc-perl-init () + (let* ((temp-file (flymake-proc-init-create-temp-buffer-copy + 'flymake-proc-create-temp-inplace)) (local-file (file-relative-name temp-file (file-name-directory buffer-file-name)))) (list "perl" (list "-wc " local-file)))) ;;;; php-specific init-cleanup routines -(defun flymake-php-init () - (let* ((temp-file (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)) +(defun flymake-proc-php-init () + (let* ((temp-file (flymake-proc-init-create-temp-buffer-copy + 'flymake-proc-create-temp-inplace)) (local-file (file-relative-name temp-file (file-name-directory buffer-file-name)))) (list "php" (list "-f" local-file "-l")))) ;;;; tex-specific init-cleanup routines -(defun flymake-get-tex-args (file-name) +(defun flymake-proc--get-tex-args (file-name) ;;(list "latex" (list "-c-style-errors" file-name)) (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name))) -(defun flymake-simple-tex-init () - (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))) +(defun flymake-proc-simple-tex-init () + (flymake-proc--get-tex-args (flymake-proc-init-create-temp-buffer-copy 'flymake-proc-create-temp-inplace))) ;; Perhaps there should be a buffer-local variable flymake-master-file ;; that people can set to override this stuff. Could inherit from ;; the similar AUCTeX variable. -(defun flymake-master-tex-init () - (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy - 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace +(defun flymake-proc-master-tex-init () + (let* ((temp-master-file-name (flymake-proc--init-create-temp-source-and-master-buffer-copy + 'flymake-proc-get-include-dirs-dot 'flymake-proc-create-temp-inplace '("\\.tex\\'") "[ \t]*\\in\\(?:put\\|clude\\)[ \t]*{\\(.*%s\\)}"))) (when temp-master-file-name - (flymake-get-tex-args temp-master-file-name)))) + (flymake-proc--get-tex-args temp-master-file-name)))) -(defun flymake-get-include-dirs-dot (_base-dir) +(defun flymake-proc--get-include-dirs-dot (_base-dir) '(".")) ;;;; xml-specific init-cleanup routines -(defun flymake-xml-init () - (list flymake-xml-program - (list "val" (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)))) +(defun flymake-proc-xml-init () + (list flymake-proc-xml-program + (list "val" (flymake-proc-init-create-temp-buffer-copy + 'flymake-proc-create-temp-inplace)))) + + +;;;; + +(progn + (define-obsolete-variable-alias 'flymake-compilation-prevents-syntax-check + 'flymake-proc-compilation-prevents-syntax-check "26.1" + "If non-nil, don't start syntax check if compilation is running.") + (define-obsolete-variable-alias 'flymake-xml-program + 'flymake-proc-xml-program "26.1" + "Program to use for XML validation.") + (define-obsolete-variable-alias 'flymake-master-file-dirs + 'flymake-proc-master-file-dirs "26.1" + "Dirs where to look for master files.") + (define-obsolete-variable-alias 'flymake-master-file-count-limit + 'flymake-proc-master-file-count-limit "26.1" + "Max number of master files to check.") + (define-obsolete-variable-alias 'flymake-allowed-file-name-masks + 'flymake-proc-allowed-file-name-masks "26.1" + "Files syntax checking is allowed for. +This is an alist with elements of the form: + REGEXP INIT [CLEANUP [NAME]] +REGEXP is a regular expression that matches a file name. +INIT is the init function to use. +CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'. +NAME is the file name function to use, default `flymake-get-real-file-name'.") + (define-obsolete-variable-alias 'flymake-processes + 'flymake-proc--processes "26.1" + "List of currently active flymake processes.") + (define-obsolete-function-alias 'flymake-get-file-name-mode-and-masks + 'flymake-proc--get-file-name-mode-and-masks "26.1" + "Return the corresponding entry from ‘flymake-allowed-file-name-masks’.") + (define-obsolete-function-alias 'flymake-get-init-function + 'flymake-proc--get-init-function "26.1" + "Return init function to be used for the file.") + (define-obsolete-function-alias 'flymake-get-cleanup-function + 'flymake-proc--get-cleanup-function "26.1" + "Return cleanup function to be used for the file.") + (define-obsolete-function-alias 'flymake-get-real-file-name-function + 'flymake-proc--get-real-file-name-function "26.1" + nil) + (define-obsolete-variable-alias 'flymake-find-buildfile-cache + 'flymake-proc--find-buildfile-cache "26.1" + nil) + (define-obsolete-function-alias 'flymake-get-buildfile-from-cache + 'flymake-proc--get-buildfile-from-cache "26.1" + "Look up DIR-NAME in cache and return its associated value. +If DIR-NAME is not found, return nil.") + (define-obsolete-function-alias 'flymake-add-buildfile-to-cache + 'flymake-proc--add-buildfile-to-cache "26.1" + "Associate DIR-NAME with BUILDFILE in the buildfile cache.") + (define-obsolete-function-alias 'flymake-clear-buildfile-cache + 'flymake-proc--clear-buildfile-cache "26.1" + "Clear the buildfile cache.") + (define-obsolete-function-alias 'flymake-find-buildfile + 'flymake-proc--find-buildfile "26.1" + "Find buildfile starting from current directory. +Buildfile includes Makefile, build.xml etc. +Return its file name if found, or nil if not found.") + (define-obsolete-function-alias 'flymake-fix-file-name + 'flymake-proc--fix-file-name "26.1" + "Replace all occurrences of ‘\\’ with ‘/’.") + (define-obsolete-function-alias 'flymake-same-files + 'flymake-proc--same-files "26.1" + "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file. +Return t if so, nil if not. + +(fn FILE-NAME-ONE FILE-NAME-TWO)") + (define-obsolete-variable-alias 'flymake-included-file-name\) + 'flymake-proc--included-file-name\) "26.1" + nil) + (define-obsolete-function-alias 'flymake-find-possible-master-files + 'flymake-proc--find-possible-master-files "26.1" + "Find (by name and location) all possible master files. + +Name is specified by FILE-NAME and location is specified by +MASTER-FILE-DIRS. Master files include .cpp and .c for .h. +Files are searched for starting from the .h directory and max +max-level parent dirs. File contents are not checked.") + (define-obsolete-function-alias 'flymake-master-file-compare + 'flymake-proc--master-file-compare "26.1" + "Compare two files specified by FILE-ONE and FILE-TWO. +This function is used in sort to move most possible file names +to the beginning of the list (File.h -> File.cpp moved to top).") + (define-obsolete-variable-alias 'flymake-check-file-limit + 'flymake-proc-check-file-limit "26.1" + "Maximum number of chars to look at when checking possible master file. +Nil means search the entire file.") + (define-obsolete-function-alias 'flymake-check-patch-master-file-buffer + 'flymake-proc--check-patch-master-file-buffer "26.1" + "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME. +If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME +instead of SOURCE-FILE-NAME. + +For example, foo.cpp is a master file if it includes foo.h. + +When a buffer for MASTER-FILE-NAME exists, use it as a source +instead of reading master file from disk.") + (define-obsolete-function-alias 'flymake-replace-region + 'flymake-proc--replace-region "26.1" + "Replace text in BUFFER in region (BEG END) with REP.") + (define-obsolete-function-alias 'flymake-read-file-to-temp-buffer + 'flymake-proc--read-file-to-temp-buffer "26.1" + "Insert contents of FILE-NAME into newly created temp buffer.") + (define-obsolete-function-alias 'flymake-copy-buffer-to-temp-buffer + 'flymake-proc--copy-buffer-to-temp-buffer "26.1" + "Copy contents of BUFFER into newly created temp buffer.") + (define-obsolete-function-alias 'flymake-check-include + 'flymake-proc--check-include "26.1" + "Check if SOURCE-FILE-NAME can be found in include path. +Return t if it can be found via include path using INC-NAME.") + (define-obsolete-function-alias 'flymake-find-buffer-for-file + 'flymake-proc--find-buffer-for-file "26.1" + "Check if there exists a buffer visiting FILE-NAME. +Return t if so, nil if not.") + (define-obsolete-function-alias 'flymake-create-master-file + 'flymake-proc--create-master-file "26.1" + "Save SOURCE-FILE-NAME with a different name. +Find master file, patch and save it.") + (define-obsolete-function-alias 'flymake-save-buffer-in-file + 'flymake-proc--save-buffer-in-file "26.1" + "Save the entire buffer contents into file FILE-NAME. +Create parent directories as needed.") + (define-obsolete-function-alias 'flymake-process-filter + 'flymake-proc--process-filter "26.1" + "Parse OUTPUT and highlight error lines. +It’s flymake process filter.") + (define-obsolete-function-alias 'flymake-process-sentinel + 'flymake-proc--process-sentinel "26.1" + "Sentinel for syntax check buffers.") + (define-obsolete-function-alias 'flymake-post-syntax-check + 'flymake-proc--post-syntax-check "26.1" + nil) + (define-obsolete-function-alias 'flymake-reformat-err-line-patterns-from-compile-el + 'flymake-proc-reformat-err-line-patterns-from-compile-el "26.1" + "Grab error line patterns from ORIGINAL-LIST in compile.el format. +Convert it to flymake internal format.") + (define-obsolete-variable-alias 'flymake-err-line-patterns + 'flymake-proc-err-line-patterns "26.1" + "Patterns for matching error/warning lines. Each pattern has the form +(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). +Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns +from compile.el") + (define-obsolete-variable-alias 'flymake-warning-predicate + 'flymake-proc-warning-predicate "26.1" + "Predicate matching against error text to detect a warning. +Takes a single argument, the error's text and should return non-nil +if it's a warning. +Instead of a function, it can also be a regular expression.") + (define-obsolete-function-alias 'flymake-parse-line + 'flymake-proc-parse-line "26.1" + "Parse LINE to see if it is an error or warning. +Return its components if so, nil otherwise.") + (define-obsolete-function-alias 'flymake-get-project-include-dirs-imp + 'flymake-proc--get-project-include-dirs-imp "26.1" + "Include dirs for the project current file belongs to.") + (define-obsolete-variable-alias 'flymake-get-project-include-dirs-function + 'flymake-proc--get-project-include-dirs-function "26.1" + "Function used to get project include dirs, one parameter: basedir name.") + (define-obsolete-function-alias 'flymake-get-project-include-dirs + 'flymake-proc--get-project-include-dirs "26.1" + nil) + (define-obsolete-function-alias 'flymake-get-system-include-dirs + 'flymake-proc--get-system-include-dirs "26.1" + "System include dirs - from the ‘INCLUDE’ env setting.") + (define-obsolete-variable-alias 'flymake-project-include-dirs-cache + 'flymake-proc--project-include-dirs-cache "26.1" + nil) + (define-obsolete-function-alias 'flymake-add-project-include-dirs-to-cache + 'flymake-proc--add-project-include-dirs-to-cache "26.1" + nil) + (define-obsolete-function-alias 'flymake-clear-project-include-dirs-cache + 'flymake-proc--clear-project-include-dirs-cache "26.1" + nil) + (define-obsolete-function-alias 'flymake-get-include-dirs + 'flymake-proc-get-include-dirs "26.1" + "Get dirs to use when resolving local file names.") + (define-obsolete-variable-alias 'flymake-restore-formatting + 'flymake-proc--restore-formatting "26.1" + nil) + (define-obsolete-variable-alias 'flymake-get-program-dir + 'flymake-proc--get-program-dir "26.1" + nil) + (define-obsolete-function-alias 'flymake-safe-delete-file + 'flymake-proc--safe-delete-file "26.1" + nil) + (define-obsolete-function-alias 'flymake-safe-delete-directory + 'flymake-proc--safe-delete-directory "26.1" + nil) + (define-obsolete-function-alias 'flymake-start-syntax-check + 'flymake-proc-start-syntax-check "26.1" + "Start syntax checking for current buffer.") + (define-obsolete-function-alias 'flymake-kill-process + 'flymake-proc--kill-process "26.1" + "Kill process PROC.") + (define-obsolete-function-alias 'flymake-stop-all-syntax-checks + 'flymake-proc-stop-all-syntax-checks "26.1" + "Kill all syntax check processes.") + (define-obsolete-function-alias 'flymake-compilation-is-running + 'flymake-proc--compilation-is-running "26.1" + nil) + (define-obsolete-function-alias 'flymake-compile + 'flymake-proc-compile "26.1" + "Kill all flymake syntax checks, start compilation.") + (define-obsolete-function-alias 'flymake-create-temp-inplace + 'flymake-proc-create-temp-inplace "26.1" + nil) + (define-obsolete-function-alias 'flymake-create-temp-with-folder-structure + 'flymake-proc-create-temp-with-folder-structure "26.1" + nil) + (define-obsolete-function-alias 'flymake-delete-temp-directory + 'flymake-proc--delete-temp-directory "26.1" + "Attempt to delete temp dir created by ‘flymake-create-temp-with-folder-structure’, do not fail on error.") + (define-obsolete-variable-alias 'flymake-temp-source-file-name + 'flymake-proc--temp-source-file-name "26.1" + nil) + (define-obsolete-variable-alias 'flymake-master-file-name + 'flymake-proc--master-file-name "26.1" + nil) + (define-obsolete-variable-alias 'flymake-temp-master-file-name + 'flymake-proc--temp-master-file-name "26.1" + nil) + (define-obsolete-variable-alias 'flymake-base-dir + 'flymake-proc--base-dir "26.1" + nil) + (define-obsolete-function-alias 'flymake-init-create-temp-buffer-copy + 'flymake-proc-init-create-temp-buffer-copy "26.1" + "Make a temporary copy of the current buffer, save its name in buffer data and return the name.") + (define-obsolete-function-alias 'flymake-simple-cleanup + 'flymake-proc-simple-cleanup "26.1" + "Do cleanup after ‘flymake-init-create-temp-buffer-copy’. +Delete temp file.") + (define-obsolete-function-alias 'flymake-get-real-file-name + 'flymake-proc-get-real-file-name "26.1" + "Translate file name from error message to \"real\" file name. +Return full-name. Names are real, not patched.") + (define-obsolete-function-alias 'flymake-get-full-patched-file-name + 'flymake-proc--get-full-patched-file-name "26.1" + nil) + (define-obsolete-function-alias 'flymake-get-full-nonpatched-file-name + 'flymake-proc--get-full-nonpatched-file-name "26.1" + nil) + (define-obsolete-function-alias 'flymake-init-find-buildfile-dir + 'flymake-proc--init-find-buildfile-dir "26.1" + "Find buildfile, store its dir in buffer data and return its dir, if found.") + (define-obsolete-function-alias 'flymake-init-create-temp-source-and-master-buffer-copy + 'flymake-proc--init-create-temp-source-and-master-buffer-copy "26.1" + "Find master file (or buffer), create its copy along with a copy of the source file.") + (define-obsolete-function-alias 'flymake-master-cleanup + 'flymake-proc-master-cleanup "26.1" + nil) + (define-obsolete-function-alias 'flymake-get-syntax-check-program-args + 'flymake-proc--get-syntax-check-program-args "26.1" + "Create a command line for syntax check using GET-CMD-LINE-F.") + (define-obsolete-function-alias 'flymake-get-make-cmdline + 'flymake-proc-get-make-cmdline "26.1" + nil) + (define-obsolete-function-alias 'flymake-get-ant-cmdline + 'flymake-proc-get-ant-cmdline "26.1" + nil) + (define-obsolete-function-alias 'flymake-simple-make-init-impl + 'flymake-proc-simple-make-init-impl "26.1" + "Create syntax check command line for a directly checked source file. +Use CREATE-TEMP-F for creating temp copy.") + (define-obsolete-function-alias 'flymake-simple-make-init + 'flymake-proc-simple-make-init "26.1" + nil) + (define-obsolete-function-alias 'flymake-master-make-init + 'flymake-proc-master-make-init "26.1" + "Create make command line for a source file checked via master file compilation.") + (define-obsolete-function-alias 'flymake-find-make-buildfile + 'flymake-proc--find-make-buildfile "26.1" + nil) + (define-obsolete-function-alias 'flymake-master-make-header-init + 'flymake-proc-master-make-header-init "26.1" + nil) + (define-obsolete-function-alias 'flymake-simple-make-java-init + 'flymake-proc-simple-make-java-init "26.1" + nil) + (define-obsolete-function-alias 'flymake-simple-ant-java-init + 'flymake-proc-simple-ant-java-init "26.1" + nil) + (define-obsolete-function-alias 'flymake-simple-java-cleanup + 'flymake-proc-simple-java-cleanup "26.1" + "Cleanup after ‘flymake-simple-make-java-init’ -- delete temp file and dirs.") + (define-obsolete-function-alias 'flymake-perl-init + 'flymake-proc-perl-init "26.1" + nil) + (define-obsolete-function-alias 'flymake-php-init + 'flymake-proc-php-init "26.1" + nil) + (define-obsolete-function-alias 'flymake-get-tex-args + 'flymake-proc--get-tex-args "26.1" + nil) + (define-obsolete-function-alias 'flymake-simple-tex-init + 'flymake-proc-simple-tex-init "26.1" + nil) + (define-obsolete-function-alias 'flymake-master-tex-init + 'flymake-proc-master-tex-init "26.1" + nil) + (define-obsolete-function-alias 'flymake-xml-init + 'flymake-proc-xml-init "26.1" + nil)) + + (provide 'flymake-proc) ;;; flymake-proc.el ends here diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index b04346fd97..dda72a35d2 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el @@ -38,7 +38,7 @@ (i 0)) (unwind-protect (with-current-buffer buffer - (setq-local flymake-warning-predicate predicate) + (setq-local flymake-proc-warning-predicate predicate) (goto-char (point-min)) (flymake-mode 1) ;; Weirdness here... https://debbugs.gnu.org/17647#25 commit 6954270e87e3a9f829cd6e8c89febc2c0bc48845 Author: João Távora Date: Wed Aug 23 02:23:41 2017 +0100 Completely rewrite Flymake's subprocess output processing Instead of parsing and matching regexps line-by-line, insert subprocess output in a separate buffer and parse using `search-forward-regexp'. This eventually enables multi-line error patterns and simplifies code all around. Store per-check information in the subprocess using `process-get' and `process-put'. Treat error messages, warnings, etc. more generically as "diagnostics". Create these objects as soon as possible, reusing existing `flymake-ler' structure. Fix some whitespace. * lisp/progmodes/flymake.el (cl-lib): Require also when loading. (flymake--fix-line-numbers): Rename from flymake-fix-line-numbers. Simplify. (flymake-report): Call flymake--fix-line-numbers. Rearrange plain diagnostics list into alist format expected by flymake-highlight-err-lines. * lisp/progmodes/flymake-proc.el (flymake-process-filter): Insert process output and parse in dedicated output buffer. (flymake-proc--diagnostics-for-pattern): New helper function. (flymake-process-sentinel): Call flymake-post-syntax-check with collected diagnostics. Kill output buffer. (flymake-post-syntax-check): Receive diagnostics as third argument. (flymake-parse-output-and-residual, flymake-new-err-info) (flymake-parse-residual, flymake-parse-err-lines) (flymake-split-output, flymake-proc-parse-line) (flymake-output-residual): Delete. (flymake-start-syntax-check-process): Use make-process. Setup dedicated an output buffer diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 63c65c2521..dae118eb4f 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -105,8 +105,6 @@ NAME is the file name function to use, default `flymake-get-real-file-name'." (defvar flymake-processes nil "List of currently active flymake processes.") -(defvar-local flymake-output-residual nil) - (defun flymake-get-file-name-mode-and-masks (file-name) "Return the corresponding entry from `flymake-allowed-file-name-masks'." (unless (stringp file-name) @@ -395,16 +393,75 @@ Create parent directories as needed." (write-region nil nil file-name nil 566) (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name)) -(defun flymake-process-filter (process output) - "Parse OUTPUT and highlight error lines. -It's flymake process filter." - (let ((source-buffer (process-buffer process))) - - (flymake-log 3 "received %d byte(s) of output from process %d" - (length output) (process-id process)) - (when (buffer-live-p source-buffer) - (with-current-buffer source-buffer - (flymake-parse-output-and-residual output))))) +(defun flymake-proc--diagnostics-for-pattern (proc pattern) + (condition-case err + (pcase-let ((`(,regexp ,file-idx ,line-idx ,_col-idx ,message-idx) + pattern) + (retval)) + (while (search-forward-regexp regexp nil t) + (let ((fname (and file-idx (match-string file-idx))) + (message (and message-idx (match-string message-idx))) + (line-number (and line-idx (string-to-number + (match-string line-idx))))) + (with-current-buffer (process-buffer proc) + (push (flymake-ler-make-ler + fname + line-number + (if (and message + (cond ((stringp flymake-warning-predicate) + (string-match flymake-warning-predicate + message)) + ((functionp flymake-warning-predicate) + (funcall flymake-warning-predicate + message)))) + "w" + "e") + message + (and fname + (funcall (flymake-get-real-file-name-function + fname) + fname))) + retval)))) + retval) + (error + (flymake-log 1 "Error parsing process output for pattern %s: %s" + pattern err) + nil))) + +(defun flymake-process-filter (proc string) + "Parse STRING and collect diagnostics info." + (flymake-log 3 "received %d byte(s) of output from process %d" + (length string) (process-id proc)) + (let ((output-buffer (process-get proc 'flymake-proc--output-buffer))) + (when (and (buffer-live-p (process-buffer proc)) + output-buffer) + (with-current-buffer output-buffer + (let ((moving (= (point) (process-mark proc))) + (inhibit-read-only t) + (unprocessed-mark + (or (process-get proc 'flymake-proc--unprocessed-mark) + (set-marker (make-marker) (point-min))))) + (save-excursion + ;; Insert the text, advancing the process marker. + (goto-char (process-mark proc)) + (insert string) + (set-marker (process-mark proc) (point))) + (if moving (goto-char (process-mark proc))) + + ;; check for new diagnostics + ;; + (save-excursion + (goto-char unprocessed-mark) + (dolist (pattern flymake-err-line-patterns) + (let ((new (flymake-proc--diagnostics-for-pattern proc pattern))) + (process-put + proc + 'flymake-proc--collected-diagnostics + (append new + (process-get proc + 'flymake-proc--collected-diagnostics))))) + (process-put proc 'flymake-proc--unprocessed-mark + (point-marker)))))))) (defun flymake-process-sentinel (process _event) "Sentinel for syntax check buffers." @@ -412,10 +469,12 @@ It's flymake process filter." (let* ((exit-status (process-exit-status process)) (command (process-command process)) (source-buffer (process-buffer process)) - (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer)))) + (cleanup-f (flymake-get-cleanup-function + (buffer-file-name source-buffer)))) (flymake-log 2 "process %d exited with code %d" (process-id process) exit-status) + (kill-buffer (process-get process 'flymake-proc--output-buffer)) (condition-case err (progn (flymake-log 3 "cleaning up using %s" cleanup-f) @@ -428,9 +487,9 @@ It's flymake process filter." (when (buffer-live-p source-buffer) (with-current-buffer source-buffer - - (flymake-parse-residual) - (flymake-post-syntax-check exit-status command) + (flymake-post-syntax-check + exit-status command + (process-get process 'flymake-proc--collected-diagnostics)) (setq flymake-is-running nil)))) (error (let ((err-str (format "Error in process sentinel for buffer %s: %s" @@ -439,79 +498,16 @@ It's flymake process filter." (with-current-buffer source-buffer (setq flymake-is-running nil)))))))) -(defun flymake-post-syntax-check (exit-status command) - (let ((err-count (flymake-get-err-count flymake-new-err-info "e")) - (warn-count (flymake-get-err-count flymake-new-err-info "w"))) - (if (equal 0 exit-status) - (flymake-report flymake-new-err-info) - (if flymake-check-was-interrupted - (flymake-report-status nil "") ;; STOPPED - (if (and (zerop err-count) (zerop warn-count)) - (flymake-report-fatal-status "CFGERR" - (format "Configuration error has occurred while running %s" command)) - (flymake-report flymake-new-err-info)))) - (setq flymake-new-err-info nil))) - - -(defun flymake-parse-output-and-residual (output) - "Split OUTPUT into lines, merge in residual if necessary." - (let* ((buffer-residual flymake-output-residual) - (total-output (if buffer-residual (concat buffer-residual output) output)) - (lines-and-residual (flymake-split-output total-output)) - (lines (nth 0 lines-and-residual)) - (new-residual (nth 1 lines-and-residual))) - (setq flymake-output-residual new-residual) - (setq flymake-new-err-info - (flymake-parse-err-lines - flymake-new-err-info lines)))) - -(defvar-local flymake-new-err-info nil - "Same as `flymake-err-info', effective when a syntax check is in progress.") - -(defun flymake-parse-residual () - "Parse residual if it's non empty." - (when flymake-output-residual - (setq flymake-new-err-info - (flymake-parse-err-lines - flymake-new-err-info - (list flymake-output-residual))) - (setq flymake-output-residual nil))) - -(defun flymake-parse-err-lines (err-info-list lines) - "Parse err LINES, store info in ERR-INFO-LIST." - (let* ((count (length lines)) - (idx 0) - (line-err-info nil) - (real-file-name nil) - (source-file-name buffer-file-name) - (get-real-file-name-f (flymake-get-real-file-name-function source-file-name))) - - (while (< idx count) - (setq line-err-info (flymake-parse-line (nth idx lines))) - (when line-err-info - (setq real-file-name (funcall get-real-file-name-f - (flymake-ler-file line-err-info))) - (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name)) - - (when (flymake-same-files real-file-name source-file-name) - (setq line-err-info (flymake-ler-set-file line-err-info nil)) - (setq err-info-list (flymake-add-err-info err-info-list line-err-info)))) - (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no")) - (setq idx (1+ idx))) - err-info-list)) - -(defun flymake-split-output (output) - "Split OUTPUT into lines. -Return last one as residual if it does not end with newline char. -Returns ((LINES) RESIDUAL)." - (when (and output (> (length output) 0)) - (let* ((lines (split-string output "[\n\r]+" t)) - (complete (equal "\n" (char-to-string (aref output (1- (length output)))))) - (residual nil)) - (when (not complete) - (setq residual (car (last lines))) - (setq lines (butlast lines))) - (list lines residual)))) +(defun flymake-post-syntax-check (exit-status command diagnostics) + (if (equal 0 exit-status) + (flymake-report diagnostics) + (if flymake-check-was-interrupted + (flymake-report-status nil "") ;; STOPPED + (if (null diagnostics) + (flymake-report-fatal-status + "CFGERR" + (format "Configuration error has occurred while running %s" command)) + (flymake-report diagnostics))))) (defun flymake-reformat-err-line-patterns-from-compile-el (original-list) "Grab error line patterns from ORIGINAL-LIST in compile.el format. @@ -570,43 +566,6 @@ Takes a single argument, the error's text and should return non-nil if it's a warning. Instead of a function, it can also be a regular expression.") -(defun flymake-parse-line (line) - "Parse LINE to see if it is an error or warning. -Return its components if so, nil otherwise." - (let ((raw-file-name nil) - (line-no 0) - (err-type "e") - (err-text nil) - (patterns flymake-err-line-patterns) - (matched nil)) - (while (and patterns (not matched)) - (when (string-match (car (car patterns)) line) - (let* ((file-idx (nth 1 (car patterns))) - (line-idx (nth 2 (car patterns)))) - - (setq raw-file-name (if file-idx (match-string file-idx line) nil)) - (setq line-no (if line-idx (string-to-number - (match-string line-idx line)) 0)) - (setq err-text (if (> (length (car patterns)) 4) - (match-string (nth 4 (car patterns)) line) - (flymake-patch-err-text - (substring line (match-end 0))))) - (if (null err-text) - (setq err-text "") - (when (cond ((stringp flymake-warning-predicate) - (string-match flymake-warning-predicate err-text)) - ((functionp flymake-warning-predicate) - (funcall flymake-warning-predicate err-text))) - (setq err-type "w"))) - (flymake-log - 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s" - file-idx line-idx raw-file-name line-no err-text) - (setq matched t))) - (setq patterns (cdr patterns))) - (if matched - (flymake-ler-make-ler raw-file-name line-no err-type err-text) - ()))) - (defun flymake-get-project-include-dirs-imp (basedir) "Include dirs for the project current file belongs to." (if (flymake-get-project-include-dirs-from-cache basedir) @@ -714,37 +673,41 @@ Return its components if so, nil otherwise." (flymake-start-syntax-check-process cmd args dir))))))) (defun flymake-start-syntax-check-process (cmd args dir) -"Start syntax check process." -(condition-case err - (let* ((process - (let ((default-directory (or dir default-directory))) - (when dir - (flymake-log 3 "starting process on dir %s" dir)) - (apply 'start-file-process - "flymake-proc" (current-buffer) cmd args)))) - (set-process-sentinel process 'flymake-process-sentinel) - (set-process-filter process 'flymake-process-filter) - (set-process-query-on-exit-flag process nil) - (push process flymake-processes) - - (setq flymake-is-running t) - (setq flymake-last-change-time nil) - - (flymake-report-status nil "*") - (flymake-log 2 "started process %d, command=%s, dir=%s" - (process-id process) (process-command process) - default-directory) - process) - (error - (let* ((err-str - (format-message - "Failed to launch syntax check process `%s' with args %s: %s" - cmd args (error-message-string err))) - (source-file-name buffer-file-name) - (cleanup-f (flymake-get-cleanup-function source-file-name))) - (flymake-log 0 err-str) - (funcall cleanup-f) - (flymake-report-fatal-status "PROCERR" err-str))))) + "Start syntax check process." + (condition-case err + (let* ((process + (let ((default-directory (or dir default-directory))) + (when dir + (flymake-log 3 "starting process on dir %s" dir)) + (make-process :name "flymake-proc" + :buffer (current-buffer) + :command (cons cmd args) + :noquery t + :filter 'flymake-process-filter + :sentinel 'flymake-process-sentinel)))) + (setf (process-get process 'flymake-proc--output-buffer) + (generate-new-buffer + (format " *flymake output for %s*" (current-buffer)))) + (push process flymake-processes) + + (setq flymake-is-running t) + (setq flymake-last-change-time nil) + + (flymake-report-status nil "*") + (flymake-log 2 "started process %d, command=%s, dir=%s" + (process-id process) (process-command process) + default-directory) + process) + (error + (let* ((err-str + (format-message + "Failed to launch syntax check process `%s' with args %s: %s" + cmd args (error-message-string err))) + (source-file-name buffer-file-name) + (cleanup-f (flymake-get-cleanup-function source-file-name))) + (flymake-log 0 err-str) + (funcall cleanup-f) + (flymake-report-fatal-status "PROCERR" err-str))))) (defun flymake-kill-process (proc) "Kill process PROC." diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index a1b16c0a13..a360306503 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -32,7 +32,7 @@ ;; ;;; Code: -(eval-when-compile (require 'cl-lib)) +(require 'cl-lib) (defgroup flymake nil "Universal on-the-fly syntax checker." @@ -427,24 +427,6 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (setq flymake-mode-line mode-line) (force-mode-line-update))) -(defun flymake-report (diagnostics) - (save-restriction - (widen) - (setq flymake-err-info - (flymake-fix-line-numbers - diagnostics 1 (count-lines (point-min) (point-max)))) - (flymake-delete-own-overlays) - (flymake-highlight-err-lines flymake-err-info) - (let ((err-count (flymake-get-err-count flymake-err-info "e")) - (warn-count (flymake-get-err-count flymake-err-info "w"))) - (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)" - (buffer-name) err-count warn-count - (- (float-time) flymake-check-start-time)) - (if (and (equal 0 err-count) (equal 0 warn-count)) - (flymake-report-status "" "") - (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) - - ;; Nothing in flymake uses this at all any more, so this is just for ;; third-party compatibility. (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") @@ -460,25 +442,42 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s" (buffer-name) status warning)) -(defun flymake-fix-line-numbers (err-info-list min-line max-line) - "Replace line numbers with fixed value. -If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE. -If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE. -The reason for this fix is because some compilers might report -line number outside the file being compiled." - (let* ((count (length err-info-list)) - (err-info nil) - (line 0)) - (while (> count 0) - (setq err-info (nth (1- count) err-info-list)) - (setq line (flymake-er-get-line err-info)) - (when (or (< line min-line) (> line max-line)) - (setq line (if (< line min-line) min-line max-line)) - (setq err-info-list (flymake-set-at err-info-list (1- count) - (flymake-er-make-er line - (flymake-er-get-line-err-info-list err-info))))) - (setq count (1- count)))) - err-info-list) +(defun flymake--fix-line-numbers (diagnostic) + "Ensure DIAGNOSTIC has sensible error lines" + (setf (flymake-ler-line diagnostic) + (min (max (flymake-ler-line diagnostic) + 1) + (line-number-at-pos (point-max) 'absolute)))) + +(defun flymake-report (diagnostics) + (save-restriction + (widen) + (mapc #'flymake--fix-line-numbers diagnostics) + (flymake-delete-own-overlays) + (setq flymake-err-info + (cl-loop with grouped + for diag in diagnostics + for line = (flymake-ler-line diag) + for existing = (assoc line grouped) + if existing + do (setcdr existing + (list diag (cl-second existing))) + else + do (push (list line (list diag)) grouped) + finally (return grouped))) + (flymake-highlight-err-lines flymake-err-info) + (let ((err-count (flymake-get-err-count flymake-err-info "e")) + (warn-count (flymake-get-err-count flymake-err-info "w"))) + (when flymake-check-start-time + (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)" + (buffer-name) err-count warn-count + (- (float-time) flymake-check-start-time))) + (if (and (equal 0 err-count) (equal 0 warn-count)) + (flymake-report-status "" "") + (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) + +(defvar-local flymake--backend nil + "The currently active backend selected by `flymake-mode'") ;;;###autoload (define-minor-mode flymake-mode nil commit f1601bef93a23ecd5092d9360a48e2288d835886 Author: João Távora Date: Mon Aug 21 00:17:05 2017 +0100 Flymake provides flymake-report re-entry point for backends * lisp/progmodes/flymake-proc.el (flymake-post-syntax-check): Simplify. Call flymake-report. * lisp/progmodes/flymake.el (flymake-report): New function. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 89633feaab..63c65c2521 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -440,31 +440,18 @@ It's flymake process filter." (setq flymake-is-running nil)))))))) (defun flymake-post-syntax-check (exit-status command) - (save-restriction - (widen) - (setq flymake-err-info flymake-new-err-info) - (setq flymake-new-err-info nil) - (setq flymake-err-info - (flymake-fix-line-numbers - flymake-err-info 1 (count-lines (point-min) (point-max)))) - (flymake-delete-own-overlays) - (flymake-highlight-err-lines flymake-err-info) - (let (err-count warn-count) - (setq err-count (flymake-get-err-count flymake-err-info "e")) - (setq warn-count (flymake-get-err-count flymake-err-info "w")) - (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)" - (buffer-name) err-count warn-count - (- (float-time) flymake-check-start-time)) - (setq flymake-check-start-time nil) - - (if (and (equal 0 err-count) (equal 0 warn-count)) - (if (equal 0 exit-status) - (flymake-report-status "" "") ; PASSED - (if (not flymake-check-was-interrupted) - (flymake-report-fatal-status "CFGERR" - (format "Configuration error has occurred while running %s" command)) - (flymake-report-status nil ""))) ; "STOPPED" - (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) + (let ((err-count (flymake-get-err-count flymake-new-err-info "e")) + (warn-count (flymake-get-err-count flymake-new-err-info "w"))) + (if (equal 0 exit-status) + (flymake-report flymake-new-err-info) + (if flymake-check-was-interrupted + (flymake-report-status nil "") ;; STOPPED + (if (and (zerop err-count) (zerop warn-count)) + (flymake-report-fatal-status "CFGERR" + (format "Configuration error has occurred while running %s" command)) + (flymake-report flymake-new-err-info)))) + (setq flymake-new-err-info nil))) + (defun flymake-parse-output-and-residual (output) "Split OUTPUT into lines, merge in residual if necessary." @@ -709,6 +696,7 @@ Return its components if so, nil otherwise." (flymake-clear-project-include-dirs-cache) (setq flymake-check-was-interrupted nil) + (setq flymake-check-start-time (float-time)) (let* ((source-file-name buffer-file-name) (init-f (flymake-get-init-function source-file-name)) diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 441784c8a1..a1b16c0a13 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -427,6 +427,24 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (setq flymake-mode-line mode-line) (force-mode-line-update))) +(defun flymake-report (diagnostics) + (save-restriction + (widen) + (setq flymake-err-info + (flymake-fix-line-numbers + diagnostics 1 (count-lines (point-min) (point-max)))) + (flymake-delete-own-overlays) + (flymake-highlight-err-lines flymake-err-info) + (let ((err-count (flymake-get-err-count flymake-err-info "e")) + (warn-count (flymake-get-err-count flymake-err-info "w"))) + (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)" + (buffer-name) err-count warn-count + (- (float-time) flymake-check-start-time)) + (if (and (equal 0 err-count) (equal 0 warn-count)) + (flymake-report-status "" "") + (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) + + ;; Nothing in flymake uses this at all any more, so this is just for ;; third-party compatibility. (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") commit fe9dc7a087ad2b1ac94d32f975f649a2d7dfeb65 Author: João Távora Date: Thu Aug 17 15:38:12 2017 +0100 Split Flymake into flymake.el into flymake-proc.el (again!) After deciding that this work would continue on master only, which caused two commits named Revert "Split flymake.el into flymake-proc.el and flymake-ui.el" and Revert "Add flymake-backends defcustom" to be added to the emacs-26 branch, further discussion reversed that decision. See: https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg01020.html https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg01030.html This means that those two commits MUST be merged to master AFTER ALL. flymke-proc.el contains the main syntax-checking backend, while flymake.el keeps mostly the UI part. * lisp/progmodes/flymake-proc.el: New file. Require flymake. * lisp/progmodes/flymake.el: Require flymake-proc.el at the end. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el new file mode 100644 index 0000000000..89633feaab --- /dev/null +++ b/lisp/progmodes/flymake-proc.el @@ -0,0 +1,1070 @@ +;;; flymake-proc.el --- Flymake for external syntax checker processes -*- lexical-binding: t; -*- + +;; Copyright (C) 2003-2017 Free Software Foundation, Inc. + +;; Author: Pavel Kobyakov +;; Maintainer: Leo Liu +;; Version: 0.3 +;; Keywords: c languages tools + +;; 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 . + +;;; Commentary: +;; +;; Flymake is a minor Emacs mode performing on-the-fly syntax checks. +;; +;; This file contains the most original implementation of flymake's +;; main source of on-the-fly diagnostic info, the external syntax +;; checker backend. +;; +;;; Bugs/todo: + +;; - Only uses "Makefile", not "makefile" or "GNUmakefile" +;; (from http://bugs.debian.org/337339). + +;;; Code: + +(require 'flymake) + +(defcustom flymake-compilation-prevents-syntax-check t + "If non-nil, don't start syntax check if compilation is running." + :group 'flymake + :type 'boolean) + +(defcustom flymake-xml-program + (if (executable-find "xmlstarlet") "xmlstarlet" "xml") + "Program to use for XML validation." + :type 'file + :group 'flymake + :version "24.4") + +(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest") + "Dirs where to look for master files." + :group 'flymake + :type '(repeat (string))) + +(defcustom flymake-master-file-count-limit 32 + "Max number of master files to check." + :group 'flymake + :type 'integer) + +(defcustom flymake-allowed-file-name-masks + '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-simple-make-init) + ("\\.xml\\'" flymake-xml-init) + ("\\.html?\\'" flymake-xml-init) + ("\\.cs\\'" flymake-simple-make-init) + ("\\.p[ml]\\'" flymake-perl-init) + ("\\.php[345]?\\'" flymake-php-init) + ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup) + ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup) + ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup) + ("\\.tex\\'" flymake-simple-tex-init) + ("\\.idl\\'" flymake-simple-make-init) + ;; ("\\.cpp\\'" 1) + ;; ("\\.java\\'" 3) + ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'") + ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2)) + ;; ("\\.idl\\'" 1) + ;; ("\\.odl\\'" 1) + ;; ("[0-9]+\\.tex\\'" 2 ("\\.tex\\'") + ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 )) + ;; ("\\.tex\\'" 1) + ) + "Files syntax checking is allowed for. +This is an alist with elements of the form: + REGEXP INIT [CLEANUP [NAME]] +REGEXP is a regular expression that matches a file name. +INIT is the init function to use. +CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'. +NAME is the file name function to use, default `flymake-get-real-file-name'." + :group 'flymake + :type '(alist :key-type (regexp :tag "File regexp") + :value-type + (list :tag "Handler functions" + (function :tag "Init function") + (choice :tag "Cleanup function" + (const :tag "flymake-simple-cleanup" nil) + function) + (choice :tag "Name function" + (const :tag "flymake-get-real-file-name" nil) + function)))) + +(defvar flymake-processes nil + "List of currently active flymake processes.") + +(defvar-local flymake-output-residual nil) + +(defun flymake-get-file-name-mode-and-masks (file-name) + "Return the corresponding entry from `flymake-allowed-file-name-masks'." + (unless (stringp file-name) + (error "Invalid file-name")) + (let ((fnm flymake-allowed-file-name-masks) + (mode-and-masks nil)) + (while (and (not mode-and-masks) fnm) + (if (string-match (car (car fnm)) file-name) + (setq mode-and-masks (cdr (car fnm)))) + (setq fnm (cdr fnm))) + (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks)) + mode-and-masks)) + +(defun flymake-can-syntax-check-file (file-name) + "Determine whether we can syntax check FILE-NAME. +Return nil if we cannot, non-nil if we can." + (if (flymake-get-init-function file-name) t nil)) + +(defun flymake-get-init-function (file-name) + "Return init function to be used for the file." + (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name)))) + ;;(flymake-log 0 "calling %s" init-f) + ;;(funcall init-f (current-buffer)) + init-f)) + +(defun flymake-get-cleanup-function (file-name) + "Return cleanup function to be used for the file." + (or (nth 1 (flymake-get-file-name-mode-and-masks file-name)) + 'flymake-simple-cleanup)) + +(defun flymake-get-real-file-name-function (file-name) + (or (nth 2 (flymake-get-file-name-mode-and-masks file-name)) + 'flymake-get-real-file-name)) + +(defvar flymake-find-buildfile-cache (make-hash-table :test #'equal)) + +(defun flymake-get-buildfile-from-cache (dir-name) + "Look up DIR-NAME in cache and return its associated value. +If DIR-NAME is not found, return nil." + (gethash dir-name flymake-find-buildfile-cache)) + +(defun flymake-add-buildfile-to-cache (dir-name buildfile) + "Associate DIR-NAME with BUILDFILE in the buildfile cache." + (puthash dir-name buildfile flymake-find-buildfile-cache)) + +(defun flymake-clear-buildfile-cache () + "Clear the buildfile cache." + (clrhash flymake-find-buildfile-cache)) + +(defun flymake-find-buildfile (buildfile-name source-dir-name) + "Find buildfile starting from current directory. +Buildfile includes Makefile, build.xml etc. +Return its file name if found, or nil if not found." + (or (flymake-get-buildfile-from-cache source-dir-name) + (let* ((file (locate-dominating-file source-dir-name buildfile-name))) + (if file + (progn + (flymake-log 3 "found buildfile at %s" file) + (flymake-add-buildfile-to-cache source-dir-name file) + file) + (progn + (flymake-log 3 "buildfile for %s not found" source-dir-name) + nil))))) + +(defun flymake-fix-file-name (name) + "Replace all occurrences of `\\' with `/'." + (when name + (setq name (expand-file-name name)) + (setq name (abbreviate-file-name name)) + (setq name (directory-file-name name)) + name)) + +(defun flymake-same-files (file-name-one file-name-two) + "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file. +Return t if so, nil if not." + (equal (flymake-fix-file-name file-name-one) + (flymake-fix-file-name file-name-two))) + +;; This is bound dynamically to pass a parameter to a sort predicate below +(defvar flymake-included-file-name) + +(defun flymake-find-possible-master-files (file-name master-file-dirs masks) + "Find (by name and location) all possible master files. + +Name is specified by FILE-NAME and location is specified by +MASTER-FILE-DIRS. Master files include .cpp and .c for .h. +Files are searched for starting from the .h directory and max +max-level parent dirs. File contents are not checked." + (let* ((dirs master-file-dirs) + (files nil) + (done nil)) + + (while (and (not done) dirs) + (let* ((dir (expand-file-name (car dirs) (file-name-directory file-name))) + (masks masks)) + (while (and (file-exists-p dir) (not done) masks) + (let* ((mask (car masks)) + (dir-files (directory-files dir t mask))) + + (flymake-log 3 "dir %s, %d file(s) for mask %s" + dir (length dir-files) mask) + (while (and (not done) dir-files) + (when (not (file-directory-p (car dir-files))) + (setq files (cons (car dir-files) files)) + (when (>= (length files) flymake-master-file-count-limit) + (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit) + (setq done t))) + (setq dir-files (cdr dir-files)))) + (setq masks (cdr masks)))) + (setq dirs (cdr dirs))) + (when files + (let ((flymake-included-file-name (file-name-nondirectory file-name))) + (setq files (sort files 'flymake-master-file-compare)))) + (flymake-log 3 "found %d possible master file(s)" (length files)) + files)) + +(defun flymake-master-file-compare (file-one file-two) + "Compare two files specified by FILE-ONE and FILE-TWO. +This function is used in sort to move most possible file names +to the beginning of the list (File.h -> File.cpp moved to top)." + (and (equal (file-name-sans-extension flymake-included-file-name) + (file-name-base file-one)) + (not (equal file-one file-two)))) + +(defvar flymake-check-file-limit 8192 + "Maximum number of chars to look at when checking possible master file. +Nil means search the entire file.") + +(defun flymake-check-patch-master-file-buffer + (master-file-temp-buffer + master-file-name patched-master-file-name + source-file-name patched-source-file-name + include-dirs regexp) + "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME. +If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME +instead of SOURCE-FILE-NAME. + +For example, foo.cpp is a master file if it includes foo.h. + +When a buffer for MASTER-FILE-NAME exists, use it as a source +instead of reading master file from disk." + (let* ((source-file-nondir (file-name-nondirectory source-file-name)) + (source-file-extension (file-name-extension source-file-nondir)) + (source-file-nonext (file-name-sans-extension source-file-nondir)) + (found nil) + (inc-name nil) + (search-limit flymake-check-file-limit)) + (setq regexp + (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\"" + ;; Hack for tex files, where \include often excludes .tex. + ;; Maybe this is safe generally. + (if (and (> (length source-file-extension) 1) + (string-equal source-file-extension "tex")) + (format "%s\\(?:\\.%s\\)?" + (regexp-quote source-file-nonext) + (regexp-quote source-file-extension)) + (regexp-quote source-file-nondir)))) + (unwind-protect + (with-current-buffer master-file-temp-buffer + (if (or (not search-limit) + (> search-limit (point-max))) + (setq search-limit (point-max))) + (flymake-log 3 "checking %s against regexp %s" + master-file-name regexp) + (goto-char (point-min)) + (while (and (< (point) search-limit) + (re-search-forward regexp search-limit t)) + (let ((match-beg (match-beginning 1)) + (match-end (match-end 1))) + + (flymake-log 3 "found possible match for %s" source-file-nondir) + (setq inc-name (match-string 1)) + (and (> (length source-file-extension) 1) + (string-equal source-file-extension "tex") + (not (string-match (format "\\.%s\\'" source-file-extension) + inc-name)) + (setq inc-name (concat inc-name "." source-file-extension))) + (when (eq t (compare-strings + source-file-nondir nil nil + inc-name (- (length inc-name) + (length source-file-nondir)) nil)) + (flymake-log 3 "inc-name=%s" inc-name) + (when (flymake-check-include source-file-name inc-name + include-dirs) + (setq found t) + ;; replace-match is not used here as it fails in + ;; XEmacs with 'last match not a buffer' error as + ;; check-includes calls replace-in-string + (flymake-replace-region + match-beg match-end + (file-name-nondirectory patched-source-file-name)))) + (forward-line 1))) + (when found + (flymake-save-buffer-in-file patched-master-file-name))) + ;;+(flymake-log 3 "killing buffer %s" + ;; (buffer-name master-file-temp-buffer)) + (kill-buffer master-file-temp-buffer)) + ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found) + (when found + (flymake-log 2 "found master file %s" master-file-name)) + found)) + +;;; XXX: remove +(defun flymake-replace-region (beg end rep) + "Replace text in BUFFER in region (BEG END) with REP." + (save-excursion + (goto-char end) + ;; Insert before deleting, so as to better preserve markers's positions. + (insert rep) + (delete-region beg end))) + +(defun flymake-read-file-to-temp-buffer (file-name) + "Insert contents of FILE-NAME into newly created temp buffer." + (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name)))))) + (with-current-buffer temp-buffer + (insert-file-contents file-name)) + temp-buffer)) + +(defun flymake-copy-buffer-to-temp-buffer (buffer) + "Copy contents of BUFFER into newly created temp buffer." + (with-current-buffer + (get-buffer-create (generate-new-buffer-name + (concat "flymake:" (buffer-name buffer)))) + (insert-buffer-substring buffer) + (current-buffer))) + +(defun flymake-check-include (source-file-name inc-name include-dirs) + "Check if SOURCE-FILE-NAME can be found in include path. +Return t if it can be found via include path using INC-NAME." + (if (file-name-absolute-p inc-name) + (flymake-same-files source-file-name inc-name) + (while (and include-dirs + (not (flymake-same-files + source-file-name + (concat (file-name-directory source-file-name) + "/" (car include-dirs) + "/" inc-name)))) + (setq include-dirs (cdr include-dirs))) + include-dirs)) + +(defun flymake-find-buffer-for-file (file-name) + "Check if there exists a buffer visiting FILE-NAME. +Return t if so, nil if not." + (let ((buffer-name (get-file-buffer file-name))) + (if buffer-name + (get-buffer buffer-name)))) + +(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp) + "Save SOURCE-FILE-NAME with a different name. +Find master file, patch and save it." + (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks)) + (master-file-count (length possible-master-files)) + (idx 0) + (temp-buffer nil) + (master-file-name nil) + (patched-master-file-name nil) + (found nil)) + + (while (and (not found) (< idx master-file-count)) + (setq master-file-name (nth idx possible-master-files)) + (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master")) + (if (flymake-find-buffer-for-file master-file-name) + (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name))) + (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name))) + (setq found + (flymake-check-patch-master-file-buffer + temp-buffer + master-file-name + patched-master-file-name + source-file-name + patched-source-file-name + (funcall get-incl-dirs-f (file-name-directory master-file-name)) + include-regexp)) + (setq idx (1+ idx))) + (if found + (list master-file-name patched-master-file-name) + (progn + (flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count + (file-name-nondirectory source-file-name)) + nil)))) + +(defun flymake-save-buffer-in-file (file-name) + "Save the entire buffer contents into file FILE-NAME. +Create parent directories as needed." + (make-directory (file-name-directory file-name) 1) + (write-region nil nil file-name nil 566) + (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name)) + +(defun flymake-process-filter (process output) + "Parse OUTPUT and highlight error lines. +It's flymake process filter." + (let ((source-buffer (process-buffer process))) + + (flymake-log 3 "received %d byte(s) of output from process %d" + (length output) (process-id process)) + (when (buffer-live-p source-buffer) + (with-current-buffer source-buffer + (flymake-parse-output-and-residual output))))) + +(defun flymake-process-sentinel (process _event) + "Sentinel for syntax check buffers." + (when (memq (process-status process) '(signal exit)) + (let* ((exit-status (process-exit-status process)) + (command (process-command process)) + (source-buffer (process-buffer process)) + (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer)))) + + (flymake-log 2 "process %d exited with code %d" + (process-id process) exit-status) + (condition-case err + (progn + (flymake-log 3 "cleaning up using %s" cleanup-f) + (when (buffer-live-p source-buffer) + (with-current-buffer source-buffer + (funcall cleanup-f))) + + (delete-process process) + (setq flymake-processes (delq process flymake-processes)) + + (when (buffer-live-p source-buffer) + (with-current-buffer source-buffer + + (flymake-parse-residual) + (flymake-post-syntax-check exit-status command) + (setq flymake-is-running nil)))) + (error + (let ((err-str (format "Error in process sentinel for buffer %s: %s" + source-buffer (error-message-string err)))) + (flymake-log 0 err-str) + (with-current-buffer source-buffer + (setq flymake-is-running nil)))))))) + +(defun flymake-post-syntax-check (exit-status command) + (save-restriction + (widen) + (setq flymake-err-info flymake-new-err-info) + (setq flymake-new-err-info nil) + (setq flymake-err-info + (flymake-fix-line-numbers + flymake-err-info 1 (count-lines (point-min) (point-max)))) + (flymake-delete-own-overlays) + (flymake-highlight-err-lines flymake-err-info) + (let (err-count warn-count) + (setq err-count (flymake-get-err-count flymake-err-info "e")) + (setq warn-count (flymake-get-err-count flymake-err-info "w")) + (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)" + (buffer-name) err-count warn-count + (- (float-time) flymake-check-start-time)) + (setq flymake-check-start-time nil) + + (if (and (equal 0 err-count) (equal 0 warn-count)) + (if (equal 0 exit-status) + (flymake-report-status "" "") ; PASSED + (if (not flymake-check-was-interrupted) + (flymake-report-fatal-status "CFGERR" + (format "Configuration error has occurred while running %s" command)) + (flymake-report-status nil ""))) ; "STOPPED" + (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) + +(defun flymake-parse-output-and-residual (output) + "Split OUTPUT into lines, merge in residual if necessary." + (let* ((buffer-residual flymake-output-residual) + (total-output (if buffer-residual (concat buffer-residual output) output)) + (lines-and-residual (flymake-split-output total-output)) + (lines (nth 0 lines-and-residual)) + (new-residual (nth 1 lines-and-residual))) + (setq flymake-output-residual new-residual) + (setq flymake-new-err-info + (flymake-parse-err-lines + flymake-new-err-info lines)))) + +(defvar-local flymake-new-err-info nil + "Same as `flymake-err-info', effective when a syntax check is in progress.") + +(defun flymake-parse-residual () + "Parse residual if it's non empty." + (when flymake-output-residual + (setq flymake-new-err-info + (flymake-parse-err-lines + flymake-new-err-info + (list flymake-output-residual))) + (setq flymake-output-residual nil))) + +(defun flymake-parse-err-lines (err-info-list lines) + "Parse err LINES, store info in ERR-INFO-LIST." + (let* ((count (length lines)) + (idx 0) + (line-err-info nil) + (real-file-name nil) + (source-file-name buffer-file-name) + (get-real-file-name-f (flymake-get-real-file-name-function source-file-name))) + + (while (< idx count) + (setq line-err-info (flymake-parse-line (nth idx lines))) + (when line-err-info + (setq real-file-name (funcall get-real-file-name-f + (flymake-ler-file line-err-info))) + (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name)) + + (when (flymake-same-files real-file-name source-file-name) + (setq line-err-info (flymake-ler-set-file line-err-info nil)) + (setq err-info-list (flymake-add-err-info err-info-list line-err-info)))) + (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no")) + (setq idx (1+ idx))) + err-info-list)) + +(defun flymake-split-output (output) + "Split OUTPUT into lines. +Return last one as residual if it does not end with newline char. +Returns ((LINES) RESIDUAL)." + (when (and output (> (length output) 0)) + (let* ((lines (split-string output "[\n\r]+" t)) + (complete (equal "\n" (char-to-string (aref output (1- (length output)))))) + (residual nil)) + (when (not complete) + (setq residual (car (last lines))) + (setq lines (butlast lines))) + (list lines residual)))) + +(defun flymake-reformat-err-line-patterns-from-compile-el (original-list) + "Grab error line patterns from ORIGINAL-LIST in compile.el format. +Convert it to flymake internal format." + (let* ((converted-list '())) + (dolist (item original-list) + (setq item (cdr item)) + (let ((regexp (nth 0 item)) + (file (nth 1 item)) + (line (nth 2 item)) + (col (nth 3 item))) + (if (consp file) (setq file (car file))) + (if (consp line) (setq line (car line))) + (if (consp col) (setq col (car col))) + + (when (not (functionp line)) + (setq converted-list (cons (list regexp file line col) converted-list))))) + converted-list)) + +(require 'compile) + +(defvar flymake-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text + (append + '( + ;; MS Visual C++ 6.0 + ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)" + 1 3 nil 4) + ;; jikes + ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)" + 1 3 nil 4) + ;; MS midl + ("midl[ ]*:[ ]*\\(command line error .*\\)" + nil nil nil 1) + ;; MS C# + ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)" + 1 3 nil 4) + ;; perl + ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1) + ;; PHP + ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1) + ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1) + ;; ant/javac. Note this also matches gcc warnings! + (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)" + 2 4 nil 5)) + ;; compilation-error-regexp-alist) + (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist)) + "Patterns for matching error/warning lines. Each pattern has the form +\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). +Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns +from compile.el") + +(define-obsolete-variable-alias 'flymake-warning-re 'flymake-warning-predicate "24.4") +(defvar flymake-warning-predicate "^[wW]arning" + "Predicate matching against error text to detect a warning. +Takes a single argument, the error's text and should return non-nil +if it's a warning. +Instead of a function, it can also be a regular expression.") + +(defun flymake-parse-line (line) + "Parse LINE to see if it is an error or warning. +Return its components if so, nil otherwise." + (let ((raw-file-name nil) + (line-no 0) + (err-type "e") + (err-text nil) + (patterns flymake-err-line-patterns) + (matched nil)) + (while (and patterns (not matched)) + (when (string-match (car (car patterns)) line) + (let* ((file-idx (nth 1 (car patterns))) + (line-idx (nth 2 (car patterns)))) + + (setq raw-file-name (if file-idx (match-string file-idx line) nil)) + (setq line-no (if line-idx (string-to-number + (match-string line-idx line)) 0)) + (setq err-text (if (> (length (car patterns)) 4) + (match-string (nth 4 (car patterns)) line) + (flymake-patch-err-text + (substring line (match-end 0))))) + (if (null err-text) + (setq err-text "") + (when (cond ((stringp flymake-warning-predicate) + (string-match flymake-warning-predicate err-text)) + ((functionp flymake-warning-predicate) + (funcall flymake-warning-predicate err-text))) + (setq err-type "w"))) + (flymake-log + 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s" + file-idx line-idx raw-file-name line-no err-text) + (setq matched t))) + (setq patterns (cdr patterns))) + (if matched + (flymake-ler-make-ler raw-file-name line-no err-type err-text) + ()))) + +(defun flymake-get-project-include-dirs-imp (basedir) + "Include dirs for the project current file belongs to." + (if (flymake-get-project-include-dirs-from-cache basedir) + (progn + (flymake-get-project-include-dirs-from-cache basedir)) + ;;else + (let* ((command-line (concat "make -C " + (shell-quote-argument basedir) + " DUMPVARS=INCLUDE_DIRS dumpvars")) + (output (shell-command-to-string command-line)) + (lines (split-string output "\n" t)) + (count (length lines)) + (idx 0) + (inc-dirs nil)) + (while (and (< idx count) (not (string-match "^INCLUDE_DIRS=.*" (nth idx lines)))) + (setq idx (1+ idx))) + (when (< idx count) + (let* ((inc-lines (split-string (nth idx lines) " *-I" t)) + (inc-count (length inc-lines))) + (while (> inc-count 0) + (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines))) + (push (replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs)) + (setq inc-count (1- inc-count))))) + (flymake-add-project-include-dirs-to-cache basedir inc-dirs) + inc-dirs))) + +(defvar flymake-get-project-include-dirs-function #'flymake-get-project-include-dirs-imp + "Function used to get project include dirs, one parameter: basedir name.") + +(defun flymake-get-project-include-dirs (basedir) + (funcall flymake-get-project-include-dirs-function basedir)) + +(defun flymake-get-system-include-dirs () + "System include dirs - from the `INCLUDE' env setting." + (let* ((includes (getenv "INCLUDE"))) + (if includes (split-string includes path-separator t) nil))) + +(defvar flymake-project-include-dirs-cache (make-hash-table :test #'equal)) + +(defun flymake-get-project-include-dirs-from-cache (base-dir) + (gethash base-dir flymake-project-include-dirs-cache)) + +(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs) + (puthash base-dir include-dirs flymake-project-include-dirs-cache)) + +(defun flymake-clear-project-include-dirs-cache () + (clrhash flymake-project-include-dirs-cache)) + +(defun flymake-get-include-dirs (base-dir) + "Get dirs to use when resolving local file names." + (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs)))) + include-dirs)) + +;; (defun flymake-restore-formatting () +;; "Remove any formatting made by flymake." +;; ) + +;; (defun flymake-get-program-dir (buffer) +;; "Get dir to start program in." +;; (unless (bufferp buffer) +;; (error "Invalid buffer")) +;; (with-current-buffer buffer +;; default-directory)) + +(defun flymake-safe-delete-file (file-name) + (when (and file-name (file-exists-p file-name)) + (delete-file file-name) + (flymake-log 1 "deleted file %s" file-name))) + +(defun flymake-safe-delete-directory (dir-name) + (condition-case nil + (progn + (delete-directory dir-name) + (flymake-log 1 "deleted dir %s" dir-name)) + (error + (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) + +(defun flymake-start-syntax-check () + "Start syntax checking for current buffer." + (interactive) + (flymake-log 3 "flymake is running: %s" flymake-is-running) + (when (and (not flymake-is-running) + (flymake-can-syntax-check-file buffer-file-name)) + (when (or (not flymake-compilation-prevents-syntax-check) + (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP") + (flymake-clear-buildfile-cache) + (flymake-clear-project-include-dirs-cache) + + (setq flymake-check-was-interrupted nil) + + (let* ((source-file-name buffer-file-name) + (init-f (flymake-get-init-function source-file-name)) + (cleanup-f (flymake-get-cleanup-function source-file-name)) + (cmd-and-args (funcall init-f)) + (cmd (nth 0 cmd-and-args)) + (args (nth 1 cmd-and-args)) + (dir (nth 2 cmd-and-args))) + (if (not cmd-and-args) + (progn + (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name) + (funcall cleanup-f)) + (progn + (setq flymake-last-change-time nil) + (flymake-start-syntax-check-process cmd args dir))))))) + +(defun flymake-start-syntax-check-process (cmd args dir) +"Start syntax check process." +(condition-case err + (let* ((process + (let ((default-directory (or dir default-directory))) + (when dir + (flymake-log 3 "starting process on dir %s" dir)) + (apply 'start-file-process + "flymake-proc" (current-buffer) cmd args)))) + (set-process-sentinel process 'flymake-process-sentinel) + (set-process-filter process 'flymake-process-filter) + (set-process-query-on-exit-flag process nil) + (push process flymake-processes) + + (setq flymake-is-running t) + (setq flymake-last-change-time nil) + + (flymake-report-status nil "*") + (flymake-log 2 "started process %d, command=%s, dir=%s" + (process-id process) (process-command process) + default-directory) + process) + (error + (let* ((err-str + (format-message + "Failed to launch syntax check process `%s' with args %s: %s" + cmd args (error-message-string err))) + (source-file-name buffer-file-name) + (cleanup-f (flymake-get-cleanup-function source-file-name))) + (flymake-log 0 err-str) + (funcall cleanup-f) + (flymake-report-fatal-status "PROCERR" err-str))))) + +(defun flymake-kill-process (proc) + "Kill process PROC." + (kill-process proc) + (let* ((buf (process-buffer proc))) + (when (buffer-live-p buf) + (with-current-buffer buf + (setq flymake-check-was-interrupted t)))) + (flymake-log 1 "killed process %d" (process-id proc))) + +(defun flymake-stop-all-syntax-checks () + "Kill all syntax check processes." + (interactive) + (while flymake-processes + (flymake-kill-process (pop flymake-processes)))) + +(defun flymake-compilation-is-running () + (and (boundp 'compilation-in-progress) + compilation-in-progress)) + +(defun flymake-compile () + "Kill all flymake syntax checks, start compilation." + (interactive) + (flymake-stop-all-syntax-checks) + (call-interactively 'compile)) + +;;;; general init-cleanup and helper routines +(defun flymake-create-temp-inplace (file-name prefix) + (unless (stringp file-name) + (error "Invalid file-name")) + (or prefix + (setq prefix "flymake")) + (let* ((ext (file-name-extension file-name)) + (temp-name (file-truename + (concat (file-name-sans-extension file-name) + "_" prefix + (and ext (concat "." ext)))))) + (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name) + temp-name)) + +(defun flymake-create-temp-with-folder-structure (file-name _prefix) + (unless (stringp file-name) + (error "Invalid file-name")) + + (let* ((dir (file-name-directory file-name)) + ;; Not sure what this slash-pos is all about, but I guess it's just + ;; trying to remove the leading / of absolute file names. + (slash-pos (string-match "/" dir)) + (temp-dir (expand-file-name (substring dir (1+ slash-pos)) + temporary-file-directory))) + + (file-truename (expand-file-name (file-name-nondirectory file-name) + temp-dir)))) + +(defun flymake-delete-temp-directory (dir-name) + "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error." + (let* ((temp-dir temporary-file-directory) + (suffix (substring dir-name (1+ (length temp-dir))))) + + (while (> (length suffix) 0) + (setq suffix (directory-file-name suffix)) + ;;+(flymake-log 0 "suffix=%s" suffix) + (flymake-safe-delete-directory + (file-truename (expand-file-name suffix temp-dir))) + (setq suffix (file-name-directory suffix))))) + +(defvar-local flymake-temp-source-file-name nil) +(defvar-local flymake-master-file-name nil) +(defvar-local flymake-temp-master-file-name nil) +(defvar-local flymake-base-dir nil) + +(defun flymake-init-create-temp-buffer-copy (create-temp-f) + "Make a temporary copy of the current buffer, save its name in buffer data and return the name." + (let* ((source-file-name buffer-file-name) + (temp-source-file-name (funcall create-temp-f source-file-name "flymake"))) + + (flymake-save-buffer-in-file temp-source-file-name) + (setq flymake-temp-source-file-name temp-source-file-name) + temp-source-file-name)) + +(defun flymake-simple-cleanup () + "Do cleanup after `flymake-init-create-temp-buffer-copy'. +Delete temp file." + (flymake-safe-delete-file flymake-temp-source-file-name) + (setq flymake-last-change-time nil)) + +(defun flymake-get-real-file-name (file-name-from-err-msg) + "Translate file name from error message to \"real\" file name. +Return full-name. Names are real, not patched." + (let* ((real-name nil) + (source-file-name buffer-file-name) + (master-file-name flymake-master-file-name) + (temp-source-file-name flymake-temp-source-file-name) + (temp-master-file-name flymake-temp-master-file-name) + (base-dirs + (list flymake-base-dir + (file-name-directory source-file-name) + (if master-file-name (file-name-directory master-file-name)))) + (files (list (list source-file-name source-file-name) + (list temp-source-file-name source-file-name) + (list master-file-name master-file-name) + (list temp-master-file-name master-file-name)))) + + (when (equal 0 (length file-name-from-err-msg)) + (setq file-name-from-err-msg source-file-name)) + + (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files)) + ;; if real-name is nil, than file name from err msg is none of the files we've patched + (if (not real-name) + (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs))) + (if (not real-name) + (setq real-name file-name-from-err-msg)) + (setq real-name (flymake-fix-file-name real-name)) + (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name) + real-name)) + +(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files) + (let* ((base-dirs-count (length base-dirs)) + (file-count (length files)) + (real-name nil)) + + (while (and (not real-name) (> base-dirs-count 0)) + (setq file-count (length files)) + (while (and (not real-name) (> file-count 0)) + (let* ((this-dir (nth (1- base-dirs-count) base-dirs)) + (this-file (nth 0 (nth (1- file-count) files))) + (this-real-name (nth 1 (nth (1- file-count) files)))) + ;;+(flymake-log 0 "this-dir=%s this-file=%s this-real=%s msg-file=%s" this-dir this-file this-real-name file-name-from-err-msg) + (when (and this-dir this-file (flymake-same-files + (expand-file-name file-name-from-err-msg this-dir) + this-file)) + (setq real-name this-real-name))) + (setq file-count (1- file-count))) + (setq base-dirs-count (1- base-dirs-count))) + real-name)) + +(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs) + (let* ((real-name nil)) + (if (file-name-absolute-p file-name-from-err-msg) + (setq real-name file-name-from-err-msg) + (let* ((base-dirs-count (length base-dirs))) + (while (and (not real-name) (> base-dirs-count 0)) + (let* ((full-name (expand-file-name file-name-from-err-msg + (nth (1- base-dirs-count) base-dirs)))) + (if (file-exists-p full-name) + (setq real-name full-name)) + (setq base-dirs-count (1- base-dirs-count)))))) + real-name)) + +(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name) + "Find buildfile, store its dir in buffer data and return its dir, if found." + (let* ((buildfile-dir + (flymake-find-buildfile buildfile-name + (file-name-directory source-file-name)))) + (if buildfile-dir + (setq flymake-base-dir buildfile-dir) + (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name) + (flymake-report-fatal-status + "NOMK" (format "No buildfile (%s) found for %s" + buildfile-name source-file-name))))) + +(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp) + "Find master file (or buffer), create its copy along with a copy of the source file." + (let* ((source-file-name buffer-file-name) + (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)) + (master-and-temp-master (flymake-create-master-file + source-file-name temp-source-file-name + get-incl-dirs-f create-temp-f + master-file-masks include-regexp))) + + (if (not master-and-temp-master) + (progn + (flymake-log 1 "cannot find master file for %s" source-file-name) + (flymake-report-status "!" "") ; NOMASTER + nil) + (setq flymake-master-file-name (nth 0 master-and-temp-master)) + (setq flymake-temp-master-file-name (nth 1 master-and-temp-master))))) + +(defun flymake-master-cleanup () + (flymake-simple-cleanup) + (flymake-safe-delete-file flymake-temp-master-file-name)) + +;;;; make-specific init-cleanup routines +(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f) + "Create a command line for syntax check using GET-CMD-LINE-F." + (funcall get-cmd-line-f + (if use-relative-source + (file-relative-name source-file-name base-dir) + source-file-name) + (if use-relative-base-dir + (file-relative-name base-dir + (file-name-directory source-file-name)) + base-dir))) + +(defun flymake-get-make-cmdline (source base-dir) + (list "make" + (list "-s" + "-C" + base-dir + (concat "CHK_SOURCES=" source) + "SYNTAX_CHECK_MODE=1" + "check-syntax"))) + +(defun flymake-get-ant-cmdline (source base-dir) + (list "ant" + (list "-buildfile" + (concat base-dir "/" "build.xml") + (concat "-DCHK_SOURCES=" source) + "check-syntax"))) + +(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f) + "Create syntax check command line for a directly checked source file. +Use CREATE-TEMP-F for creating temp copy." + (let* ((args nil) + (source-file-name buffer-file-name) + (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name))) + (if buildfile-dir + (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))) + (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir + use-relative-base-dir use-relative-source + get-cmdline-f)))) + args)) + +(defun flymake-simple-make-init () + (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline)) + +(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp) + "Create make command line for a source file checked via master file compilation." + (let* ((make-args nil) + (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy + get-incl-dirs-f 'flymake-create-temp-inplace + master-file-masks include-regexp))) + (when temp-master-file-name + (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile"))) + (if buildfile-dir + (setq make-args (flymake-get-syntax-check-program-args + temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline))))) + make-args)) + +(defun flymake-find-make-buildfile (source-dir) + (flymake-find-buildfile "Makefile" source-dir)) + +;;;; .h/make specific +(defun flymake-master-make-header-init () + (flymake-master-make-init + 'flymake-get-include-dirs + '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'") + "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\"")) + +;;;; .java/make specific +(defun flymake-simple-make-java-init () + (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline)) + +(defun flymake-simple-ant-java-init () + (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline)) + +(defun flymake-simple-java-cleanup () + "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs." + (flymake-safe-delete-file flymake-temp-source-file-name) + (when flymake-temp-source-file-name + (flymake-delete-temp-directory + (file-name-directory flymake-temp-source-file-name)))) + +;;;; perl-specific init-cleanup routines +(defun flymake-perl-init () + (let* ((temp-file (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-inplace)) + (local-file (file-relative-name + temp-file + (file-name-directory buffer-file-name)))) + (list "perl" (list "-wc " local-file)))) + +;;;; php-specific init-cleanup routines +(defun flymake-php-init () + (let* ((temp-file (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-inplace)) + (local-file (file-relative-name + temp-file + (file-name-directory buffer-file-name)))) + (list "php" (list "-f" local-file "-l")))) + +;;;; tex-specific init-cleanup routines +(defun flymake-get-tex-args (file-name) + ;;(list "latex" (list "-c-style-errors" file-name)) + (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name))) + +(defun flymake-simple-tex-init () + (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))) + +;; Perhaps there should be a buffer-local variable flymake-master-file +;; that people can set to override this stuff. Could inherit from +;; the similar AUCTeX variable. +(defun flymake-master-tex-init () + (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy + 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace + '("\\.tex\\'") + "[ \t]*\\in\\(?:put\\|clude\\)[ \t]*{\\(.*%s\\)}"))) + (when temp-master-file-name + (flymake-get-tex-args temp-master-file-name)))) + +(defun flymake-get-include-dirs-dot (_base-dir) + '(".")) + +;;;; xml-specific init-cleanup routines +(defun flymake-xml-init () + (list flymake-xml-program + (list "val" (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-inplace)))) + +(provide 'flymake-proc) +;;; flymake-proc.el ends here diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 866116fbec..441784c8a1 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -1,4 +1,4 @@ -;;; flymake.el --- a universal on-the-fly syntax checker -*- lexical-binding: t; -*- +;;; flymake.el --- A universal on-the-fly syntax checker -*- lexical-binding: t; -*- ;; Copyright (C) 2003-2017 Free Software Foundation, Inc. @@ -20,19 +20,16 @@ ;; 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 . +;; along with GNU Emacs. If not, see . ;;; Commentary: ;; -;; Flymake is a minor Emacs mode performing on-the-fly syntax checks -;; using the external syntax check tool (for C/C++ this is usually the -;; compiler). - -;;; Bugs/todo: - -;; - Only uses "Makefile", not "makefile" or "GNUmakefile" -;; (from http://bugs.debian.org/337339). - +;; Flymake is a minor Emacs mode performing on-the-fly syntax checks. +;; +;; This file contains the UI for displaying and interacting with the +;; results of such checks, as well as entry points for backends to +;; hook on to. Backends are sources of diagnostic info. +;; ;;; Code: (eval-when-compile (require 'cl-lib)) @@ -83,11 +80,6 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." (const right-fringe) (const :tag "No fringe indicators" nil))) -(defcustom flymake-compilation-prevents-syntax-check t - "If non-nil, don't start syntax check if compilation is running." - :group 'flymake - :type 'boolean) - (defcustom flymake-start-syntax-check-on-newline t "Start syntax check if newline char was added/removed from the buffer." :group 'flymake @@ -116,69 +108,6 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." :group 'flymake :type 'integer) -(defcustom flymake-xml-program - (if (executable-find "xmlstarlet") "xmlstarlet" "xml") - "Program to use for XML validation." - :type 'file - :group 'flymake - :version "24.4") - -(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest") - "Dirs where to look for master files." - :group 'flymake - :type '(repeat (string))) - -(defcustom flymake-master-file-count-limit 32 - "Max number of master files to check." - :group 'flymake - :type 'integer) - -(defcustom flymake-allowed-file-name-masks - '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-simple-make-init) - ("\\.xml\\'" flymake-xml-init) - ("\\.html?\\'" flymake-xml-init) - ("\\.cs\\'" flymake-simple-make-init) - ("\\.p[ml]\\'" flymake-perl-init) - ("\\.php[345]?\\'" flymake-php-init) - ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup) - ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup) - ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup) - ("\\.tex\\'" flymake-simple-tex-init) - ("\\.idl\\'" flymake-simple-make-init) - ;; ("\\.cpp\\'" 1) - ;; ("\\.java\\'" 3) - ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'") - ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2)) - ;; ("\\.idl\\'" 1) - ;; ("\\.odl\\'" 1) - ;; ("[0-9]+\\.tex\\'" 2 ("\\.tex\\'") - ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 )) - ;; ("\\.tex\\'" 1) - ) - "Files syntax checking is allowed for. -This is an alist with elements of the form: - REGEXP INIT [CLEANUP [NAME]] -REGEXP is a regular expression that matches a file name. -INIT is the init function to use, missing means disable `flymake-mode'. -CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'. -NAME is the file name function to use, default `flymake-get-real-file-name'." - :group 'flymake - :type '(alist :key-type (regexp :tag "File regexp") - :value-type - (list :tag "Handler functions" - (choice :tag "Init function" - (const :tag "disable" nil) - function) - (choice :tag "Cleanup function" - (const :tag "flymake-simple-cleanup" nil) - function) - (choice :tag "Name function" - (const :tag "flymake-get-real-file-name" nil) - function)))) - -(defvar-local flymake-is-running nil - "If t, flymake syntax check process is running for the current buffer.") - (defvar-local flymake-timer nil "Timer for starting syntax check.") @@ -221,392 +150,6 @@ POS counts from zero." (setcar (nthcdr pos tmp) val) tmp)) -(defvar flymake-processes nil - "List of currently active flymake processes.") - -(defvar-local flymake-output-residual nil) - -(defun flymake-get-file-name-mode-and-masks (file-name) - "Return the corresponding entry from `flymake-allowed-file-name-masks'." - (unless (stringp file-name) - (error "Invalid file-name")) - (let ((fnm flymake-allowed-file-name-masks) - (mode-and-masks nil)) - (while (and (not mode-and-masks) fnm) - (let ((item (pop fnm))) - (when (string-match (car item) file-name) - (setq mode-and-masks item)))) ; (cdr item) may be nil - (setq mode-and-masks (cdr mode-and-masks)) - (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks)) - mode-and-masks)) - -(defun flymake-can-syntax-check-file (file-name) - "Determine whether we can syntax check FILE-NAME. -Return nil if we cannot, non-nil if we can." - (if (flymake-get-init-function file-name) t nil)) - -(defun flymake-get-init-function (file-name) - "Return init function to be used for the file." - (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name)))) - ;;(flymake-log 0 "calling %s" init-f) - ;;(funcall init-f (current-buffer)) - init-f)) - -(defun flymake-get-cleanup-function (file-name) - "Return cleanup function to be used for the file." - (or (nth 1 (flymake-get-file-name-mode-and-masks file-name)) - 'flymake-simple-cleanup)) - -(defun flymake-get-real-file-name-function (file-name) - (or (nth 2 (flymake-get-file-name-mode-and-masks file-name)) - 'flymake-get-real-file-name)) - -(defvar flymake-find-buildfile-cache (make-hash-table :test #'equal)) - -(defun flymake-get-buildfile-from-cache (dir-name) - "Look up DIR-NAME in cache and return its associated value. -If DIR-NAME is not found, return nil." - (gethash dir-name flymake-find-buildfile-cache)) - -(defun flymake-add-buildfile-to-cache (dir-name buildfile) - "Associate DIR-NAME with BUILDFILE in the buildfile cache." - (puthash dir-name buildfile flymake-find-buildfile-cache)) - -(defun flymake-clear-buildfile-cache () - "Clear the buildfile cache." - (clrhash flymake-find-buildfile-cache)) - -(defun flymake-find-buildfile (buildfile-name source-dir-name) - "Find buildfile starting from current directory. -Buildfile includes Makefile, build.xml etc. -Return its file name if found, or nil if not found." - (or (flymake-get-buildfile-from-cache source-dir-name) - (let* ((file (locate-dominating-file source-dir-name buildfile-name))) - (if file - (progn - (flymake-log 3 "found buildfile at %s" file) - (flymake-add-buildfile-to-cache source-dir-name file) - file) - (progn - (flymake-log 3 "buildfile for %s not found" source-dir-name) - nil))))) - -(defun flymake-fix-file-name (name) - "Replace all occurrences of `\\' with `/'." - (when name - (setq name (expand-file-name name)) - (setq name (abbreviate-file-name name)) - (setq name (directory-file-name name)) - name)) - -(defun flymake-same-files (file-name-one file-name-two) - "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file. -Return t if so, nil if not." - (equal (flymake-fix-file-name file-name-one) - (flymake-fix-file-name file-name-two))) - -;; This is bound dynamically to pass a parameter to a sort predicate below -(defvar flymake-included-file-name) - -(defun flymake-find-possible-master-files (file-name master-file-dirs masks) - "Find (by name and location) all possible master files. - -Name is specified by FILE-NAME and location is specified by -MASTER-FILE-DIRS. Master files include .cpp and .c for .h. -Files are searched for starting from the .h directory and max -max-level parent dirs. File contents are not checked." - (let* ((dirs master-file-dirs) - (files nil) - (done nil)) - - (while (and (not done) dirs) - (let* ((dir (expand-file-name (car dirs) (file-name-directory file-name))) - (masks masks)) - (while (and (file-exists-p dir) (not done) masks) - (let* ((mask (car masks)) - (dir-files (directory-files dir t mask))) - - (flymake-log 3 "dir %s, %d file(s) for mask %s" - dir (length dir-files) mask) - (while (and (not done) dir-files) - (when (not (file-directory-p (car dir-files))) - (setq files (cons (car dir-files) files)) - (when (>= (length files) flymake-master-file-count-limit) - (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit) - (setq done t))) - (setq dir-files (cdr dir-files)))) - (setq masks (cdr masks)))) - (setq dirs (cdr dirs))) - (when files - (let ((flymake-included-file-name (file-name-nondirectory file-name))) - (setq files (sort files 'flymake-master-file-compare)))) - (flymake-log 3 "found %d possible master file(s)" (length files)) - files)) - -(defun flymake-master-file-compare (file-one file-two) - "Compare two files specified by FILE-ONE and FILE-TWO. -This function is used in sort to move most possible file names -to the beginning of the list (File.h -> File.cpp moved to top)." - (and (equal (file-name-sans-extension flymake-included-file-name) - (file-name-base file-one)) - (not (equal file-one file-two)))) - -(defvar flymake-check-file-limit 8192 - "Maximum number of chars to look at when checking possible master file. -Nil means search the entire file.") - -(defun flymake-check-patch-master-file-buffer - (master-file-temp-buffer - master-file-name patched-master-file-name - source-file-name patched-source-file-name - include-dirs regexp) - "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME. -If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME -instead of SOURCE-FILE-NAME. - -For example, foo.cpp is a master file if it includes foo.h. - -When a buffer for MASTER-FILE-NAME exists, use it as a source -instead of reading master file from disk." - (let* ((source-file-nondir (file-name-nondirectory source-file-name)) - (source-file-extension (file-name-extension source-file-nondir)) - (source-file-nonext (file-name-sans-extension source-file-nondir)) - (found nil) - (inc-name nil) - (search-limit flymake-check-file-limit)) - (setq regexp - (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\"" - ;; Hack for tex files, where \include often excludes .tex. - ;; Maybe this is safe generally. - (if (and (> (length source-file-extension) 1) - (string-equal source-file-extension "tex")) - (format "%s\\(?:\\.%s\\)?" - (regexp-quote source-file-nonext) - (regexp-quote source-file-extension)) - (regexp-quote source-file-nondir)))) - (unwind-protect - (with-current-buffer master-file-temp-buffer - (if (or (not search-limit) - (> search-limit (point-max))) - (setq search-limit (point-max))) - (flymake-log 3 "checking %s against regexp %s" - master-file-name regexp) - (goto-char (point-min)) - (while (and (< (point) search-limit) - (re-search-forward regexp search-limit t)) - (let ((match-beg (match-beginning 1)) - (match-end (match-end 1))) - - (flymake-log 3 "found possible match for %s" source-file-nondir) - (setq inc-name (match-string 1)) - (and (> (length source-file-extension) 1) - (string-equal source-file-extension "tex") - (not (string-match (format "\\.%s\\'" source-file-extension) - inc-name)) - (setq inc-name (concat inc-name "." source-file-extension))) - (when (eq t (compare-strings - source-file-nondir nil nil - inc-name (- (length inc-name) - (length source-file-nondir)) nil)) - (flymake-log 3 "inc-name=%s" inc-name) - (when (flymake-check-include source-file-name inc-name - include-dirs) - (setq found t) - ;; replace-match is not used here as it fails in - ;; XEmacs with 'last match not a buffer' error as - ;; check-includes calls replace-in-string - (flymake-replace-region - match-beg match-end - (file-name-nondirectory patched-source-file-name)))) - (forward-line 1))) - (when found - (flymake-save-buffer-in-file patched-master-file-name))) - ;;+(flymake-log 3 "killing buffer %s" - ;; (buffer-name master-file-temp-buffer)) - (kill-buffer master-file-temp-buffer)) - ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found) - (when found - (flymake-log 2 "found master file %s" master-file-name)) - found)) - -;;; XXX: remove -(defun flymake-replace-region (beg end rep) - "Replace text in BUFFER in region (BEG END) with REP." - (save-excursion - (goto-char end) - ;; Insert before deleting, so as to better preserve markers's positions. - (insert rep) - (delete-region beg end))) - -(defun flymake-read-file-to-temp-buffer (file-name) - "Insert contents of FILE-NAME into newly created temp buffer." - (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name)))))) - (with-current-buffer temp-buffer - (insert-file-contents file-name)) - temp-buffer)) - -(defun flymake-copy-buffer-to-temp-buffer (buffer) - "Copy contents of BUFFER into newly created temp buffer." - (with-current-buffer - (get-buffer-create (generate-new-buffer-name - (concat "flymake:" (buffer-name buffer)))) - (insert-buffer-substring buffer) - (current-buffer))) - -(defun flymake-check-include (source-file-name inc-name include-dirs) - "Check if SOURCE-FILE-NAME can be found in include path. -Return t if it can be found via include path using INC-NAME." - (if (file-name-absolute-p inc-name) - (flymake-same-files source-file-name inc-name) - (while (and include-dirs - (not (flymake-same-files - source-file-name - (concat (file-name-directory source-file-name) - "/" (car include-dirs) - "/" inc-name)))) - (setq include-dirs (cdr include-dirs))) - include-dirs)) - -(defun flymake-find-buffer-for-file (file-name) - "Check if there exists a buffer visiting FILE-NAME. -Return t if so, nil if not." - (let ((buffer-name (get-file-buffer file-name))) - (if buffer-name - (get-buffer buffer-name)))) - -(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp) - "Save SOURCE-FILE-NAME with a different name. -Find master file, patch and save it." - (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks)) - (master-file-count (length possible-master-files)) - (idx 0) - (temp-buffer nil) - (master-file-name nil) - (patched-master-file-name nil) - (found nil)) - - (while (and (not found) (< idx master-file-count)) - (setq master-file-name (nth idx possible-master-files)) - (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master")) - (if (flymake-find-buffer-for-file master-file-name) - (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name))) - (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name))) - (setq found - (flymake-check-patch-master-file-buffer - temp-buffer - master-file-name - patched-master-file-name - source-file-name - patched-source-file-name - (funcall get-incl-dirs-f (file-name-directory master-file-name)) - include-regexp)) - (setq idx (1+ idx))) - (if found - (list master-file-name patched-master-file-name) - (progn - (flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count - (file-name-nondirectory source-file-name)) - nil)))) - -(defun flymake-save-buffer-in-file (file-name) - "Save the entire buffer contents into file FILE-NAME. -Create parent directories as needed." - (make-directory (file-name-directory file-name) 1) - (write-region nil nil file-name nil 566) - (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name)) - -(defun flymake-process-filter (process output) - "Parse OUTPUT and highlight error lines. -It's flymake process filter." - (let ((source-buffer (process-buffer process))) - - (flymake-log 3 "received %d byte(s) of output from process %d" - (length output) (process-id process)) - (when (buffer-live-p source-buffer) - (with-current-buffer source-buffer - (flymake-parse-output-and-residual output))))) - -(defun flymake-process-sentinel (process _event) - "Sentinel for syntax check buffers." - (when (memq (process-status process) '(signal exit)) - (let* ((exit-status (process-exit-status process)) - (command (process-command process)) - (source-buffer (process-buffer process)) - (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer)))) - - (flymake-log 2 "process %d exited with code %d" - (process-id process) exit-status) - (condition-case err - (progn - (flymake-log 3 "cleaning up using %s" cleanup-f) - (when (buffer-live-p source-buffer) - (with-current-buffer source-buffer - (funcall cleanup-f))) - - (delete-process process) - (setq flymake-processes (delq process flymake-processes)) - - (when (buffer-live-p source-buffer) - (with-current-buffer source-buffer - - (flymake-parse-residual) - (flymake-post-syntax-check exit-status command) - (setq flymake-is-running nil)))) - (error - (let ((err-str (format "Error in process sentinel for buffer %s: %s" - source-buffer (error-message-string err)))) - (flymake-log 0 err-str) - (with-current-buffer source-buffer - (setq flymake-is-running nil)))))))) - -(defun flymake-post-syntax-check (exit-status command) - (save-restriction - (widen) - (setq flymake-err-info flymake-new-err-info) - (setq flymake-new-err-info nil) - (setq flymake-err-info - (flymake-fix-line-numbers - flymake-err-info 1 (count-lines (point-min) (point-max)))) - (flymake-delete-own-overlays) - (flymake-highlight-err-lines flymake-err-info) - (let (err-count warn-count) - (setq err-count (flymake-get-err-count flymake-err-info "e")) - (setq warn-count (flymake-get-err-count flymake-err-info "w")) - (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)" - (buffer-name) err-count warn-count - (- (float-time) flymake-check-start-time)) - (setq flymake-check-start-time nil) - - (if (and (equal 0 err-count) (equal 0 warn-count)) - (if (equal 0 exit-status) - (flymake-report-status "" "") ; PASSED - (if (not flymake-check-was-interrupted) - (flymake-report-fatal-status "CFGERR" - (format "Configuration error has occurred while running %s" command)) - (flymake-report-status nil ""))) ; "STOPPED" - (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) - -(defun flymake-parse-output-and-residual (output) - "Split OUTPUT into lines, merge in residual if necessary." - (let* ((buffer-residual flymake-output-residual) - (total-output (if buffer-residual (concat buffer-residual output) output)) - (lines-and-residual (flymake-split-output total-output)) - (lines (nth 0 lines-and-residual)) - (new-residual (nth 1 lines-and-residual))) - (setq flymake-output-residual new-residual) - (setq flymake-new-err-info - (flymake-parse-err-lines - flymake-new-err-info lines)))) - -(defun flymake-parse-residual () - "Parse residual if it's non empty." - (when flymake-output-residual - (setq flymake-new-err-info - (flymake-parse-err-lines - flymake-new-err-info - (list flymake-output-residual))) - (setq flymake-output-residual nil))) - (defun flymake-er-make-er (line-no line-err-info-list) (list line-no line-err-info-list)) @@ -665,26 +208,6 @@ Value of TYPE is either \"e\" or \"w\"." (setq idx (1+ idx))) err-count)) -(defun flymake-fix-line-numbers (err-info-list min-line max-line) - "Replace line numbers with fixed value. -If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE. -If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE. -The reason for this fix is because some compilers might report -line number outside the file being compiled." - (let* ((count (length err-info-list)) - (err-info nil) - (line 0)) - (while (> count 0) - (setq err-info (nth (1- count) err-info-list)) - (setq line (flymake-er-get-line err-info)) - (when (or (< line min-line) (> line max-line)) - (setq line (if (< line min-line) min-line max-line)) - (setq err-info-list (flymake-set-at err-info-list (1- count) - (flymake-er-make-er line - (flymake-er-get-line-err-info-list err-info))))) - (setq count (1- count)))) - err-info-list) - (defun flymake-highlight-err-lines (err-info-list) "Highlight error lines in BUFFER using info from ERR-INFO-LIST." (save-excursion @@ -771,136 +294,6 @@ Perhaps use text from LINE-ERR-INFO-LIST to enhance highlighting." (list 'flymake-warnline flymake-warning-bitmap)))) (flymake-make-overlay beg end tooltip-text face bitmap))) -(defun flymake-parse-err-lines (err-info-list lines) - "Parse err LINES, store info in ERR-INFO-LIST." - (let* ((count (length lines)) - (idx 0) - (line-err-info nil) - (real-file-name nil) - (source-file-name buffer-file-name) - (get-real-file-name-f (flymake-get-real-file-name-function source-file-name))) - - (while (< idx count) - (setq line-err-info (flymake-parse-line (nth idx lines))) - (when line-err-info - (setq real-file-name (funcall get-real-file-name-f - (flymake-ler-file line-err-info))) - (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name)) - - (when (flymake-same-files real-file-name source-file-name) - (setq line-err-info (flymake-ler-set-file line-err-info nil)) - (setq err-info-list (flymake-add-err-info err-info-list line-err-info)))) - (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no")) - (setq idx (1+ idx))) - err-info-list)) - -(defun flymake-split-output (output) - "Split OUTPUT into lines. -Return last one as residual if it does not end with newline char. -Returns ((LINES) RESIDUAL)." - (when (and output (> (length output) 0)) - (let* ((lines (split-string output "[\n\r]+" t)) - (complete (equal "\n" (char-to-string (aref output (1- (length output)))))) - (residual nil)) - (when (not complete) - (setq residual (car (last lines))) - (setq lines (butlast lines))) - (list lines residual)))) - -(defun flymake-reformat-err-line-patterns-from-compile-el (original-list) - "Grab error line patterns from ORIGINAL-LIST in compile.el format. -Convert it to flymake internal format." - (let* ((converted-list '())) - (dolist (item original-list) - (setq item (cdr item)) - (let ((regexp (nth 0 item)) - (file (nth 1 item)) - (line (nth 2 item)) - (col (nth 3 item))) - (if (consp file) (setq file (car file))) - (if (consp line) (setq line (car line))) - (if (consp col) (setq col (car col))) - - (when (not (functionp line)) - (setq converted-list (cons (list regexp file line col) converted-list))))) - converted-list)) - -(require 'compile) - -(defvar flymake-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text - (append - '( - ;; MS Visual C++ 6.0 - ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)" - 1 3 nil 4) - ;; jikes - ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)" - 1 3 nil 4) - ;; MS midl - ("midl[ ]*:[ ]*\\(command line error .*\\)" - nil nil nil 1) - ;; MS C# - ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)" - 1 3 nil 4) - ;; perl - ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1) - ;; PHP - ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1) - ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1) - ;; ant/javac. Note this also matches gcc warnings! - (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)" - 2 4 nil 5)) - ;; compilation-error-regexp-alist) - (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist)) - "Patterns for matching error/warning lines. Each pattern has the form -\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). -Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns -from compile.el") - -(define-obsolete-variable-alias 'flymake-warning-re 'flymake-warning-predicate "24.4") -(defvar flymake-warning-predicate "^[wW]arning" - "Predicate matching against error text to detect a warning. -Takes a single argument, the error's text and should return non-nil -if it's a warning. -Instead of a function, it can also be a regular expression.") - -(defun flymake-parse-line (line) - "Parse LINE to see if it is an error or warning. -Return its components if so, nil otherwise." - (let ((raw-file-name nil) - (line-no 0) - (err-type "e") - (err-text nil) - (patterns flymake-err-line-patterns) - (matched nil)) - (while (and patterns (not matched)) - (when (string-match (car (car patterns)) line) - (let* ((file-idx (nth 1 (car patterns))) - (line-idx (nth 2 (car patterns)))) - - (setq raw-file-name (if file-idx (match-string file-idx line) nil)) - (setq line-no (if line-idx (string-to-number - (match-string line-idx line)) 0)) - (setq err-text (if (> (length (car patterns)) 4) - (match-string (nth 4 (car patterns)) line) - (flymake-patch-err-text - (substring line (match-end 0))))) - (if (null err-text) - (setq err-text "") - (when (cond ((stringp flymake-warning-predicate) - (string-match flymake-warning-predicate err-text)) - ((functionp flymake-warning-predicate) - (funcall flymake-warning-predicate err-text))) - (setq err-type "w"))) - (flymake-log - 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s" - file-idx line-idx raw-file-name line-no err-text) - (setq matched t))) - (setq patterns (cdr patterns))) - (if matched - (flymake-ler-make-ler raw-file-name line-no err-type err-text) - ()))) - (defun flymake-find-err-info (err-info-list line-no) "Find (line-err-info-list pos) for specified LINE-NO." (if err-info-list @@ -961,169 +354,8 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (t (setq err-info-list (flymake-ins-after err-info-list (1- pos) err-info)))) err-info-list)) -(defun flymake-get-project-include-dirs-imp (basedir) - "Include dirs for the project current file belongs to." - (if (flymake-get-project-include-dirs-from-cache basedir) - (progn - (flymake-get-project-include-dirs-from-cache basedir)) - ;;else - (let* ((command-line (concat "make -C " - (shell-quote-argument basedir) - " DUMPVARS=INCLUDE_DIRS dumpvars")) - (output (shell-command-to-string command-line)) - (lines (split-string output "\n" t)) - (count (length lines)) - (idx 0) - (inc-dirs nil)) - (while (and (< idx count) (not (string-match "^INCLUDE_DIRS=.*" (nth idx lines)))) - (setq idx (1+ idx))) - (when (< idx count) - (let* ((inc-lines (split-string (nth idx lines) " *-I" t)) - (inc-count (length inc-lines))) - (while (> inc-count 0) - (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines))) - (push (replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs)) - (setq inc-count (1- inc-count))))) - (flymake-add-project-include-dirs-to-cache basedir inc-dirs) - inc-dirs))) - -(defvar flymake-get-project-include-dirs-function #'flymake-get-project-include-dirs-imp - "Function used to get project include dirs, one parameter: basedir name.") - -(defun flymake-get-project-include-dirs (basedir) - (funcall flymake-get-project-include-dirs-function basedir)) - -(defun flymake-get-system-include-dirs () - "System include dirs - from the `INCLUDE' env setting." - (let* ((includes (getenv "INCLUDE"))) - (if includes (split-string includes path-separator t) nil))) - -(defvar flymake-project-include-dirs-cache (make-hash-table :test #'equal)) - -(defun flymake-get-project-include-dirs-from-cache (base-dir) - (gethash base-dir flymake-project-include-dirs-cache)) - -(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs) - (puthash base-dir include-dirs flymake-project-include-dirs-cache)) - -(defun flymake-clear-project-include-dirs-cache () - (clrhash flymake-project-include-dirs-cache)) - -(defun flymake-get-include-dirs (base-dir) - "Get dirs to use when resolving local file names." - (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs)))) - include-dirs)) - -;; (defun flymake-restore-formatting () -;; "Remove any formatting made by flymake." -;; ) - -;; (defun flymake-get-program-dir (buffer) -;; "Get dir to start program in." -;; (unless (bufferp buffer) -;; (error "Invalid buffer")) -;; (with-current-buffer buffer -;; default-directory)) - -(defun flymake-safe-delete-file (file-name) - (when (and file-name (file-exists-p file-name)) - (delete-file file-name) - (flymake-log 1 "deleted file %s" file-name))) - -(defun flymake-safe-delete-directory (dir-name) - (condition-case nil - (progn - (delete-directory dir-name) - (flymake-log 1 "deleted dir %s" dir-name)) - (error - (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) - -(defun flymake-start-syntax-check () - "Start syntax checking for current buffer." - (interactive) - (flymake-log 3 "flymake is running: %s" flymake-is-running) - (when (and (not flymake-is-running) - (flymake-can-syntax-check-file buffer-file-name)) - (when (or (not flymake-compilation-prevents-syntax-check) - (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP") - (flymake-clear-buildfile-cache) - (flymake-clear-project-include-dirs-cache) - - (setq flymake-check-was-interrupted nil) - - (let* ((source-file-name buffer-file-name) - (init-f (flymake-get-init-function source-file-name)) - (cleanup-f (flymake-get-cleanup-function source-file-name)) - (cmd-and-args (funcall init-f)) - (cmd (nth 0 cmd-and-args)) - (args (nth 1 cmd-and-args)) - (dir (nth 2 cmd-and-args))) - (if (not cmd-and-args) - (progn - (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name) - (funcall cleanup-f)) - (progn - (setq flymake-last-change-time nil) - (flymake-start-syntax-check-process cmd args dir))))))) - -(defun flymake-start-syntax-check-process (cmd args dir) - "Start syntax check process." - (condition-case err - (let* ((process - (let ((default-directory (or dir default-directory))) - (when dir - (flymake-log 3 "starting process on dir %s" dir)) - (apply 'start-file-process - "flymake-proc" (current-buffer) cmd args)))) - (set-process-sentinel process 'flymake-process-sentinel) - (set-process-filter process 'flymake-process-filter) - (set-process-query-on-exit-flag process nil) - (push process flymake-processes) - - (setq flymake-is-running t) - (setq flymake-last-change-time nil) - (setq flymake-check-start-time (float-time)) - - (flymake-report-status nil "*") - (flymake-log 2 "started process %d, command=%s, dir=%s" - (process-id process) (process-command process) - default-directory) - process) - (error - (let* ((err-str - (format-message - "Failed to launch syntax check process `%s' with args %s: %s" - cmd args (error-message-string err))) - (source-file-name buffer-file-name) - (cleanup-f (flymake-get-cleanup-function source-file-name))) - (flymake-log 0 err-str) - (funcall cleanup-f) - (flymake-report-fatal-status "PROCERR" err-str))))) - -(defun flymake-kill-process (proc) - "Kill process PROC." - (kill-process proc) - (let* ((buf (process-buffer proc))) - (when (buffer-live-p buf) - (with-current-buffer buf - (setq flymake-check-was-interrupted t)))) - (flymake-log 1 "killed process %d" (process-id proc))) - -(defun flymake-stop-all-syntax-checks () - "Kill all syntax check processes." - (interactive) - (while flymake-processes - (flymake-kill-process (pop flymake-processes)))) - -(defun flymake-compilation-is-running () - (and (boundp 'compilation-in-progress) - compilation-in-progress)) - -(defun flymake-compile () - "Kill all flymake syntax checks, start compilation." - (interactive) - (flymake-stop-all-syntax-checks) - (call-interactively 'compile)) +(defvar-local flymake-is-running nil + "If t, flymake syntax check process is running for the current buffer.") (defun flymake-on-timer-event (buffer) "Start a syntax check for buffer BUFFER if necessary." @@ -1210,6 +442,26 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s" (buffer-name) status warning)) +(defun flymake-fix-line-numbers (err-info-list min-line max-line) + "Replace line numbers with fixed value. +If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE. +If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE. +The reason for this fix is because some compilers might report +line number outside the file being compiled." + (let* ((count (length err-info-list)) + (err-info nil) + (line 0)) + (while (> count 0) + (setq err-info (nth (1- count) err-info-list)) + (setq line (flymake-er-get-line err-info)) + (when (or (< line min-line) (> line max-line)) + (setq line (if (< line min-line) min-line max-line)) + (setq err-info-list (flymake-set-at err-info-list (1- count) + (flymake-er-make-er line + (flymake-er-get-line-err-info-list err-info))))) + (setq count (1- count)))) + err-info-list) + ;;;###autoload (define-minor-mode flymake-mode nil :group 'flymake :lighter flymake-mode-line @@ -1365,288 +617,10 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (match-string 1 string) string)) -;;;; general init-cleanup and helper routines -(defun flymake-create-temp-inplace (file-name prefix) - (unless (stringp file-name) - (error "Invalid file-name")) - (or prefix - (setq prefix "flymake")) - (let* ((ext (file-name-extension file-name)) - (temp-name (file-truename - (concat (file-name-sans-extension file-name) - "_" prefix - (and ext (concat "." ext)))))) - (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name) - temp-name)) - -(defun flymake-create-temp-with-folder-structure (file-name _prefix) - (unless (stringp file-name) - (error "Invalid file-name")) - - (let* ((dir (file-name-directory file-name)) - ;; Not sure what this slash-pos is all about, but I guess it's just - ;; trying to remove the leading / of absolute file names. - (slash-pos (string-match "/" dir)) - (temp-dir (expand-file-name (substring dir (1+ slash-pos)) - temporary-file-directory))) - - (file-truename (expand-file-name (file-name-nondirectory file-name) - temp-dir)))) - -(defun flymake-delete-temp-directory (dir-name) - "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error." - (let* ((temp-dir temporary-file-directory) - (suffix (substring dir-name (1+ (length temp-dir))))) - - (while (> (length suffix) 0) - (setq suffix (directory-file-name suffix)) - ;;+(flymake-log 0 "suffix=%s" suffix) - (flymake-safe-delete-directory - (file-truename (expand-file-name suffix temp-dir))) - (setq suffix (file-name-directory suffix))))) - -(defvar-local flymake-temp-source-file-name nil) -(defvar-local flymake-master-file-name nil) -(defvar-local flymake-temp-master-file-name nil) -(defvar-local flymake-base-dir nil) - -(defun flymake-init-create-temp-buffer-copy (create-temp-f) - "Make a temporary copy of the current buffer, save its name in buffer data and return the name." - (let* ((source-file-name buffer-file-name) - (temp-source-file-name (funcall create-temp-f source-file-name "flymake"))) - - (flymake-save-buffer-in-file temp-source-file-name) - (setq flymake-temp-source-file-name temp-source-file-name) - temp-source-file-name)) - -(defun flymake-simple-cleanup () - "Do cleanup after `flymake-init-create-temp-buffer-copy'. -Delete temp file." - (flymake-safe-delete-file flymake-temp-source-file-name) - (setq flymake-last-change-time nil)) - -(defun flymake-get-real-file-name (file-name-from-err-msg) - "Translate file name from error message to \"real\" file name. -Return full-name. Names are real, not patched." - (let* ((real-name nil) - (source-file-name buffer-file-name) - (master-file-name flymake-master-file-name) - (temp-source-file-name flymake-temp-source-file-name) - (temp-master-file-name flymake-temp-master-file-name) - (base-dirs - (list flymake-base-dir - (file-name-directory source-file-name) - (if master-file-name (file-name-directory master-file-name)))) - (files (list (list source-file-name source-file-name) - (list temp-source-file-name source-file-name) - (list master-file-name master-file-name) - (list temp-master-file-name master-file-name)))) - - (when (equal 0 (length file-name-from-err-msg)) - (setq file-name-from-err-msg source-file-name)) - - (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files)) - ;; if real-name is nil, than file name from err msg is none of the files we've patched - (if (not real-name) - (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs))) - (if (not real-name) - (setq real-name file-name-from-err-msg)) - (setq real-name (flymake-fix-file-name real-name)) - (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name) - real-name)) - -(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files) - (let* ((base-dirs-count (length base-dirs)) - (file-count (length files)) - (real-name nil)) - - (while (and (not real-name) (> base-dirs-count 0)) - (setq file-count (length files)) - (while (and (not real-name) (> file-count 0)) - (let* ((this-dir (nth (1- base-dirs-count) base-dirs)) - (this-file (nth 0 (nth (1- file-count) files))) - (this-real-name (nth 1 (nth (1- file-count) files)))) - ;;+(flymake-log 0 "this-dir=%s this-file=%s this-real=%s msg-file=%s" this-dir this-file this-real-name file-name-from-err-msg) - (when (and this-dir this-file (flymake-same-files - (expand-file-name file-name-from-err-msg this-dir) - this-file)) - (setq real-name this-real-name))) - (setq file-count (1- file-count))) - (setq base-dirs-count (1- base-dirs-count))) - real-name)) - -(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs) - (let* ((real-name nil)) - (if (file-name-absolute-p file-name-from-err-msg) - (setq real-name file-name-from-err-msg) - (let* ((base-dirs-count (length base-dirs))) - (while (and (not real-name) (> base-dirs-count 0)) - (let* ((full-name (expand-file-name file-name-from-err-msg - (nth (1- base-dirs-count) base-dirs)))) - (if (file-exists-p full-name) - (setq real-name full-name)) - (setq base-dirs-count (1- base-dirs-count)))))) - real-name)) - -(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name) - "Find buildfile, store its dir in buffer data and return its dir, if found." - (let* ((buildfile-dir - (flymake-find-buildfile buildfile-name - (file-name-directory source-file-name)))) - (if buildfile-dir - (setq flymake-base-dir buildfile-dir) - (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name) - (flymake-report-fatal-status - "NOMK" (format "No buildfile (%s) found for %s" - buildfile-name source-file-name))))) - -(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp) - "Find master file (or buffer), create its copy along with a copy of the source file." - (let* ((source-file-name buffer-file-name) - (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)) - (master-and-temp-master (flymake-create-master-file - source-file-name temp-source-file-name - get-incl-dirs-f create-temp-f - master-file-masks include-regexp))) - - (if (not master-and-temp-master) - (progn - (flymake-log 1 "cannot find master file for %s" source-file-name) - (flymake-report-status "!" "") ; NOMASTER - nil) - (setq flymake-master-file-name (nth 0 master-and-temp-master)) - (setq flymake-temp-master-file-name (nth 1 master-and-temp-master))))) - -(defun flymake-master-cleanup () - (flymake-simple-cleanup) - (flymake-safe-delete-file flymake-temp-master-file-name)) - -;;;; make-specific init-cleanup routines -(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f) - "Create a command line for syntax check using GET-CMD-LINE-F." - (funcall get-cmd-line-f - (if use-relative-source - (file-relative-name source-file-name base-dir) - source-file-name) - (if use-relative-base-dir - (file-relative-name base-dir - (file-name-directory source-file-name)) - base-dir))) - -(defun flymake-get-make-cmdline (source base-dir) - (list "make" - (list "-s" - "-C" - base-dir - (concat "CHK_SOURCES=" source) - "SYNTAX_CHECK_MODE=1" - "check-syntax"))) - -(defun flymake-get-ant-cmdline (source base-dir) - (list "ant" - (list "-buildfile" - (concat base-dir "/" "build.xml") - (concat "-DCHK_SOURCES=" source) - "check-syntax"))) - -(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f) - "Create syntax check command line for a directly checked source file. -Use CREATE-TEMP-F for creating temp copy." - (let* ((args nil) - (source-file-name buffer-file-name) - (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name))) - (if buildfile-dir - (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))) - (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir - use-relative-base-dir use-relative-source - get-cmdline-f)))) - args)) - -(defun flymake-simple-make-init () - (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline)) - -(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp) - "Create make command line for a source file checked via master file compilation." - (let* ((make-args nil) - (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy - get-incl-dirs-f 'flymake-create-temp-inplace - master-file-masks include-regexp))) - (when temp-master-file-name - (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile"))) - (if buildfile-dir - (setq make-args (flymake-get-syntax-check-program-args - temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline))))) - make-args)) - -(defun flymake-find-make-buildfile (source-dir) - (flymake-find-buildfile "Makefile" source-dir)) - -;;;; .h/make specific -(defun flymake-master-make-header-init () - (flymake-master-make-init - 'flymake-get-include-dirs - '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'") - "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\"")) - -;;;; .java/make specific -(defun flymake-simple-make-java-init () - (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline)) - -(defun flymake-simple-ant-java-init () - (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline)) - -(defun flymake-simple-java-cleanup () - "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs." - (flymake-safe-delete-file flymake-temp-source-file-name) - (when flymake-temp-source-file-name - (flymake-delete-temp-directory - (file-name-directory flymake-temp-source-file-name)))) - -;;;; perl-specific init-cleanup routines -(defun flymake-perl-init () - (let* ((temp-file (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)) - (local-file (file-relative-name - temp-file - (file-name-directory buffer-file-name)))) - (list "perl" (list "-wc " local-file)))) - -;;;; php-specific init-cleanup routines -(defun flymake-php-init () - (let* ((temp-file (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)) - (local-file (file-relative-name - temp-file - (file-name-directory buffer-file-name)))) - (list "php" (list "-f" local-file "-l")))) - -;;;; tex-specific init-cleanup routines -(defun flymake-get-tex-args (file-name) - ;;(list "latex" (list "-c-style-errors" file-name)) - (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name))) - -(defun flymake-simple-tex-init () - (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))) - -;; Perhaps there should be a buffer-local variable flymake-master-file -;; that people can set to override this stuff. Could inherit from -;; the similar AUCTeX variable. -(defun flymake-master-tex-init () - (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy - 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace - '("\\.tex\\'") - "[ \t]*\\in\\(?:put\\|clude\\)[ \t]*{\\(.*%s\\)}"))) - (when temp-master-file-name - (flymake-get-tex-args temp-master-file-name)))) - -(defun flymake-get-include-dirs-dot (_base-dir) - '(".")) - -;;;; xml-specific init-cleanup routines -(defun flymake-xml-init () - (list flymake-xml-program - (list "val" (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)))) - (provide 'flymake) + +(declare-function flymake-start-syntax-check "flymake-proc") +(declare-function flymake-can-syntax-check-file "flymake-proc") + +(require 'flymake-proc) ;;; flymake.el ends here commit ae42ae79f5b045456c49c7fd8a9b6ee8983080bf Author: Nicolas Petton Date: Tue Oct 3 13:12:56 2017 +0200 ; ChangeLog fixes diff --git a/ChangeLog.3 b/ChangeLog.3 index fb24476a0b..c65cf94a3f 100644 --- a/ChangeLog.3 +++ b/ChangeLog.3 @@ -193,10 +193,13 @@ ibuffer: compare marks with EQ - * lisp/ibuffer (ibuffer-update-title-and-summary, ibuffer-redisplay-current) + * lisp/ibuffer.el: + (ibuffer-update-title-and-summary) + (ibuffer-redisplay-current) (ibuffer-buffer-name-face, ibuffer-unmark-all) (ibuffer-count-deletion-lines, ibuffer-buffer-names-with-mark): - Use 'eq' instead of 'char-equal' when comparing mark characters (Bug#25000). + Use 'eq' instead of 'char-equal' when comparing mark characters + (Bug#25000). * test/lisp/ibuffer-tests.el (ibuffer-test-Bug25000): Update test result as pass. @@ -341,7 +344,7 @@ Make TAB and M-TAB run widget-forward and widget-backward (bug#25091) - * lisp/gnus/mm-decode (mm-convert-shr-links): Avoid `shr-next-link' + * lisp/gnus/mm-decode.el (mm-convert-shr-links): Avoid `shr-next-link' and `shr-previous-link' so TAB and M-TAB run `widget-forward' and `widget-backward' instead (bug#25091). @@ -1138,7 +1141,7 @@ Allow user control of progress messages in cpp.el - * progmodes/cpp.el (cpp-message-min-time-interval): New defcustom. + * lisp/progmodes/cpp.el (cpp-message-min-time-interval): New defcustom. (cpp-progress-time): Use 'cpp-message-min-time-interval'. Improve the doc string. (cpp-highlight-buffer): Use 'cpp-progress-message' instead of @@ -1216,7 +1219,7 @@ This option allows the user to specify where to place point after these commands. - * comint.el (comint-move-point-for-matching-input): New user option. + * lisp/comint.el (comint-move-point-for-matching-input): New user option. (comint-previous-matching-input-from-input): Use user option. 2016-11-22 Michael Albinus @@ -1804,7 +1807,7 @@ Check for header-line-format instead. * lisp/emulation/viper.el (viper-load-custom-file): Reference major-mode instead. - * lisp-mail-feedmail.el (feedmail-fill-to-cc-fill-column): Use + * lisp/mail/feedmail.el (feedmail-fill-to-cc-fill-column): Use fill-column instead. 2016-11-15 Simen Heggestøyl @@ -1844,9 +1847,9 @@ Update verilog-mode.el - * verilog-mode.el (verilog-read-decls, verilog-calc-1): Fix - "default clocking" indentation and preventing AUTOs from working, - bug1084. Reported by Alan Morgan. + * lisp/progmodes/verilog-mode.el (verilog-read-decls) + (verilog-calc-1): Fix "default clocking" indentation and + preventing AUTOs from working, bug1084. Reported by Alan Morgan. (verilog-diff-report): Fix `verilog-diff-report' not returning bad status on differences, bug1087. Reported by Eric Jackowski. @@ -1920,7 +1923,7 @@ * lisp/net/tramp-adb.el (tramp-adb-maybe-open-connection): * lisp/net/tramp-gvfs.el (tramp-gvfs-maybe-open-connection): - * lisp/net/lisp/net/tramp-sh.el (tramp-maybe-open-connection): + * lisp/net/tramp-sh.el (tramp-maybe-open-connection): * lisp/net/tramp-smb.el (tramp-smb-maybe-open-connection): Use it. * test/lisp/files-x-tests.el: New file. @@ -1953,7 +1956,7 @@ * lisp/international/mule.el (auto-coding-alist-lookup): * lisp/files.el (file-truename): (abbreviate-file-name, set-auto-mode, file-relative-name): - * package.el (package-untar-buffer): Use + * lisp/emacs-lisp/package.el (package-untar-buffer): Use 'file-name-case-insensitive-p' instead of 'system-type' to test case-insensitivity. @@ -2254,7 +2257,7 @@ Fix references to long obsoleted functions/aliases - * doc/lispintro/emacs-list-intro.texi (Miscellaneous): + * doc/lispintro/emacs-lisp-intro.texi (Miscellaneous): * doc/misc/cl.texi (Conditionals): * doc/misc/speedbar.texi (Major Display Modes): Use string-to-number, not string-to-int. @@ -3079,13 +3082,13 @@ Only two of the commands there were autoloaded, one of which is an easter egg. - * lisp/miscl.el (copy-from-above-command): - * lisp/miscl.el (zap-up-to-char): - * lisp/miscl.el (mark-beginning-of-buffer): - * lisp/miscl.el (mark-end-of-buffer): - * lisp/miscl.el (upcase-char): - * lisp/miscl.el (forward-to-word): - * lisp/miscl.el (backward-to-word): + * lisp/misc.el (copy-from-above-command): + * lisp/misc.el (zap-up-to-char): + * lisp/misc.el (mark-beginning-of-buffer): + * lisp/misc.el (mark-end-of-buffer): + * lisp/misc.el (upcase-char): + * lisp/misc.el (forward-to-word): + * lisp/misc.el (backward-to-word): Add autoload cookie. 2016-10-22 Martin Rudalics @@ -3157,7 +3160,7 @@ See Bug#24747. - * progmodes/cc-mode-tests.el: Rename from cc-mode.el; fix typo in + * test/lisp/progmodes/cc-mode-tests.el: Rename from cc-mode.el; fix typo in file-local variable; add comments to make checkdoc happy. 2016-10-20 Michael Albinus @@ -3330,14 +3333,14 @@ use full time objects (lists) instead of floats when possible - * midnight.el (midnight-buffer-display-time): Remove + * lisp/midnight.el (midnight-buffer-display-time): Remove (clean-buffer-list): Use float time only for time comparison 2016-10-15 Sam Steingold Save and restore buffer-display-time - * desktop.el (desktop-locals-to-save): Add `buffer-display-time' + * lisp/desktop.el (desktop-locals-to-save): Add `buffer-display-time' (desktop-read): Set `desktop-file-modtime' before loading the desktop file (desktop-create-buffer): Adjust `buffer-display-time' for the downtime @@ -3351,7 +3354,7 @@ bracketed paste for that buffer. If bracketed paste is inhiited for at least one buffer in a terminal, it is disabled for the whole terminal. - * term/xterm.el (xterm-inhibit-bracketed-paste-mode): New mode to + * lisp/term/xterm.el (xterm-inhibit-bracketed-paste-mode): New mode to inhibit XTerm bracketed paste per buffer. (xterm--buffer-terminals, xterm--update-bracketed-paste) (xterm--bracketed-paste-possible, xterm--is-xterm): New helper @@ -3361,7 +3364,7 @@ (terminal-init-xterm): Update bracketed paste status when initializing an XTerm and on window configuration change. - * term.el (term-char-mode, term-line-mode): Inhibit XTerm + * lisp/term.el (term-char-mode, term-line-mode): Inhibit XTerm bracketed paste in char mode. 2016-10-15 Dima Kogan @@ -3437,7 +3440,7 @@ Add test for Bug#24627 - * /test/lisp/thingatpt-tests.el (thing-at-point-bug24627): New test. + * test/lisp/thingatpt-tests.el (thing-at-point-bug24627): New test. 2016-10-12 Eli Zaretskii @@ -3633,7 +3636,7 @@ 2016-10-05 Mark Oteiza - * lisp/url-url-parse.el (url-generic-parse-url): Unquote macro URL argument. + * lisp/url/url-parse.el (url-generic-parse-url): Unquote macro URL argument. 2016-10-05 Mark Oteiza @@ -5432,13 +5435,13 @@ * lisp/net/tramp-adb.el (tramp-adb-parse-device-names) (tramp-adb-maybe-open-connection): * lisp/net/tramp-cache.el (tramp-get-connection-property): - * tramp-gvfs.el (tramp-gvfs-handle-file-notify-add-watch): + * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-file-notify-add-watch): * lisp/net/tramp-gw.el (tramp-gw-gw-proc-sentinel) (tramp-gw-aux-proc-sentinel, tramp-gw-open-connection): - * tramp-sh.el (tramp-process-sentinel) + * lisp/net/tramp-sh.el (tramp-process-sentinel) (tramp-sh-handle-file-notify-add-watch) (tramp-maybe-open-connection): - * lisp/net/lisp/net/lisp/net/tramp-smb.el (tramp-smb-action-with-tar) + * lisp/net/tramp-smb.el (tramp-smb-action-with-tar) (tramp-smb-handle-copy-directory, tramp-smb-action-get-acl) (tramp-smb-handle-process-file, tramp-smb-action-set-acl) (tramp-smb-get-cifs-capabilities) @@ -6386,7 +6389,7 @@ This can happen with `revert-buffer' or sometimes `find-file', when the file is already in a buffer, but the file has been changed outside of Emacs. - * lisp/progmodes/cc-mode (c-after-change): When we detect a missing + * lisp/progmodes/cc-mode.el (c-after-change): When we detect a missing invocation of c-before-change-functions, we assume the changed region is the entire buffer, and call c-before-change explicitly before proceding. @@ -6828,7 +6831,7 @@ Widen in certain low level CC Mode functions. This fixes bug #24148. - * lisp/progmodes/cc-engine (c-state-semi-pp-to-literal) + * lisp/progmodes/cc-engine.el (c-state-semi-pp-to-literal) (c-state-full-pp-to-literal): Widen around the functionality. (c-parse-ps-state-below): Correct the order of save-excursion and save-restriction. @@ -7001,7 +7004,8 @@ 2016-08-02 Stefan Monnier - * cl-generic.el: Fix problems introduced by new load-history format + * lisp/emacs-lisp/cl-generic.el: Fix problems introduced by new + load-history format * lisp/emacs-lisp/cl-generic.el (cl--generic-load-hist-format): New function. (cl-generic-define-method, cl--generic-describe): Use it. @@ -7128,7 +7132,7 @@ Don’t (require 'cl) - * test/src/regex-test.el: Don’t (require 'cl). + * test/src/regex-tests.el: Don’t (require 'cl). (regex-tests-PCRE): s/loop/cl-loop/ 2016-08-02 Michal Nazarewicz @@ -7292,8 +7296,8 @@ prompt the user to save it, so the customization is not lost on restart. - * gnus-srvr.el (gnus-server-toggle-cloud-method-server): Prompt to - save the customization of `gnus-cloud-method'. + * lisp/gnus/gnus-srvr.el (gnus-server-toggle-cloud-method-server): + Prompt to save the customization of `gnus-cloud-method'. 2016-07-27 Ken Brown @@ -7344,7 +7348,8 @@ 2016-07-25 Ted Zlatanov - * gnus-cloud.el (gnus-cloud-encode-data): Fix 'base64-gzip encoding. + * lisp/gnus/gnus-cloud.el (gnus-cloud-encode-data): Fix + 'base64-gzip encoding. 2016-07-25 Andrew Hyatt @@ -7940,7 +7945,7 @@ 2016-07-12 Stefan Monnier - * cl-generic.el (cl-defmethod): Make docstring dynamic + * lisp/emacs-lisp/cl-generic.el (cl-defmethod): Make docstring dynamic * lisp/emacs-lisp/cl-generic.el (cl-defmethod): Make docstring dynamic. (cl--generic-make-defmethod-docstring): New function for that. @@ -7999,7 +8004,7 @@ * src/gtkutil.c (xg_hide_tip): New function. (xg_hide_tooltip): Adjust to cancel GTK event loop timeout if needed. * src/menu.c (Fx_popup_menu): Adjust call to Fx_hide_tip. - * src/nsfns.c (toplevel): Remove 'tip_frame' leftover. + * src/nsfns.m (toplevel): Remove 'tip_frame' leftover. * src/w32fns.c (unwind_create_tip_frame): Remove. (w32_display_monitor_attributes_list) (w32_display_monitor_attributes_list_fallback): Use FRAME_TOOLTIP_P. @@ -8086,7 +8091,7 @@ Dired always read file system - * dired.el (dired-always-read-filesystem): Add new option. + * lisp/dired.el (dired-always-read-filesystem): Add new option. (dired-mark-files-containing-regexp): Use it (Bug#22694). * doc/emacs/dired.texi: Mention it in the manual. * test/lisp/dired-tests.el (dired-test-bug22694): Add test. @@ -8289,8 +8294,8 @@ Copy buffer names to kill ring - * ibuf-ext.el (ibuffer-copy-buffername-as-kill): New command. - * lisp/ibuffer (ibuffer-mode-map): Bound it to 'B'. + * lisp/ibuf-ext.el (ibuffer-copy-buffername-as-kill): New command. + * lisp/ibuffer.el (ibuffer-mode-map): Bound it to 'B'. ;* etc/NEWS: Add entry for this new feature. 2016-07-07 Tino Calancha @@ -8313,8 +8318,8 @@ Prevent NS event loop being re-entered (bug#11049) - * nsterm.m (ns_read_socket, ns_select): Return -1 if already in event - loop instead of aborting. + * src/nsterm.m (ns_read_socket, ns_select): Return -1 if already + in event loop instead of aborting. 2016-07-07 Alan Third @@ -8519,11 +8524,11 @@ * configure.ac [USE_X_TOOLKIT]: Define X_TOOLKIT_EDITRES if _XEditResCheckMessages is declared in X11/Xmu/Editres.h and may be linked with -lXmu. This should work with any non-ancient Xmu library. - * xfns.c (toplevel): Remove old cruft. + * src/xfns.c (toplevel): Remove old cruft. (x_window) [USE_X_TOOLKIT]: Use X_TOOLKIT_EDITRES. - * xterm.c (toplevel): Remove old cruft. + * src/xterm.c (toplevel): Remove old cruft. (handle_one_xevent): Use X_TOOLKIT_EDITRES. - * xterm.h (toplevel): Include X11/Xmu/Editres.h if X_TOOLKIT_EDITRES. + * src/xterm.h (toplevel): Include X11/Xmu/Editres.h if X_TOOLKIT_EDITRES. 2016-07-04 Michael Albinus @@ -9661,7 +9666,8 @@ 2016-06-15 Ted Zlatanov - * generic-x.el (ansible-inventory-generic-mode): Warn if value is missing + * lisp/generic-x.el (ansible-inventory-generic-mode): Warn if + value is missing 2016-06-15 Tim Chambers (tiny change) @@ -10333,7 +10339,7 @@ Fix incomplete handling of translation table in a coding system. - * coding.c (get_translation): New arg NCHARS. Even if TRANS + * src/coding.c (get_translation): New arg NCHARS. Even if TRANS is an alist, return a character or a vector of character. (produce_chars): Adjust for the above change. (consume_chars): Likewise. @@ -11721,8 +11727,8 @@ Fixes bug #16759 and bug #23476. - * .dir-locals: Put the c-noise-macros-with-paren-names setting back into the C - Mode value. + * .dir-locals.el: Put the c-noise-macros-with-paren-names setting + back into the C Mode value. * lisp/progmodes/cc-mode.el: (c-basic-common-init): Remove the call to c-make-macro-with-semi-re. @@ -12100,8 +12106,7 @@ 2016-05-04 Stefan Monnier - * lisp/emulation/viper(-cmd)?.el: Use lexical-binding. - + * lisp/emulation/viper.el: * lisp/emulation/viper-cmd.el: Use lexical-binding. (viper-change-state-to-vi, viper-change-state-to-emacs): Allow dummy args, for use in advice-add. @@ -12346,8 +12351,9 @@ gitmerge: Add cherry pick to gitmerge-skip-regexp - * gitmerge.el (gitmerge-skip-regexp): Add "cherry picked from commit", - which is the string appended by 'git cherry-pick -x'. + * admin/gitmerge.el (gitmerge-skip-regexp): Add "cherry picked + from commit", which is the string appended by 'git cherry-pick + -x'. 2016-05-01 Lars Ingebrigtsen @@ -12642,7 +12648,8 @@ 2016-04-30 Alan Mackenzie - * .dir-locals: Amend for correct fontification of *.[ch] containing "IF_LINT" + * .dir-locals.el: Amend for correct fontification of *.[ch] + containing "IF_LINT" 2016-04-30 Lars Ingebrigtsen @@ -12669,7 +12676,7 @@ CC Mode: Recognize a noise macro with parens after a declarator's identifier - * lisp/progmodes/cc-engine (c-forward-decl-or-cast-1): In the while loop + * lisp/progmodes/cc-engine.el (c-forward-decl-or-cast-1): In the while loop following comment "Skip over type decl suffix operators." insert code also to check for noise macros with parentheses. @@ -13173,9 +13180,10 @@ Add a number of Python 3 exceptions - * lisp/progmoes/python.el (python-font-lock-keywords): Clean up the exception - list, adding a number of new Python 3 exceptions and moving some exceptions - to the Python 2 and 3 list as Python 2.7 includes them. + * lisp/progmodes/python.el (python-font-lock-keywords): Clean up + the exception list, adding a number of new Python 3 exceptions and + moving some exceptions to the Python 2 and 3 list as Python 2.7 + includes them. 2016-04-26 Anders Lindgren @@ -13475,12 +13483,12 @@ Compute User-Agent dynamically in url-http - * url-http.el (url-http-user-agent-string): Compute User-Agent - string dynamically. + * lisp/url/url-http.el (url-http-user-agent-string): Compute + User-Agent string dynamically. (url-http--user-agent-default-string): New function. - * url-vars.el (url-privacy-level): Allow `emacs' in list of - information not to send. + * lisp/url/url-vars.el (url-privacy-level): Allow `emacs' in list + of information not to send. (url-user-agent): Add nil and `default' options; do not pre-compute value. @@ -13581,7 +13589,7 @@ Use 'ucs-names' for character name escapes - * lread.c (invalid_character_name, check_scalar_value) + * src/lread.c (invalid_character_name, check_scalar_value) (parse_code_after_prefix, character_name_to_code): New helper functions that use 'ucs-names' and parsing for CJK ideographs. (read_escape): Use helper functions. @@ -13605,7 +13613,7 @@ Implement named character escapes, similar to Perl - * lread.c (init_character_names): New function. + * src/lread.c (init_character_names): New function. (read_escape): Read Perl-style named character escape sequences. (syms_of_lread): Initialize new variable 'character_names'. * test/src/lread-tests.el (lread-char-empty-name): Add test file @@ -13652,13 +13660,13 @@ Prevent bootstrap autoload backup files - * lisp/emacs-lisp/autoload (autoload-find-generated-file): Suppress - backups in newly created file. + * lisp/emacs-lisp/autoload.el (autoload-find-generated-file): Suppress + backups in newly created file. - (autoload-ensure-default-file): Function split into two. - (autoload-ensure-file-writeable): New function from split. + (autoload-ensure-default-file): Function split into two. + (autoload-ensure-file-writeable): New function from split. - (Bug#23203) + (Bug#23203) 2016-04-20 Paul Eggert @@ -14955,8 +14963,8 @@ * lisp/progmodes/cc-engine.el (c-back-over-member-initializers): Check more robustly for ":" token when searching backwards for it. - * lisp/progmodes/cc-langs (c-:$-multichar-token-regexp): New language - variable. + * lisp/progmodes/cc-langs.el (c-:$-multichar-token-regexp): New + language variable. [This reapplies commit 9e5452f7166e3634f2d8e943815ed722e1672714, which was inadvertently lost by merge commit @@ -15159,15 +15167,15 @@ Add a Catalan language environment - * international/mule-cmds.el (locale-language-names): Map locale + * lisp/international/mule-cmds.el (locale-language-names): Map locale language name `ca' to language environment `Catalan'. - * language/european.el: Add definition of language environment for - the Catalan language. + * lisp/language/european.el: Add definition of language + environment for the Catalan language. - * leim/quail/latin-pre.el: Add quail rule to the `catalan-prefix' - input method to support input of middle dot characters through - composition (bug#18279). + * lisp/leim/quail/latin-pre.el: Add quail rule to the + `catalan-prefix' input method to support input of middle dot + characters through composition (bug#18279). 2016-03-19 Paul Eggert @@ -15820,7 +15828,7 @@ This is possible in all functions where we catch signals anyway. - * emacs-module.c (module_make_global_ref, module_funcall) + * src/emacs-module.c (module_make_global_ref, module_funcall) (module_copy_string_contents, module_make_string): Use xsignal0 and CHECK macros for argument checks. @@ -15832,7 +15840,7 @@ and negate its sense. Use it via AC_SUBST, not AC_DEFINE, and have its value be either empty or --no-build-details. All uses changed. Change option to --disable-build-details. - * doc/lispref/cmdargs.texi (Initial Options): + * doc/emacs/cmdargs.texi (Initial Options): Document --no-build-details. * doc/lispref/internals.texi (Building Emacs): * etc/NEWS: @@ -16335,9 +16343,9 @@ Make checkdoc warn about variables described as "True" - * checkdoc.el (checkdoc-this-string-valid-engine): Docstrings for - variables "True...", and functions "Return true...", should usually be - "non-nil" (bug#15506). + * lisp/emacs-lisp/checkdoc.el (checkdoc-this-string-valid-engine): + Docstrings for variables "True...", and functions "Return + true...", should usually be "non-nil" (bug#15506). 2016-02-24 Lars Ingebrigtsen @@ -16738,7 +16746,7 @@ Test message-strip-subject-trailing-was - * test/lisp/gnus/message-test.el (message-strip-subject-trailing-was): + * test/lisp/gnus/message-tests.el (message-strip-subject-trailing-was): New test (bug#22632). 2016-02-22 Michal Nazarewicz @@ -16775,7 +16783,7 @@ * lisp/gnus/gnus-rfc1843.el: New file for Gnus/rfc1843 interface functions. - * lisp/gnus/rfc1843.el: Move all Gnus-specifig functions to + * lisp/gnus/gnus-rfc1843.el: Move all Gnus-specifig functions to gnus-rfc1843. 2016-02-22 Lars Ingebrigtsen @@ -16791,7 +16799,7 @@ Don't require mm-util - * lisp/gnus/ietf-drums.el (mm-util): Don't require. + * lisp/mail/ietf-drums.el (mm-util): Don't require. 2016-02-22 Lars Ingebrigtsen @@ -17435,7 +17443,7 @@ * lisp/gnus/pop3.el: Ditto. - * lisp/gnus/sieve-manage.el: Ditto. + * lisp/net/sieve-manage.el: Ditto. * lisp/net/network-stream.el (open-protocol-stream): Make obsolete. @@ -17460,7 +17468,7 @@ Remove compat functions from starttls.el - * lisp/gnus/starttls.el + * lisp/net/starttls.el (starttls-set-process-query-on-exit-flag): Remove. 2016-02-13 Lars Ingebrigtsen @@ -17480,7 +17488,7 @@ Remove compat code from rfc2047 - * lisp/gnus/rfc2047.el (rfc2047-encode-message-header): Remove + * lisp/mail/rfc2047.el (rfc2047-encode-message-header): Remove compat code. (rfc2047-decode-string): Ditto. @@ -17817,9 +17825,9 @@ Fix encoding problem introduced by previous patch series - * lisp/gnus/rfc2047.el: Ditto (bug#22648). + * lisp/mail/rfc2047.el: Ditto (bug#22648). - * lisp/gnus/rfc2231.el: Fix problem created by the + * lisp/mail/rfc2231.el: Fix problem created by the mm-replace-in-string conversion. 2016-02-12 Lars Ingebrigtsen @@ -17915,7 +17923,7 @@ Make sieve-manage require sasl - * lisp/gnus/sieve-manage.el: Fix compilation warning by + * lisp/net/sieve-manage.el: Fix compilation warning by requiring sasl. 2016-02-11 Lars Ingebrigtsen @@ -18021,7 +18029,7 @@ Don't use mm-with-unibyte-buffer in utf7 - * lisp/gnus/utf7.el (utf7-fragment-encode): Don't use + * lisp/international/utf7.el (utf7-fragment-encode): Don't use mm-with-unibyte-buffer. 2016-02-11 Lars Ingebrigtsen @@ -18102,7 +18110,7 @@ Remove XEmacs compat code from ietf-drums.el - * lisp/gnus/ietf-drums.el (ietf-drums-syntax-table): Drop + * lisp/mail/ietf-drums.el (ietf-drums-syntax-table): Drop XEmacs compat. 2016-02-10 Lars Ingebrigtsen @@ -18295,7 +18303,7 @@ Remove compat code from compface.el - * lisp/gnus/compface.el: Remove XEmacs compat code throughout. + * lisp/image/compface.el: Remove XEmacs compat code throughout. 2016-02-09 Lars Ingebrigtsen @@ -18526,7 +18534,7 @@ Make `message-beginning-of-line' aware of folded headers - * lisp/gnus/message.pl (message-beginning-of-header): New function which + * lisp/gnus/message.el (message-beginning-of-header): New function which moves point to the beginning of a mail header. The function is aware of folded headers and with non-nil argument looks for the true beginning of a header while with nil argument moves to the indented text of header's @@ -19209,7 +19217,7 @@ Build fix for --enable-check-lisp-object-type - * process.c (check_for_dns): Type fix reported by YAMAMOTO + * src/process.c (check_for_dns): Type fix reported by YAMAMOTO Mitsuharu. 2016-02-01 Glenn Morris @@ -19225,7 +19233,7 @@ Boot parameter check fix - * process.c (send_process): Fix test for boot parameters noted + * src/process.c (send_process): Fix test for boot parameters noted by Andy Moreton. 2016-02-01 Paul Eggert @@ -19251,7 +19259,7 @@ Return the correct server port number - * process.c (connect_network_socket): Return the correct + * src/process.c (connect_network_socket): Return the correct server port number. 2016-01-31 Lars Ingebrigtsen @@ -19264,7 +19272,7 @@ Better async error reporting - * process.c (connect_network_socket): Mark failed processes + * src/process.c (connect_network_socket): Mark failed processes with a better error message. (check_for_dns): Ditto. @@ -19314,7 +19322,7 @@ Windows build fix - * process.c (Fmake_network_process): Build fix for systems + * src/process.c (Fmake_network_process): Build fix for systems without local sockets. 2016-01-31 Lars Ingebrigtsen @@ -19325,21 +19333,21 @@ Fix GC problem in async TLS connection - * process.h: All Lisp_Object slots have to come first, + * src/process.h: All Lisp_Object slots have to come first, otherwise they won't be protected from gc. 2016-01-31 Lars Ingebrigtsen Further TLS async work - * gnutls.c (boot_error): New function to either signal an + * src/gnutls.c (boot_error): New function to either signal an error or return an error code. (Fgnutls_boot): Don't signal errors when running asynchronously. - * process.h (pset_status): Move here from process.c to be + * src/process.h (pset_status): Move here from process.c to be able to use from gnutls.c. - * process.c (connect_network_socket): Do the TLS boot here + * src/process.c (connect_network_socket): Do the TLS boot here when running asynchronously. (wait_reading_process_output): Rework the dns_processes handling for more safety. @@ -19456,7 +19464,7 @@ Fix segfault from double free - * process.c (check_for_dns): Protect against double free + * src/process.c (check_for_dns): Protect against double free issues. 2016-01-30 Lars Ingebrigtsen @@ -19744,7 +19752,7 @@ Re-enable checks in member, memql, delete to complain about non-lists - * fns.c (Fmember, Fmemql, Fdelete): Revert 2007-10-16 change. + * src/fns.c (Fmember, Fmemql, Fdelete): Revert 2007-10-16 change. 2016-01-30 Lars Ingebrigtsen @@ -19754,7 +19762,7 @@ Make async resolution more efficient - * process.c (wait_reading_process_output): Use a list of + * src/process.c (wait_reading_process_output): Use a list of process objects instead of looping through an array to check for name resolution. This should be much faster. @@ -19768,66 +19776,66 @@ Compilation for for systems with getaddrinfo_a - * process.c (Fmake_network_process): Make stuff work again on + * src/process.c (Fmake_network_process): Make stuff work again on systems with getaddrinfo_a. 2016-01-29 Lars Ingebrigtsen Save correct server data - * process.c (connect_network_socket): Save the correct contact + * src/process.c (connect_network_socket): Save the correct contact info for servers. 2016-01-29 Lars Ingebrigtsen Compilation for for non-GNU systems - * process.c (Fmake_network_process): Make compilation work + * src/process.c (Fmake_network_process): Make compilation work again on hosts that don't have getaddrinfo_a. 2016-01-29 Lars Ingebrigtsen Avoid memory leaks in async DNS - * process.c (check_for_dns): Free async DNS resources after + * src/process.c (check_for_dns): Free async DNS resources after they've been used. 2016-01-29 Lars Ingebrigtsen - * process.c (check_for_dns): Free the result data. + * src/process.c (check_for_dns): Free the result data. 2016-01-29 Lars Ingebrigtsen Fix server connections - * process.c (Fmake_network_process): Make creating server + * src/process.c (Fmake_network_process): Make creating server listening ports work again. 2016-01-29 Lars Ingebrigtsen Further make_network_process clean up - * process.c (Fmake_network_process): Remove setting of unused + * src/process.c (Fmake_network_process): Remove setting of unused family variable. 2016-01-29 Lars Ingebrigtsen Clean up GETADDRINFO usage in make-network-process - * process.c (Fmake_network_process): Clean up the GETADDRINFO + * src/process.c (Fmake_network_process): Clean up the GETADDRINFO handling. 2016-01-29 Lars Ingebrigtsen Implement asynchronous name resolution - * process.c (Fmake_network_process): Do asynchronous DNS + * src/process.c (Fmake_network_process): Do asynchronous DNS lookups if we have getaddrinfo_a and the user requests :nowait. (check_for_dns): New function. (wait_reading_process_output): Check for pending name resolution in the idle loop. - * process.h: Add structure for async DNS. + * src/process.h: Add structure for async DNS. 2016-01-28 Glenn Morris @@ -19841,7 +19849,7 @@ Fix memory leak - * process.c (connect_network_socket): Free previous sockaddr + * src/process.c (connect_network_socket): Free previous sockaddr before allocating a new one. 2016-01-28 Lars Ingebrigtsen @@ -19877,7 +19885,7 @@ 2016-01-27 Glenn Morris - * test/lisp/vc/vc-hg.el: Move from test/automated/. + * test/lisp/vc/vc-hg-tests.el: Move from test/automated/. 2016-01-25 Stefan Monnier @@ -20144,7 +20152,7 @@ 2016-01-17 Bill Wohler - * mh-e.el (mh-version): Add +git to version. + * lisp/mh-e/mh-e.el (mh-version): Add +git to version. 2016-01-16 Stefan Monnier @@ -20186,7 +20194,8 @@ 2016-01-16 Stefan Monnier - * elisp-mode.el (elisp--font-lock-flush-elisp-buffers): Fix comment + * lisp/progmodes/elisp-mode.el + (elisp--font-lock-flush-elisp-buffers): Fix comment 2016-01-16 Stefan Monnier @@ -20231,7 +20240,8 @@ 2016-01-15 Stefan Monnier - * xmltok.el: Mark the "sole --" rather than the comment opener + * lisp/nxml/xmltok.el: Mark the "sole --" rather than the comment + opener. * lisp/nxml/xmltok.el (xmltok-scan-after-comment-open): Put the error marker on the "sole --" rather than on the comment opener. @@ -20805,12 +20815,12 @@ free to format differently a really empty cell, ie. containing nil, from a cell containing an empty string "". - * ses.el (ses-call-printer): Replace `(or value "")' by just `value' - in the case of a lambda expression printer function. + * lisp/ses.el (ses-call-printer): Replace `(or value "")' by just + `value' in the case of a lambda expression printer function. - * ses.texi (Printer functions): Add example and description about - lambda expression printer function handling all the possible values, - including unexpected ones. + * doc/misc/ses.texi (Printer functions): Add example and + description about lambda expression printer function handling all + the possible values, including unexpected ones. 2015-12-30 Vincent Belaïche @@ -20821,7 +20831,7 @@ removed the (setq ses--curcell t) setting in the ses-command-hook function. - * ses.el (ses-check-curcell): replace `(eq ses--curcell t)' by just `t' as + * lisp/ses.el (ses-check-curcell): replace `(eq ses--curcell t)' by just `t' as a condition to call function `ses-set-curcell'. Comment this as a quick temporary hack to make it work, as I don't know yet whether a definite correction would be to make the ses-set-curcell at every ses-check-curcell, @@ -20863,14 +20873,14 @@ Further Unicode restrictive fixups - * puny.el (puny-highly-restrictive-p): Include the extra + * lisp/net/puny.el (puny-highly-restrictive-p): Include the extra identifier characters from table 3. 2015-12-29 Lars Ingebrigtsen Add a new function to say whether a string is restrictive - * puny.el (puny-highly-restrictive-p): New function. + * lisp/net/puny.el (puny-highly-restrictive-p): New function. 2015-12-28 Lars Ingebrigtsen @@ -20891,7 +20901,7 @@ IDNA-encode all domain names in `open-network-stream' - * network-stream.el (open-network-stream) + * lisp/net/network-stream.el (open-network-stream) (network-stream-open-plain, network-stream-open-starttls): IDNA-encode all domain names, if needed. @@ -20899,13 +20909,14 @@ Fix puny-encoding all-non-ASCII domains - * puny.el (puny-encode-string): Fix the all-non-ASCII encoding case. + * lisp/net/puny.el (puny-encode-string): Fix the all-non-ASCII + encoding case. 2015-12-28 Lars Ingebrigtsen shr link traversal fixup - * shr.el (shr-next-link): Don't bug out on adjacent links. + * lisp/net/shr.el (shr-next-link): Don't bug out on adjacent links. 2015-12-28 Lars Ingebrigtsen @@ -20918,19 +20929,19 @@ Fix punycode short circuit logic - * puny.el (puny-encode-domain): Fix short-circuit logic. + * lisp/net//puny.el (puny-encode-domain): Fix short-circuit logic. 2015-12-28 Lars Ingebrigtsen IDNA speed up - * puny.el (puny-encode-domain): Make the common non-IDNA case faster + * lisp/net/puny.el (puny-encode-domain): Make the common non-IDNA case faster 2015-12-28 Lars Ingebrigtsen Add IDNA domain encode/decode functions - * puny.el (puny-decode-domain): New function. + * lisp/net/puny.el (puny-decode-domain): New function. (puny-encode-domain): Ditto. (puny-decode-digit): Fix digit decoding error. @@ -20938,7 +20949,7 @@ Rename idna.el to puny.el - * puny.el: Renamed from idna.el to avoid name collisions with + * lisp/net/puny.el: Renamed from idna.el to avoid name collisions with the external idna.el library. 2015-12-27 Katsumi Yamaoka @@ -20950,7 +20961,8 @@ 2015-12-27 Lars Ingebrigtsen - * idna.el (idna-decode-string-internal): Implement decoding. + * lisp/net/idna.el (idna-decode-string-internal): Implement + decoding. 2015-12-27 Lars Ingebrigtsen @@ -21276,7 +21288,7 @@ * lisp/dired.el: Remove autoloads. * lisp/Makefile.in: Add dired to autogenel. - * lisp/dired-aux.el,lisp/dired-x.el: Update file local. + * lisp/dired-aux.el, lisp/dired-x.el: Update file local. * test/lisp/dired-tests.el: Add new test. 2015-12-17 Phillip Lord @@ -21284,9 +21296,9 @@ eieio generate autoloads to non-versioned file. * lisp/Makefile.in: eieio-loaddefs add to autogenel. - * lisp/emacs-lisp/eieio.el,lisp/emacs-lisp/eieio-core.el: + * lisp/emacs-lisp/eieio.el, lisp/emacs-lisp/eieio-core.el: Remove autoloads. - * lisp/emacs-lisp/eieio-compat.el,lisp/emacs-lisp/eieio-custom.el, + * lisp/emacs-lisp/eieio-compat.el, lisp/emacs-lisp/eieio-custom.el, lisp/emacs-lisp/eieio-opt.el: Update file local. * test/lisp/emacs-lisp/eieio-tests/eieio-tests.el: New test. @@ -21314,10 +21326,10 @@ * lisp/Makefile.in: Add rmail-loaddefs.el to autogenel. * lisp/mail/rmail.el: Remove autoloads, add require. - * lisp/mail/rmailedit.el,lisp/mail/rmailkwd.el, - lisp/mail/rmailmm.el,lisp/mail/rmailmsc.el, - lisp/mail/rmailsort.el,lisp/mail/rmailsum.el, - lisp/mail/undigest.el: Update file-local. + * lisp/mail/rmailedit.el, lisp/mail/rmailkwd.el: + * lisp/mail/rmailmm.el, lisp/mail/rmailmsc.el: + * lisp/mail/rmailsort.el, lisp/mail/rmailsum.el: + * lisp/mail/undigest.el: Update file-local. * test/lisp/mail/rmail-tests.el: 2015-12-17 Phillip Lord @@ -21341,11 +21353,11 @@ * lisp/Makefile.in: Add reftex-loaddefs to autogen files * lisp/textmodes/reftex.el: Remove autoloads. - * lisp/textmodes/reftex-auc.el,lisp/textmodes/reftex-cite.el, - lisp/textmodes/reftex-dcr.el,lisp/textmodes/reftex-global.el, - lisp/textmodes/reftex-index.el,lisp/textmodes/reftex-parse.el, - lisp/textmodes/reftex-ref.el,lisp/textmodes/reftex-sel.el, - lisp/textmodes/reftex-toc.el: Update autoload file-local. + * lisp/textmodes/reftex-auc.el, lisp/textmodes/reftex-cite.el: + * lisp/textmodes/reftex-dcr.el, lisp/textmodes/reftex-global.el: + * lisp/textmodes/reftex-index.el, lisp/textmodes/reftex-parse.el: + * lisp/textmodes/reftex-ref.el, lisp/textmodes/reftex-sel.el: + * lisp/textmodes/reftex-toc.el: Update autoload file-local. * test/lisp/textmodes/reftex-tests.el: Add test of an autoloaded function. @@ -21933,262 +21945,263 @@ Rename all test files to reflect source layout. - * CONTRIBUTE,Makefile.in,configure.ac: Update to reflect - test directory moves. - * test/file-organisation.org: New file. - * test/automated/Makefile.in - test/automated/data/decompress/foo.gz - test/automated/data/epg/pubkey.asc - test/automated/data/epg/seckey.asc - test/automated/data/files-bug18141.el.gz - test/automated/data/flymake/test.c - test/automated/data/flymake/test.pl - test/automated/data/package/archive-contents - test/automated/data/package/key.pub - test/automated/data/package/key.sec - test/automated/data/package/multi-file-0.2.3.tar - test/automated/data/package/multi-file-readme.txt - test/automated/data/package/newer-versions/archive-contents - test/automated/data/package/newer-versions/new-pkg-1.0.el - test/automated/data/package/newer-versions/simple-single-1.4.el - test/automated/data/package/package-test-server.py - test/automated/data/package/signed/archive-contents - test/automated/data/package/signed/archive-contents.sig - test/automated/data/package/signed/signed-bad-1.0.el - test/automated/data/package/signed/signed-bad-1.0.el.sig - test/automated/data/package/signed/signed-good-1.0.el - test/automated/data/package/signed/signed-good-1.0.el.sig - test/automated/data/package/simple-depend-1.0.el - test/automated/data/package/simple-single-1.3.el - test/automated/data/package/simple-single-readme.txt - test/automated/data/package/simple-two-depend-1.1.el - test/automated/abbrev-tests.el - test/automated/auto-revert-tests.el - test/automated/calc-tests.el - test/automated/icalendar-tests.el - test/automated/character-fold-tests.el - test/automated/comint-testsuite.el - test/automated/descr-text-test.el - test/automated/electric-tests.el - test/automated/cl-generic-tests.el - test/automated/cl-lib-tests.el - test/automated/eieio-test-methodinvoke.el - test/automated/eieio-test-persist.el - test/automated/eieio-tests.el - test/automated/ert-tests.el - test/automated/ert-x-tests.el - test/automated/generator-tests.el - test/automated/let-alist.el - test/automated/map-tests.el - test/automated/advice-tests.el - test/automated/package-test.el - test/automated/pcase-tests.el - test/automated/regexp-tests.el - test/automated/seq-tests.el - test/automated/subr-x-tests.el - test/automated/tabulated-list-test.el - test/automated/thunk-tests.el - test/automated/timer-tests.el - test/automated/epg-tests.el - test/automated/eshell.el - test/automated/faces-tests.el - test/automated/file-notify-tests.el - test/automated/auth-source-tests.el - test/automated/gnus-tests.el - test/automated/message-mode-tests.el - test/automated/help-fns.el - test/automated/imenu-test.el - test/automated/info-xref.el - test/automated/mule-util.el - test/automated/isearch-tests.el - test/automated/json-tests.el - test/automated/bytecomp-tests.el - test/automated/coding-tests.el - test/automated/core-elisp-tests.el - test/automated/decoder-tests.el - test/automated/files.el - test/automated/font-parse-tests.el - test/automated/lexbind-tests.el - test/automated/occur-tests.el - test/automated/process-tests.el - test/automated/syntax-tests.el - test/automated/textprop-tests.el - test/automated/undo-tests.el - test/automated/man-tests.el - test/automated/completion-tests.el - test/automated/dbus-tests.el - test/automated/newsticker-tests.el - test/automated/sasl-scram-rfc-tests.el - test/automated/tramp-tests.el - test/automated/obarray-tests.el - test/automated/compile-tests.el - test/automated/elisp-mode-tests.el - test/automated/f90.el - test/automated/flymake-tests.el - test/automated/python-tests.el - test/automated/ruby-mode-tests.el - test/automated/subword-tests.el - test/automated/replace-tests.el - test/automated/simple-test.el - test/automated/sort-tests.el - test/automated/subr-tests.el - test/automated/reftex-tests.el - test/automated/sgml-mode-tests.el - test/automated/tildify-tests.el - test/automated/thingatpt.el - test/automated/url-future-tests.el - test/automated/url-util-tests.el - test/automated/add-log-tests.el - test/automated/vc-bzr.el - test/automated/vc-tests.el - test/automated/xml-parse-tests.el - test/BidiCharacterTest.txt - test/biditest.el - test/cedet/cedet-utests.el - test/cedet/ede-tests.el - test/cedet/semantic-ia-utest.el - test/cedet/semantic-tests.el - test/cedet/semantic-utest-c.el - test/cedet/semantic-utest.el - test/cedet/srecode-tests.el - test/cedet/tests/test.c - test/cedet/tests/test.el - test/cedet/tests/test.make - test/cedet/tests/testdoublens.cpp - test/cedet/tests/testdoublens.hpp - test/cedet/tests/testfriends.cpp - test/cedet/tests/testjavacomp.java - test/cedet/tests/testnsp.cpp - test/cedet/tests/testpolymorph.cpp - test/cedet/tests/testspp.c - test/cedet/tests/testsppcomplete.c - test/cedet/tests/testsppreplace.c - test/cedet/tests/testsppreplaced.c - test/cedet/tests/testsubclass.cpp - test/cedet/tests/testsubclass.hh - test/cedet/tests/testtypedefs.cpp - test/cedet/tests/testvarnames.c - test/etags/CTAGS.good - test/etags/ETAGS.good_1 - test/etags/ETAGS.good_2 - test/etags/ETAGS.good_3 - test/etags/ETAGS.good_4 - test/etags/ETAGS.good_5 - test/etags/ETAGS.good_6 - test/etags/a-src/empty.zz - test/etags/a-src/empty.zz.gz - test/etags/ada-src/2ataspri.adb - test/etags/ada-src/2ataspri.ads - test/etags/ada-src/etags-test-for.ada - test/etags/ada-src/waroquiers.ada - test/etags/c-src/a/b/b.c - test/etags/c-src/abbrev.c - test/etags/c-src/c.c - test/etags/c-src/dostorture.c - test/etags/c-src/emacs/src/gmalloc.c - test/etags/c-src/emacs/src/keyboard.c - test/etags/c-src/emacs/src/lisp.h - test/etags/c-src/emacs/src/regex.h - test/etags/c-src/etags.c - test/etags/c-src/exit.c - test/etags/c-src/exit.strange_suffix - test/etags/c-src/fail.c - test/etags/c-src/getopt.h - test/etags/c-src/h.h - test/etags/c-src/machsyscalls.c - test/etags/c-src/machsyscalls.h - test/etags/c-src/sysdep.h - test/etags/c-src/tab.c - test/etags/c-src/torture.c - test/etags/cp-src/MDiagArray2.h - test/etags/cp-src/Range.h - test/etags/cp-src/burton.cpp - test/etags/cp-src/c.C - test/etags/cp-src/clheir.cpp.gz - test/etags/cp-src/clheir.hpp - test/etags/cp-src/conway.cpp - test/etags/cp-src/conway.hpp - test/etags/cp-src/fail.C - test/etags/cp-src/functions.cpp - test/etags/cp-src/screen.cpp - test/etags/cp-src/screen.hpp - test/etags/cp-src/x.cc - test/etags/el-src/TAGTEST.EL - test/etags/el-src/emacs/lisp/progmodes/etags.el - test/etags/erl-src/gs_dialog.erl - test/etags/f-src/entry.for - test/etags/f-src/entry.strange.gz - test/etags/f-src/entry.strange_suffix - test/etags/forth-src/test-forth.fth - test/etags/html-src/algrthms.html - test/etags/html-src/index.shtml - test/etags/html-src/software.html - test/etags/html-src/softwarelibero.html - test/etags/lua-src/allegro.lua - test/etags/objc-src/PackInsp.h - test/etags/objc-src/PackInsp.m - test/etags/objc-src/Subprocess.h - test/etags/objc-src/Subprocess.m - test/etags/objcpp-src/SimpleCalc.H - test/etags/objcpp-src/SimpleCalc.M - test/etags/pas-src/common.pas - test/etags/perl-src/htlmify-cystic - test/etags/perl-src/kai-test.pl - test/etags/perl-src/yagrip.pl - test/etags/php-src/lce_functions.php - test/etags/php-src/ptest.php - test/etags/php-src/sendmail.php - test/etags/prol-src/natded.prolog - test/etags/prol-src/ordsets.prolog - test/etags/ps-src/rfc1245.ps - test/etags/pyt-src/server.py - test/etags/tex-src/gzip.texi - test/etags/tex-src/nonewline.tex - test/etags/tex-src/testenv.tex - test/etags/tex-src/texinfo.tex - test/etags/y-src/atest.y - test/etags/y-src/cccp.c - test/etags/y-src/cccp.y - test/etags/y-src/parse.c - test/etags/y-src/parse.y - test/indent/css-mode.css - test/indent/js-indent-init-dynamic.js - test/indent/js-indent-init-t.js - test/indent/js-jsx.js - test/indent/js.js - test/indent/latex-mode.tex - test/indent/modula2.mod - test/indent/nxml.xml - test/indent/octave.m - test/indent/pascal.pas - test/indent/perl.perl - test/indent/prolog.prolog - test/indent/ps-mode.ps - test/indent/ruby.rb - test/indent/scheme.scm - test/indent/scss-mode.scss - test/indent/sgml-mode-attribute.html - test/indent/shell.rc - test/indent/shell.sh - test/redisplay-testsuite.el - test/rmailmm.el - test/automated/buffer-tests.el - test/automated/cmds-tests.el - test/automated/data-tests.el - test/automated/finalizer-tests.el - test/automated/fns-tests.el - test/automated/inotify-test.el - test/automated/keymap-tests.el - test/automated/print-tests.el - test/automated/libxml-tests.el - test/automated/zlib-tests.el: Files Moved. + * CONTRIBUTE, Makefile.in, configure.ac: Update to reflect + test directory moves. + * test/file-organisation.org: New file. + * test/automated/Makefile.in: + * test/automated/data/decompress/foo.gz: + * test/automated/data/epg/pubkey.asc: + * test/automated/data/epg/seckey.asc: + * test/automated/data/files-bug18141.el.gz: + * test/automated/data/flymake/test.c: + * test/automated/data/flymake/test.pl: + * test/automated/data/package/archive-contents: + * test/automated/data/package/key.pub: + * test/automated/data/package/key.sec: + * test/automated/data/package/multi-file-0.2.3.tar: + * test/automated/data/package/multi-file-readme.txt: + * test/automated/data/package/newer-versions/archive-contents: + * test/automated/data/package/newer-versions/new-pkg-1.0.el: + * test/automated/data/package/newer-versions/simple-single-1.4.el: + * test/automated/data/package/package-test-server.py: + * test/automated/data/package/signed/archive-contents: + * test/automated/data/package/signed/archive-contents.sig: + * test/automated/data/package/signed/signed-bad-1.0.el: + * test/automated/data/package/signed/signed-bad-1.0.el.sig: + * test/automated/data/package/signed/signed-good-1.0.el: + * test/automated/data/package/signed/signed-good-1.0.el.sig: + * test/automated/data/package/simple-depend-1.0.el: + * test/automated/data/package/simple-single-1.3.el: + * test/automated/data/package/simple-single-readme.txt: + * test/automated/data/package/simple-two-depend-1.1.el: + * test/automated/abbrev-tests.el: + * test/automated/auto-revert-tests.el: + * test/automated/calc-tests.el: + * test/automated/icalendar-tests.el: + * test/automated/character-fold-tests.el: + * test/automated/comint-testsuite.el: + * test/automated/descr-text-test.el: + * test/automated/electric-tests.el: + * test/automated/cl-generic-tests.el: + * test/automated/cl-lib-tests.el: + * test/automated/eieio-test-methodinvoke.el: + * test/automated/eieio-test-persist.el: + * test/automated/eieio-tests.el: + * test/automated/ert-tests.el: + * test/automated/ert-x-tests.el: + * test/automated/generator-tests.el: + * test/automated/let-alist.el: + * test/automated/map-tests.el: + * test/automated/advice-tests.el: + * test/automated/package-test.el: + * test/automated/pcase-tests.el: + * test/automated/regexp-tests.el: + * test/automated/seq-tests.el: + * test/automated/subr-x-tests.el: + * test/automated/tabulated-list-test.el: + * test/automated/thunk-tests.el: + * test/automated/timer-tests.el: + * test/automated/epg-tests.el: + * test/automated/eshell.el: + * test/automated/faces-tests.el: + * test/automated/file-notify-tests.el: + * test/automated/auth-source-tests.el: + * test/automated/gnus-tests.el: + * test/automated/message-mode-tests.el: + * test/automated/help-fns.el: + * test/automated/imenu-test.el: + * test/automated/info-xref.el: + * test/automated/mule-util.el: + * test/automated/isearch-tests.el: + * test/automated/json-tests.el: + * test/automated/bytecomp-tests.el: + * test/automated/coding-tests.el: + * test/automated/core-elisp-tests.el: + * test/automated/decoder-tests.el: + * test/automated/files.el: + * test/automated/font-parse-tests.el: + * test/automated/lexbind-tests.el: + * test/automated/occur-tests.el: + * test/automated/process-tests.el: + * test/automated/syntax-tests.el: + * test/automated/textprop-tests.el: + * test/automated/undo-tests.el: + * test/automated/man-tests.el: + * test/automated/completion-tests.el: + * test/automated/dbus-tests.el: + * test/automated/newsticker-tests.el: + * test/automated/sasl-scram-rfc-tests.el: + * test/automated/tramp-tests.el: + * test/automated/obarray-tests.el: + * test/automated/compile-tests.el: + * test/automated/elisp-mode-tests.el: + * test/automated/f90.el: + * test/automated/flymake-tests.el: + * test/automated/python-tests.el: + * test/automated/ruby-mode-tests.el: + * test/automated/subword-tests.el: + * test/automated/replace-tests.el: + * test/automated/simple-test.el: + * test/automated/sort-tests.el: + * test/automated/subr-tests.el: + * test/automated/reftex-tests.el: + * test/automated/sgml-mode-tests.el: + * test/automated/tildify-tests.el: + * test/automated/thingatpt.el: + * test/automated/url-future-tests.el: + * test/automated/url-util-tests.el: + * test/automated/add-log-tests.el: + * test/automated/vc-bzr.el: + * test/automated/vc-tests.el: + * test/automated/xml-parse-tests.el: + * test/BidiCharacterTest.txt: + * test/biditest.el: + * test/cedet/cedet-utests.el: + * test/cedet/ede-tests.el: + * test/cedet/semantic-ia-utest.el: + * test/cedet/semantic-tests.el: + * test/cedet/semantic-utest-c.el: + * test/cedet/semantic-utest.el: + * test/cedet/srecode-tests.el: + * test/cedet/tests/test.c: + * test/cedet/tests/test.el: + * test/cedet/tests/test.make: + * test/cedet/tests/testdoublens.cpp: + * test/cedet/tests/testdoublens.hpp: + * test/cedet/tests/testfriends.cpp: + * test/cedet/tests/testjavacomp.java: + * test/cedet/tests/testnsp.cpp: + * test/cedet/tests/testpolymorph.cpp: + * test/cedet/tests/testspp.c: + * test/cedet/tests/testsppcomplete.c: + * test/cedet/tests/testsppreplace.c: + * test/cedet/tests/testsppreplaced.c: + * test/cedet/tests/testsubclass.cpp: + * test/cedet/tests/testsubclass.hh: + * test/cedet/tests/testtypedefs.cpp: + * test/cedet/tests/testvarnames.c: + * test/etags/CTAGS.good: + * test/etags/ETAGS.good_1: + * test/etags/ETAGS.good_2: + * test/etags/ETAGS.good_3: + * test/etags/ETAGS.good_4: + * test/etags/ETAGS.good_5: + * test/etags/ETAGS.good_6: + * test/etags/a-src/empty.zz: + * test/etags/a-src/empty.zz.gz: + * test/etags/ada-src/2ataspri.adb: + * test/etags/ada-src/2ataspri.ads: + * test/etags/ada-src/etags-test-for.ada: + * test/etags/ada-src/waroquiers.ada: + * test/etags/c-src/a/b/b.c: + * test/etags/c-src/abbrev.c: + * test/etags/c-src/c.c: + * test/etags/c-src/dostorture.c: + * test/etags/c-src/emacs/src/gmalloc.c: + * test/etags/c-src/emacs/src/keyboard.c: + * test/etags/c-src/emacs/src/lisp.h: + * test/etags/c-src/emacs/src/regex.h: + * test/etags/c-src/etags.c: + * test/etags/c-src/exit.c: + * test/etags/c-src/exit.strange_suffix: + * test/etags/c-src/fail.c: + * test/etags/c-src/getopt.h: + * test/etags/c-src/h.h: + * test/etags/c-src/machsyscalls.c: + * test/etags/c-src/machsyscalls.h: + * test/etags/c-src/sysdep.h: + * test/etags/c-src/tab.c: + * test/etags/c-src/torture.c: + * test/etags/cp-src/MDiagArray2.h: + * test/etags/cp-src/Range.h: + * test/etags/cp-src/burton.cpp: + * test/etags/cp-src/c.C: + * test/etags/cp-src/clheir.cpp.gz: + * test/etags/cp-src/clheir.hpp: + * test/etags/cp-src/conway.cpp: + * test/etags/cp-src/conway.hpp: + * test/etags/cp-src/fail.C: + * test/etags/cp-src/functions.cpp: + * test/etags/cp-src/screen.cpp: + * test/etags/cp-src/screen.hpp: + * test/etags/cp-src/x.cc: + * test/etags/el-src/TAGTEST.EL: + * test/etags/el-src/emacs/lisp/progmodes/etags.el: + * test/etags/erl-src/gs_dialog.erl: + * test/etags/f-src/entry.for: + * test/etags/f-src/entry.strange.gz: + * test/etags/f-src/entry.strange_suffix: + * test/etags/forth-src/test-forth.fth: + * test/etags/html-src/algrthms.html: + * test/etags/html-src/index.shtml: + * test/etags/html-src/software.html: + * test/etags/html-src/softwarelibero.html: + * test/etags/lua-src/allegro.lua: + * test/etags/objc-src/PackInsp.h: + * test/etags/objc-src/PackInsp.m: + * test/etags/objc-src/Subprocess.h: + * test/etags/objc-src/Subprocess.m: + * test/etags/objcpp-src/SimpleCalc.H: + * test/etags/objcpp-src/SimpleCalc.M: + * test/etags/pas-src/common.pas: + * test/etags/perl-src/htlmify-cystic: + * test/etags/perl-src/kai-test.pl: + * test/etags/perl-src/yagrip.pl: + * test/etags/php-src/lce_functions.php: + * test/etags/php-src/ptest.php: + * test/etags/php-src/sendmail.php: + * test/etags/prol-src/natded.prolog: + * test/etags/prol-src/ordsets.prolog: + * test/etags/ps-src/rfc1245.ps: + * test/etags/pyt-src/server.py: + * test/etags/tex-src/gzip.texi: + * test/etags/tex-src/nonewline.tex: + * test/etags/tex-src/testenv.tex: + * test/etags/tex-src/texinfo.tex: + * test/etags/y-src/atest.y: + * test/etags/y-src/cccp.c: + * test/etags/y-src/cccp.y: + * test/etags/y-src/parse.c: + * test/etags/y-src/parse.y: + * test/indent/css-mode.css: + * test/indent/js-indent-init-dynamic.js: + * test/indent/js-indent-init-t.js: + * test/indent/js-jsx.js: + * test/indent/js.js: + * test/indent/latex-mode.tex: + * test/indent/modula2.mod: + * test/indent/nxml.xml: + * test/indent/octave.m: + * test/indent/pascal.pas: + * test/indent/perl.perl: + * test/indent/prolog.prolog: + * test/indent/ps-mode.ps: + * test/indent/ruby.rb: + * test/indent/scheme.scm: + * test/indent/scss-mode.scss: + * test/indent/sgml-mode-attribute.html: + * test/indent/shell.rc: + * test/indent/shell.sh: + * test/redisplay-testsuite.el: + * test/rmailmm.el: + * test/automated/buffer-tests.el: + * test/automated/cmds-tests.el: + * test/automated/data-tests.el: + * test/automated/finalizer-tests.el: + * test/automated/fns-tests.el: + * test/automated/inotify-test.el: + * test/automated/keymap-tests.el: + * test/automated/print-tests.el: + * test/automated/libxml-tests.el: + * test/automated/zlib-tests.el: Files Moved. 2015-11-21 Wilson Snyder verilog-mode.el: Commentary and fix pre-Emacs 21 behavior. - * verilog-mode.el (verilog-save-font-no-change-functions): - Commentary and fix pre-Emacs 21 behavior. + * lisp/progmodes/verilog-mode.el + (verilog-save-font-no-change-functions): Commentary and fix + pre-Emacs 21 behavior. 2015-11-20 Michael Albinus @@ -22324,8 +22337,8 @@ Minor fix to comment indentation and typo in last commit - * linum.el (linum-update-window): Fix comment indentation and a - typo. + * lisp/linum.el (linum-update-window): Fix comment indentation and + a typo. 2015-11-17 João Távora @@ -22342,7 +22355,7 @@ A similar fix was commited to nlinum.el in ELPA.git's e7f5f549fbfb740b911fb7f33b42381ecece56d8 - * linum.el (linum-delete-overlays): Restore margins more + * lisp/linum.el (linum-delete-overlays): Restore margins more criteriously. (linum-update-window): Set margins more criteriously. @@ -22518,7 +22531,7 @@ Update verilog-mode.el to 2015-11-09-b121d60-vpo. - * verilog-mode.el (verilog-auto, verilog-delete-auto) + * lisp/progmodes/verilog-mode.el (verilog-auto, verilog-delete-auto) (verilog-modi-cache-results, verilog-save-buffer-state) (verilog-save-font-no-change-functions): When internally suppressing change functions, use `inhibit-modification-hooks' and call diff --git a/lisp/gnus/ChangeLog.3 b/lisp/gnus/ChangeLog.3 index 0d4b268f16..c2e4846442 100644 --- a/lisp/gnus/ChangeLog.3 +++ b/lisp/gnus/ChangeLog.3 @@ -9303,7 +9303,7 @@ * mail-source.el, message.el, mm-bodies.el, mm-decode.el, mm-extern.el: * mm-util.el, mm-view.el, mml-smime.el, mml.el, mml1991.el, mml2015.el: * nnfolder.el, nnheader.el, nnmail.el, nnmaildir.el, nnrss.el, nntp.el: - * rfc1843.el, sieve-manage.el, smime.el, spam.el: + * gnus-rfc1843.el, sieve-manage.el, smime.el, spam.el: Fix comment for declare-function. 2010-10-11 Lars Magne Ingebrigtsen @@ -10470,7 +10470,7 @@ 2010-09-25 Julien Danjou - * rfc1843.el: Remove useless rfc1843-old-gnus-decode-header-function + * gnus-rfc1843.el: Remove useless rfc1843-old-gnus-decode-header-function variables. * nnheader.el: Remove useless variables news-reply-yank-from and @@ -14716,14 +14716,14 @@ * mml2015.el (gnus-buffer-live-p, gnus-get-buffer-create): * nnfolder.el (gnus-request-group): * nnheader.el (ietf-drums-unfold-fws): - * rfc1843.el (mail-header-parse-content-type, message-narrow-to-head): + * gnus-rfc1843.el (mail-header-parse-content-type, message-narrow-to-head): * smime.el (gnus-run-mode-hooks): * spam-stat.el (gnus-message): Autoload. * gnus-cache.el, gnus-fun.el, gnus-group.el, gnus.el, mail-source.el: * mm-bodies.el, mm-decode.el, mm-extern.el, mm-util.el: * mml-smime.el, mml.el, mml1991.el, mml2015.el, nndb.el, nnfolder.el: - * nnmail.el, nnmaildir.el, nnrss.el, rfc1843.el, spam.el: + * nnmail.el, nnmaildir.el, nnrss.el, gnus-rfc1843.el, spam.el: Add declare-function compatibility definition. * gnus-cache.el (nnvirtual-find-group-art): @@ -14753,7 +14753,7 @@ * nnmail.el (gnus-activate-group, gnus-group-mark-article-read): * nnmaildir.el (gnus-group-mark-article-read): * nnrss.el (w3-parse-buffer, gnus-group-make-rss-group): - * rfc1843.el (message-fetch-field): + * gnus-rfc1843.el (message-fetch-field): * spam.el (gnus-extract-address-components): Declare as functions. @@ -19139,7 +19139,7 @@ (mml-insert-parameter): Fold lines properly even if a parameter is segmented into two or more lines; change the max column to 76. - * rfc1843.el (rfc1843-decode-article-body): Don't use + * gnus-rfc1843.el (rfc1843-decode-article-body): Don't use ignore-errors when calling mail-header-parse-content-type. * rfc2231.el (rfc2231-parse-string): Return at least type if @@ -20525,7 +20525,7 @@ * mml1991.el (mc-pgp-always-sign): * mml2015.el (mc-pgp-always-sign): * nnheader.el (nnmail-extra-headers): - * rfc1843.el (gnus-decode-encoded-word-function) + * gnus-rfc1843.el (gnus-decode-encoded-word-function) (gnus-decode-header-function, gnus-newsgroup-name): * spam-stat.el (gnus-original-article-buffer): Add defvars. commit b9712f568641d81a939fd7dd02f4f5f87b7f6f5c Author: Nicolas Petton Date: Tue Oct 3 13:11:07 2017 +0200 Update authors.el * admin/authors.el (authors-ignored-files, authors-valid-file-names) (authors-renamed-files-alist): Additions. diff --git a/admin/authors.el b/admin/authors.el index c69ca9405c..f79c2e8877 100644 --- a/admin/authors.el +++ b/admin/authors.el @@ -1,3 +1,4 @@ + ;;; authors.el --- utility for maintaining Emacs's AUTHORS file ;; Copyright (C) 2000-2017 Free Software Foundation, Inc. @@ -391,7 +392,7 @@ Changes to files matching one of the regexps in this list are not listed.") "vms" "mac" "url" "tree-widget" "info/dir" ;; Not in gnulib anymore - "lib/qset-acl.c" "lib/qcopy-acl.c" "lib/file-has-acl.c" + "lib/qset-acl.c" "lib/qcopy-acl.c" "lib/file-has-acl.c" "lib/secure_getenv.c" ;; files from old MS Windows build procedures "nt/gnulib-modules-to-delete.cfg" "makefile.w32-in" @@ -736,6 +737,8 @@ Changes to files in this list are not listed.") "org-exp-blocks.el" ; maybe this is ob-exp now? dunno "org-lparse.el" "org-special-blocks.el" "org-taskjuggler.el" + "ob-sh.el" + "ob-scala.el" "progmodes/cap-words.el" "w32-common-fns.el" ;; gnus @@ -751,7 +754,7 @@ Changes to files in this list are not listed.") "format-spec.el" "gnus-move.el" "gnus-sync.el" "auth-source.el" "ecomplete.el" "gravatar.el" "mailcap.el" "plstore.el" "pop3.el" "qp.el" "registry.el" "rfc2231.el" "rtree.el" - "sieve.el" "sieve-mode.el" + "sieve.el" "sieve-mode.el" "gnus-ems.el" ;; doc "getopt.c" "texindex.c" "news.texi" "vc.texi" "vc2-xtra.texi" "back.texi" "vol1.texi" "vol2.texi" "elisp-covers.texi" "two.el" @@ -801,7 +804,9 @@ Changes to files in this list are not listed.") "cedet-utests.el" "ede-tests.el" "semantic-ia-utest.el" "semantic-tests.el" "semantic-utest-c.el" "semantic-utest.el" "srecode-tests.el" "make-test-deps.emacs-lisp" - ) + "nxml-uchnm.el" + "decoder-tests.el" + "obsolete/scribe.el") "File names which are valid, but no longer exist (or cannot be found) in the repository.") @@ -906,6 +911,8 @@ in the repository.") ("patcomp.el" . "patcomp.el") ("emulation/ws-mode.el" . "ws-mode.el") ("vc/vc-arch.el" . "vc-arch.el") + ("lisp/gnus/messcompat.el" . "messcompat.el") + ("lisp/gnus/html2text.el" . "html2text.el") ;; From lisp to etc/forms. ("forms-d2.el" . "forms-d2.el") ("forms-pass.el" . "forms-pass.el") @@ -950,9 +957,17 @@ in the repository.") ;; Moved from lisp/gnus/ to lisp/mail/ ("binhex.el" . "mail/binhex.el") ("uudecode.el" . "mail/uudecode.el") + ("mail-parse.el" . "mail/mail-parse.el") + ("yenc.el" . "mail/yenc.el") + ("flow-fill.el" . "mail/flow-fill.el") + ("ietf-drums.el" . "mail/ietf-drums.el") + ("sieve-manage.el" . "mail/sieve-manage.el") + ;; Moved from lisp/gnus/ to lisp/image/ + ("compface.el" . "image/compface.el") ;; Moved from lisp/gnus/ to lisp/net/ ("imap.el" . "net/imap.el") ("rfc2104.el" . "net/rfc2104.el") + ("starttls.el" . "net/starttls.el") ;; And from emacs/ to misc/ and back again. ("ns-emacs.texi" . "macos.texi") ("overrides.texi" . "gnus-overrides.texi") @@ -993,6 +1008,7 @@ in the repository.") ("edt-user.doc" . "edt.texi") ("DEV-NOTES" . "nextstep") ("org/COPYRIGHT-AND-LICENSE" . "org/README") + ("lisp/net/idna.el" . "puny.el") ;; Moved to different directories. ("ctags.1" . "ctags.1") ("etags.1" . "etags.1") @@ -1021,6 +1037,8 @@ in the repository.") ;; module.* moved to emacs-module.* ("src/module.h" . "src/emacs-module.h") ("src/module.c" . "src/emacs-module.c") + ;; gnulib + ("lib/strftime.c" . "lib/nstrftime.c") ) "Alist of files which have been renamed during their lifetime. Elements are (OLDNAME . NEWNAME).") commit b33808ce77ef15c1f233790a2c93d9db4cc588ab Author: Noam Postavsky Date: Mon Oct 2 22:54:36 2017 -0400 Give more helpful messages for python completion setup failures * lisp/progmodes/python.el (python-shell-completion-native-setup): In case the completion setup failed with some exception, print out the exception type and message. If libedit is detected, raise an exception, since this is known to fail. diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 365191c56b..9aa5134ca0 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -3442,6 +3442,8 @@ def __PYTHON_EL_native_completion_setup(): instance.rlcomplete = new_completer if readline.__doc__ and 'libedit' in readline.__doc__: + raise Exception('''libedit based readline is known not to work, + see etc/PROBLEMS under \"In Inferior Python mode, input is echoed\".''') readline.parse_and_bind('bind ^I rl_complete') else: readline.parse_and_bind('tab: complete') @@ -3450,7 +3452,9 @@ def __PYTHON_EL_native_completion_setup(): print ('python.el: native completion setup loaded') except: - print ('python.el: native completion setup failed') + import sys + print ('python.el: native completion setup failed, %s: %s' + % sys.exc_info()[:2]) __PYTHON_EL_native_completion_setup()" process) (when (and commit f6efc067237ff4c531ea1a43b85cd09e78f6ea0c Author: Glenn Morris Date: Mon Oct 2 14:47:55 2017 -0400 ; Auto-commit of loaddefs files. diff --git a/lisp/ldefs-boot.el b/lisp/ldefs-boot.el index 32f2a179c3..9c057231df 100644 --- a/lisp/ldefs-boot.el +++ b/lisp/ldefs-boot.el @@ -4961,12 +4961,16 @@ call other entry points instead, such as `cl-prin1'. \(fn OBJECT STREAM)" nil nil) (autoload 'cl-prin1 "cl-print" "\ - +Print OBJECT on STREAM according to its type. +Output is further controlled by the variables +`cl-print-readably', `cl-print-compiled', along with output +variables for the standard printing functions. See Info +node `(elisp)Output Variables'. \(fn OBJECT &optional STREAM)" nil nil) (autoload 'cl-prin1-to-string "cl-print" "\ - +Return a string containing the `cl-prin1'-printed representation of OBJECT. \(fn OBJECT)" nil nil) @@ -5032,7 +5036,7 @@ is run). (autoload 'color-name-to-rgb "color" "\ Convert COLOR string to a list of normalized RGB components. COLOR should be a color name (e.g. \"white\") or an RGB triplet -string (e.g. \"#ff12ec\"). +string (e.g. \"#ffff1122eecc\"). Normally the return value is a list of three floating-point numbers, (RED GREEN BLUE), each between 0.0 and 1.0 inclusive. @@ -7412,7 +7416,7 @@ May contain all other options that don't contradict `-l'; may contain even `F', `b', `i' and `s'. See also the variable `dired-ls-F-marks-symlinks' concerning the `F' switch. Options that include embedded whitespace must be quoted -like this: \\\"--option=value with spaces\\\"; you can use +like this: \"--option=value with spaces\"; you can use `combine-and-quote-strings' to produce the correct quoting of each option. On systems such as MS-DOS and MS-Windows, which use `ls' emulation in Lisp, @@ -13072,23 +13076,7 @@ to get the effect of a C-q. ;;; Generated autoloads from progmodes/flymake.el (push (purecopy '(flymake 0 3)) package--builtin-versions) -;;;*** - -;;;### (autoloads nil "flymake-proc" "progmodes/flymake-proc.el" -;;;;;; (0 0 0 0)) -;;; Generated autoloads from progmodes/flymake-proc.el -(push (purecopy '(flymake-proc 0 3)) package--builtin-versions) - -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "flymake-proc" '("flymake-"))) - -;;;*** - -;;;### (autoloads nil "flymake-ui" "progmodes/flymake-ui.el" (0 0 -;;;;;; 0 0)) -;;; Generated autoloads from progmodes/flymake-ui.el -(push (purecopy '(flymake-ui 0 3)) package--builtin-versions) - -(autoload 'flymake-mode "flymake-ui" "\ +(autoload 'flymake-mode "flymake" "\ Toggle Flymake mode on or off. With a prefix argument ARG, enable Flymake mode if ARG is positive, and disable it otherwise. If called from Lisp, enable @@ -13097,22 +13085,22 @@ the mode if ARG is omitted or nil, and toggle it if ARG is `toggle'. \(fn &optional ARG)" t nil) -(autoload 'flymake-mode-on "flymake-ui" "\ +(autoload 'flymake-mode-on "flymake" "\ Turn flymake mode on. \(fn)" nil nil) -(autoload 'flymake-mode-off "flymake-ui" "\ +(autoload 'flymake-mode-off "flymake" "\ Turn flymake mode off. \(fn)" nil nil) -(autoload 'flymake-find-file-hook "flymake-ui" "\ +(autoload 'flymake-find-file-hook "flymake" "\ \(fn)" nil nil) -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "flymake-ui" '("flymake-"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "flymake" '("flymake-"))) ;;;*** @@ -13537,7 +13525,7 @@ and choose the directory as the fortune-file. Minimum set of parameters to filter for live (on-session) framesets. DO NOT MODIFY. See `frameset-filter-alist' for a full description.") -(defvar frameset-persistent-filter-alist (nconc '((background-color . frameset-filter-sanitize-color) (buffer-list . :never) (buffer-predicate . :never) (buried-buffer-list . :never) (delete-before . :never) (font . frameset-filter-shelve-param) (foreground-color . frameset-filter-sanitize-color) (fullscreen . frameset-filter-shelve-param) (GUI:font . frameset-filter-unshelve-param) (GUI:fullscreen . frameset-filter-unshelve-param) (GUI:height . frameset-filter-unshelve-param) (GUI:width . frameset-filter-unshelve-param) (height . frameset-filter-shelve-param) (outer-window-id . :never) (parent-frame . :never) (parent-id . :never) (mouse-wheel-frame . :never) (tty . frameset-filter-tty-to-GUI) (tty-type . frameset-filter-tty-to-GUI) (width . frameset-filter-shelve-param) (window-id . :never) (window-system . :never)) frameset-session-filter-alist) "\ +(defvar frameset-persistent-filter-alist (nconc '((background-color . frameset-filter-sanitize-color) (buffer-list . :never) (buffer-predicate . :never) (buried-buffer-list . :never) (delete-before . :never) (font . frameset-filter-font-param) (foreground-color . frameset-filter-sanitize-color) (fullscreen . frameset-filter-shelve-param) (GUI:font . frameset-filter-unshelve-param) (GUI:fullscreen . frameset-filter-unshelve-param) (GUI:height . frameset-filter-unshelve-param) (GUI:width . frameset-filter-unshelve-param) (height . frameset-filter-shelve-param) (outer-window-id . :never) (parent-frame . :never) (parent-id . :never) (mouse-wheel-frame . :never) (tty . frameset-filter-tty-to-GUI) (tty-type . frameset-filter-tty-to-GUI) (width . frameset-filter-shelve-param) (window-id . :never) (window-system . :never)) frameset-session-filter-alist) "\ Parameters to filter for persistent framesets. DO NOT MODIFY. See `frameset-filter-alist' for a full description.") @@ -22984,6 +22972,13 @@ Many aspects this mode can be customized using ;;;*** +;;;### (autoloads nil "ob-hledger" "org/ob-hledger.el" (0 0 0 0)) +;;; Generated autoloads from org/ob-hledger.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "ob-hledger" '("org-babel-"))) + +;;;*** + ;;;### (autoloads nil "ob-io" "org/ob-io.el" (0 0 0 0)) ;;; Generated autoloads from org/ob-io.el @@ -23155,13 +23150,6 @@ Many aspects this mode can be customized using ;;;*** -;;;### (autoloads nil "ob-scala" "org/ob-scala.el" (0 0 0 0)) -;;; Generated autoloads from org/ob-scala.el - -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "ob-scala" '("org-babel-"))) - -;;;*** - ;;;### (autoloads nil "ob-scheme" "org/ob-scheme.el" (0 0 0 0)) ;;; Generated autoloads from org/ob-scheme.el @@ -23234,6 +23222,13 @@ Many aspects this mode can be customized using ;;;*** +;;;### (autoloads nil "ob-vala" "org/ob-vala.el" (0 0 0 0)) +;;; Generated autoloads from org/ob-vala.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "ob-vala" '("org-babel-"))) + +;;;*** + ;;;### (autoloads nil "octave" "progmodes/octave.el" (0 0 0 0)) ;;; Generated autoloads from progmodes/octave.el @@ -23561,7 +23556,6 @@ T Call `org-todo-list' to display the global todo list, select only m Call `org-tags-view' to display headlines with tags matching a condition (the user is prompted for the condition). M Like `m', but select only TODO entries, no ordinary headlines. -L Create a timeline for the current buffer. e Export views to associated files. s Search entries for keywords. S Search entries for keywords, only with TODO keywords. @@ -23701,8 +23695,9 @@ as a whole, to include whitespace. with a colon, this will mean that the (non-regexp) snippets of the Boolean search must match as full words. -This command searches the agenda files, and in addition the files listed -in `org-agenda-text-search-extra-files'. +This command searches the agenda files, and in addition the files +listed in `org-agenda-text-search-extra-files' unless a restriction lock +is active. \(fn &optional TODO-ONLY STRING EDIT-AT)" t nil) @@ -23872,6 +23867,9 @@ With a `\\[universal-argument] \\[universal-argument]' prefix argument, go to th When called with a `C-0' (zero) prefix, insert a template at point. +When called with a `C-1' (one) prefix, force prompting for a date when +a datetree entry is made. + ELisp programs can set KEYS to a string associated with a template in `org-capture-templates'. In this case, interactive selection will be bypassed. @@ -24011,6 +24009,63 @@ Try very hard to provide sensible version strings. ;;;*** +;;;### (autoloads nil "org-duration" "org/org-duration.el" (0 0 0 +;;;;;; 0)) +;;; Generated autoloads from org/org-duration.el + +(autoload 'org-duration-set-regexps "org-duration" "\ +Set duration related regexps. + +\(fn)" t nil) + +(autoload 'org-duration-p "org-duration" "\ +Non-nil when string S is a time duration. + +\(fn S)" nil nil) + +(autoload 'org-duration-to-minutes "org-duration" "\ +Return number of minutes of DURATION string. + +When optional argument CANONICAL is non-nil, ignore +`org-duration-units' and use standard time units value. + +A bare number is translated into minutes. The empty string is +translated into 0.0. + +Return value as a float. Raise an error if duration format is +not recognized. + +\(fn DURATION &optional CANONICAL)" nil nil) + +(autoload 'org-duration-from-minutes "org-duration" "\ +Return duration string for a given number of MINUTES. + +Format duration according to `org-duration-format' or FMT, when +non-nil. + +When optional argument CANONICAL is non-nil, ignore +`org-duration-units' and use standard time units value. + +Raise an error if expected format is unknown. + +\(fn MINUTES &optional FMT CANONICAL)" nil nil) + +(autoload 'org-duration-h:mm-only-p "org-duration" "\ +Non-nil when every duration in TIMES has \"H:MM\" or \"H:MM:SS\" format. + +TIMES is a list of duration strings. + +Return nil if any duration is expressed with units, as defined in +`org-duration-units'. Otherwise, if any duration is expressed +with \"H:MM:SS\" format, return `h:mm:ss'. Otherwise, return +`h:mm'. + +\(fn TIMES)" nil nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "org-duration" '("org-duration-"))) + +;;;*** + ;;;### (autoloads "actual autoloads are elsewhere" "org-element" ;;;;;; "org/org-element.el" (0 0 0 0)) ;;; Generated autoloads from org/org-element.el @@ -24067,7 +24122,7 @@ Try very hard to provide sensible version strings. ;;;### (autoloads nil "org-gnus" "org/org-gnus.el" (0 0 0 0)) ;;; Generated autoloads from org/org-gnus.el -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "org-gnus" '("org-"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "org-gnus" '("org-gnus-"))) ;;;*** @@ -31848,7 +31903,7 @@ Studlify-case the current buffer. ;;;### (autoloads nil "subr-x" "emacs-lisp/subr-x.el" (0 0 0 0)) ;;; Generated autoloads from emacs-lisp/subr-x.el -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "subr-x" '("read-multiple-choice" "string-" "hash-table-" "and-let*" "when-let" "internal--" "if-let" "thread-"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "subr-x" '("read-multiple-choice" "string-" "hash-table-" "when-let" "internal--" "if-let" "and-let*" "thread-"))) ;;;*** @@ -33660,7 +33715,7 @@ Return a string giving the duration of the Emacs initialization. \(fn)" t nil) -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "time" '("display-time-" "legacy-style-world-list" "zoneinfo-style-world-list"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "time" '("display-time-" "time--display-world-list" "legacy-style-world-list" "zoneinfo-style-world-list"))) ;;;*** @@ -33923,11 +33978,11 @@ relative only to the time worked today, and not to past time. ;;;;;; 0 0 0)) ;;; Generated autoloads from emacs-lisp/timer-list.el -(autoload 'timer-list "timer-list" "\ +(autoload 'list-timers "timer-list" "\ List all timers in a buffer. \(fn &optional IGNORE-AUTO NONCONFIRM)" t nil) - (put 'timer-list 'disabled "Beware: manually canceling timers can ruin your Emacs session.") + (put 'list-timers 'disabled "Beware: manually canceling timers can ruin your Emacs session.") (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "timer-list" '("timer-list-"))) @@ -34253,8 +34308,10 @@ Value for `tramp-file-name-regexp' for autoload. It must match the initial `tramp-syntax' settings.") (defvar tramp-file-name-regexp tramp-initial-file-name-regexp "\ -Value for `tramp-file-name-regexp' for autoload. -It must match the initial `tramp-syntax' settings.") +Regular expression matching file names handled by Tramp. +This regexp should match Tramp file names but no other file +names. When calling `tramp-register-file-name-handlers', the +initial value is overwritten by the car of `tramp-file-name-structure'.") (defconst tramp-completion-file-name-regexp-default (concat "\\`/\\(" "\\([^/|:]+:[^/|:]*|\\)*" (if (memq system-type '(cygwin windows-nt)) "\\(-\\|[^/|:]\\{2,\\}\\)" "[^/|:]+") "\\(:[^/|:]*\\)?" "\\)?\\'") "\ Value for `tramp-completion-file-name-regexp' for default remoting. @@ -34374,7 +34431,7 @@ Reenable Ange-FTP, when Tramp is unloaded. ;;;### (autoloads nil "trampver" "net/trampver.el" (0 0 0 0)) ;;; Generated autoloads from net/trampver.el -(push (purecopy '(tramp 2 3 3 -1)) package--builtin-versions) +(push (purecopy '(tramp 2 3 3 26 1)) package--builtin-versions) (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "trampver" '("tramp-"))) @@ -38447,53 +38504,44 @@ Zone out, completely. ;;;;;; "eshell/em-term.el" "eshell/em-tramp.el" "eshell/em-unix.el" ;;;;;; "eshell/em-xtra.el" "facemenu.el" "faces.el" "files.el" "font-core.el" ;;;;;; "font-lock.el" "format.el" "frame.el" "help.el" "hfy-cmap.el" -;;;;;; "ibuf-ext.el" "indent.el" "international/characters.el" "international/charprop.el" -;;;;;; "international/charscript.el" "international/cp51932.el" -;;;;;; "international/eucjp-ms.el" "international/mule-cmds.el" -;;;;;; "international/mule-conf.el" "international/mule.el" "international/uni-bidi.el" -;;;;;; "international/uni-brackets.el" "international/uni-category.el" -;;;;;; "international/uni-combining.el" "international/uni-comment.el" -;;;;;; "international/uni-decimal.el" "international/uni-decomposition.el" -;;;;;; "international/uni-digit.el" "international/uni-lowercase.el" -;;;;;; "international/uni-mirrored.el" "international/uni-name.el" -;;;;;; "international/uni-numeric.el" "international/uni-old-name.el" -;;;;;; "international/uni-titlecase.el" "international/uni-uppercase.el" -;;;;;; "isearch.el" "jit-lock.el" "jka-cmpr-hook.el" "language/burmese.el" -;;;;;; "language/cham.el" "language/chinese.el" "language/cyrillic.el" -;;;;;; "language/czech.el" "language/english.el" "language/ethiopic.el" -;;;;;; "language/european.el" "language/georgian.el" "language/greek.el" -;;;;;; "language/hebrew.el" "language/indian.el" "language/japanese.el" -;;;;;; "language/khmer.el" "language/korean.el" "language/lao.el" -;;;;;; "language/misc-lang.el" "language/romanian.el" "language/sinhala.el" -;;;;;; "language/slovak.el" "language/tai-viet.el" "language/thai.el" -;;;;;; "language/tibetan.el" "language/utf-8-lang.el" "language/vietnamese.el" -;;;;;; "ldefs-boot.el" "leim/ja-dic/ja-dic.el" "leim/leim-list.el" -;;;;;; "leim/quail/4Corner.el" "leim/quail/ARRAY30.el" "leim/quail/CCDOSPY.el" -;;;;;; "leim/quail/CTLau-b5.el" "leim/quail/CTLau.el" "leim/quail/ECDICT.el" -;;;;;; "leim/quail/ETZY.el" "leim/quail/PY-b5.el" "leim/quail/PY.el" -;;;;;; "leim/quail/Punct-b5.el" "leim/quail/Punct.el" "leim/quail/QJ-b5.el" -;;;;;; "leim/quail/QJ.el" "leim/quail/SW.el" "leim/quail/TONEPY.el" -;;;;;; "leim/quail/ZIRANMA.el" "leim/quail/ZOZY.el" "leim/quail/arabic.el" -;;;;;; "leim/quail/croatian.el" "leim/quail/cyril-jis.el" "leim/quail/cyrillic.el" -;;;;;; "leim/quail/czech.el" "leim/quail/georgian.el" "leim/quail/greek.el" -;;;;;; "leim/quail/hanja-jis.el" "leim/quail/hanja.el" "leim/quail/hanja3.el" -;;;;;; "leim/quail/hebrew.el" "leim/quail/ipa-praat.el" "leim/quail/latin-alt.el" -;;;;;; "leim/quail/latin-ltx.el" "leim/quail/latin-post.el" "leim/quail/latin-pre.el" -;;;;;; "leim/quail/persian.el" "leim/quail/programmer-dvorak.el" -;;;;;; "leim/quail/py-punct.el" "leim/quail/pypunct-b5.el" "leim/quail/quick-b5.el" -;;;;;; "leim/quail/quick-cns.el" "leim/quail/rfc1345.el" "leim/quail/sgml-input.el" -;;;;;; "leim/quail/slovak.el" "leim/quail/symbol-ksc.el" "leim/quail/tamil-dvorak.el" -;;;;;; "leim/quail/tsang-b5.el" "leim/quail/tsang-cns.el" "leim/quail/vntelex.el" -;;;;;; "leim/quail/vnvni.el" "leim/quail/welsh.el" "loadup.el" "mail/blessmail.el" -;;;;;; "mail/rmailedit.el" "mail/rmailkwd.el" "mail/rmailmm.el" -;;;;;; "mail/rmailmsc.el" "mail/rmailsort.el" "mail/rmailsum.el" -;;;;;; "mail/undigest.el" "menu-bar.el" "mh-e/mh-gnus.el" "mh-e/mh-loaddefs.el" -;;;;;; "minibuffer.el" "mouse.el" "net/tramp-loaddefs.el" "newcomment.el" -;;;;;; "obarray.el" "org/ob-core.el" "org/ob-keys.el" "org/ob-lob.el" -;;;;;; "org/ob-matlab.el" "org/ob-tangle.el" "org/ob.el" "org/org-archive.el" -;;;;;; "org/org-attach.el" "org/org-bbdb.el" "org/org-clock.el" -;;;;;; "org/org-datetree.el" "org/org-element.el" "org/org-feed.el" -;;;;;; "org/org-footnote.el" "org/org-id.el" "org/org-indent.el" +;;;;;; "ibuf-ext.el" "indent.el" "international/characters.el" "international/charscript.el" +;;;;;; "international/cp51932.el" "international/eucjp-ms.el" "international/mule-cmds.el" +;;;;;; "international/mule-conf.el" "international/mule.el" "isearch.el" +;;;;;; "jit-lock.el" "jka-cmpr-hook.el" "language/burmese.el" "language/cham.el" +;;;;;; "language/chinese.el" "language/cyrillic.el" "language/czech.el" +;;;;;; "language/english.el" "language/ethiopic.el" "language/european.el" +;;;;;; "language/georgian.el" "language/greek.el" "language/hebrew.el" +;;;;;; "language/indian.el" "language/japanese.el" "language/khmer.el" +;;;;;; "language/korean.el" "language/lao.el" "language/misc-lang.el" +;;;;;; "language/romanian.el" "language/sinhala.el" "language/slovak.el" +;;;;;; "language/tai-viet.el" "language/thai.el" "language/tibetan.el" +;;;;;; "language/utf-8-lang.el" "language/vietnamese.el" "ldefs-boot.el" +;;;;;; "leim/ja-dic/ja-dic.el" "leim/leim-list.el" "leim/quail/4Corner.el" +;;;;;; "leim/quail/ARRAY30.el" "leim/quail/CCDOSPY.el" "leim/quail/CTLau-b5.el" +;;;;;; "leim/quail/CTLau.el" "leim/quail/ECDICT.el" "leim/quail/ETZY.el" +;;;;;; "leim/quail/PY-b5.el" "leim/quail/PY.el" "leim/quail/Punct-b5.el" +;;;;;; "leim/quail/Punct.el" "leim/quail/QJ-b5.el" "leim/quail/QJ.el" +;;;;;; "leim/quail/SW.el" "leim/quail/TONEPY.el" "leim/quail/ZIRANMA.el" +;;;;;; "leim/quail/ZOZY.el" "leim/quail/arabic.el" "leim/quail/croatian.el" +;;;;;; "leim/quail/cyril-jis.el" "leim/quail/cyrillic.el" "leim/quail/czech.el" +;;;;;; "leim/quail/georgian.el" "leim/quail/greek.el" "leim/quail/hanja-jis.el" +;;;;;; "leim/quail/hanja.el" "leim/quail/hanja3.el" "leim/quail/hebrew.el" +;;;;;; "leim/quail/ipa-praat.el" "leim/quail/latin-alt.el" "leim/quail/latin-ltx.el" +;;;;;; "leim/quail/latin-post.el" "leim/quail/latin-pre.el" "leim/quail/persian.el" +;;;;;; "leim/quail/programmer-dvorak.el" "leim/quail/py-punct.el" +;;;;;; "leim/quail/pypunct-b5.el" "leim/quail/quick-b5.el" "leim/quail/quick-cns.el" +;;;;;; "leim/quail/rfc1345.el" "leim/quail/sgml-input.el" "leim/quail/slovak.el" +;;;;;; "leim/quail/symbol-ksc.el" "leim/quail/tamil-dvorak.el" "leim/quail/tsang-b5.el" +;;;;;; "leim/quail/tsang-cns.el" "leim/quail/vntelex.el" "leim/quail/vnvni.el" +;;;;;; "leim/quail/welsh.el" "loadup.el" "mail/blessmail.el" "mail/rmailedit.el" +;;;;;; "mail/rmailkwd.el" "mail/rmailmm.el" "mail/rmailmsc.el" "mail/rmailsort.el" +;;;;;; "mail/rmailsum.el" "mail/undigest.el" "menu-bar.el" "mh-e/mh-gnus.el" +;;;;;; "mh-e/mh-loaddefs.el" "minibuffer.el" "mouse.el" "net/tramp-loaddefs.el" +;;;;;; "newcomment.el" "obarray.el" "org/ob-core.el" "org/ob-keys.el" +;;;;;; "org/ob-lob.el" "org/ob-matlab.el" "org/ob-tangle.el" "org/ob.el" +;;;;;; "org/org-archive.el" "org/org-attach.el" "org/org-bbdb.el" +;;;;;; "org/org-clock.el" "org/org-datetree.el" "org/org-element.el" +;;;;;; "org/org-feed.el" "org/org-footnote.el" "org/org-id.el" "org/org-indent.el" ;;;;;; "org/org-install.el" "org/org-irc.el" "org/org-mobile.el" ;;;;;; "org/org-plot.el" "org/org-table.el" "org/org-timer.el" "org/ox-ascii.el" ;;;;;; "org/ox-beamer.el" "org/ox-html.el" "org/ox-icalendar.el" commit d09ac15005f36b2788406da6c573487fc323129d Author: Eli Zaretskii Date: Mon Oct 2 21:35:51 2017 +0300 Fix the --without-x build * src/frame.c (Ficonify_frame) [HAVE_WINDOW_SYSTEM]: Use frame_parent only in GUI builds to avoid compilation errors in --without-x builds. (Bug#28611) diff --git a/src/frame.c b/src/frame.c index 1aff3a007a..ab801eec9c 100644 --- a/src/frame.c +++ b/src/frame.c @@ -2542,6 +2542,7 @@ for how to proceed. */) (Lisp_Object frame) { struct frame *f = decode_live_frame (frame); +#ifdef HAVE_WINDOW_SYSTEM Lisp_Object parent = f->parent_frame; if (!NILP (parent)) @@ -2562,6 +2563,7 @@ for how to proceed. */) return Qnil; } } +#endif /* HAVE_WINDOW_SYSTEM */ /* Don't allow minibuf_window to remain on an iconified frame. */ check_minibuf_window (frame, EQ (minibuf_window, selected_window)); commit f204e6e1a418073bd1e24a83947f1f3c53581c7f Author: Glenn Morris Date: Mon Oct 2 13:44:50 2017 -0400 ; Auto-commit of loaddefs files. diff --git a/lisp/ldefs-boot.el b/lisp/ldefs-boot.el index 32f2a179c3..e2c211e0e2 100644 --- a/lisp/ldefs-boot.el +++ b/lisp/ldefs-boot.el @@ -4961,12 +4961,16 @@ call other entry points instead, such as `cl-prin1'. \(fn OBJECT STREAM)" nil nil) (autoload 'cl-prin1 "cl-print" "\ - +Print OBJECT on STREAM according to its type. +Output is further controlled by the variables +`cl-print-readably', `cl-print-compiled', along with output +variables for the standard printing functions. See Info +node `(elisp)Output Variables'. \(fn OBJECT &optional STREAM)" nil nil) (autoload 'cl-prin1-to-string "cl-print" "\ - +Return a string containing the `cl-prin1'-printed representation of OBJECT. \(fn OBJECT)" nil nil) @@ -5032,7 +5036,7 @@ is run). (autoload 'color-name-to-rgb "color" "\ Convert COLOR string to a list of normalized RGB components. COLOR should be a color name (e.g. \"white\") or an RGB triplet -string (e.g. \"#ff12ec\"). +string (e.g. \"#ffff1122eecc\"). Normally the return value is a list of three floating-point numbers, (RED GREEN BLUE), each between 0.0 and 1.0 inclusive. @@ -7412,7 +7416,7 @@ May contain all other options that don't contradict `-l'; may contain even `F', `b', `i' and `s'. See also the variable `dired-ls-F-marks-symlinks' concerning the `F' switch. Options that include embedded whitespace must be quoted -like this: \\\"--option=value with spaces\\\"; you can use +like this: \"--option=value with spaces\"; you can use `combine-and-quote-strings' to produce the correct quoting of each option. On systems such as MS-DOS and MS-Windows, which use `ls' emulation in Lisp, @@ -8095,12 +8099,16 @@ the constant's documentation. \(fn M BS DOC &rest ARGS)" nil t) +(function-put 'easy-mmode-defmap 'lisp-indent-function '1) + (autoload 'easy-mmode-defsyntax "easy-mmode" "\ Define variable ST as a syntax-table. CSS contains a list of syntax specifications of the form (CHAR . SYNTAX). \(fn ST CSS DOC &rest ARGS)" nil t) +(function-put 'easy-mmode-defsyntax 'lisp-indent-function '1) + (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "easy-mmode" '("easy-mmode-"))) ;;;*** @@ -13537,7 +13545,7 @@ and choose the directory as the fortune-file. Minimum set of parameters to filter for live (on-session) framesets. DO NOT MODIFY. See `frameset-filter-alist' for a full description.") -(defvar frameset-persistent-filter-alist (nconc '((background-color . frameset-filter-sanitize-color) (buffer-list . :never) (buffer-predicate . :never) (buried-buffer-list . :never) (delete-before . :never) (font . frameset-filter-shelve-param) (foreground-color . frameset-filter-sanitize-color) (fullscreen . frameset-filter-shelve-param) (GUI:font . frameset-filter-unshelve-param) (GUI:fullscreen . frameset-filter-unshelve-param) (GUI:height . frameset-filter-unshelve-param) (GUI:width . frameset-filter-unshelve-param) (height . frameset-filter-shelve-param) (outer-window-id . :never) (parent-frame . :never) (parent-id . :never) (mouse-wheel-frame . :never) (tty . frameset-filter-tty-to-GUI) (tty-type . frameset-filter-tty-to-GUI) (width . frameset-filter-shelve-param) (window-id . :never) (window-system . :never)) frameset-session-filter-alist) "\ +(defvar frameset-persistent-filter-alist (nconc '((background-color . frameset-filter-sanitize-color) (buffer-list . :never) (buffer-predicate . :never) (buried-buffer-list . :never) (delete-before . :never) (font . frameset-filter-font-param) (foreground-color . frameset-filter-sanitize-color) (fullscreen . frameset-filter-shelve-param) (GUI:font . frameset-filter-unshelve-param) (GUI:fullscreen . frameset-filter-unshelve-param) (GUI:height . frameset-filter-unshelve-param) (GUI:width . frameset-filter-unshelve-param) (height . frameset-filter-shelve-param) (outer-window-id . :never) (parent-frame . :never) (parent-id . :never) (mouse-wheel-frame . :never) (tty . frameset-filter-tty-to-GUI) (tty-type . frameset-filter-tty-to-GUI) (width . frameset-filter-shelve-param) (window-id . :never) (window-system . :never)) frameset-session-filter-alist) "\ Parameters to filter for persistent framesets. DO NOT MODIFY. See `frameset-filter-alist' for a full description.") @@ -22984,6 +22992,13 @@ Many aspects this mode can be customized using ;;;*** +;;;### (autoloads nil "ob-hledger" "org/ob-hledger.el" (0 0 0 0)) +;;; Generated autoloads from org/ob-hledger.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "ob-hledger" '("org-babel-"))) + +;;;*** + ;;;### (autoloads nil "ob-io" "org/ob-io.el" (0 0 0 0)) ;;; Generated autoloads from org/ob-io.el @@ -23155,13 +23170,6 @@ Many aspects this mode can be customized using ;;;*** -;;;### (autoloads nil "ob-scala" "org/ob-scala.el" (0 0 0 0)) -;;; Generated autoloads from org/ob-scala.el - -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "ob-scala" '("org-babel-"))) - -;;;*** - ;;;### (autoloads nil "ob-scheme" "org/ob-scheme.el" (0 0 0 0)) ;;; Generated autoloads from org/ob-scheme.el @@ -23234,6 +23242,13 @@ Many aspects this mode can be customized using ;;;*** +;;;### (autoloads nil "ob-vala" "org/ob-vala.el" (0 0 0 0)) +;;; Generated autoloads from org/ob-vala.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "ob-vala" '("org-babel-"))) + +;;;*** + ;;;### (autoloads nil "octave" "progmodes/octave.el" (0 0 0 0)) ;;; Generated autoloads from progmodes/octave.el @@ -23561,7 +23576,6 @@ T Call `org-todo-list' to display the global todo list, select only m Call `org-tags-view' to display headlines with tags matching a condition (the user is prompted for the condition). M Like `m', but select only TODO entries, no ordinary headlines. -L Create a timeline for the current buffer. e Export views to associated files. s Search entries for keywords. S Search entries for keywords, only with TODO keywords. @@ -23701,8 +23715,9 @@ as a whole, to include whitespace. with a colon, this will mean that the (non-regexp) snippets of the Boolean search must match as full words. -This command searches the agenda files, and in addition the files listed -in `org-agenda-text-search-extra-files'. +This command searches the agenda files, and in addition the files +listed in `org-agenda-text-search-extra-files' unless a restriction lock +is active. \(fn &optional TODO-ONLY STRING EDIT-AT)" t nil) @@ -23872,6 +23887,9 @@ With a `\\[universal-argument] \\[universal-argument]' prefix argument, go to th When called with a `C-0' (zero) prefix, insert a template at point. +When called with a `C-1' (one) prefix, force prompting for a date when +a datetree entry is made. + ELisp programs can set KEYS to a string associated with a template in `org-capture-templates'. In this case, interactive selection will be bypassed. @@ -24011,6 +24029,63 @@ Try very hard to provide sensible version strings. ;;;*** +;;;### (autoloads nil "org-duration" "org/org-duration.el" (0 0 0 +;;;;;; 0)) +;;; Generated autoloads from org/org-duration.el + +(autoload 'org-duration-set-regexps "org-duration" "\ +Set duration related regexps. + +\(fn)" t nil) + +(autoload 'org-duration-p "org-duration" "\ +Non-nil when string S is a time duration. + +\(fn S)" nil nil) + +(autoload 'org-duration-to-minutes "org-duration" "\ +Return number of minutes of DURATION string. + +When optional argument CANONICAL is non-nil, ignore +`org-duration-units' and use standard time units value. + +A bare number is translated into minutes. The empty string is +translated into 0.0. + +Return value as a float. Raise an error if duration format is +not recognized. + +\(fn DURATION &optional CANONICAL)" nil nil) + +(autoload 'org-duration-from-minutes "org-duration" "\ +Return duration string for a given number of MINUTES. + +Format duration according to `org-duration-format' or FMT, when +non-nil. + +When optional argument CANONICAL is non-nil, ignore +`org-duration-units' and use standard time units value. + +Raise an error if expected format is unknown. + +\(fn MINUTES &optional FMT CANONICAL)" nil nil) + +(autoload 'org-duration-h:mm-only-p "org-duration" "\ +Non-nil when every duration in TIMES has \"H:MM\" or \"H:MM:SS\" format. + +TIMES is a list of duration strings. + +Return nil if any duration is expressed with units, as defined in +`org-duration-units'. Otherwise, if any duration is expressed +with \"H:MM:SS\" format, return `h:mm:ss'. Otherwise, return +`h:mm'. + +\(fn TIMES)" nil nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "org-duration" '("org-duration-"))) + +;;;*** + ;;;### (autoloads "actual autoloads are elsewhere" "org-element" ;;;;;; "org/org-element.el" (0 0 0 0)) ;;; Generated autoloads from org/org-element.el @@ -24067,7 +24142,7 @@ Try very hard to provide sensible version strings. ;;;### (autoloads nil "org-gnus" "org/org-gnus.el" (0 0 0 0)) ;;; Generated autoloads from org/org-gnus.el -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "org-gnus" '("org-"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "org-gnus" '("org-gnus-"))) ;;;*** @@ -31848,7 +31923,7 @@ Studlify-case the current buffer. ;;;### (autoloads nil "subr-x" "emacs-lisp/subr-x.el" (0 0 0 0)) ;;; Generated autoloads from emacs-lisp/subr-x.el -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "subr-x" '("read-multiple-choice" "string-" "hash-table-" "and-let*" "when-let" "internal--" "if-let" "thread-"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "subr-x" '("read-multiple-choice" "string-" "hash-table-" "when-let" "internal--" "if-let" "and-let*" "thread-"))) ;;;*** @@ -33407,7 +33482,7 @@ Return the Lisp list at point, or nil if none is found. \(fn)" nil nil) -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "thingatpt" '("form-at-point" "thing-at-point-" "sentence-at-point" "word-at-point" "in-string-p" "end-of-thing" "beginning-of-thing"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "thingatpt" '("filename" "form-at-point" "thing-at-point-" "sentence-at-point" "word-at-point" "define-thing-chars" "in-string-p" "end-of-thing" "beginning-of-thing"))) ;;;*** @@ -33923,11 +33998,11 @@ relative only to the time worked today, and not to past time. ;;;;;; 0 0 0)) ;;; Generated autoloads from emacs-lisp/timer-list.el -(autoload 'timer-list "timer-list" "\ +(autoload 'list-timers "timer-list" "\ List all timers in a buffer. \(fn &optional IGNORE-AUTO NONCONFIRM)" t nil) - (put 'timer-list 'disabled "Beware: manually canceling timers can ruin your Emacs session.") + (put 'list-timers 'disabled "Beware: manually canceling timers can ruin your Emacs session.") (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "timer-list" '("timer-list-"))) @@ -34253,8 +34328,10 @@ Value for `tramp-file-name-regexp' for autoload. It must match the initial `tramp-syntax' settings.") (defvar tramp-file-name-regexp tramp-initial-file-name-regexp "\ -Value for `tramp-file-name-regexp' for autoload. -It must match the initial `tramp-syntax' settings.") +Regular expression matching file names handled by Tramp. +This regexp should match Tramp file names but no other file +names. When calling `tramp-register-file-name-handlers', the +initial value is overwritten by the car of `tramp-file-name-structure'.") (defconst tramp-completion-file-name-regexp-default (concat "\\`/\\(" "\\([^/|:]+:[^/|:]*|\\)*" (if (memq system-type '(cygwin windows-nt)) "\\(-\\|[^/|:]\\{2,\\}\\)" "[^/|:]+") "\\(:[^/|:]*\\)?" "\\)?\\'") "\ Value for `tramp-completion-file-name-regexp' for default remoting. commit b69bcf34523551a93635bef90f0eb1d109c1076c Author: Glenn Morris Date: Mon Oct 2 13:19:11 2017 -0400 ; * lisp/emacs-lisp/cl-print.el (cl-prin1): Whitespace fix. diff --git a/lisp/emacs-lisp/cl-print.el b/lisp/emacs-lisp/cl-print.el index 87c03280f7..4fc178c29a 100644 --- a/lisp/emacs-lisp/cl-print.el +++ b/lisp/emacs-lisp/cl-print.el @@ -268,7 +268,7 @@ into a button whose action shows the function's disassembly.") Output is further controlled by the variables `cl-print-readably', `cl-print-compiled', along with output variables for the standard printing functions. See Info -node `(elisp)Output Variables'. " +node `(elisp)Output Variables'." (cond (cl-print-readably (prin1 object stream)) ((not print-circle) (cl-print-object object stream)) commit 5a56b572e2187b67947797a1d36a257f08ab1035 Author: Paul Eggert Date: Mon Oct 2 09:19:07 2017 -0700 Merge from Gnulib This incorporates: 2017-10-02 fsusage: fix typo in previous change * lib/fsusage.c: Copy from Gnulib. diff --git a/lib/fsusage.c b/lib/fsusage.c index a0f763be05..b670c0c43a 100644 --- a/lib/fsusage.c +++ b/lib/fsusage.c @@ -49,7 +49,6 @@ # if HAVE_DUSTAT_H /* AIX PS/2 */ # include # endif -# include "full-read.h" #endif /* Many space usage primitives use all 1 bits to denote a value that is commit 987f39a124af378966dc5832a48599b110bba436 Author: Paul Eggert Date: Mon Oct 2 09:04:46 2017 -0700 Fix customization of zoneinfo-style-world-list A customizable variable's initial value cannot depend on that of another customizable variable, since the variables are initialized in other than textual order. Problem reported by N. Jackson (Bug#24291). * lisp/time.el (display-time-world-list): Default to t, a special value that expands to zoneinfo-style-word-list if that works, and to legacy-style-word-list otherwise. (time--display-world-list): New function. (display-time-world, display-time-world-timer): Use it. diff --git a/lisp/time.el b/lisp/time.el index 5c0eac0c20..c8726a9a1b 100644 --- a/lisp/time.el +++ b/lisp/time.el @@ -160,24 +160,33 @@ LABEL is a string to display as the label of that TIMEZONE's time." :type '(repeat (list string string)) :version "23.1") -(defcustom display-time-world-list - ;; Determine if zoneinfo style timezones are supported by testing that - ;; America/New York and Europe/London return different timezones. - (let ((nyt (format-time-string "%z" nil "America/New_York")) - (gmt (format-time-string "%z" nil "Europe/London"))) - (if (string-equal nyt gmt) - legacy-style-world-list - zoneinfo-style-world-list)) +(defcustom display-time-world-list t "Alist of time zones and places for `display-time-world' to display. Each element has the form (TIMEZONE LABEL). TIMEZONE should be in a format supported by your system. See the documentation of `zoneinfo-style-world-list' and `legacy-style-world-list' for two widely used formats. LABEL is -a string to display as the label of that TIMEZONE's time." +a string to display as the label of that TIMEZONE's time. + +If the value is t instead of an alist, use the value of +`zoneinfo-style-world-list' if it works on this platform, and of +`legacy-style-world-list' otherwise." + :group 'display-time :type '(repeat (list string string)) :version "23.1") +(defun time--display-world-list () + (if (listp display-time-world-list) + display-time-world-list + ;; Determine if zoneinfo style timezones are supported by testing that + ;; America/New York and Europe/London return different timezones. + (let ((nyt (format-time-string "%z" nil "America/New_York")) + (gmt (format-time-string "%z" nil "Europe/London"))) + (if (string-equal nyt gmt) + legacy-style-world-list + zoneinfo-style-world-list)))) + (defcustom display-time-world-time-format "%A %d %B %R %Z" "Format of the time displayed, see `format-time-string'." :group 'display-time @@ -548,7 +557,7 @@ To turn off the world time display, go to that window and type `q'." (not (get-buffer display-time-world-buffer-name))) (run-at-time t display-time-world-timer-second 'display-time-world-timer)) (with-current-buffer (get-buffer-create display-time-world-buffer-name) - (display-time-world-display display-time-world-list) + (display-time-world-display (time--display-world-list)) (display-buffer display-time-world-buffer-name (cons nil '((window-height . fit-window-to-buffer)))) (display-time-world-mode))) @@ -556,7 +565,7 @@ To turn off the world time display, go to that window and type `q'." (defun display-time-world-timer () (if (get-buffer display-time-world-buffer-name) (with-current-buffer (get-buffer display-time-world-buffer-name) - (display-time-world-display display-time-world-list)) + (display-time-world-display (time--display-world-list))) ;; cancel timer (let ((list timer-list)) (while list commit 712cc158772697058c1436a6e5bacc00d262f49c Author: Alan Mackenzie Date: Mon Oct 2 14:42:13 2017 +0000 Fix a CC Mode brace stack cache bug. * lisp/progmodes/cc-engine.el (c-update-brace-stack): Call c-beginning-of-current-token after a failing search operation, to ensure we don't cache a point inside a token. diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 05b391a3d3..9d65383e25 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -5410,8 +5410,7 @@ comment at the start of cc-engine.el for more info." ;; value. (let (match kwd-sym (prev-match-pos 1) (s (cdr stack)) - (bound-<> (car stack)) - ) + (bound-<> (car stack))) (save-excursion (cond ((and bound-<> (<= to bound-<>)) @@ -5472,6 +5471,9 @@ comment at the start of cc-engine.el for more info." (setq s (cdr s)))) ((c-keyword-member kwd-sym 'c-flat-decl-block-kwds) (push 0 s)))) + ;; The failing `c-syntactic-re-search-forward' may have left us in the + ;; middle of a token, which might be a significant token. Fix this! + (c-beginning-of-current-token) (cons (point) (cons bound-<> s))))) commit f71569ac82fb216b01df1c25b814a775a30fd309 Author: Paul Eggert Date: Sun Oct 1 23:15:36 2017 -0700 * etc/PROBLEMS: Document Bug#26638. diff --git a/etc/PROBLEMS b/etc/PROBLEMS index 94c78b696d..a67771d474 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -2033,6 +2033,19 @@ Definitions" to make them defined. We list bugs in current versions here. See also the section on legacy systems. +*** On Solaris 10, Emacs crashes during the build process. +This was reported for Emacs 25.2 on i386-pc-solaris2.10 with Sun +Studio 12 (Sun C 5.9) and with Oracle Developer Studio 12.6 (Sun C +5.15), and intermittently for sparc-sun-solaris2.10 with Oracle +Developer Studio 12.5 (Sun C 5.14). Disabling compiler optimization +seems to fix the bug, as does upgrading the Solaris 10 operating +system to Update 11. The cause of the bug is unknown: it may be that +Emacs's archaic memory-allocation scheme is not compatible with +slightly-older versions of Solaris and/or Oracle Studio, or it may be +something else. Since the cause is not known, possibly the bug is +still present in newer versions of Emacs, Oracle Studio, and/or +Solaris. See Bug#26638. + *** On Solaris, C-x doesn't get through to Emacs when you use the console. This is a Solaris feature (at least on Intel x86 cpus). Type C-r commit 135bca574c31b7bf6df6c63d28f180956928dde7 Author: Paul Eggert Date: Sun Oct 1 22:31:39 2017 -0700 Port file-system-info to non-Microsoft * admin/merge-gnulib (GNULIB_MODULES): Add fsusage. * doc/emacs/files.texi (Directories): Remove documentation of now-obsolete directory-free-space-program and directory-free-space-args. * etc/NEWS: Mention change. * etc/PROBLEMS: Slow df is no longer a problem. * lib/fsusage.c, lib/fsusage.h, m4/fsusage.m4: New files, copied from Gnulib. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. * lisp/dired.el (dired-free-space-program) (dired-free-space-args): These aliases are now obsolete. * lisp/files.el (directory-free-space-program) (directory-free-space-args): Now obsolete. (get-free-disk-space): Just call file-system-info instead of the now-obsolete directory-free-space-program. * nt/gnulib-cfg.mk (OMIT_GNULIB_MODULE_fsusage): New macro. * src/fileio.c: Include fsusage.h. (blocks_to_bytes, Ffile_system_info) [!DOS_NT]: New functions. (syms_of_fileio) [!DOS_NT]: Defsubr file-system-info. diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 60104e86c6..4b1dc592b9 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -33,7 +33,7 @@ GNULIB_MODULES=' d-type diffseq dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat fcntl fcntl-h fdatasync fdopendir - filemode filevercmp flexmember fstatat fsync + filemode filevercmp flexmember fstatat fsusage fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog ignore-value intprops largefile lstat manywarnings memrchr minmax mkostemp mktime nstrftime diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi index 18f1c28571..2c4a0ca30c 100644 --- a/doc/emacs/files.texi +++ b/doc/emacs/files.texi @@ -1279,13 +1279,8 @@ default), and @code{list-directory-verbose-switches} is a string giving the switches to use in a verbose listing (@code{"-l"} by default). -@vindex directory-free-space-program -@vindex directory-free-space-args In verbose directory listings, Emacs adds information about the -amount of free space on the disk that contains the directory. To do -this, it runs the program specified by -@code{directory-free-space-program} with arguments -@code{directory-free-space-args}. +amount of free space on the disk that contains the directory. The command @kbd{M-x delete-directory} prompts for a directory's name using the minibuffer, and deletes the directory if it is empty. If diff --git a/etc/NEWS b/etc/NEWS index 42c1b04816..28789a956a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -73,6 +73,10 @@ calling 'eldoc-message' directly. * Lisp Changes in Emacs 27.1 +** The 'file-system-info' function is now available on all platforms. +instead of just Microsoft platforms. This fixes a get-free-disk-space +bug on OS X 10.8 and later (Bug#28639). + * Changes in Emacs 27.1 on Non-Free Operating Systems diff --git a/etc/PROBLEMS b/etc/PROBLEMS index 94c78b696d..2da99324b5 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -557,7 +557,7 @@ and then choose /usr/bin/netkit-ftp. *** Dired is very slow. -This could happen if invocation of the 'df' program takes a long +This could happen if getting a file system's status takes a long time. Possible reasons for this include: - ClearCase mounted filesystems (VOBs) that sometimes make 'df' @@ -565,12 +565,8 @@ time. Possible reasons for this include: - slow automounters on some old versions of Unix; - - slow operation of some versions of 'df'. - -To work around the problem, you could either (a) set the variable -'directory-free-space-program' to nil, and thus prevent Emacs from -invoking 'df'; (b) use 'df' from the GNU Coreutils package; or -(c) use CVS, which is Free Software, instead of ClearCase. +To work around the problem, you could use Git or some other +free-software program, instead of ClearCase. *** ps-print commands fail to find prologue files ps-prin*.ps. diff --git a/lib/fsusage.c b/lib/fsusage.c new file mode 100644 index 0000000000..a0f763be05 --- /dev/null +++ b/lib/fsusage.c @@ -0,0 +1,288 @@ +/* fsusage.c -- return space usage of mounted file systems + + Copyright (C) 1991-1992, 1996, 1998-1999, 2002-2006, 2009-2017 Free Software + Foundation, Inc. + + 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 . */ + +#include + +#include "fsusage.h" + +#include +#include + +#if STAT_STATVFS || STAT_STATVFS64 /* POSIX 1003.1-2001 (and later) with XSI */ +# include +#else +/* Don't include backward-compatibility files unless they're needed. + Eventually we'd like to remove all this cruft. */ +# include +# include +# include +#if HAVE_SYS_PARAM_H +# include +#endif +#if HAVE_SYS_MOUNT_H +# include +#endif +#if HAVE_SYS_VFS_H +# include +#endif +# if HAVE_SYS_FS_S5PARAM_H /* Fujitsu UXP/V */ +# include +# endif +# if HAVE_SYS_STATFS_H +# include +# endif +# if HAVE_DUSTAT_H /* AIX PS/2 */ +# include +# endif +# include "full-read.h" +#endif + +/* Many space usage primitives use all 1 bits to denote a value that is + not applicable or unknown. Propagate this information by returning + a uintmax_t value that is all 1 bits if X is all 1 bits, even if X + is unsigned and narrower than uintmax_t. */ +#define PROPAGATE_ALL_ONES(x) \ + ((sizeof (x) < sizeof (uintmax_t) \ + && (~ (x) == (sizeof (x) < sizeof (int) \ + ? - (1 << (sizeof (x) * CHAR_BIT)) \ + : 0))) \ + ? UINTMAX_MAX : (uintmax_t) (x)) + +/* Extract the top bit of X as an uintmax_t value. */ +#define EXTRACT_TOP_BIT(x) ((x) \ + & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1))) + +/* If a value is negative, many space usage primitives store it into an + integer variable by assignment, even if the variable's type is unsigned. + So, if a space usage variable X's top bit is set, convert X to the + uintmax_t value V such that (- (uintmax_t) V) is the negative of + the original value. If X's top bit is clear, just yield X. + Use PROPAGATE_TOP_BIT if the original value might be negative; + otherwise, use PROPAGATE_ALL_ONES. */ +#define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1)) + +#ifdef STAT_STATVFS +/* Return true if statvfs works. This is false for statvfs on systems + with GNU libc on Linux kernels before 2.6.36, which stats all + preceding entries in /proc/mounts; that makes df hang if even one + of the corresponding file systems is hard-mounted but not available. */ +# if ! (__linux__ && (__GLIBC__ || __UCLIBC__)) +/* The FRSIZE fallback is not required in this case. */ +# undef STAT_STATFS2_FRSIZE +static int statvfs_works (void) { return 1; } +# else +# include /* for strverscmp */ +# include +# include +# define STAT_STATFS2_BSIZE 1 + +static int +statvfs_works (void) +{ + static int statvfs_works_cache = -1; + struct utsname name; + if (statvfs_works_cache < 0) + statvfs_works_cache = (uname (&name) == 0 + && 0 <= strverscmp (name.release, "2.6.36")); + return statvfs_works_cache; +} +# endif +#endif + + +/* Fill in the fields of FSP with information about space usage for + the file system on which FILE resides. + DISK is the device on which FILE is mounted, for space-getting + methods that need to know it. + Return 0 if successful, -1 if not. When returning -1, ensure that + ERRNO is either a system error value, or zero if DISK is NULL + on a system that requires a non-NULL value. */ +int +get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp) +{ +#ifdef STAT_STATVFS /* POSIX, except pre-2.6.36 glibc/Linux */ + + if (statvfs_works ()) + { + struct statvfs vfsd; + + if (statvfs (file, &vfsd) < 0) + return -1; + + /* f_frsize isn't guaranteed to be supported. */ + fsp->fsu_blocksize = (vfsd.f_frsize + ? PROPAGATE_ALL_ONES (vfsd.f_frsize) + : PROPAGATE_ALL_ONES (vfsd.f_bsize)); + + fsp->fsu_blocks = PROPAGATE_ALL_ONES (vfsd.f_blocks); + fsp->fsu_bfree = PROPAGATE_ALL_ONES (vfsd.f_bfree); + fsp->fsu_bavail = PROPAGATE_TOP_BIT (vfsd.f_bavail); + fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (vfsd.f_bavail) != 0; + fsp->fsu_files = PROPAGATE_ALL_ONES (vfsd.f_files); + fsp->fsu_ffree = PROPAGATE_ALL_ONES (vfsd.f_ffree); + return 0; + } + +#endif + +#if defined STAT_STATVFS64 /* AIX */ + + struct statvfs64 fsd; + + if (statvfs64 (file, &fsd) < 0) + return -1; + + /* f_frsize isn't guaranteed to be supported. */ + fsp->fsu_blocksize = (fsd.f_frsize + ? PROPAGATE_ALL_ONES (fsd.f_frsize) + : PROPAGATE_ALL_ONES (fsd.f_bsize)); + +#elif defined STAT_STATFS2_FS_DATA /* Ultrix */ + + struct fs_data fsd; + + if (statfs (file, &fsd) != 1) + return -1; + + fsp->fsu_blocksize = 1024; + fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.fd_req.btot); + fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.fd_req.bfree); + fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.fd_req.bfreen); + fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.fd_req.bfreen) != 0; + fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.fd_req.gtot); + fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.fd_req.gfree); + +#elif defined STAT_STATFS3_OSF1 /* OSF/1 */ + + struct statfs fsd; + + if (statfs (file, &fsd, sizeof (struct statfs)) != 0) + return -1; + + fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize); + +#elif defined STAT_STATFS2_FRSIZE /* 2.6 < glibc/Linux < 2.6.36 */ + + struct statfs fsd; + + if (statfs (file, &fsd) < 0) + return -1; + + fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize); + +#elif defined STAT_STATFS2_BSIZE /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, \ + Mac OS X < 10.4, FreeBSD < 5.0, \ + NetBSD < 3.0, OpenBSD < 4.4 */ + + struct statfs fsd; + + if (statfs (file, &fsd) < 0) + return -1; + + fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize); + +# ifdef STATFS_TRUNCATES_BLOCK_COUNTS + + /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the + struct statfs are truncated to 2GB. These conditions detect that + truncation, presumably without botching the 4.1.1 case, in which + the values are not truncated. The correct counts are stored in + undocumented spare fields. */ + if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0) + { + fsd.f_blocks = fsd.f_spare[0]; + fsd.f_bfree = fsd.f_spare[1]; + fsd.f_bavail = fsd.f_spare[2]; + } +# endif /* STATFS_TRUNCATES_BLOCK_COUNTS */ + +#elif defined STAT_STATFS2_FSIZE /* 4.4BSD and older NetBSD */ + + struct statfs fsd; + + if (statfs (file, &fsd) < 0) + return -1; + + fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize); + +#elif defined STAT_STATFS4 /* SVR3, Dynix, old Irix, old AIX, \ + Dolphin */ + +# if !_AIX && !defined _SEQUENT_ && !defined DOLPHIN +# define f_bavail f_bfree +# endif + + struct statfs fsd; + + if (statfs (file, &fsd, sizeof fsd, 0) < 0) + return -1; + + /* Empirically, the block counts on most SVR3 and SVR3-derived + systems seem to always be in terms of 512-byte blocks, + no matter what value f_bsize has. */ +# if _AIX || defined _CRAY + fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize); +# else + fsp->fsu_blocksize = 512; +# endif + +#endif + +#if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1 \ + || defined STAT_STATFS2_FRSIZE || defined STAT_STATFS2_BSIZE \ + || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4) + + fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks); + fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree); + fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail); + fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0; + fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files); + fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree); + +#endif + + (void) disk; /* avoid argument-unused warning */ + return 0; +} + +#if defined _AIX && defined _I386 +/* AIX PS/2 does not supply statfs. */ + +int +statfs (char *file, struct statfs *fsb) +{ + struct stat stats; + struct dustat fsd; + + if (stat (file, &stats) != 0) + return -1; + if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd))) + return -1; + fsb->f_type = 0; + fsb->f_bsize = fsd.du_bsize; + fsb->f_blocks = fsd.du_fsize - fsd.du_isize; + fsb->f_bfree = fsd.du_tfree; + fsb->f_bavail = fsd.du_tfree; + fsb->f_files = (fsd.du_isize - 2) * fsd.du_inopb; + fsb->f_ffree = fsd.du_tinode; + fsb->f_fsid.val[0] = fsd.du_site; + fsb->f_fsid.val[1] = fsd.du_pckno; + return 0; +} + +#endif /* _AIX && _I386 */ diff --git a/lib/fsusage.h b/lib/fsusage.h new file mode 100644 index 0000000000..f78edc6a0c --- /dev/null +++ b/lib/fsusage.h @@ -0,0 +1,40 @@ +/* fsusage.h -- declarations for file system space usage info + + Copyright (C) 1991-1992, 1997, 2003-2006, 2009-2017 Free Software + Foundation, Inc. + + 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 . */ + +/* Space usage statistics for a file system. Blocks are 512-byte. */ + +#if !defined FSUSAGE_H_ +# define FSUSAGE_H_ + +# include +# include + +struct fs_usage +{ + uintmax_t fsu_blocksize; /* Size of a block. */ + uintmax_t fsu_blocks; /* Total blocks. */ + uintmax_t fsu_bfree; /* Free blocks available to superuser. */ + uintmax_t fsu_bavail; /* Free blocks available to non-superuser. */ + bool fsu_bavail_top_bit_set; /* 1 if fsu_bavail represents a value < 0. */ + uintmax_t fsu_files; /* Total file nodes. */ + uintmax_t fsu_ffree; /* Free file nodes. */ +}; + +int get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp); + +#endif diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 0f795b3d82..e9358a6855 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -21,7 +21,7 @@ # the same distribution terms as the rest of that program. # # Generated by gnulib-tool. -# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=setenv --avoid=sigprocmask --avoid=stat --avoid=stdarg --avoid=stdbool --avoid=threadlib --avoid=tzset --avoid=unsetenv --avoid=utime --avoid=utime-h --gnu-make --makefile-name=gnulib.mk.in --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-leading-zeros count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 d-type diffseq dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat fcntl fcntl-h fdatasync fdopendir filemode filevercmp flexmember fstatat fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog ignore-value intprops largefile lstat manywarnings memrchr minmax mkostemp mktime nstrftime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time std-gnu11 stdalign stddef stdio stpcpy strtoimax symlink sys_stat sys_time tempname time time_r time_rz timegm timer-time timespec-add timespec-sub unlocked-io update-copyright utimens vla warnings +# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=setenv --avoid=sigprocmask --avoid=stat --avoid=stdarg --avoid=stdbool --avoid=threadlib --avoid=tzset --avoid=unsetenv --avoid=utime --avoid=utime-h --gnu-make --makefile-name=gnulib.mk.in --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-leading-zeros count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 d-type diffseq dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat fcntl fcntl-h fdatasync fdopendir filemode filevercmp flexmember fstatat fsusage fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog ignore-value intprops largefile lstat manywarnings memrchr minmax mkostemp mktime nstrftime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time std-gnu11 stdalign stddef stdio stpcpy strtoimax symlink sys_stat sys_time tempname time time_r time_rz timegm timer-time timespec-add timespec-sub unlocked-io update-copyright utimens vla warnings MOSTLYCLEANFILES += core *.stackdump @@ -1516,6 +1516,17 @@ EXTRA_libgnu_a_SOURCES += at-func.c fstatat.c endif ## end gnulib module fstatat +## begin gnulib module fsusage +ifeq (,$(OMIT_GNULIB_MODULE_fsusage)) + + +EXTRA_DIST += fsusage.c fsusage.h + +EXTRA_libgnu_a_SOURCES += fsusage.c + +endif +## end gnulib module fsusage + ## begin gnulib module fsync ifeq (,$(OMIT_GNULIB_MODULE_fsync)) diff --git a/lisp/dired.el b/lisp/dired.el index 9e09d349f7..1ec3ac4f99 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -198,8 +198,10 @@ The target is used in the prompt for file copy, rename etc." ; These variables were deleted and the replacements are on files.el. ; We leave aliases behind for back-compatibility. -(defvaralias 'dired-free-space-program 'directory-free-space-program) -(defvaralias 'dired-free-space-args 'directory-free-space-args) +(define-obsolete-variable-alias 'dired-free-space-program + 'directory-free-space-program "27.1") +(define-obsolete-variable-alias 'dired-free-space-args + 'directory-free-space-args "27.1") ;;; Hook variables diff --git a/lisp/files.el b/lisp/files.el index 336bbc8648..194c87ab68 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -6386,58 +6386,33 @@ if you want to specify options, use `directory-free-space-args'. A value of nil disables this feature. -If the function `file-system-info' is defined, it is always used in -preference to the program given by this variable." +This variable is obsolete; Emacs no longer uses it." :type '(choice (string :tag "Program") (const :tag "None" nil)) :group 'dired) +(make-obsolete-variable 'directory-free-space-program + "ignored, as Emacs uses `file-system-info' instead" + "27.1") (defcustom directory-free-space-args (purecopy (if (eq system-type 'darwin) "-k" "-Pk")) "Options to use when running `directory-free-space-program'." :type 'string :group 'dired) +(make-obsolete-variable 'directory-free-space-args + "ignored, as Emacs uses `file-system-info' instead" + "27.1") (defun get-free-disk-space (dir) "Return the amount of free space on directory DIR's file system. The return value is a string describing the amount of free space (normally, the number of free 1KB blocks). -This function calls `file-system-info' if it is available, or -invokes the program specified by `directory-free-space-program' -and `directory-free-space-args'. If the system call or program -is unsuccessful, or if DIR is a remote directory, this function -returns nil." +If DIR's free space cannot be obtained, or if DIR is a remote +directory, this function returns nil." (unless (file-remote-p (expand-file-name dir)) - ;; Try to find the number of free blocks. Non-Posix systems don't - ;; always have df, but might have an equivalent system call. - (if (fboundp 'file-system-info) - (let ((fsinfo (file-system-info dir))) - (if fsinfo - (format "%.0f" (/ (nth 2 fsinfo) 1024)))) - (setq dir (expand-file-name dir)) - (save-match-data - (with-temp-buffer - (when (and directory-free-space-program - ;; Avoid failure if the default directory does - ;; not exist (Bug#2631, Bug#3911). - (let ((default-directory - (locate-dominating-file dir 'file-directory-p))) - (eq (process-file directory-free-space-program - nil t nil - directory-free-space-args - (file-relative-name dir)) - 0))) - ;; Assume that the "available" column is before the - ;; "capacity" column. Find the "%" and scan backward. - (goto-char (point-min)) - (forward-line 1) - (when (re-search-forward - "[[:space:]]+[^[:space:]]+%[^%]*$" - (line-end-position) t) - (goto-char (match-beginning 0)) - (let ((endpt (point))) - (skip-chars-backward "^[:space:]") - (buffer-substring-no-properties (point) endpt))))))))) + (let ((avail (nth 2 (file-system-info dir)))) + (if avail + (format "%.0f" (/ avail 1024)))))) ;; The following expression replaces `dired-move-to-filename-regexp'. (defvar directory-listing-before-filename-regexp diff --git a/m4/fsusage.m4 b/m4/fsusage.m4 new file mode 100644 index 0000000000..1d6ad41cd3 --- /dev/null +++ b/m4/fsusage.m4 @@ -0,0 +1,336 @@ +# serial 32 +# Obtaining file system usage information. + +# Copyright (C) 1997-1998, 2000-2001, 2003-2017 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Written by Jim Meyering. + +AC_DEFUN([gl_FSUSAGE], +[ + AC_CHECK_HEADERS_ONCE([sys/param.h]) + AC_CHECK_HEADERS_ONCE([sys/vfs.h sys/fs_types.h]) + AC_CHECK_HEADERS([sys/mount.h], [], [], + [AC_INCLUDES_DEFAULT + [#if HAVE_SYS_PARAM_H + #include + #endif]]) + gl_FILE_SYSTEM_USAGE([gl_cv_fs_space=yes], [gl_cv_fs_space=no]) +]) + +# Try to determine how a program can obtain file system usage information. +# If successful, define the appropriate symbol (see fsusage.c) and +# execute ACTION-IF-FOUND. Otherwise, execute ACTION-IF-NOT-FOUND. +# +# gl_FILE_SYSTEM_USAGE([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) + +AC_DEFUN([gl_FILE_SYSTEM_USAGE], +[ +dnl Enable large-file support. This has the effect of changing the size +dnl of field f_blocks in 'struct statvfs' from 32 bit to 64 bit on +dnl glibc/Hurd, HP-UX 11, Solaris (32-bit mode). It also changes the size +dnl of field f_blocks in 'struct statfs' from 32 bit to 64 bit on +dnl Mac OS X >= 10.5 (32-bit mode). +AC_REQUIRE([AC_SYS_LARGEFILE]) + +AC_MSG_CHECKING([how to get file system space usage]) +ac_fsusage_space=no + +# Perform only the link test since it seems there are no variants of the +# statvfs function. This check is more than just AC_CHECK_FUNCS([statvfs]) +# because that got a false positive on SCO OSR5. Adding the declaration +# of a 'struct statvfs' causes this test to fail (as it should) on such +# systems. That system is reported to work fine with STAT_STATFS4 which +# is what it gets when this test fails. +if test $ac_fsusage_space = no; then + # glibc/{Hurd,kFreeBSD}, FreeBSD >= 5.0, NetBSD >= 3.0, + # OpenBSD >= 4.4, AIX, HP-UX, IRIX, Solaris, Cygwin, Interix, BeOS. + AC_CACHE_CHECK([for statvfs function (SVR4)], [fu_cv_sys_stat_statvfs], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include +#ifdef __osf__ +"Do not use Tru64's statvfs implementation" +#endif + +#include + +struct statvfs fsd; + +#if defined __APPLE__ && defined __MACH__ +#include +/* On Mac OS X >= 10.5, f_blocks in 'struct statvfs' is a 32-bit quantity; + that commonly limits file systems to 4 TiB. Whereas f_blocks in + 'struct statfs' is a 64-bit type, thanks to the large-file support + that was enabled above. In this case, don't use statvfs(); use statfs() + instead. */ +int check_f_blocks_size[sizeof fsd.f_blocks * CHAR_BIT <= 32 ? -1 : 1]; +#endif +]], + [[statvfs (0, &fsd);]])], + [fu_cv_sys_stat_statvfs=yes], + [fu_cv_sys_stat_statvfs=no])]) + if test $fu_cv_sys_stat_statvfs = yes; then + ac_fsusage_space=yes + # AIX >= 5.2 has statvfs64 that has a wider f_blocks field than statvfs. + # glibc, HP-UX, IRIX, Solaris have statvfs64 as well, but on these systems + # statvfs with large-file support is already equivalent to statvfs64. + AC_CACHE_CHECK([whether to use statvfs64], + [fu_cv_sys_stat_statvfs64], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include + #include + struct statvfs64 fsd; + int check_f_blocks_larger_in_statvfs64 + [sizeof (((struct statvfs64 *) 0)->f_blocks) + > sizeof (((struct statvfs *) 0)->f_blocks) + ? 1 : -1]; + ]], + [[statvfs64 (0, &fsd);]])], + [fu_cv_sys_stat_statvfs64=yes], + [fu_cv_sys_stat_statvfs64=no]) + ]) + if test $fu_cv_sys_stat_statvfs64 = yes; then + AC_DEFINE([STAT_STATVFS64], [1], + [ Define if statvfs64 should be preferred over statvfs.]) + else + AC_DEFINE([STAT_STATVFS], [1], + [ Define if there is a function named statvfs. (SVR4)]) + fi + fi +fi + +# Check for this unconditionally so we have a +# good fallback on glibc/Linux > 2.6 < 2.6.36 +AC_MSG_CHECKING([for two-argument statfs with statfs.f_frsize member]) +AC_CACHE_VAL([fu_cv_sys_stat_statfs2_frsize], +[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_VFS_H +#include +#endif + int + main () + { + struct statfs fsd; + fsd.f_frsize = 0; + return statfs (".", &fsd) != 0; + }]])], + [fu_cv_sys_stat_statfs2_frsize=yes], + [fu_cv_sys_stat_statfs2_frsize=no], + [fu_cv_sys_stat_statfs2_frsize=no])]) +AC_MSG_RESULT([$fu_cv_sys_stat_statfs2_frsize]) +if test $fu_cv_sys_stat_statfs2_frsize = yes; then + ac_fsusage_space=yes + AC_DEFINE([STAT_STATFS2_FRSIZE], [1], +[ Define if statfs takes 2 args and struct statfs has a field named f_frsize. + (glibc/Linux > 2.6)]) +fi + +if test $ac_fsusage_space = no; then + # DEC Alpha running OSF/1 + AC_MSG_CHECKING([for 3-argument statfs function (DEC OSF/1)]) + AC_CACHE_VAL([fu_cv_sys_stat_statfs3_osf1], + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +#include + int + main () + { + struct statfs fsd; + fsd.f_fsize = 0; + return statfs (".", &fsd, sizeof (struct statfs)) != 0; + }]])], + [fu_cv_sys_stat_statfs3_osf1=yes], + [fu_cv_sys_stat_statfs3_osf1=no], + [fu_cv_sys_stat_statfs3_osf1=no])]) + AC_MSG_RESULT([$fu_cv_sys_stat_statfs3_osf1]) + if test $fu_cv_sys_stat_statfs3_osf1 = yes; then + ac_fsusage_space=yes + AC_DEFINE([STAT_STATFS3_OSF1], [1], + [ Define if statfs takes 3 args. (DEC Alpha running OSF/1)]) + fi +fi + +if test $ac_fsusage_space = no; then + # glibc/Linux, Mac OS X, FreeBSD < 5.0, NetBSD < 3.0, OpenBSD < 4.4. + # (glibc/{Hurd,kFreeBSD}, FreeBSD >= 5.0, NetBSD >= 3.0, + # OpenBSD >= 4.4, AIX, HP-UX, OSF/1, Cygwin already handled above.) + # (On IRIX you need to include , not only and + # .) + # (On Solaris, statfs has 4 arguments.) + AC_MSG_CHECKING([for two-argument statfs with statfs.f_bsize dnl +member (AIX, 4.3BSD)]) + AC_CACHE_VAL([fu_cv_sys_stat_statfs2_bsize], + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_VFS_H +#include +#endif + int + main () + { + struct statfs fsd; + fsd.f_bsize = 0; + return statfs (".", &fsd) != 0; + }]])], + [fu_cv_sys_stat_statfs2_bsize=yes], + [fu_cv_sys_stat_statfs2_bsize=no], + [fu_cv_sys_stat_statfs2_bsize=no])]) + AC_MSG_RESULT([$fu_cv_sys_stat_statfs2_bsize]) + if test $fu_cv_sys_stat_statfs2_bsize = yes; then + ac_fsusage_space=yes + AC_DEFINE([STAT_STATFS2_BSIZE], [1], +[ Define if statfs takes 2 args and struct statfs has a field named f_bsize. + (4.3BSD, SunOS 4, HP-UX, AIX PS/2)]) + fi +fi + +if test $ac_fsusage_space = no; then + # SVR3 + # (Solaris already handled above.) + AC_MSG_CHECKING([for four-argument statfs (AIX-3.2.5, SVR3)]) + AC_CACHE_VAL([fu_cv_sys_stat_statfs4], + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include + int + main () + { + struct statfs fsd; + return statfs (".", &fsd, sizeof fsd, 0) != 0; + }]])], + [fu_cv_sys_stat_statfs4=yes], + [fu_cv_sys_stat_statfs4=no], + [fu_cv_sys_stat_statfs4=no])]) + AC_MSG_RESULT([$fu_cv_sys_stat_statfs4]) + if test $fu_cv_sys_stat_statfs4 = yes; then + ac_fsusage_space=yes + AC_DEFINE([STAT_STATFS4], [1], + [ Define if statfs takes 4 args. (SVR3, Dynix, old Irix, old AIX, Dolphin)]) + fi +fi + +if test $ac_fsusage_space = no; then + # 4.4BSD and older NetBSD + # (OSF/1 already handled above.) + # (On AIX, you need to include , not only .) + # (On Solaris, statfs has 4 arguments and 'struct statfs' is not declared in + # .) + AC_MSG_CHECKING([for two-argument statfs with statfs.f_fsize dnl +member (4.4BSD and NetBSD)]) + AC_CACHE_VAL([fu_cv_sys_stat_statfs2_fsize], + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif + int + main () + { + struct statfs fsd; + fsd.f_fsize = 0; + return statfs (".", &fsd) != 0; + }]])], + [fu_cv_sys_stat_statfs2_fsize=yes], + [fu_cv_sys_stat_statfs2_fsize=no], + [fu_cv_sys_stat_statfs2_fsize=no])]) + AC_MSG_RESULT([$fu_cv_sys_stat_statfs2_fsize]) + if test $fu_cv_sys_stat_statfs2_fsize = yes; then + ac_fsusage_space=yes + AC_DEFINE([STAT_STATFS2_FSIZE], [1], +[ Define if statfs takes 2 args and struct statfs has a field named f_fsize. + (4.4BSD, NetBSD)]) + fi +fi + +if test $ac_fsusage_space = no; then + # Ultrix + AC_MSG_CHECKING([for two-argument statfs with struct fs_data (Ultrix)]) + AC_CACHE_VAL([fu_cv_sys_stat_fs_data], + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_FS_TYPES_H +#include +#endif + int + main () + { + struct fs_data fsd; + /* Ultrix's statfs returns 1 for success, + 0 for not mounted, -1 for failure. */ + return statfs (".", &fsd) != 1; + }]])], + [fu_cv_sys_stat_fs_data=yes], + [fu_cv_sys_stat_fs_data=no], + [fu_cv_sys_stat_fs_data=no])]) + AC_MSG_RESULT([$fu_cv_sys_stat_fs_data]) + if test $fu_cv_sys_stat_fs_data = yes; then + ac_fsusage_space=yes + AC_DEFINE([STAT_STATFS2_FS_DATA], [1], +[ Define if statfs takes 2 args and the second argument has + type struct fs_data. (Ultrix)]) + fi +fi + +AS_IF([test $ac_fsusage_space = yes], [$1], [$2]) + +]) + + +# Check for SunOS statfs brokenness wrt partitions 2GB and larger. +# If exists and struct statfs has a member named f_spare, +# enable the work-around code in fsusage.c. +AC_DEFUN([gl_STATFS_TRUNCATES], +[ + AC_MSG_CHECKING([for statfs that truncates block counts]) + AC_CACHE_VAL([fu_cv_sys_truncating_statfs], + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if !defined(sun) && !defined(__sun) +choke -- this is a workaround for a Sun-specific problem +#endif +#include +#include ]], + [[struct statfs t; long c = *(t.f_spare); + if (c) return 0;]])], + [fu_cv_sys_truncating_statfs=yes], + [fu_cv_sys_truncating_statfs=no])]) + if test $fu_cv_sys_truncating_statfs = yes; then + AC_DEFINE([STATFS_TRUNCATES_BLOCK_COUNTS], [1], + [Define if the block counts reported by statfs may be truncated to 2GB + and the correct values may be stored in the f_spare array. + (SunOS 4.1.2, 4.1.3, and 4.1.3_U1 are reported to have this problem. + SunOS 4.1.1 seems not to be affected.)]) + fi + AC_MSG_RESULT([$fu_cv_sys_truncating_statfs]) +]) + + +# Prerequisites of lib/fsusage.c not done by gl_FILE_SYSTEM_USAGE. +AC_DEFUN([gl_PREREQ_FSUSAGE_EXTRA], +[ + AC_CHECK_HEADERS([dustat.h sys/fs/s5param.h sys/statfs.h]) + gl_STATFS_TRUNCATES +]) diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index c5517529f0..cb255fcf6d 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -87,6 +87,7 @@ AC_DEFUN([gl_EARLY], # Code from module flexmember: # Code from module fpending: # Code from module fstatat: + # Code from module fsusage: # Code from module fsync: # Code from module getdtablesize: # Code from module getgroups: @@ -256,6 +257,11 @@ AC_DEFUN([gl_INIT], AC_LIBOBJ([fstatat]) fi gl_SYS_STAT_MODULE_INDICATOR([fstatat]) + gl_FSUSAGE + if test $gl_cv_fs_space = yes; then + AC_LIBOBJ([fsusage]) + gl_PREREQ_FSUSAGE_EXTRA + fi gl_FUNC_FSYNC if test $HAVE_FSYNC = 0; then AC_LIBOBJ([fsync]) @@ -864,6 +870,8 @@ AC_DEFUN([gl_FILE_LIST], [ lib/fpending.c lib/fpending.h lib/fstatat.c + lib/fsusage.c + lib/fsusage.h lib/fsync.c lib/ftoastr.c lib/ftoastr.h @@ -995,6 +1003,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/flexmember.m4 m4/fpending.m4 m4/fstatat.m4 + m4/fsusage.m4 m4/fsync.m4 m4/getdtablesize.m4 m4/getgroups.m4 diff --git a/nt/gnulib-cfg.mk b/nt/gnulib-cfg.mk index 419099ece3..f62166759d 100644 --- a/nt/gnulib-cfg.mk +++ b/nt/gnulib-cfg.mk @@ -49,6 +49,7 @@ OMIT_GNULIB_MODULE_dirent = true OMIT_GNULIB_MODULE_dirfd = true OMIT_GNULIB_MODULE_fcntl = true OMIT_GNULIB_MODULE_fcntl-h = true +OMIT_GNULIB_MODULE_fsusage = true OMIT_GNULIB_MODULE_inttypes-incomplete = true OMIT_GNULIB_MODULE_open = true OMIT_GNULIB_MODULE_pipe2 = true diff --git a/src/fileio.c b/src/fileio.c index adb3534532..11370279d1 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -96,6 +96,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include #include @@ -5765,6 +5766,40 @@ effect except for flushing STREAM's data. */) return (set_binary_mode (fileno (fp), binmode) == O_BINARY) ? Qt : Qnil; } +#ifndef DOS_NT + +/* Yield a Lisp float as close as possible to BLOCKSIZE * BLOCKS, with + the result negated if NEGATE. */ +static Lisp_Object +blocks_to_bytes (uintmax_t blocksize, uintmax_t blocks, bool negate) +{ + /* On typical platforms the following code is accurate to 53 bits, + which is close enough. BLOCKSIZE is invariably a power of 2, so + converting it to double does not lose information. */ + double bs = blocksize; + return make_float (negate ? -bs * -blocks : bs * blocks); +} + +DEFUN ("file-system-info", Ffile_system_info, Sfile_system_info, 1, 1, 0, + doc: /* Return storage information about the file system FILENAME is on. +Value is a list of numbers (TOTAL FREE AVAIL), where TOTAL is the total +storage of the file system, FREE is the free storage, and AVAIL is the +storage available to a non-superuser. All 3 numbers are in bytes. +If the underlying system call fails, value is nil. */) + (Lisp_Object filename) +{ + Lisp_Object encoded = ENCODE_FILE (Fexpand_file_name (filename, Qnil)); + struct fs_usage u; + if (get_fs_usage (SSDATA (encoded), NULL, &u) != 0) + return Qnil; + return list3 (blocks_to_bytes (u.fsu_blocksize, u.fsu_blocks, false), + blocks_to_bytes (u.fsu_blocksize, u.fsu_bfree, false), + blocks_to_bytes (u.fsu_blocksize, u.fsu_bavail, + u.fsu_bavail_top_bit_set)); +} + +#endif /* !DOS_NT */ + void init_fileio (void) { @@ -6115,6 +6150,10 @@ This includes interactive calls to `delete-file' and defsubr (&Sset_binary_mode); +#ifndef DOS_NT + defsubr (&Sfile_system_info); +#endif + #ifdef HAVE_SYNC defsubr (&Sunix_sync); #endif commit 4829a3b033b119b088947d14b73efc197b2547fa Merge: ee3024c70d 5172fa02cc Author: Paul Eggert Date: Sun Oct 1 21:58:52 2017 -0700 Merge from origin/emacs-26 5172fa02cc Prefer HTTPS to HTTP for gnu.org 8cdd8b920a Merge from Gnulib 60b7668b89 Keep eww buffer current when looking up CSS on MDN bd49b6f1b3 Workaround for faulty localtime() under macOS 10.6 913808e224 Doc amendment for syntax-ppss. 98dc91fda8 Remove incorrect NEWS entry about 'find-library' 539d8626cd Remove inadvertent changes to syntax.texi in last commit. 8c18dcbc78 Amend documentation for text-quoting-style becoming a user... 5f76ac150a Make the value nil in text-quoting-style mean what it does... d5e4e004fa Make text-quoting-style customizable. Introduce t and new... 1ba3471b9b eshell.texi improvements 7abb5c3960 Fix ns-win.el on GNUstep 07ea5ef99a Fix reference style in org.texi b03b4f6d79 Improve handling of iconification of child frames (Bug#28611) ba9139c501 Revert "Don't lose arguments to eshell aliases (Bug#27954)" 43fac3beae Make "unsafe directory" error message more informative (Bu... c59ddb2120 Fix slot typecheck in eieio-persistent 8b2ab5014b Fix semantic-ia-fast-jump 5b45e7e1c3 Bind vc-region-history f172894595 Exit macro definition on undefined keys 289fe6c0d1 Reset bidi-paragraph-direction on article rendering a4f7518817 Fix url-http use of url-current-object 4a755ed421 Avoid assertions in vc-hg.el on MS-Windows cb93a6ce72 Improve documentation of 'copy-sequence' 200ef6f721 Minor update of ack.texi cb407d3e87 * doc/emacs/emacs.texi (Acknowledgments): Add more contrib... 82b6c765ff Improve indexing of multi-file/buffer Isearch commands 645ff6c702 Add CAM02 JCh and CAM02-UCS J'a'b' conversions 157007b58e Fix uses of @kindex in the Emacs manual 63a45e8837 Merge branch 'emacs-26' of git.savannah.gnu.org:/srv/git/e... 3ab2f9bbb9 Merge from gnulib cbc8324488 Prefer HTTPS to HTTP for gnu.org bbda601d1d ; Spelling fixes 695cf5300b Wait for frame visibility with timeout in w32term too e1f6e3127a Bring back the busy wait after x_make_frame_visible (Bug#2... bccf635217 ; * src/gtkutil.c (xg_check_special_colors): Add another G... f428757cdb Merge branch 'emacs-26' of git.savannah.gnu.org:/srv/git/e... 26d58f0c58 ; Standardize license notices 73dba0f466 Fix last doc string change in simple.el commit 5172fa02cccaab2500ecf85aaf65b8deed54d42e Author: Paul Eggert Date: Sun Oct 1 19:53:56 2017 -0700 Prefer HTTPS to HTTP for gnu.org This fixes some URLs I omitted from my previous pass, notably those in lists.gnu.org. Although lists.gnu.org does not yet support TLS 1.1, TLS 1.0 is better than nothing. * lisp/erc/erc.el (erc-official-location): * lisp/mail/emacsbug.el (report-emacs-bug): Use https:, not http:. diff --git a/CONTRIBUTE b/CONTRIBUTE index 90c6a86b12..e1ba506c72 100644 --- a/CONTRIBUTE +++ b/CONTRIBUTE @@ -30,7 +30,7 @@ Discussion about Emacs development takes place on emacs-devel@gnu.org. You can subscribe to the emacs-devel@gnu.org mailing list, paying attention to postings with subject lines containing "emacs-announce", as these discuss important events like feature freezes. See -http://lists.gnu.org/mailman/listinfo/emacs-devel for mailing list +https://lists.gnu.org/mailman/listinfo/emacs-devel for mailing list instructions and archives. You can develop and commit changes in your own copy of the repository, and discuss proposed changes on the mailing list. Frequent contributors to Emacs can request write access diff --git a/ChangeLog.1 b/ChangeLog.1 index 00c66d6059..bb49169fb9 100644 --- a/ChangeLog.1 +++ b/ChangeLog.1 @@ -33,7 +33,7 @@ Fix 'commit-msg' to cite 'CONTRIBUTE' As suggested in: - http://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00947.html + https://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00947.html Also, have the two files match better. * CONTRIBUTE: Match what's in build-aux/git-hooks/commit-msg. * build-aux/git-hooks/commit-msg: Mention 'CONTRIBUTE'. @@ -145,7 +145,7 @@ * configure.ac (HAVE_W32): Abort with error message if --without-toolkit-scroll-bars was specified. See - http://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00525.html + https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00525.html for the details. 2015-01-27 Paul Eggert @@ -156,7 +156,7 @@ configuration. The downside is that patch applications won't be checked, but that's better than autogen.sh failing. Problem reported by Sam Steingold in: - http://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00898.html + https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00898.html 2015-01-26 Paul Eggert @@ -184,7 +184,7 @@ Give up on -Wsuggest-attribute=const The attribute doesn't help performance significantly, and the warning seems to be more trouble than it's worth. See the thread at: - http://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00361.html + https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00361.html * configure.ac (WERROR_CFLAGS): Don't use -Wsuggest-attribute=const. 2015-01-11 Paul Eggert @@ -297,7 +297,7 @@ 2014-12-13 Paul Eggert Port commit-msg to mawk. Reported by Ted Zlatanov in: - http://lists.gnu.org/archive/html/emacs-devel/2014-12/msg01093.html + https://lists.gnu.org/archive/html/emacs-devel/2014-12/msg01093.html * build-aux/git-hooks/commit-msg (space, non_space, non_print): New vars. Use them as approximations to POSIX bracket expressions, on implementations like mawk that do not support POSIX regexps. @@ -313,7 +313,7 @@ Port commit-message checking to FreeBSD 9. Reported by Jan Djärv in: - http://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00704.html + https://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00704.html along with some other issues I noticed while testing with FreeBSD. * build-aux/git-hooks/commit-msg: Prefer gawk if available. Prefer en_US.UTF-8 to en_US.utf8, as it's more portable. @@ -391,7 +391,7 @@ Add a.out to .gitignore. Suggested by Lee Duhem in: - http://lists.gnu.org/archive/html/emacs-devel/2014-11/msg01665.html + https://lists.gnu.org/archive/html/emacs-devel/2014-11/msg01665.html * .gitignore: Add a.out. Move *.log next to *.tmp, since it's generic. Put *.exe before non-generics. @@ -442,7 +442,7 @@ Restore 'Bug#' -> 'debbugs:' rewrite in log-edit-mode. * .dir-locals.el (log-edit-mode): Restore the (log-edit-rewrite-fixes "[ \n](bug#\\([0-9]+\\))" . "debbugs:\\1"). See Glenn Morris in: - http://lists.gnu.org/archive/html/emacs-devel/2014-11/msg01187.html + https://lists.gnu.org/archive/html/emacs-devel/2014-11/msg01187.html Simplify and fix doc-related .gitignore files. This fixes some unwanted 'git status' output after 'make docs'. @@ -913,7 +913,7 @@ Omit redundant extern decls. Most of this patch is from Dmitry Antipov, in: - http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00263.html + https://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00263.html * configure.ac (WERROR_CFLAGS): Add -Wredundant-decls. Merge from gnulib, incorporating: @@ -969,7 +969,7 @@ Rely on AC_CANONICAL_HOST to detect whether we're using mingw. See the thread containing: - http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00206.html + https://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00206.html * configure.ac (AC_CANONICAL_HOST): Invoke this as early as we can, which is just after AM_INIT_AUTOMAKE. Then check for mingw just after that. @@ -1045,7 +1045,7 @@ 2014-05-29 Paul Eggert * configure.ac (pthread_sigmask): Look in LIB_PTHREAD too (Bug#17561). - Fixes configuration glitch found in . + Fixes configuration glitch found in . 2014-05-29 Eli Zaretskii @@ -1517,7 +1517,7 @@ * configure.ac (LIBXML2_CFLAGS): Fix xcrun-related quoting problem. Reported by YAMAMOTO Mitsuharu in: - http://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00995.html + https://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00995.html 2013-12-28 Jan Djärv @@ -1529,7 +1529,7 @@ * configure.ac: Don't set MAKE unless 'make' doesn't work. Set it only in the environment, not in the makefile. Reported by Glenn Morris in: - http://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00969.html + https://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00969.html 2013-12-27 Paul Eggert @@ -1635,7 +1635,7 @@ Remove the option of using libcrypto. This scorches the earth and waits for spring; see Ted Zlatanov and Stefan Monnier in - . + . * configure.ac (with_openssl_default, HAVE_LIB_CRYPTO): Remove. Do not say whether Emacs is configured to use a crypto library, since it's no longer an option. @@ -1666,7 +1666,7 @@ On commonly used platform libcrypto uses architecture-specific assembly code, which is significantly faster than the C code we were using. See Pádraig Brady's note in - . + . Merge from gnulib, incorporating: 2013-12-07 md5, sha1, sha256, sha512: add gl_SET_CRYPTO_CHECK_DEFAULT 2013-12-07 md5, sha1, sha256, sha512: add 'auto', and set-default method @@ -1987,7 +1987,7 @@ Work around performance bug on OS X 10.8 and earlier. Perhaps Apple will fix this bug some day. See the thread starting with Daniel Colascione's email in: - http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00343.html + https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00343.html * configure.ac (FORTIFY_SOUR): New verbatim section. 2013-09-19 Paul Eggert @@ -2003,9 +2003,9 @@ * configure.ac [MINGW32]: Make sure the value of 'srcdir' is in the full /d/foo/bar form. See the discussion in - http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00210.html, + https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00210.html, and in particular - http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00252.html + https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00252.html and its followups, for the details. 2013-09-17 Dmitry Antipov @@ -2617,7 +2617,7 @@ Merge from gnulib, incorporating: 2013-03-29 stdalign: port to stricter ISO C11 This helps to run 'configure' on MS-Windows; see Eli Zaretskii in - . + . 2013-03-27 Paul Eggert @@ -2812,7 +2812,7 @@ Enable conservative stack scanning for all architectures. Suggested by Stefan Monnier in - . + . * configure.ac (GC_MARK_STACK): Remove. 2013-01-11 Paul Eggert @@ -3182,7 +3182,7 @@ Check more robustly for timer_settime. This should fix an OS X build problem reported by Ivan Andrus in - . + . * configure.ac (gl_THREADLIB): Define to empty, since Emacs does threads its own way. * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. @@ -3196,7 +3196,7 @@ * Makefile.in (bootstrap): Simplify build procedure. Suggested by Wolfgang Jenker in - . + . Merge from gnulib, incorporating: 2012-09-22 sockets, sys_stat: remove AC_C_INLINE in MSVC-only cases @@ -3216,14 +3216,14 @@ * Makefile.in: Fix build error on FreeBSD. ($(MAKEFILE_NAME)): Pass MAKE='$(MAKE)' to config.status's env. Suggested by Wolfgang Jenker in - . + . (MAKE_CONFIG_STATUS): Remove. Remaining use expanded. This undoes part of the 2012-09-10 patch. (bootstrap): Run ./configure, rather than trying to run config.status if it exists. That builds src/epaths.h more reliably. Run autogen/copy_autogen if autogen.sh fails, to create 'configure'. Reported by Andreas Schwab in - . + . * autogen.sh: Exit with status 1 when failing due to missing tools, reverting the 2012-09-10 change to this file. * autogen/copy_autogen: Fail if one of the subsidiary actions fail. @@ -3360,7 +3360,7 @@ * configure.ac (WARN_CFLAGS): Omit -Wjump-misses-init. It generates false alarms in doc.c, regex.c, xdisp.c. See - . + . Merge from gnulib, incorporating: 2012-08-29 stdbool: be more compatible with mixed C/C++ compiles @@ -3375,7 +3375,7 @@ * configure.ac (_FORTIFY_SOURCE): Define only when optimizing. This ports to glibc 2.15 or later, when configured with --enable-gcc-warnings. See Eric Blake in - . + . 2012-09-01 Daniel Colascione @@ -3812,7 +3812,7 @@ Improve static checking when configured --with-ns. See Samuel Bronson's remarks in - . + . * configure.in (WARN_CFLAGS): Omit -Wunreachable-code, as it's a no-op with recent GCC and harmful in earlier ones. Omit -Wsync-nand, as it's irrelevant to Emacs and provokes a @@ -4072,7 +4072,7 @@ Remove --disable-maintainer-mode option from 'configure'. (Bug#11555) It is confusingly named and rarely useful. See, for example, - . + . * INSTALL.BZR: Don't mention --disable-maintainer-mode. * Makefile.in (MAINTAINER_MODE_FLAG): Remove; all uses removed. * configure.in: Remove --disable-maintainer-mode. @@ -4555,7 +4555,7 @@ Check pkg-config exit status when configuring (Bug#10626). * configure.in (PKG_CHECK_MODULES): Do not assume that pkg-config works; check its exit status. Reported by Jordi Gutiérrez Hermoso in - . + . 2012-04-07 Glenn Morris @@ -4628,7 +4628,7 @@ * configure.in (HAVE_PTHREAD): Check for pthread_atfork if linking to gmalloc.c. This should prevent a MirBSD 10 build failure reported by Nelson H. F. Beebe in - . + . 2011-12-10 Juanma Barranquero @@ -4775,7 +4775,7 @@ Merge from gnulib, improving some licensing wording. This clarifies and fixes some licensing issues raised by Glenn Morris - . + . It also merges the latest version of texinfo.tex and has some MSVC-related changes that don't affect Emacs. * Makefile.in (GNULIB_TOOL_FLAGS): Avoid msvc-inval, msvc-nothrow, @@ -4944,7 +4944,7 @@ test, which runs afoul of Automake installations where, for example, /usr/share/aclocal contains a copy of gl_THREADLIB. Reported by Sven Joachim in - . + . This is just a quick temporary fix, specific to Emacs; I'll work with the other gnulib maintainers to get a more-permanent fix. @@ -5556,7 +5556,7 @@ * arg-nonnull.h, c++defs.h, warn-on-use.h: Fix licenses. Sync from gnulib, which has been patched to fix the problem with the license notices. Reported by Glenn Morris in - . + . 2011-02-09 Stefan Monnier @@ -5590,7 +5590,7 @@ gnulib: adjust to upstream _HEADERS change * lib/Makefile.am (EXTRA_HEADERS, nodist_pkginclude_HEADERS): New empty macros, to accommodate recent changes to gnulib. See - . + . * c++defs.h, lib/Makefile.in, lib/ftoastr.h, lib/getopt.in.h: * lib/gnulib.mk, lib/ignore-value.h, lib/stdbool.in.h, lib/stddef.in.h: * lib/time.in.h, lib/unistd.in.h: @@ -5664,7 +5664,7 @@ * lib/mktime.c (long_int_is_wide_enough): Move this assertion to the top level, to make it clearer that the assumption about long_int width is being checked. See - . + . 2011-01-29 Paul Eggert @@ -5673,7 +5673,7 @@ negative number, which the C Standard says has undefined behavior. In practice this is not a problem, but might as well do it by the book. Reported by Rich Felker and Eric Blake; see - . + . * m4/mktime.m4 (AC_FUNC_MKTIME): Likewise. * lib/mktime.c (TYPE_MAXIMUM): Redo slightly to match the others. @@ -5691,7 +5691,7 @@ mktime: fix some integer overflow issues and sidestep the rest This was prompted by a bug report by Benjamin Lindner for MinGW - . + . His bug is due to signed integer overflow (0 - INT_MIN), and I I scanned through mktime.c looking for other integer overflow problems, fixing all the bugs I found. @@ -5710,7 +5710,7 @@ no need to test for alternatives. All uses removed. (TYPE_MAXIMUM): Don't rely here on overflow behavior not defined by the C standard. Reported by Rich Felker in - . + . (twos_complement_arithmetic): Also check long_int and time_t. (time_t_avg, time_t_add_ok, time_t_int_add_ok): New functions. (guess_time_tm, ranged_convert, __mktime_internal): Use them. @@ -5810,7 +5810,7 @@ aclocal.m4: put this file back into repository This way, we don't have to assume that the maintainer has the automake package installed. See - . + . * .bzrignore: Remove aclocal.m4, undoing the previous change. * Makefile.in (top_maintainer_clean): Do not remove aclocal.m4, undoing the previous change. @@ -5837,7 +5837,7 @@ aclocal.m4: tweaks to regenerate more conveniently This attempts to act better when the source is in a weird state. See - . + . * Makefile.in (am--refresh): Add aclocal.m4, configure, config.in. * .bzrignore: Add aclocal.m4. @@ -5848,12 +5848,12 @@ the most recent change here. * aclocal.m4: Remove from bzr repository. This file is auto-generated and isn't needed to run 'configure'. See - . + . 2011-01-19 Paul Eggert Minor Makefile.in tweaks to build from gnulib better. - + * Makefile.in (sync-from-gnulib): Also run autoreconf -I m4. (top_maintainer_clean): Don't remove aclocal.m4. @@ -5881,7 +5881,7 @@ * Makefile.in (GNULIB_MODULES): Change ftoastr to dtoastr. This avoids building ftoastr and ldtoastr, which aren't needed. See - . + . * .bzrignore: Add .h files that are host-dependent. Add lib/.deps/, lib/arg-nonnull.h, lib/c++defs.h, lib/getopt.h, diff --git a/ChangeLog.2 b/ChangeLog.2 index 289cc2be1d..e7befde64a 100644 --- a/ChangeLog.2 +++ b/ChangeLog.2 @@ -118,7 +118,7 @@ org-src fontify buffers" the hooks were enabled also for modifications to the original org buffer. This causes fontification errors when combined with certain packages, as reported in - http://lists.gnu.org/archive/html/emacs-orgmode/2017-03/msg00420.html. + https://lists.gnu.org/archive/html/emacs-orgmode/2017-03/msg00420.html. * lisp/org/org-src.el (org-src-font-lock-fontify-block): Reduce scope of inhibit-modification-hooks let-binding. @@ -1508,7 +1508,7 @@ This should make ralloc-related bugs less likely on GNU/Linux systems with bleeding-edge glibc. See the email thread containing: - http://lists.gnu.org/archive/html/emacs-devel/2016-10/msg00801.html + https://lists.gnu.org/archive/html/emacs-devel/2016-10/msg00801.html Do not merge to master. * configure.ac (REL_ALLOC): Default to 'no' on all platforms, not merely on platforms with Doug Lea malloc. Although bleeding-edge @@ -2381,7 +2381,7 @@ parameters restored by desktop.el take precedence over the customizations in the init file, and explain how to countermand that. For the details of the issue, see - http://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00318.html. + https://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00318.html. 2016-09-15 Nicolas Petton @@ -2424,7 +2424,7 @@ * lisp/url/url-http.el (url-http-create-request): Make sure the cookie headers are a unibyte string. For the details, see - http://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00202.html. + https://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00202.html. 2016-09-08 Martin Rudalics @@ -2945,9 +2945,9 @@ * src/indent.c (Fvertical_motion): Don't return uninitialized value in non-interactive session. This fixes random errors in batch mode, see - http://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00609.html + https://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00609.html and - http://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00500.html + https://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00500.html for the details. 2016-07-10 Andreas Schwab @@ -3287,7 +3287,7 @@ * src/xfns.c (x_get_monitor_attributes_xrandr): Use #if, not #ifdef. This ports to systems that predate xrandr 1.3. See Christian Lynbech in: - http://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00198.html + https://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00198.html (cherry picked from commit dce99f222f1ca33265cd56ddb157817be1dc078e) @@ -3305,7 +3305,7 @@ * lisp/calendar/todo-mode.el (todo-read-category): Use set-keymap-parent instead of copy-keymap, and default (as previously) to the global binding (for rationale, see - http://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00217.html). + https://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00217.html). 2016-06-14 Stephen Berman @@ -3889,7 +3889,7 @@ * lisp/replace.el (replace-char-fold): Rename from replace-character-fold. * test/automated/char-fold-tests.el: Rename from character-fold-tests.el. - http://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00529.html + https://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00529.html 2016-05-17 Nicolas Petton @@ -5047,7 +5047,7 @@ * lisp/faces.el (variable-pitch) [w32]: Name a variable-pitch font explicitly, to avoid Emacs picking up a bold-italic variant on some MS-Windows systems. See this thread for details: - http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00746.html. + https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00746.html. 2016-04-29 Alan Mackenzie @@ -5461,8 +5461,8 @@ buffer text was overwritten with binary nulls, because mmap_realloc copied only part of buffer text when extending it. See - http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00325.html - and http://debbugs.gnu.org/cgi/bugreport.cgi?bug=23223#55 for two + https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00325.html + and https://debbugs.gnu.org/cgi/bugreport.cgi?bug=23223#55 for two examples of the related problems. 2016-04-14 Michael Albinus @@ -5496,7 +5496,7 @@ * lisp/cedet/semantic/symref/grep.el (semantic-symref-filepattern-alist): Add entry for lisp-interaction-mode - (http://debbugs.gnu.org/cgi/bugreport.cgi?bug=23223#47) + (https://debbugs.gnu.org/cgi/bugreport.cgi?bug=23223#47) 2016-04-12 Dmitry Gutov @@ -5706,7 +5706,7 @@ fixing most of Bug#20202. The only part of the change that is still reverted is the change to M-x term, where compatibility with current Bash constrains us from moving too quickly (Bug#20484). - Problem reported by Phillip Lord in: http://bugs.gnu.org/20484#108 + Problem reported by Phillip Lord in: https://bugs.gnu.org/20484#108 * etc/NEWS: Document this. * lisp/comint.el (comint-exec-1): * lisp/net/tramp-sh.el (tramp-remote-process-environment): @@ -6031,7 +6031,7 @@ * lisp/progmodes/prog-mode.el: (prog-indentation-context) (prog-first-column, prog-widen): Remove, as discussed in - http://lists.gnu.org/archive/html/emacs-devel/2016-03/msg01425.html. + https://lists.gnu.org/archive/html/emacs-devel/2016-03/msg01425.html. * doc/lispref/text.texi (Mode-Specific Indent): Remove references to them. @@ -6662,7 +6662,7 @@ * etc/NEWS, nextstep/README: Prefer curved quotes in the recently-changed text documentation. See: - http://lists.gnu.org/archive/html/emacs-devel/2016-03/msg00860.html + https://lists.gnu.org/archive/html/emacs-devel/2016-03/msg00860.html 2016-03-14 Paul Eggert @@ -7280,7 +7280,7 @@ * lisp/progmodes/xref.el (xref--xref-buffer-mode): Uncomment the next-error-function integration - (http://debbugs.gnu.org/cgi/bugreport.cgi?bug=20489#110). + (https://debbugs.gnu.org/cgi/bugreport.cgi?bug=20489#110). 2016-02-29 Dmitry Gutov @@ -7594,7 +7594,7 @@ * lisp/progmodes/ruby-mode.el (ruby-mode-syntax-table): Change the syntax classes of $, : and @ to "prefix character" - (http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00272.html). + (https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00272.html). (ruby-syntax-propertize): Undo that specifically for colons followed by an opening paren or bracket. (ruby-font-lock-keyword-beg-re): Include colon character. @@ -7690,7 +7690,7 @@ (xref-show-location-at-point): Make an effort to avoid the original window when showing the location. (xref-goto-xref): Don't quit the xref window (bug#20487 and - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01133.html). + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01133.html). (xref--query-replace-1): Use xref--with-dedicated-window as well. (xref--next-error-function): Call xref--show-location instead of xref--pop-to-location. @@ -8535,7 +8535,7 @@ Port to FreeBSD 11-CURRENT i386 Problem reported by Herbert J. Skuhra in: - http://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00354.html + https://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00354.html Instead of trying * src/alloc.c (lmalloc, lrealloc, laligned): New functions. (xmalloc, xzalloc, xrealloc, lisp_malloc): Use them. @@ -8581,7 +8581,7 @@ Fix test for dladdr Problem reported by Andreas Schwab in: - http://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00327.html + https://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00327.html * configure.ac (dladdr): Link with LIBMODULES when checking for this function. @@ -8920,7 +8920,7 @@ Remove 'def X' from the example * test/etags/ruby-src/test1.ru (A::B): Remove 'def X' - (http://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00167.html). + (https://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00167.html). * test/etags/CTAGS.good: * test/etags/ETAGS.good_1: * test/etags/ETAGS.good_2: @@ -9354,7 +9354,7 @@ autogen.sh now arranges for git to check hashes Suggested by Karl Fogel in: - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01802.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01802.html * autogen.sh: Do "git config transfer.fsckObjects true". 2016-01-31 Paul Eggert @@ -9646,7 +9646,7 @@ (project-find-file-in): Use it. (project-file-completion-table): Move the default implementation inside the cl-defgeneric form. - (http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01720.html) + (https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01720.html) 2016-01-30 Dmitry Gutov @@ -9695,7 +9695,7 @@ Correct a whole bunch of bugs coming with renamed cell relocation. This is the same change as commit on master branch. See - http://git.savannah.gnu.org/cgit/emacs.git/commit/?id=badcd38aa86ed7973f2be2743c405710973a0bdd + https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=badcd38aa86ed7973f2be2743c405710973a0bdd * lisp/ses.el (ses-localvars): rename variable `ses--renamed-cell-symb-list' into `ses--in-killing-named-cell-list' @@ -9777,8 +9777,8 @@ * doc/lispref/control.texi (Pattern matching case statement): Improve the documentation of 'pcase' per comments. See two discussion threads on emacs-devel@gnu.org for the details: - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01335.html - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01336.html. + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01335.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01336.html. 2016-01-29 Glenn Morris @@ -10047,7 +10047,7 @@ Port "$@" to OpenIndiana ksh93 - In http://lists.gnu.org/archive/html/bug-autoconf/2015-12/msg00000.html + In https://lists.gnu.org/archive/html/bug-autoconf/2015-12/msg00000.html Pavel Raiskup reports that ${1+"$@"} runs afoul of a bug in /bin/sh (derived from ksh 93t+ 2010-03-05). ${1+"$@"} works around an ancient bug in long-dead shells, so remove the workaround. @@ -10197,7 +10197,7 @@ * lisp/progmodes/xref.el(xref-query-replace): Rename to xref-query-replace-in-results. - (http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01240.html) + (https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01240.html) * lisp/progmodes/xref.el (xref--xref-buffer-mode-map): * lisp/dired-aux.el (dired-do-find-regexp-and-replace): @@ -10220,7 +10220,7 @@ * lisp/progmodes/xref.el (xref--xref-buffer-mode): Comment out next-error-function integration - (http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01286.html). + (https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01286.html). 2016-01-23 John Wiegley @@ -10289,7 +10289,7 @@ Pacify --enable-gcc-warnings --with-cairo Problem reported by Alexander Kuleshov in: - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01289.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01289.html * src/gtkutil.c (xg_get_page_setup): Use switch rather than if-then-else. * src/image.c (COLOR_TABLE_SUPPORT): @@ -10529,7 +10529,7 @@ No need to configure gobject-introspection It wasn’t needed for the recently-installed xwidget_mvp code; see: - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01154.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg01154.html * configure.ac (DOES_XWIDGETS_USE_GIR, GIR_REQUIRED, GIR_MODULES): (HAVE_GIR): * src/Makefile.in (GIR_LIBS, GIR_CFLAGS): @@ -10811,7 +10811,7 @@ * lisp/dired-aux.el (dired-do-find-regexp) (dired-do-find-regexp-and-replace): New commands. - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00864.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00864.html * lisp/dired.el (dired-mode-map): Change bindings for `A' and `Q' to the new commands. @@ -10834,7 +10834,7 @@ * doc/emacs/maintaining.texi (Xref, Find Identifiers) (Looking Up Identifiers, Identifier Search, List Identifiers): Adjudicate comments by Dmitry Gutov . See - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00650.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00650.html for the details. 2016-01-18 Eli Zaretskii @@ -11009,7 +11009,7 @@ Use it instead of the literal MB_ERR_INVALID_CHARS. (maybe_load_unicows_dll): Initialize multiByteToWideCharFlags as appropriate for the underlying OS version. For details, see - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00835.html. + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00835.html. * src/w32.h: Declare multiByteToWideCharFlags. * src/w32fns.c (Fx_file_dialog, Fw32_shell_execute) (add_tray_notification): Use multiByteToWideCharFlags instead of @@ -11025,7 +11025,7 @@ * etc/PROBLEMS (MS-Windows): Mention the problem with Shell32.dll on Windows NT4. For the details, see - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00835.html. + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00835.html. 2016-01-16 Jussi Lahdenniemi (tiny change) @@ -11035,7 +11035,7 @@ special functions on Windows 9X. Refuse to dump Emacs on Windows 9X. (malloc_after_dump_9x, realloc_after_dump_9x) (free_after_dump_9x): New functions. (Bug#22379) See also - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00852.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00852.html for more details about the original problem. * nt/inc/ms-w32.h (malloc_after_dump_9x, realloc_after_dump_9x) @@ -11180,7 +11180,7 @@ Un-obsolete tags-loop-continue * lisp/progmodes/etags.el (tags-loop-continue): Un-obsolete. - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00682.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00682.html 2016-01-13 Eli Zaretskii @@ -11402,7 +11402,7 @@ (vc-hg-annotate-extract-revision-at-line-with-filename) (vc-hg-annotate-extract-revision-at-line-with-both): Don't refer to source-directory. - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00755.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00755.html 2016-01-11 Michael Albinus @@ -11469,7 +11469,7 @@ This use of 'noexcept' runs afoul of the C++11 standard. Problem reported by Philipp Stephani in: - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00706.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00706.html * src/emacs-module.c (emacs_finalizer_function): Move this typedef here ... * src/emacs-module.h: ... from here, and use only the C @@ -11633,7 +11633,7 @@ Fix (error ...) error Problem reported by Glenn Morris in: - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00561.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00561.html * lisp/vc/add-log.el (change-log-goto-source): Fix typos introduced in my Aug 28 change, where I got confused by the two meanings of (error ...). @@ -11784,7 +11784,7 @@ * src/xdisp.c (message_to_stderr): If coding-system-for-write has a non-nil value, use it to encode output in preference to locale-coding-system. See the discussions in - http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00048.html + https://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00048.html for the details. * doc/lispref/os.texi (Terminal Output): Document how to send @@ -12419,7 +12419,7 @@ * lisp/mail/emacsbug.el (report-emacs-bug): Future-proof the recent "built on" change to deterministic builds where emacs-build-system will be nil. See: - http://lists.gnu.org/archive/html/emacs-devel/2015-12/msg01369.html + https://lists.gnu.org/archive/html/emacs-devel/2015-12/msg01369.html 2015-12-29 Jose A. Ortega Ruiz (tiny change) @@ -12619,7 +12619,7 @@ * lisp/startup.el (initial-scratch-message): Reword to avoid apostrophes, and to make it shorter. See the thread starting in: - http://lists.gnu.org/archive/html/emacs-devel/2015-12/msg01241.html + https://lists.gnu.org/archive/html/emacs-devel/2015-12/msg01241.html 2015-12-26 Leo Liu @@ -13443,7 +13443,7 @@ commands for interactive Python and Guile interpreters. (gdb-send): Recognize various ways of exiting from Python and Guile interpreters and returning to GDB. For details, see - http://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00693.html + https://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00693.html and http://stackoverflow.com/questions/31514741. 2015-12-16 Paul Eggert @@ -13452,7 +13452,7 @@ C11 threads are not needed for Emacs now, and their use is causing hassles on FreeBSD 10.x. Problem reported by Ashish SHUKLA in: - http://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00648.html + https://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00648.html * configure.ac: Do not check for C11 threads. Remove unnecessary fiddling with CPPFLAGS when configuring pthreads. * src/emacs-module.c (main_thread, check_main_thread) @@ -13991,7 +13991,7 @@ * src/lisp.h (XSYMBOL): Remove eassert incorrectly added in previous change. It breaks on MS-Windows --with-wide-int. Problem reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00275.html + https://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00275.html 2015-12-06 Paul Eggert @@ -14426,7 +14426,7 @@ * lisp/progmodes/xref.el (xref-backend-functions): Use APPEND when adding the default element - (http://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00061.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-12/msg00061.html). 2015-12-01 Eli Zaretskii @@ -14918,7 +14918,7 @@ Matches". Improve wording. Fix lost extra whitespace. (Search Customizations): Improve wording. (Bug#22036) See also comments in - http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg02376.html. + https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg02376.html. * lisp/replace.el (query-replace, query-replace-regexp) (query-replace-regexp-eval, replace-string, replace-regexp): @@ -15021,7 +15021,7 @@ (module_non_local_exit_signal_1, module_non_local_exit_throw_1): Do nothing and return with failure indication immediately, if some previous module call signaled an error or wants to throw. See - http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg02133.html + https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg02133.html for the relevant discussions. 2015-11-27 Eli Zaretskii @@ -15215,7 +15215,7 @@ * src/emacs-module.c (module_format_fun_env): exprintf doesn’t support %p, so use %x. Reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg02122.html + https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg02122.html 2015-11-25 Paul Eggert @@ -15224,7 +15224,7 @@ * lisp/help-fns.el (describe-variable): Quote the variable’s value if it is a symbol other than t or nil. See: T.V Raman in: - http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg02147.html + https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg02147.html 2015-11-25 Dmitry Gutov @@ -15701,7 +15701,7 @@ Although the patch does fix Bug#21688 and prevents a core dump, it also makes the message-mode-propertize test fail; see: - http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg01667.html + https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg01667.html Perhaps someone else can come up with a better fix some day. * src/syntax.c (update_syntax_table_forward): Propertize even when truncated. @@ -16272,7 +16272,7 @@ Be more systematic about quoting symbols `like-this' rather than `like-this or 'like-this' in docstrings. This follows up Artur Malabarba's email in: - http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg01647.html + https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg01647.html 2015-11-18 Peder O. Klingenberg @@ -16332,7 +16332,7 @@ Fix docstring quoting problems with ‘ '’ Problem reported by Artur Malabarba in: - http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg01513.html + https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg01513.html Most of these fixes are to documentation; many involve fixing longstanding quoting glitches that are independent of the recent substitute-command-keys changes. The changes to code are: @@ -17778,7 +17778,7 @@ * lisp/progmodes/project.el (project-library-roots): Remove directories inside the project roots from the result. - (http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00536.html) + (https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00536.html) 2015-11-08 Dmitry Gutov @@ -17990,7 +17990,7 @@ Avoid division by zero crash observed by Yuan MEI - See http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html. + See https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html. * src/dispnew.c (required_matrix_height, required_matrix_width): Avoid division by zero. @@ -18324,7 +18324,7 @@ * etc/PROBLEMS: Describe the problem with pinning Emacs to taskbar on Windows 10. For the details, see the discussion starting at - http://lists.gnu.org/archive/html/help-emacs-windows/2015-09/msg00000.html. + https://lists.gnu.org/archive/html/help-emacs-windows/2015-09/msg00000.html. 2015-10-30 Artur Malabarba @@ -18390,7 +18390,7 @@ * lisp/ielm.el (ielm-indent-line): Use non-nil arg of comint-bol to go to the beginning of text line instead of command line. - http://lists.gnu.org/archive/html/emacs-devel/2015-10/msg02360.html + https://lists.gnu.org/archive/html/emacs-devel/2015-10/msg02360.html 2015-10-29 Eli Zaretskii @@ -18521,7 +18521,7 @@ * src/fileio.c (unhandled-file-name-directory): Default to calling `file-name-as-directory' - (http://lists.gnu.org/archive/html/emacs-devel/2015-10/msg02294.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-10/msg02294.html). 2015-10-28 Artur Malabarba @@ -18684,7 +18684,7 @@ * lisp/vc/vc-hg.el (vc-hg-log-format): Pipe commit description through 'tabindent'. (vc-hg-log-view-mode): Set tab-width to 2 locally. - (http://lists.gnu.org/archive/html/emacs-devel/2015-10/msg02259.html) + (https://lists.gnu.org/archive/html/emacs-devel/2015-10/msg02259.html) 2015-10-27 Stefan Monnier @@ -18752,7 +18752,7 @@ * lisp/vc/vc-hg.el (vc-hg-log-format): New variable. (vc-hg-print-log, vc-hg-expanded-log-entry): Use it. - (http://lists.gnu.org/archive/html/emacs-devel/2015-10/msg02191.html) + (https://lists.gnu.org/archive/html/emacs-devel/2015-10/msg02191.html) 2015-10-27 Nicolas Petton @@ -20044,7 +20044,7 @@ (/ N) now returns the reciprocal of N This is more compatible with Common Lisp and XEmacs (Bug#21690). See: - http://lists.gnu.org/archive/html/emacs-devel/2015-10/msg01053.html + https://lists.gnu.org/archive/html/emacs-devel/2015-10/msg01053.html * lisp/color.el (color-hue-to-rgb, color-hsl-to-rgb) (color-xyz-to-srgb, color-xyz-to-lab): * lisp/emacs-lisp/cl-extra.el (cl-float-limits): @@ -21452,7 +21452,7 @@ Fix a few problems with directed quotes This is in response to a problem report by Kaushal Modi in: - http://bugs.gnu.org/21588#25 + https://bugs.gnu.org/21588#25 * lisp/cedet/mode-local.el (describe-mode-local-overload): * lisp/emacs-lisp/bytecomp.el (byte-compile-fix-header): * lisp/info-xref.el (info-xref-check-all-custom): @@ -21639,7 +21639,7 @@ * src/window.c (Fpos_visible_in_window_p): Clarify the meaning of t for POS. See - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg01040.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg01040.html for the original report. * doc/lispref/windows.texi (Window Start and End): Clarify the @@ -22140,7 +22140,7 @@ Improve git diff hunk headers for .el, .texi Problem reported by Alan Mackenzie in: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00826.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00826.html * .gitattributes (*.el, *.texi): New patterns. * autogen.sh: Configure diff.elisp.xfuncname and diff.texinfo.xfuncname if using Git. @@ -22415,7 +22415,7 @@ a revision to checkin. * lisp/vc/vc.el (vc-next-action): Allow to optionally specify the revision when checking in files. - See http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00688.html + See https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00688.html for the details. 2015-09-18 Wilson Snyder @@ -22600,7 +22600,7 @@ winner no longer holds on to dead frames * lisp/winner.el (winner-change-fun): Cull dead frames. This prevents a potentially massive memory leak. See: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00619.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00619.html 2015-09-16 Michael Albinus @@ -23035,7 +23035,7 @@ Port Unicode char detection to FreeBSD+svgalib Problem reported by Ashish SHUKLA in: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00531.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00531.html * configure.ac: Check for struct unipair.unicode instead of for , since that’s more specific to what the code actually needs. @@ -23170,7 +23170,7 @@ Revert some stray curved quotes I missed earlier Problem reported by David Kastrup in: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00440.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00440.html * lisp/international/mule-cmds.el (leim-list-header): Use format-message with an ASCII-only format. @@ -23200,12 +23200,12 @@ Add patch-sending instructions to git-workflow From a suggestion by Mitchel Humpherys in: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00421.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00421.html * admin/notes/git-workflow (Sending patches): New section. Port to GIFLIB 5.0.6 and later Problem reported by Mitchel Humpherys in: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00420.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00420.html * src/image.c (HAVE_GIFERRORSTRING) [HAVE_GIF]: New macro. (GifErrorString, init_gif_functions) [HAVE_GIF && WINDOWSNT]: (gif_load) [HAVE_GIF]: Use it. @@ -23223,7 +23223,7 @@ Refix movemail GCC pacification Problem reported by Ken Brown in: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00406.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00406.html * lib-src/movemail.c (main): Fix previous change. 2015-09-09 Stefan Monnier @@ -23282,7 +23282,7 @@ Define internal-char-font even if --without-x The function is used now even in non-graphical environments. Problem reported by Glenn Morris in: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00401.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00401.html * src/font.c (Finternal_char_font): Move here ... * src/fontset.c (Finternal_char_font): ... from here. @@ -23324,7 +23324,7 @@ Also, undo the recent change that caused text-quoting-style to affect quote display on terminals, so that the two features are independent. See Alan Mackenzie in: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00244.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00244.html Finally, add a style parameter to startup--setup-quote-display, so that this function can also be invoked after startup, with different styles depending on user preference at the time. @@ -23994,7 +23994,7 @@ Follow text-quoting-style in display table init This attempts to fix a problem reported by Alan Mackenzie in: - http://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00112.html + https://lists.gnu.org/archive/html/emacs-devel/2015-09/msg00112.html * doc/lispref/display.texi (Active Display Table): Mention how text-quoting-style affects it. * doc/lispref/help.texi (Keys in Documentation): @@ -24362,7 +24362,7 @@ Make ‘text-quoting-style’ a plain defvar It doesn’t need customization, as it’s likely useful only by experts. Suggested by Stefan Monnier in: - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg01020.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg01020.html * lisp/cus-start.el: Remove doc.c section for builtin customized vars. Quoting fixes in lisp/textmodes @@ -24645,7 +24645,7 @@ Assume GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS This removes the need for GCPRO1 etc. Suggested by Stefan Monnier in: - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00918.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00918.html * doc/lispref/internals.texi (Writing Emacs Primitives): * etc/NEWS: Document the change. @@ -24822,7 +24822,7 @@ That way, the caller doesn’t have to use curved quotes to get diagnostics that match the text-quoting-style preferences. Suggested by Dmitry Gutov in: - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00893.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00893.html This means we no longer need %qs, so remove that format. While we’re at it, fix an unlikely bug and lessen the pressure on the garbage collector by processing the string once rather @@ -25129,8 +25129,8 @@ and using the new function instead of ‘format’ only in contexts where this seems appropriate. Problem reported by Dmitry Gutov and Andreas Schwab in: - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00826.html - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00827.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00826.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00827.html * doc/lispref/commands.texi (Using Interactive): * doc/lispref/control.texi (Signaling Errors, Signaling Errors): * doc/lispref/display.texi (Displaying Messages, Progress): @@ -26222,7 +26222,7 @@ When run with --batch, check that curved quotes are compatible with the system locale before outputting them in diagnostics. Problem reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00594.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00594.html * lisp/startup.el (command-line): Set internal--text-quoting-flag after the standard display table is initialized. * src/doc.c (default_to_grave_quoting_style): New function. @@ -26813,7 +26813,7 @@ unread-command-events and unread-post-input-method-events are always recorded by record_char. Reported by David Kastrup , see - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00193.html. + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00193.html. 2015-08-10 Samer Masterson @@ -26869,7 +26869,7 @@ ChangeLog.2 ignores remote-tracking merges * build-aux/gitlog-to-emacslog: Ignore commit logs matching "Merge remote-tracking branch '.*'" too. See Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00384.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00384.html 2015-08-09 Nicolas Richard @@ -27096,7 +27096,7 @@ Preserve window point in xref-find-definitions-other-window Fix the problem reported by Ingo Logmar in - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00152.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00152.html * lisp/progmodes/xref.el (xref--goto-char): Extract from xref--goto-location. (xref--pop-to-location): Use it. Replace xref--goto-location with @@ -27182,9 +27182,9 @@ Rename help-quote-translation to text-quoting-style, and use symbols rather than characters as values. This follows suggestions along these lines by Alan Mackenzie in: - http://lists.gnu.org/archive/html/emacs-devel/2015-06/msg00343.html + https://lists.gnu.org/archive/html/emacs-devel/2015-06/msg00343.html and by Drew Adams in: - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00048.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00048.html * doc/lispref/help.texi (Keys in Documentation) * etc/NEWS: * lisp/cus-start.el (standard): @@ -27214,7 +27214,7 @@ Also mention "curly quotes" See Drew Adams's email in: - http://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00040.html + https://lists.gnu.org/archive/html/emacs-devel/2015-08/msg00040.html * doc/lispref/help.texi (Keys in Documentation): Add index entry "curly quotes". * etc/NEWS: Use the phrase "curly quotes" too. @@ -27484,7 +27484,7 @@ Don't worry about $ac_cv_header_sys_resource_h and $ac_cv_func_getrlimit, as they're no longer needed for this. Problem reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-07/msg00443.html + https://lists.gnu.org/archive/html/emacs-devel/2015-07/msg00443.html 2015-07-28 Andy Moreton (tiny change) @@ -27866,7 +27866,7 @@ (xref-pulse-momentarily): Rename from xref--maybe-pulse. (xref--pop-to-location, xref--display-position) (xref-pop-marker-stack): Use the new hooks, as requested in - http://lists.gnu.org/archive/html/emacs-devel/2015-07/msg00213.html + https://lists.gnu.org/archive/html/emacs-devel/2015-07/msg00213.html 2015-07-19 Bozhidar Batsov @@ -28671,7 +28671,7 @@ * src/frame.c (x_set_font): If font_spec_from_name returns nil, don't barf; instead, request a new fontset to be generated. This avoids unnecessarily rejecting fonts named against XLFD rules. See - http://lists.gnu.org/archive/html/help-emacs-windows/2015-06/msg00001.html, + https://lists.gnu.org/archive/html/help-emacs-windows/2015-06/msg00001.html, for the description of the original problem. * lisp/faces.el (set-face-attribute): Don't be fooled too easily by a hyphen in a font's name. @@ -28845,7 +28845,7 @@ * src/font.c (font_load_for_lface): If the font-spec didn't match any available fonts, try again without interpreting trailing "-NN" as the font size. For the description of the original problem, see - http://lists.gnu.org/archive/html/help-emacs-windows/2015-06/msg00001.html + https://lists.gnu.org/archive/html/help-emacs-windows/2015-06/msg00001.html .gdbinit followup to changes in !USE_LSB_TAG * src/.gdbinit (xgetsym): Don't left-shift $ptr even under @@ -28935,7 +28935,7 @@ Improve docstring for macroexp-let2 * lisp/emacs-lisp/macroexp.el (macroexp-let2): Improve as per suggestion by RMS in: - http://lists.gnu.org/archive/html/emacs-devel/2015-06/msg00621.html + https://lists.gnu.org/archive/html/emacs-devel/2015-06/msg00621.html Also, rename args to match new doc string. 2015-06-27 Eli Zaretskii @@ -29025,7 +29025,7 @@ * lisp/term/w32console.el (terminal-init-w32console): * src/doc.c (Fsubstitute_command_keys, Vhelp_quote_translation): If ‘ is not displayable, transliterate it to `, not to '. See: - http://lists.gnu.org/archive/html/emacs-devel/2015-06/msg00542.html + https://lists.gnu.org/archive/html/emacs-devel/2015-06/msg00542.html Fix C99 incompatibilities in Cairo code * src/image.c (xpm_load) [USE_CAIRO]: @@ -29811,7 +29811,7 @@ * lisp/emacs-lisp/derived.el (derived-mode-make-docstring): Nest regexp-quote inside format, not the reverse. Problem reported by Artur Malabarba in: - http://lists.gnu.org/archive/html/emacs-devel/2015-06/msg00206.html + https://lists.gnu.org/archive/html/emacs-devel/2015-06/msg00206.html 2015-06-15 Eli Zaretskii @@ -30843,7 +30843,7 @@ use CRLF (or CR!) termination for lines. Update .gitattributes to match current sources - http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00879.html + https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00879.html * .gitattributes: Accommodate tests that insist on DOS format. Remove test/automated/data/decompress/foo-gzipped. Add etc/e/eterm-color. @@ -30868,7 +30868,7 @@ Use list for the tags completion table, not obarray * lisp/progmodes/etags.el (etags-tags-completion-table): Return a list instead of an obarray - (http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00876.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00876.html). (tags-completion-table): Combine those lists. (tags-completion-table): Update the docstring. @@ -31783,7 +31783,7 @@ Revert doc string changes to f90.el Problem reported by Glenn Morris in: - http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00596.html + https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00596.html * lisp/progmodes/f90.el (f90-mode, f90-abbrev-start): Revert recent changes to doc strings, as it's intended that they use grave accent, not quote. @@ -31869,7 +31869,7 @@ Prefer "this" to “this” in doc strings This mostly just straightens quotes introduced in my previous patch. Suggested by Dmitry Gutov in: - http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00565.html + https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00565.html * lisp/faces.el, lisp/gnus/gnus-group.el, lisp/ldefs-boot.el: * lisp/mail/supercite.el, lisp/net/tramp.el, lisp/recentf.el: * lisp/textmodes/artist.el, lisp/textmodes/rst.el: @@ -31979,8 +31979,8 @@ New command icomplete-force-complete-and-exit * lisp/icomplete.el (icomplete-force-complete-and-exit): New command - (http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00461.html) - (http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00516.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00461.html) + (https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00516.html). (icomplete-minibuffer-map): Bind C-j to it. (icomplete-forward-completions, icomplete-backward-completions): Mention the new command in the docstring. @@ -32807,7 +32807,7 @@ Fix tagging of symbols in C enumerations * lib-src/etags.c (consider_token): Don't tag symbols in expressions that assign values to enum constants. See - http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00291.html + https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00291.html for details. (C_entries): Reset fvdef to fvnone after processing a preprocessor conditional and after a comma outside of parentheses. @@ -32966,7 +32966,7 @@ Remove tag-symbol-match-p from etags-xref-find-definitions-tag-order * lisp/progmodes/etags.el (etags-xref-find-definitions-tag-order): Remove tag-symbol-match-p from the default value - (http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00292.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00292.html). Declare find-tag obsolete * lisp/progmodes/etags.el (find-tag): Declare obsolete in favor of @@ -33162,7 +33162,7 @@ * lisp/cedet/pulse.el (pulse-momentary-unhighlight): Only cancel timer when it is non-nil - (http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00223.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00223.html). 2015-05-06 Glenn Morris @@ -33262,7 +33262,7 @@ * lisp/cedet/pulse.el (pulse-momentary-stop-time): New variable. (pulse-momentary-highlight-overlay): Set up the timer instead of calling `pulse' - (http://lists.gnu.org/archive/html/emacs-devel/2015-05/). + (https://lists.gnu.org/archive/html/emacs-devel/2015-05/). (pulse-tick): New function. (pulse-momentary-unhighlight): Cut off the stop time. (pulse-delay): Update the docstring WRT to not using sit-for. @@ -33455,7 +33455,7 @@ (pulse-momentary-highlight-region): Add autoload cookie. * lisp/progmodes/xref.el (xref--maybe-pulse): Don't highlight the indentation, or the newline, if the line's non-empty - (http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00118.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00118.html). 2015-05-04 Daniel Colascione @@ -33822,7 +33822,7 @@ * lisp/progmodes/xref.el (xref--xref-buffer-mode): Set `next-error-function' and `next-error-last-buffer'. (xref--next-error-function): New function. - (http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg01311.html) + (https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg01311.html) 2015-04-29 Fabián Ezequiel Gallina @@ -34096,7 +34096,7 @@ Introduce xref-prompt-for-identifier * lisp/progmodes/xref.el (xref-prompt-for-identifier): New option. (xref--read-identifier): Use it - (http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg01205.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg01205.html). 2015-04-26 João Távora @@ -34113,7 +34113,7 @@ Pass `id' to `completing-read' as def instead of initial input * lisp/progmodes/xref.el (xref--read-identifier): Pass `id' to `completing-read' as the default value instead of initial input - (http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg01182.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg01182.html). 2015-04-25 Paul Eggert @@ -34724,7 +34724,7 @@ Standardize names of ChangeLog history files Suggested by Glenn Morris in: - http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00678.html + https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00678.html * Makefile.in (install-man): Don't treat ChangeLog.1 as a man page. * doc/man/ChangeLog.1: Rename back from doc/man/ChangeLog.01. * lisp/erc/ChangeLog.1: New file, containing the old contents of ... @@ -34737,7 +34737,7 @@ This more clearly distingiushes pre-April-7 ChangeLog entries (which are for top-level files only) from post-April-7 entries (which are about files at all levels. Problem reported by Glenn Morris in: - http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00678.html + https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00678.html * ChangeLog.1: Move post-April-7 entries from here ... * ChangeLog.2: ... to this new file. * Makefile.in (CHANGELOG_HISTORY_INDEX_MAX): Bump to 2. @@ -35131,7 +35131,7 @@ * doc/man/ChangeLog.01: Rename from doc/man/ChangeLog.1. That way, 'make install' won't think it's a man page. Reported by Ashish SHUKLA in: - http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00656.html + https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00656.html Improve 'make change-history' prereq tests * Makefile.in (gen_origin): Fix to match what's in the master branch. @@ -35320,7 +35320,7 @@ Port commit-msg to MSYS Bash+Gawk See Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00610.html + https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00610.html * build-aux/git-hooks/commit-msg (cent_sign_utf8_format) (cent_sign, print_at_sign, at_sign): Revert previous change. (print_at_sign): Prepend "BEGIN". @@ -35330,7 +35330,7 @@ * build-aux/git-hooks/commit-msg (cent_sign): Just use UTF-8 here rather than ASCII + printf, as the latter fails on a broken MS-Windows shell. Reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00592.html + https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00592.html 2015-04-11 Chris Zheng (tiny change) @@ -35365,7 +35365,7 @@ Add a FIXME comment. (log-edit-changelog-entries): Extract from `log-edit-changelog-entries', handle FILE being a directory - (http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00555.html). + (https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00555.html). 2015-04-10 Paul Eggert @@ -35377,19 +35377,19 @@ * build-aux/git-hooks/commit-msg: Ignore every line after a scissors line, such as a line generated by 'git commit -v'. Problem reported by Johan Bockgård in: - http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00580.html + https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00580.html port commit-msg to Gawk 3.0.4 (1999) * build-aux/git-hooks/commit-msg (cent_sign_utf8_format, cent_sign) (print_at_sign, at_sign): New vars. Use them to avoid problems Eli Zaretskii encountered with Gawk 3.0.4 (1999) on MSYS. See: - http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00566.html + https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00566.html Have commit-msg report commit failure * build-aux/git-hooks/commit-msg: If the commit is aborted, say so. Simplify by doing this at the end. Problem reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00566.html + https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00566.html 2015-04-10 Thomas Fitzsimmons @@ -35463,7 +35463,7 @@ vmotion, for the same reason. Fix the clipping of the argument value to support scroll-margin in all cases and avoid unwarranted recentering. Reported by Milan Stanojević in - http://lists.gnu.org/archive/html/help-gnu-emacs/2015-04/msg00092.html, + https://lists.gnu.org/archive/html/help-gnu-emacs/2015-04/msg00092.html, which see. 2015-04-09 Stefan Monnier @@ -35554,7 +35554,7 @@ for copyright notice prototype, so that we get a proper "coding:" cookie. Use 'mv -i' to avoid unconditionally overwriting an existing ChangeLog. Problems reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00504.html + https://lists.gnu.org/archive/html/emacs-devel/2015-04/msg00504.html Merge from gnulib * build-aux/gitlog-to-changelog: Update from gnulib, incorporating: diff --git a/ChangeLog.3 b/ChangeLog.3 index c74aede8cd..fb24476a0b 100644 --- a/ChangeLog.3 +++ b/ChangeLog.3 @@ -1302,7 +1302,7 @@ Fix another CANNOT_DUMP problem Reported by Robert Pluim in: - http://lists.gnu.org/archive/html/emacs-devel/2016-11/msg00468.html + https://lists.gnu.org/archive/html/emacs-devel/2016-11/msg00468.html * src/emacs.c (might_dump) [CANNOT_DUMP]: Move enum decl from here ... * src/lisp.h: ... to here. @@ -1342,7 +1342,7 @@ Fix undefined refs on some GNU/Linux hosts Problem reported by Ken Raeburn in: - http://lists.gnu.org/archive/html/emacs-devel/2016-11/msg00463.html + https://lists.gnu.org/archive/html/emacs-devel/2016-11/msg00463.html * src/emacs.c (heap_bss_diff) [CANNOT_DUMP]: Remove, as this is not needed in the CANNOT_UNDUMP case. All uses removed. This removes unwanted references to my_endbss and my_endbss_static, @@ -4361,7 +4361,7 @@ Define _GNU_SOURCE in files delaying config.h Problem reported by Richard Copley in: - http://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00440.html + https://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00440.html * src/w32.c, src/w32notify.c, src/w32proc.c (_GNU_SOURCE): Define early. @@ -4403,7 +4403,7 @@ Define _GNU_SOURCE in unexmacosx.c Problem reported by Bob Halley in: - http://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00427.html + https://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00427.html * src/unexmacosx.c (_GNU_SOURCE): Define if not already defined. 2016-09-16 Alan Mackenzie @@ -5206,14 +5206,14 @@ * nt/inc/ms-w32.h (execve) [MINGW_W64]: Make the prototype match the GCC 6 builtin, to avoid warnings. For more details, see - http://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00721.html. + https://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00721.html. 2016-08-31 Paul Eggert Fix over-substitution of quotes on error Problem reported by Tino Calancha in: - http://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00000.html + https://lists.gnu.org/archive/html/emacs-devel/2016-09/msg00000.html * src/print.c (print_error_message): Substitute quotes in errmsg only when gotten from a property. @@ -5452,7 +5452,7 @@ * src/keyboard.c (parse_solitary_modifier): If the argument SYMBOL is not a symbol, don't try to recognize it. See - http://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00502.html + https://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00502.html for the details. * test/src/keymap-tests.el (keymap-where-is-internal-test): New @@ -5565,7 +5565,7 @@ Rename option to shell-command-dont-erase-buffer Suggested by Clément Pit--Claudel in: - http://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00487.html + https://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00487.html * lisp/simple.el (shell-command-dont-erase-buffer): (shell-command--save-pos-or-erase): (shell-command--set-point-after-cmd): @@ -5611,7 +5611,7 @@ * lisp/frame.el (delete-other-frames): Delete other frames on FRAME's terminal instead of the current terminal. Delete non-minibuffer-only surrogate frames too. See - http://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00467.html + https://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00467.html * doc/lispref/frames.texi (Deleting Frames): Minor fixes for docs of `delete-frame' and `frame-live-p'. Add entry for `delete-other-frames'. @@ -5931,7 +5931,7 @@ * lisp/server.el (server-reply-print): Fix check for truncated quote sequence at end of message. Problem reported in: - http://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00101.html + https://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00101.html 2016-08-18 Eli Zaretskii @@ -6008,7 +6008,7 @@ Return a sublist of the attributes returned by 'file-attributes'. Suggested by Ted Zlatanov in: - http://lists.gnu.org/archive/html/emacs-devel/2016-07/msg01195.html + https://lists.gnu.org/archive/html/emacs-devel/2016-07/msg01195.html 2016-08-17 Michael Albinus @@ -6048,7 +6048,7 @@ * doc/emacs/misc.texi (shell-command-not-erase-buffer): Document this feature in the manual. See discussion on: - http://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00610.html + https://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00610.html 2016-08-16 Michael Albinus @@ -6857,7 +6857,7 @@ a second time; although it doesn’t hurt, it’s not needed. * src/sysdep.c [!HAVE_GNUTLS]: Don’t include gnutls/crypto.h, as it may not be available. Problem reported by Glenn Morris in: - http://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00100.html + https://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00100.html 2016-08-03 Michal Nazarewicz @@ -6889,7 +6889,7 @@ Port to systems lacking GNUTLS_NONBLOCK Problem reported by Colin Baxter in: - http://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00096.html + https://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00096.html * src/gnutls.c (Fgnutls_boot): Don’t assume GNUTLS_NONBLOCK is defined. 2016-08-03 Paul Eggert @@ -7171,7 +7171,7 @@ * src/process.c (connect_network_socket): Reverse sense of previous fix. Problem reported by Ken Brown in: - http://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00004.html + https://lists.gnu.org/archive/html/emacs-devel/2016-08/msg00004.html 2016-08-01 Glenn Morris @@ -8364,7 +8364,7 @@ * lisp/ibuffer.el (ibuffer-mode-map): 'ibuffer-mark-by-content-regexp' just bound to '% g'. As suggested in: - http://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00165.html + https://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00165.html 2016-07-07 Tino Calancha @@ -8573,7 +8573,7 @@ * src/process.c (wait_reading_process_output): Further fix for typo introduced in 2015-07-06T02:19:13Z!eggert@cs.ucla.edu when wait == INFINITY and got_output_end_time is invalid. See: - http://bugs.gnu.org/23864#20 + https://bugs.gnu.org/23864#20 2016-07-03 Alan Mackenzie @@ -8705,7 +8705,7 @@ strings which affect the stringiness of a piece of text. This fixes the bug reported in - http://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00695.html. + https://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00695.html. * lisp/progmodes/cc-engine.el (c-truncate-semi-nonlit-pos-cache): new defsubst. @@ -8927,7 +8927,7 @@ Problem reported by Juliusz Chroboczek (Bug#17976) and by Artur Malabarba (Bug#23620). Patch from a suggestion by Andreas Schwab in: - http://bugs.gnu.org/17976#39 + https://bugs.gnu.org/17976#39 This patch is for non-MS-Windows platforms. I don't know the situation on MS-Windows. * src/process.c (connecting_status): @@ -8951,7 +8951,7 @@ Fix GNUC_PREREQ for GCC 2.8.1 etc. Problem reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00608.html + https://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00608.html * src/conf_post.h (GNUC_PREREQ): Port to GCC versions like GCC 2.8.1 (1998), which come before GCC 3.0 and which have nonzero patchlevel numbers. @@ -8977,7 +8977,7 @@ Fix GNUC_PREREQ off-by-1 typo Problem reported by Martin Rudalics in: - http://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00587.html + https://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00587.html * src/conf_post.h (GNUC_PREREQ) [__GNUC_PATCHLEVEL__]: Fix < vs <= typo. @@ -9245,7 +9245,7 @@ * src/lread.c (Fload): Don't overwrite the last character of the file name in FOUND with 'c', unless the file name ended in ".elc" to begin with. Don't treat empty files as byte-compiled. See - http://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00463.html + https://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00463.html for more details of the problem this caused. 2016-06-22 Eli Zaretskii @@ -9675,7 +9675,7 @@ * src/xfns.c (x_get_monitor_attributes_xrandr): Use #if, not #ifdef. This ports to systems that predate xrandr 1.3. See Christian Lynbech in: - http://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00198.html + https://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00198.html 2016-06-14 Paul Eggert @@ -10030,7 +10030,7 @@ Replace IF_LINT by NONVOLATILE and UNINIT - Inspired by a suggestion from RMS in: http://bugs.gnu.org/23640#58 + Inspired by a suggestion from RMS in: https://bugs.gnu.org/23640#58 * .dir-locals.el (c-mode): Adjust to macro changes. * src/conf_post.h (NONVOLATILE, UNINIT): New macros (Bug#23640). (IF_LINT): Remove. All uses replaced by the new macros. @@ -10297,7 +10297,7 @@ * lisp/version.el (emacs-repository-get-version): Parse .git/packed-refs if it exists. Problem reported by Martin Rudalics in: - http://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00554.html + https://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00554.html 2016-06-01 Michael Albinus @@ -10630,7 +10630,7 @@ Don’t document declare-function internals Suggested by Stefan Monnier in: - http://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00618.html + https://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00618.html * doc/lispref/functions.texi (Declaring Functions): * lisp/subr.el (declare-function): * lisp/emacs-lisp/bytecomp.el: @@ -10716,7 +10716,7 @@ Fix byte-compiler pacification for declare-function Problem reported by Michael Heerdegen in: - http://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00590.html + https://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00590.html * lisp/emacs-lisp/bytecomp.el: (byte-compile-macroexpand-declare-function): Revert signature to previous value. @@ -10963,7 +10963,7 @@ Don’t use only last protocol from getaddrinfo Problem reported by Ken Brown in: - http://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00483.html + https://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00483.html * src/process.c (conv_addrinfo_to_lisp): New function. (connect_network_socket): Arg is now a list of addrinfos, not merely IP addresses. All uses changed. Use protocol from @@ -10981,7 +10981,7 @@ * lisp/image.el (image--get-image): Require seq here, not at the top level, to avoid ‘(require seq) while preparing to dump’ while bootstrapping. Suggested by Tino Calancha in: - http://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00477.html + https://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00477.html 2016-05-22 Nicolas Petton @@ -11498,7 +11498,7 @@ Port autogen.sh to Git 2.4 Problem reported by Michael Brand in: - http://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00367.html + https://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00367.html * autogen.sh (git_config): Don't assume that git rev-parse groks --git-common-dir. @@ -12436,7 +12436,7 @@ * src/buffer.c (Fgenerate_new_buffer_name): Increment count just once each time through the loop. Reported by Lars Ingebrigtsen in: - http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00918.html + https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00918.html 2016-04-30 Lars Ingebrigtsen @@ -13209,7 +13209,7 @@ Fix socketd fd startup bug that I introduced Problem reported by Matthew Leach in: - http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00778.html + https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00778.html * src/emacs.c (main): Indicate more clearly the coupling between the --daemon option and init_process_emacs. * src/lisp.h: Adjust to API changes. @@ -13269,7 +13269,7 @@ This also fixes the mishandling of "\N{CJK COMPATIBILITY IDEOGRAPH-F900}", "\N{VARIATION SELECTOR-1}", etc. Problem reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00614.html + https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00614.html * doc/lispref/nonascii.texi (Character Codes), etc/NEWS: Document this. * lisp/international/mule-cmds.el (char-from-name): New function. (read-char-by-name): Use it. Document that "BED" is treated as @@ -13340,7 +13340,7 @@ Remove the previous change. (vc-state): Same. And update the old, incorrect comment about unregistered files - (http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00526.html). + (https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00526.html). * test/lisp/vc/vc-tests.el (vc-test--state): Remove the check calling `vc-state' on default-directory (VC state is undefined @@ -13674,7 +13674,7 @@ Avoid AC_PREPROC_IFELSE glitch in configure.ac Problem reported by Angelo Graziosi in: - http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00545.html + https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00545.html * configure.ac (gl_gcc_warnings): Work around an Autoconf glitch: AC_PREPROC_IFELSE doesn’t generate a simple shell command. @@ -13871,7 +13871,7 @@ Port ‘./autogen.sh git’ to non-clones Problem reported by Angelo Graziosi in: - http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00341.html + https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00341.html * autogen.sh (do_git): Default to false when the arg is ‘all’ but there is no ‘.git’. (git_common_dir, hooks): New vars. @@ -14177,7 +14177,7 @@ Port redirect-debugging-output to MS-Windows Suggested by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00037.html + https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00037.html * src/print.c [WINDOWSNT]: Include sys/socket.h. * src/w32.c (sys_dup2): Work around problem with MS-Windows _dup2. @@ -14186,7 +14186,7 @@ Port redirect-debugging-output to non-GNU/Linux Problem reported by Kylie McClain for musl in: - http://lists.gnu.org/archive/html/emacs-devel/2016-03/msg01592.html + https://lists.gnu.org/archive/html/emacs-devel/2016-03/msg01592.html * etc/DEBUG, etc/NEWS: Mention this. * src/callproc.c (child_setup) [!MSDOS]: * src/dispnew.c (init_display): @@ -14772,7 +14772,7 @@ Compute a better commit message for merges Problem reported by David Engster in: - http://lists.gnu.org/archive/html/emacs-devel/2016-03/msg01270.html + https://lists.gnu.org/archive/html/emacs-devel/2016-03/msg01270.html * admin/gitmerge.el (gitmerge-commit-message): Truncate the computed commit message to at most 72 characters per line. (gitmerge-maybe-resume): Don’t use "-" as the commit message for @@ -14812,7 +14812,7 @@ * admin/gitmerge.el (gitmerge-skip-regexp): Omit "merge", as it causes false positives. See: - http://lists.gnu.org/archive/html/emacs-devel/2016-03/msg01234.html + https://lists.gnu.org/archive/html/emacs-devel/2016-03/msg01234.html 2016-03-22 Paul Eggert @@ -15529,7 +15529,7 @@ Unbreak the MinGW64 build * nt/inc/ms-w32.h [MINGW_W64]: Undefine HAVE_GAI_STRERROR. See - http://lists.gnu.org/archive/html/emacs-devel/2016-03/msg00130.html + https://lists.gnu.org/archive/html/emacs-devel/2016-03/msg00130.html for the details. Reported by Angelo Graziosi . @@ -15564,7 +15564,7 @@ Implement getaddrinfo fallback for MS-Windows - See http://lists.gnu.org/archive/html/emacs-devel/2016-02/msg01602.html + See https://lists.gnu.org/archive/html/emacs-devel/2016-02/msg01602.html for more details. * nt/mingw-cfg.site (ac_cv_func_getaddrinfo) @@ -16907,7 +16907,7 @@ Port recent filevercmp addition to MS-Windows Reported by Andy Moreton in: - http://lists.gnu.org/archive/html/emacs-devel/2016-02/msg01302.html + https://lists.gnu.org/archive/html/emacs-devel/2016-02/msg01302.html * nt/gnulib.mk (libgnu_a_SOURCES): Add filevercmp.c. (EXTRA_DIST): Add filevercmp.h. @@ -18510,7 +18510,7 @@ * src/alloc.c (aligned_alloc): Define to private name when a static function, to avoid collision with lisp.h extern decl. Reported by John Yates in: - http://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00439.html + https://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00439.html 2016-02-08 David Edmondson @@ -18761,7 +18761,7 @@ Port to FreeBSD x86 Reported by Herbert J. Skuhra in: - http://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00336.html + https://lists.gnu.org/archive/html/emacs-devel/2016-02/msg00336.html * src/lisp.h (NONPOINTER_BITS) [__FreeBSD__]: Zero in this case too, since malloc always returns a multiple of 8 in FreeBSD. @@ -19362,7 +19362,7 @@ Port new hybrid malloc to FreeBSD - Problem reported by Wolfgang Jenkner in: http://bugs.gnu.org/22086#118 + Problem reported by Wolfgang Jenkner in: https://bugs.gnu.org/22086#118 * src/gmalloc.c (__malloc_initialize_hook, __after_morecore_hook) (__morecore) [HYBRID_MALLOC]: Define in this case too. diff --git a/Makefile.in b/Makefile.in index 14244eabc7..b882da1928 100644 --- a/Makefile.in +++ b/Makefile.in @@ -511,7 +511,7 @@ install-nt: ## For them, it is empty. INSTALL_ARCH_INDEP_EXTRA = @INSTALL_ARCH_INDEP_EXTRA@ -## http://lists.gnu.org/archive/html/emacs-devel/2007-10/msg01672.html +## https://lists.gnu.org/archive/html/emacs-devel/2007-10/msg01672.html ## Needs to be the user running install, so configure can't set it. set_installuser=for installuser in $${LOGNAME} $${USERNAME} $${USER} \ `id -un 2> /dev/null`; do \ @@ -550,11 +550,11 @@ set_installuser=for installuser in $${LOGNAME} $${USERNAME} $${USER} \ ## Note that we use tar instead of plain old cp -R/-r because the latter ## is apparently not portable (even in 2012!). -## http://lists.gnu.org/archive/html/emacs-devel/2012-05/msg00278.html +## https://lists.gnu.org/archive/html/emacs-devel/2012-05/msg00278.html ## I have no idea which platforms Emacs supports where cp -R does not ## work correctly, and therefore no idea when tar can be replaced. ## See also these comments from 2004 about cp -r working fine: -## http://lists.gnu.org/archive/html/autoconf-patches/2004-11/msg00005.html +## https://lists.gnu.org/archive/html/autoconf-patches/2004-11/msg00005.html install-arch-indep: lisp install-info install-man ${INSTALL_ARCH_INDEP_EXTRA} -set ${COPYDESTS} ; \ unset CDPATH; \ diff --git a/README b/README index 46cff5876f..4557747afc 100644 --- a/README +++ b/README @@ -29,7 +29,7 @@ to report bugs. (The file 'BUGS' in this directory explains how you can find and read that section using the Info files that come with Emacs.) For a list of mailing lists related to Emacs, see . For the complete -list of GNU mailing lists, see . +list of GNU mailing lists, see . The 'etc' subdirectory contains several other files, named in capital letters, which you might consider looking at when installing GNU diff --git a/admin/ChangeLog.1 b/admin/ChangeLog.1 index bc3dba7171..b0dfb13930 100644 --- a/admin/ChangeLog.1 +++ b/admin/ChangeLog.1 @@ -1547,7 +1547,7 @@ * make-tarball.txt: Suggest 'autoreconf -I m4 --force' rather than doing rm and autoconf by hand. See - . + . 2011-01-17 Paul Eggert diff --git a/admin/notes/bugtracker b/admin/notes/bugtracker index 6d0fe50ae5..dfca809dc4 100644 --- a/admin/notes/bugtracker +++ b/admin/notes/bugtracker @@ -552,7 +552,7 @@ https://savannah.nongnu.org/projects/listhelper An "X-Debbugs-Envelope-To" header is used to keep track of where the mail was actually bound for: -http://lists.gnu.org/archive/html/emacs-devel/2009-11/msg01211.html +https://lists.gnu.org/archive/html/emacs-devel/2009-11/msg01211.html ** Mailing list recipient/sender filters. The following mailman filters are useful to stop messages being diff --git a/admin/notes/copyright b/admin/notes/copyright index 8345646b97..f4c8b847a5 100644 --- a/admin/notes/copyright +++ b/admin/notes/copyright @@ -86,7 +86,7 @@ in a README file in each directory with images. (Legal advice says that we need not add notices to each image file individually, if they allow for that.). It is recommended to use the word "convert" to describe the automatic process of changing an image from one format to -another (http://lists.gnu.org/archive/html/emacs-devel/2007-02/msg00618.html). +another (https://lists.gnu.org/archive/html/emacs-devel/2007-02/msg00618.html). When installing a file with an "unusual" license (after checking first @@ -159,7 +159,7 @@ etc/future-bug etc/letter.pbm,letter.xpm - trivial, no notice needed. - + etc/FTP, ORDERS - trivial (at time of writing), no license needed @@ -214,7 +214,7 @@ lib-src/etags.c from a legal point of view. lisp/cedet/semantic/imenu.el - - See http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00410.html + - See https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00410.html in which Eric Ludlam established that the remaining contributions from authors other than himself were negligible. @@ -223,7 +223,7 @@ lisp/play/tetris.el (2007/1) there is no problem with our use of the name "tetris" or the concept. rms: "My understanding is that game rules as such are not copyrightable." - + rms: Legal advice is that we are ok and need not worry about this. @@ -307,8 +307,8 @@ doc/*/*.texi - All manuals should be under GFDL (but see below), and should include a copy of it, so that they can be distributed separately. faq.texi has a different license, for some reason no-one can remember. -http://lists.gnu.org/archive/html/emacs-devel/2007-04/msg00583.html -http://lists.gnu.org/archive/html/emacs-devel/2007-04/msg00618.html +https://lists.gnu.org/archive/html/emacs-devel/2007-04/msg00583.html +https://lists.gnu.org/archive/html/emacs-devel/2007-04/msg00618.html doc/misc/mh-e.texi is dual-licensed (GPL and GFDL) per agreement with FSF (reconfirmed by rms Aug 25 2008). Discussion with @@ -397,7 +397,7 @@ lisp/term/README Accordingly, FSF copyright was added. src/unexhp9k800.c - http://lists.gnu.org/archive/html/emacs-devel/2007-02/msg00138.html + https://lists.gnu.org/archive/html/emacs-devel/2007-02/msg00138.html - briefly removed due to legal uncertainly Jan-Mar 2007. The relevant assignment is under "hp9k800" in copyright.list. File was written by John V. Morris at HP, and disclaimed by the author and @@ -406,10 +406,10 @@ src/unexhp9k800.c lisp/progmodes/python.el Dave Love alerted us to a potential legal problem: -http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-04/msg00459.html +https://lists.gnu.org/archive/html/emacs-pretest-bug/2007-04/msg00459.html On consultation with a lawyer, we found there was no problem: -http://lists.gnu.org/archive/html/emacs-devel/2007-05/msg00466.html +https://lists.gnu.org/archive/html/emacs-devel/2007-05/msg00466.html ** Issues that are "fixed" for the release of Emacs 22, but we may @@ -527,7 +527,7 @@ None known. The EMACS_22_BASE branch was changed to GPLv3 (or later) 2007/07/25. Some notes: -(see http://lists.gnu.org/archive/html/emacs-devel/2007-07/msg01431.html) +(see https://lists.gnu.org/archive/html/emacs-devel/2007-07/msg01431.html) 1. There are some files in the Emacs tree which are not part of Emacs (eg those included from Gnulib). These are all copyright FSF and (at time diff --git a/admin/notes/documentation b/admin/notes/documentation index 09476ad696..fc9c720bd0 100644 --- a/admin/notes/documentation +++ b/admin/notes/documentation @@ -5,7 +5,7 @@ Some documentation tips culled from emacs-devel postings. ** Manual indices -http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00400.html +https://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00400.html For example, this text: @@ -51,7 +51,7 @@ combine them into a single entry, e.g.: ** Point is a proper name -http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00414.html +https://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00414.html In Emacs tradition, we treat "point" as a proper name when it refers to the current editing location. It should not have an article. @@ -65,7 +65,7 @@ referring to point, please fix it. ** Don't use passive verbs -http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00414.html +https://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00414.html Documentation is clearer if it avoids the passive voice whenever possible. For example, rather than saying "Point does not move", say @@ -80,7 +80,7 @@ often provides important information which makes the text clearer, too. *** Why Antinews is useful -http://lists.gnu.org/archive/html/emacs-devel/2008-11/msg00893.html +https://lists.gnu.org/archive/html/emacs-devel/2008-11/msg00893.html The usefulness of Antinews is to help people who buy the printed manual and are still using the previous Emacs version. That's why we @@ -91,7 +91,7 @@ Of course, we try to make it amusing as well. *** Don't mention in Antinews too many features absent in old versions -http://lists.gnu.org/archive/html/emacs-devel/2008-11/msg01054.html +https://lists.gnu.org/archive/html/emacs-devel/2008-11/msg01054.html Since the purpose of Antinews is to help people use the previous Emacs version, there is usually no need to mention features that are simply @@ -114,4 +114,4 @@ In those cases, the user might have trouble figuring out how to use the old version without some sort of help. ** To indicate possession, write Emacs's rather than Emacs'. -http://lists.gnu.org/archive/html/emacs-devel/2012-02/msg00649.html +https://lists.gnu.org/archive/html/emacs-devel/2012-02/msg00649.html diff --git a/admin/notes/repo b/admin/notes/repo index 0da1e1e227..827d6ed1b9 100644 --- a/admin/notes/repo +++ b/admin/notes/repo @@ -11,7 +11,7 @@ install it only on the emacs-24 branch, not on the master as well. Installing things manually into more than one branch makes merges more difficult. -http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01124.html +https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01124.html The exception is, if you know that the change will be difficult to merge to the master (eg because the master code has changed a lot). @@ -40,7 +40,7 @@ so interim merges are unnecessary. Or use shelves; or rebase; or do something else. See the thread for yet another fun excursion into the exciting world of version control. -http://lists.gnu.org/archive/html/emacs-devel/2010-04/msg00086.html +https://lists.gnu.org/archive/html/emacs-devel/2010-04/msg00086.html * Installing changes from gnulib diff --git a/admin/notes/tags b/admin/notes/tags index a1e1b86429..16565ea00d 100644 --- a/admin/notes/tags +++ b/admin/notes/tags @@ -3,7 +3,7 @@ Apparently these date from ye olden days, when tags were common to several GNU projects. So many of them had no relevance to Emacs, and hence were removed. See: -http://lists.gnu.org/archive/html/emacs-devel/2012-04/msg00042.html +https://lists.gnu.org/archive/html/emacs-devel/2012-04/msg00042.html In the unlikely event that you need them, the removed tags were: diff --git a/admin/notes/versioning b/admin/notes/versioning index ef11335de5..dbd563cdc7 100644 --- a/admin/notes/versioning +++ b/admin/notes/versioning @@ -1,6 +1,6 @@ GNU EMACS VERSIONING -*- org -*- -Ref: http://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00872.html +Ref: https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00872.html Emacs version numbers have the form @@ -27,4 +27,3 @@ unexpected last-minute problem occurs. The development version for a new major release has "minor" = 0. The development version for a new minor release has "minor" = that of the previous release. - diff --git a/admin/notes/years b/admin/notes/years index c0db1854e3..268ef85c5d 100644 --- a/admin/notes/years +++ b/admin/notes/years @@ -37,4 +37,4 @@ but should keep the full list in a comment in the source. --RMS, 2005-07-13 [1] Note that this includes 2001 - see - + diff --git a/admin/nt/README-ftp-server b/admin/nt/README-ftp-server index 4f156b9c0c..e480465b36 100644 --- a/admin/nt/README-ftp-server +++ b/admin/nt/README-ftp-server @@ -233,12 +233,12 @@ See the end of the file for license conditions. related to the Windows port of Emacs. For information about the list, see this Web page: - http://lists.gnu.org/mailman/listinfo/help-emacs-windows + https://lists.gnu.org/mailman/listinfo/help-emacs-windows To ask questions on the mailing list, send email to help-emacs-windows@gnu.org. (You don't need to subscribe for that.) To subscribe to the list or unsubscribe from it, fill the form you - find at http://mail.gnu.org/mailman/listinfo/help-emacs-windows as + find at https://mail.gnu.org/mailman/listinfo/help-emacs-windows as explained there. Another valuable source of information and help which should not be diff --git a/configure.ac b/configure.ac index 0b0bb5e144..eb2c684040 100644 --- a/configure.ac +++ b/configure.ac @@ -396,7 +396,7 @@ OPTION_DEFAULT_OFF([xwidgets], ## For the times when you want to build Emacs but don't have ## a suitable makeinfo, and can live without the manuals. -dnl http://lists.gnu.org/archive/html/emacs-devel/2008-04/msg01844.html +dnl https://lists.gnu.org/archive/html/emacs-devel/2008-04/msg01844.html OPTION_DEFAULT_ON([makeinfo],[don't require makeinfo for building manuals]) ## Makefile.in needs the cache file name. @@ -522,7 +522,7 @@ fi dnl The name of this option is unfortunate. It predates, and has no dnl relation to, the "sampling-based elisp profiler" added in 24.3. dnl Actually, it stops it working. -dnl http://lists.gnu.org/archive/html/emacs-devel/2012-11/msg00393.html +dnl https://lists.gnu.org/archive/html/emacs-devel/2012-11/msg00393.html AC_ARG_ENABLE(profiling, [AS_HELP_STRING([--enable-profiling], [build emacs with low-level, gprof profiling support. @@ -948,7 +948,7 @@ AS_IF([test $gl_gcc_warnings = no], nw="$nw -Wbad-function-cast" # These casts are no worse than others. # Emacs doesn't care about shadowing; see - # . + # . nw="$nw -Wshadow" # Emacs's use of alloca inhibits protecting the stack. @@ -1170,7 +1170,7 @@ dnl AC_PROG_LN_S sets LN_S to 'cp -pR' for MinGW, on the premise that 'ln' dnl doesn't support links to directories, as in "ln file dir". But that dnl use is non-portable, and OTOH MinGW wants to use hard links for Emacs dnl executables at "make install" time. -dnl See http://lists.gnu.org/archive/html/emacs-devel/2013-04/msg00475.html +dnl See https://lists.gnu.org/archive/html/emacs-devel/2013-04/msg00475.html dnl for more details. if test "$opsys" = "mingw32"; then LN_S="/bin/ln" @@ -1316,7 +1316,7 @@ dnl Eg "make LDFLAGS=... all" could run into problems, dnl http://bugs.debian.org/684788 dnl * unnecessary, since temacs is the only thing that actually needs it. dnl Indeed this is where it was originally, prior to: -dnl http://lists.gnu.org/archive/html/emacs-pretest-bug/2004-03/msg00170.html +dnl https://lists.gnu.org/archive/html/emacs-pretest-bug/2004-03/msg00170.html if test x$GCC = xyes; then LDFLAGS_NOCOMBRELOC="-Wl,-znocombreloc" else diff --git a/doc/emacs/Makefile.in b/doc/emacs/Makefile.in index 5d2503ff49..f7cc2e112c 100644 --- a/doc/emacs/Makefile.in +++ b/doc/emacs/Makefile.in @@ -58,7 +58,7 @@ HTML_OPTS = --no-split --html # Options used only when making info output. # --no-split is only needed because of MS-DOS. # For a possible alternative, see -# http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg01182.html +# https://lists.gnu.org/archive/html/emacs-devel/2011-01/msg01182.html INFO_OPTS= --no-split INSTALL = @INSTALL@ diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi index ef70d58643..2559b0646c 100644 --- a/doc/emacs/emacs.texi +++ b/doc/emacs/emacs.texi @@ -1503,7 +1503,7 @@ Neal Ziring, Teodor Zlatanov, and Detlev Zundel. advanced, self-documenting, customizable, extensible editor Emacs. (The @samp{G} in @c Workaround makeinfo 4 bug. -@c http://lists.gnu.org/archive/html/bug-texinfo/2004-08/msg00009.html +@c https://lists.gnu.org/archive/html/bug-texinfo/2004-08/msg00009.html @iftex @acronym{GNU, @acronym{GNU}'s Not Unix} @end iftex diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi index 134646ccaa..1577f3d123 100644 --- a/doc/emacs/macos.texi +++ b/doc/emacs/macos.texi @@ -213,6 +213,6 @@ issues to be addressed. Interested developers should contact @email{emacs-devel@@gnu.org}. @end ifnothtml @ifhtml -@url{http://lists.gnu.org/mailman/listinfo/emacs-devel, the +@url{https://lists.gnu.org/mailman/listinfo/emacs-devel, the emacs-devel mailing list}. @end ifhtml diff --git a/doc/emacs/mule.texi b/doc/emacs/mule.texi index 2862832e72..8a4d91f80b 100644 --- a/doc/emacs/mule.texi +++ b/doc/emacs/mule.texi @@ -1356,7 +1356,7 @@ fontset}, the @dfn{startup fontset} and the @dfn{default fontset}. @c FIXME? The doc of *standard*-fontset-spec says: @c "You have the biggest chance to display international characters @c with correct glyphs by using the *standard* fontset." (my emphasis) -@c See http://lists.gnu.org/archive/html/emacs-devel/2012-04/msg00430.html +@c See https://lists.gnu.org/archive/html/emacs-devel/2012-04/msg00430.html The default fontset is most likely to have fonts for a wide variety of non-@acronym{ASCII} characters, and is the default fallback for the other two fontsets, and if you set a default font rather than fontset. diff --git a/doc/emacs/trouble.texi b/doc/emacs/trouble.texi index a029aaa2d4..d0b3419a99 100644 --- a/doc/emacs/trouble.texi +++ b/doc/emacs/trouble.texi @@ -509,7 +509,7 @@ by the Emacs maintainers, are shown by @kbd{M-x debbugs-gnu-usertags}. @item The @samp{bug-gnu-emacs} mailing list (also available as the newsgroup @samp{gnu.emacs.bug}). You can read the list archives at -@url{http://lists.gnu.org/mailman/listinfo/bug-gnu-emacs}. This list +@url{https://lists.gnu.org/mailman/listinfo/bug-gnu-emacs}. This list works as a mirror of the Emacs bug reports and follow-up messages which are sent to the bug tracker. It also contains old bug reports from before the bug tracker was introduced (in early 2008). @@ -524,7 +524,7 @@ The @samp{emacs-pretest-bug} mailing list. This list is no longer used, and is mainly of historical interest. At one time, it was used for bug reports in development (i.e., not yet released) versions of Emacs. You can read the archives for 2003 to mid 2007 at -@url{http://lists.gnu.org/archive/html/emacs-pretest-bug/}. Nowadays, +@url{https://lists.gnu.org/archive/html/emacs-pretest-bug/}. Nowadays, it is an alias for @samp{bug-gnu-emacs}. @item @@ -680,7 +680,7 @@ will be sent to the Emacs maintainers at @email{bug-gnu-emacs@@gnu.org}. @end ifnothtml @ifhtml -@url{http://lists.gnu.org/mailman/listinfo/bug-gnu-emacs, bug-gnu-emacs}. +@url{https://lists.gnu.org/mailman/listinfo/bug-gnu-emacs, bug-gnu-emacs}. @end ifhtml (If you want to suggest an improvement or new feature, use the same address.) If you cannot send mail from inside Emacs, you can copy the @@ -1251,7 +1251,7 @@ If you would like to work on improving Emacs, please contact the maintainers at @email{emacs-devel@@gnu.org}. @end ifnothtml @ifhtml -@url{http://lists.gnu.org/mailman/listinfo/emacs-devel, the +@url{https://lists.gnu.org/mailman/listinfo/emacs-devel, the emacs-devel mailing list}. @end ifhtml You can ask for suggested projects or suggest your own ideas. @@ -1262,7 +1262,7 @@ you have not yet started work, it is useful to contact @email{emacs-devel@@gnu.org} @end ifnothtml @ifhtml -@url{http://lists.gnu.org/mailman/listinfo/emacs-devel, emacs-devel} +@url{https://lists.gnu.org/mailman/listinfo/emacs-devel, emacs-devel} @end ifhtml before you start; it might be possible to suggest ways to make your extension fit in better with the rest of Emacs. @@ -1426,7 +1426,7 @@ Send a message to the mailing list @email{help-gnu-emacs@@gnu.org}, @end ifnothtml @ifhtml -@url{http://lists.gnu.org/mailman/listinfo/help-gnu-emacs, the +@url{https://lists.gnu.org/mailman/listinfo/help-gnu-emacs, the help-gnu-emacs mailing list}, @end ifhtml or post your request on newsgroup @code{gnu.emacs.help}. (This diff --git a/doc/lispref/ChangeLog.1 b/doc/lispref/ChangeLog.1 index 490f216b4c..3fab757c96 100644 --- a/doc/lispref/ChangeLog.1 +++ b/doc/lispref/ChangeLog.1 @@ -497,7 +497,7 @@ Improve doc for use of 'int', and discuss 'ssize_t'. * internals.texi (C Integer Types): Mention 'int' for other randomish values that lie in 'int' range. Mention 'ssize_t'. See: - http://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00019.html + https://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00019.html Use AUTO_CONS instead of SCOPED_CONS, etc. * internals.texi (Stack-allocated Objects): @@ -971,7 +971,7 @@ * markers.texi (Moving Marker Positions): Clarify guidance about when to move markers and when to create a new one, as discussed at - http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16818#17 + https://debbugs.gnu.org/cgi/bugreport.cgi?bug=16818#17 2014-03-02 Glenn Morris @@ -2154,7 +2154,7 @@ * internals.texi (C Integer Types): New section. This follows up and records an email in - . + . 2012-12-10 Stefan Monnier @@ -2475,7 +2475,7 @@ * os.texi (Time of Day): Update for new time stamp format (HIGH LOW MICROSEC PICOSEC). These instances were missed the first time around. - Problem reported by Glenn Morris in . + Problem reported by Glenn Morris in . 2012-10-24 Chong Yidong @@ -6288,7 +6288,7 @@ 2009-04-11 Eli Zaretskii * display.texi (Overlays): Overlays don't scale well. See - http://lists.gnu.org/archive/html/emacs-devel/2009-04/msg00243.html. + https://lists.gnu.org/archive/html/emacs-devel/2009-04/msg00243.html. 2009-04-10 Chong Yidong diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi index b7ae7fec2d..b48278e8b8 100644 --- a/doc/misc/cl.texi +++ b/doc/misc/cl.texi @@ -317,7 +317,7 @@ This is analogous to the @code{defsubst} form; @code{cl-defsubst} uses a different method (compiler macros) which works in all versions of Emacs, and also generates somewhat more @c For some examples, -@c see http://lists.gnu.org/archive/html/emacs-devel/2012-11/msg00009.html +@c see https://lists.gnu.org/archive/html/emacs-devel/2012-11/msg00009.html efficient inline expansions. In particular, @code{cl-defsubst} arranges for the processing of keyword arguments, default values, etc., to be done at compile-time whenever possible. diff --git a/doc/misc/efaq-w32.texi b/doc/misc/efaq-w32.texi index 4ebcbea6a0..eb05e8be43 100644 --- a/doc/misc/efaq-w32.texi +++ b/doc/misc/efaq-w32.texi @@ -61,7 +61,7 @@ Emacs @value{EMACSVER}. This FAQ is maintained by the developers and users of Emacs on MS Windows. If you find any errors, or have any suggestions, please send them to -the @url{http://lists.gnu.org/mailman/listinfo/help-emacs-windows, +the @url{https://lists.gnu.org/mailman/listinfo/help-emacs-windows, help-emacs-windows} mailing list. At time of writing, the latest version of GNU Emacs is version @value{EMACSVER}. @@ -2283,10 +2283,10 @@ and you can view the FAQ by typing @kbd{C-h C-f}. Other resources include: @cindex help, mailing lists The official mailing list for Windows specific help and discussion is -@url{http://lists.gnu.org/mailman/listinfo/help-emacs-windows, +@url{https://lists.gnu.org/mailman/listinfo/help-emacs-windows, help-emacs-windows}. See that link for information on how to subscribe or unsubscribe. The -@uref{http://lists.gnu.org/archive/html/help-emacs-windows/, list archives} +@uref{https://lists.gnu.org/archive/html/help-emacs-windows/, list archives} are available online. @c ------------------------------------------------------------ diff --git a/doc/misc/efaq.texi b/doc/misc/efaq.texi index 7809cfe98a..167aa45c28 100644 --- a/doc/misc/efaq.texi +++ b/doc/misc/efaq.texi @@ -421,7 +421,7 @@ posting bug reports to this newsgroup directly (@pxref{Reporting bugs}). The FSF has maintained archives of all of the GNU mailing lists for many years, although there may be some unintentional gaps in coverage. The archive can be browsed over the web at -@uref{http://lists.gnu.org/archive/html/, the GNU mail archive}. +@uref{https://lists.gnu.org/archive/html/, the GNU mail archive}. Web-based Usenet search services, such as @uref{http://groups.google.com/groups/dir?q=gnu&, Google}, also @@ -457,13 +457,13 @@ RMS says: @quotation Sending bug reports to -@url{http://lists.gnu.org/mailman/listinfo/help-gnu-emacs, +@url{https://lists.gnu.org/mailman/listinfo/help-gnu-emacs, the help-gnu-emacs mailing list} (which has the effect of posting on @uref{news:gnu.emacs.help}) is undesirable because it takes the time of an unnecessarily large group of people, most of whom are just users and have no idea how to fix these problem. -@url{http://lists.gnu.org/mailman/listinfo/bug-gnu-emacs, The +@url{https://lists.gnu.org/mailman/listinfo/bug-gnu-emacs, The bug-gnu-emacs list} reaches a much smaller group of people who are more likely to know what to do and have expressed a wish to receive more messages about Emacs than the others. @@ -1432,7 +1432,7 @@ of files from Macintosh, Microsoft, and Unix platforms. In general, new Emacs users should not be provided with @file{.emacs} files, because this can cause confusing non-standard behavior. Then they send questions to -@url{http://lists.gnu.org/mailman/listinfo/help-gnu-emacs, +@url{https://lists.gnu.org/mailman/listinfo/help-gnu-emacs, the help-gnu-emacs mailing list} asking why Emacs isn't behaving as documented. diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi index b391a88c32..49005537f8 100644 --- a/doc/misc/erc.texi +++ b/doc/misc/erc.texi @@ -804,7 +804,7 @@ emacswiki.org page for ERC@. Anyone may add tips, hints, etc.@: to it. @item You can ask questions about using ERC on the Emacs mailing list, -@uref{http://lists.gnu.org/mailman/listinfo/help-gnu-emacs}. +@uref{https://lists.gnu.org/mailman/listinfo/help-gnu-emacs}. @item You can visit the IRC Freenode channel @samp{#emacs}. Many of the diff --git a/doc/misc/pcl-cvs.texi b/doc/misc/pcl-cvs.texi index 4cf38bd1f0..1e3aeb45fd 100644 --- a/doc/misc/pcl-cvs.texi +++ b/doc/misc/pcl-cvs.texi @@ -1389,7 +1389,7 @@ the @url{http://lists.xemacs.org/mailman/listinfo/xemacs-beta, XEmacs mailing list}. If you have problems using PCL-CVS or other questions, send them to -the @url{http://lists.gnu.org/mailman/listinfo/help-gnu-emacs, +the @url{https://lists.gnu.org/mailman/listinfo/help-gnu-emacs, help-gnu-emacs mailing list}. This is a good place to get help, as is the @url{http://lists.nongnu.org/mailman/listinfo/info-cvs, info-cvs list}. diff --git a/etc/ChangeLog.1 b/etc/ChangeLog.1 index 9514ea284d..91ac79d813 100644 --- a/etc/ChangeLog.1 +++ b/etc/ChangeLog.1 @@ -195,7 +195,7 @@ Fix minor Bazaar leftovers. Reported by Perry E. Metzger in: - http://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00745.html + https://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00745.html * CONTRIBUTE: More git transition. 2014-11-11 Eric S. Raymond @@ -1695,7 +1695,7 @@ Redo spelling of Makefile variables to conform to POSIX. POSIX does not allow "-" in Makefile variable names. Reported by Bruno Haible in - . + . * refcards/Makefile (DIRED_REFCARDS_PDF): Rename from DIRED-REFCARDS_PDF. (MISC_REFCARDS_PDF): Rename from MISC-REFCARDS_PDF. @@ -4762,7 +4762,7 @@ 2005-07-07 Lute Kamstra * tasks.texi: Delete file. The GNU Task List is obsolete and has - been replaced by http://savannah.gnu.org/projects/tasklist. + been replaced by https://savannah.gnu.org/projects/tasklist. 2005-07-07 Lute Kamstra diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index b50ac7f05e..d6f550d29e 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -1992,7 +1992,7 @@ This enables SVG generation from latex code blocks. *** New option: [[doc:org-habit-show-done-always-green][org-habit-show-done-always-green]] -See [[http://lists.gnu.org/archive/html/emacs-orgmode/2013-05/msg00214.html][this message]] from Max Mikhanosha. +See [[https://lists.gnu.org/archive/html/emacs-orgmode/2013-05/msg00214.html][this message]] from Max Mikhanosha. *** New option: [[doc:org-babel-inline-result-wrap][org-babel-inline-result-wrap]] diff --git a/etc/PROBLEMS b/etc/PROBLEMS index f8f1a362bf..94c78b696d 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -540,7 +540,7 @@ to the variable 'locate-dominating-stop-dir-regexp'. For example, if the problem relates to "/smb/.dir-locals.el", set that variable to a new value where you replace "net\\|afs" with "net\\|afs\\|smb". (The default value already matches common auto-mount prefixes.) -See http://lists.gnu.org/archive/html/help-gnu-emacs/2015-02/msg00461.html . +See https://lists.gnu.org/archive/html/help-gnu-emacs/2015-02/msg00461.html . *** Attempting to visit remote files via ange-ftp fails. @@ -689,7 +689,7 @@ On some systems, there exists a font that is actually named Monospace, which takes over the virtual font. This is considered an operating system bug; see -http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00696.html +https://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00696.html If you encounter this problem, set the default font to a specific font in your .Xresources or initialization file. For instance, you can put @@ -1067,7 +1067,7 @@ during such resizing attempts (i3, IceWM). See also https://debbugs.gnu.org/cgi/bugreport.cgi?bug=15700, https://debbugs.gnu.org/cgi/bugreport.cgi?bug=22000, https://debbugs.gnu.org/cgi/bugreport.cgi?bug=22898 and -http://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00154.html. +https://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00154.html. *** Metacity: Resizing Emacs or ALT-Tab causes X to be unresponsive. @@ -2602,7 +2602,7 @@ This is a consequence of a change to src/dired.c on 2010-07-27. The issue is that Cygwin 1.5.19 did not have d_ino in 'struct dirent'. See - http://lists.gnu.org/archive/html/emacs-devel/2010-07/msg01266.html + https://lists.gnu.org/archive/html/emacs-devel/2010-07/msg01266.html *** Building the native MS-Windows port fails due to unresolved externals @@ -2703,7 +2703,7 @@ Errors and warnings can look like this: This happens when paths using backslashes are passed to the compiler or linker (via -I and possibly other compiler flags); when these paths are included in source code, the backslashes are interpreted as escape sequences. -See http://lists.gnu.org/archive/html/emacs-devel/2010-07/msg00995.html +See https://lists.gnu.org/archive/html/emacs-devel/2010-07/msg00995.html The fix is to use forward slashes in all paths passed to the compiler. diff --git a/etc/TODO b/etc/TODO index 278c0b5036..07772d5f4f 100644 --- a/etc/TODO +++ b/etc/TODO @@ -95,17 +95,17 @@ make it. ** Move idlwave to elpa.gnu.org. Need to sync up the Emacs and external versions. -See +See ** Move Org mode to elpa.gnu.org. -See - +See + ** Move verilog-mode to elpa.gnu.org. -See +See ** Move vhdl-mode to elpa.gnu.org. -See +See * Simple tasks. These don't require much Emacs knowledge, they are suitable for anyone from beginners to experts. @@ -157,7 +157,7 @@ for users to customize. ** revert-buffer should eliminate overlays and the mark. For related problems consult the thread starting with - http://lists.gnu.org/archive/html/emacs-devel/2005-11/msg01346.html + https://lists.gnu.org/archive/html/emacs-devel/2005-11/msg01346.html ** erase-buffer should perhaps disregard read-only properties of text. @@ -185,7 +185,7 @@ for users to customize. ** Define recompute-arg and recompute-arg-if for fix_command to use. See rms message of 11 Dec 05 in - http://lists.gnu.org/archive/html/emacs-pretest-bug/2005-12/msg00165.html, + https://lists.gnu.org/archive/html/emacs-pretest-bug/2005-12/msg00165.html, and the rest of that discussion. ** In Emacs Info, examples of using Customize should be clickable @@ -202,10 +202,10 @@ for users to customize. ** make back_comment use syntax-ppss or equivalent. ** Consider improving src/sysdep.c's search for a fqdn. -http://lists.gnu.org/archive/html/emacs-devel/2007-04/msg00782.html +https://lists.gnu.org/archive/html/emacs-devel/2007-04/msg00782.html ** Find a proper fix for rcirc multiline nick adding. -http://lists.gnu.org/archive/html/emacs-devel/2007-04/msg00684.html +https://lists.gnu.org/archive/html/emacs-devel/2007-04/msg00684.html ** Check for any included packages that define obsolete bug-reporting commands. Change them to use report-emacs-bug. @@ -228,12 +228,12 @@ like make-backup-file-name-function for non-numeric backup files. dired buffers and DTRT WRT 'auto-revert-mode'. ** Check uses of prin1 for error-handling. -http://lists.gnu.org/archive/html/emacs-devel/2008-08/msg00456.html +https://lists.gnu.org/archive/html/emacs-devel/2008-08/msg00456.html * Important features: ** "Emacs as word processor" -http://lists.gnu.org/archive/html/emacs-devel/2013-11/msg00515.html +https://lists.gnu.org/archive/html/emacs-devel/2013-11/msg00515.html rms writes: 25 years ago I hoped we would extend Emacs to do WYSIWYG word processing. That is why we added text properties and variable @@ -311,15 +311,15 @@ never really made it work for this. Perspectives also need to interact with the tabs. ** FFI (foreign function interface) -See eg http://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00246.html +See eg https://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00246.html One way of doing this is to start with fx's dynamic loading, and use it to implement things like auto-loaded buffer parsers and database access in cases which need more than Lisp. ** Replace unexec with a more portable form of dumping -See eg http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01034.html - http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00452.html +See eg https://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01034.html + https://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00452.html One way is to provide portable undumping using mmap (per gerd design). @@ -399,8 +399,8 @@ familiar with GNUstep and Objective C. ** A more modern printing interface. One that pops up a dialog that lets you choose printer, page style, etc. Integration with the Gtk print dialog is apparently difficult. See eg: -http://lists.gnu.org/archive/html/emacs-devel/2009-03/msg00501.html -http://lists.gnu.org/archive/html/emacs-devel/2009-04/msg00034.html +https://lists.gnu.org/archive/html/emacs-devel/2009-03/msg00501.html +https://lists.gnu.org/archive/html/emacs-devel/2009-04/msg00034.html ** Allow frames(terminals) created by emacsclient to inherit their environment from the emacsclient process. @@ -499,7 +499,7 @@ from the emacsclient process. ** Get some major packages installed: W3 (development version needs significant work), PSGML, _possibly_ ECB. - http://lists.gnu.org/archive/html/emacs-devel/2007-05/msg01493.html + https://lists.gnu.org/archive/html/emacs-devel/2007-05/msg01493.html Check the assignments file for other packages which might go in and have been missed. @@ -645,17 +645,17 @@ from the emacsclient process. ** Possibly make 'list-holidays' eval items in the calendar-holidays variable. See thread - . + . [rgm@gnu.org will look at this after 22.1] ** Possibly make cal-dst use the system timezone database directly. See thread - + ** Possibly add a "close" button to the modeline. The idea is to add an "X" of some kind, that when clicked deletes the window associated with that modeline. - http://lists.gnu.org/archive/html/emacs-devel/2007-09/msg02416.html + https://lists.gnu.org/archive/html/emacs-devel/2007-09/msg02416.html * Things to be done for specific packages or features @@ -1492,7 +1492,7 @@ presence of multi-file documents. or just an extension of buff-menu.el. ** Replace linum.el with nlinum.el - http://lists.gnu.org/archive/html/emacs-devel/2013-08/msg00379.html + https://lists.gnu.org/archive/html/emacs-devel/2013-08/msg00379.html ** Merge sendmail.el and messages.el. Probably not a complete merge, but at least arrange for messages.el to be @@ -1507,7 +1507,7 @@ presence of multi-file documents. ** Rewrite make-docfile to be clean and maintainable. It might be better to replace it with Lisp, using the byte compiler. - http://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00037.html + https://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00037.html ** Add an inferior-comint-minor-mode to capture the common set of operations offered by major modes that offer an associated inferior @@ -1523,7 +1523,7 @@ presence of multi-file documents. * Wishlist items: ** Maybe replace etags.c with a Lisp implementation. -http://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00354.html +https://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00354.html ** Maybe replace lib-src/rcs2log with a Lisp implementation. It wouldn't have to be a complete replacement, just enough diff --git a/leim/ChangeLog.1 b/leim/ChangeLog.1 index db91ac24e2..91acacc9bc 100644 --- a/leim/ChangeLog.1 +++ b/leim/ChangeLog.1 @@ -454,7 +454,7 @@ Redo spelling of Makefile variables to conform to POSIX. POSIX does not allow "-" in Makefile variable names. Reported by Bruno Haible in - . + . * Makefile.in (BUILT_EMACS): Rename from BUILT-EMACS. (TIT_GB): Rename from TIT-GB. (CHINESE_TIT): Rename from CHINESE-TIT. diff --git a/lib-src/ChangeLog.1 b/lib-src/ChangeLog.1 index 417e57f3bb..4b5422e712 100644 --- a/lib-src/ChangeLog.1 +++ b/lib-src/ChangeLog.1 @@ -5,7 +5,7 @@ is not part of Emacs and is typically not installed. Instead, just invoke xmalloc and xrealloc as usual. Problem reported by Nicolas Richard in: - http://bugs.gnu.org/20191#20 + https://bugs.gnu.org/20191#20 (xrnew): Avoid no-longer-needed cast to 'char *'. (xrealloc): First arg is now void *, not char *. @@ -65,7 +65,7 @@ Better support for future plugins See the thread containing: - http://lists.gnu.org/archive/html/emacs-devel/2015-02/msg00720.html + https://lists.gnu.org/archive/html/emacs-devel/2015-02/msg00720.html * make-docfile.c (write_globals): Generate code that #defines Qxxx macros other than Qnil only if DEFINE_NONNIL_Q_SYMBOL_MACROS. Qnil is safe to define even in plugins, since it must be zero for @@ -329,7 +329,7 @@ 2014-05-26 Paul Eggert Fix rcs2log problems with CVS. Reported by Glenn Morris in - . + . Plus, fix some security and filename quoting problems. * rcs2log (logdir): Prefer mktemp if available. (logdir, llogdir): Work even if TMPDIR begins with '-' or has spaces. @@ -937,13 +937,13 @@ * movemail.c: Add missing 'defined'. Suggested by Sven Joachim in - . + . 2012-07-11 Paul Eggert Port 'movemail' again to Solaris and similar hosts. See Susan Cragin's report in - . + . * movemail.c (xmalloc): Also define if !DISABLE_DIRECT_ACCESS && !MAIL_USE_MMDF && !MAIL_USE_SYSTEM_LOCK. Move up, so it doesn't need a forward declaration. @@ -1212,7 +1212,7 @@ Assume less-ancient POSIX support. * update-game-score.c: Include rather than rolling our own decls for optarg, optind, opterr. See - . + . 2012-04-14 Juanma Barranquero @@ -1271,7 +1271,7 @@ instead, treat both -c and -t as always requesting a new "tty" frame, and let server.el decide which kind is actually required. Reported by Uwe Siart in this thread: - http://lists.gnu.org/archive/html/emacs-devel/2011-11/msg00303.html + https://lists.gnu.org/archive/html/emacs-devel/2011-11/msg00303.html 2011-11-30 Chong Yidong @@ -3684,7 +3684,7 @@ * rcs2log (Help): Clarify wording of the usage message. Reported by Alan Mackenzie in - . + . 2004-04-07 Stefan Monnier @@ -3715,7 +3715,7 @@ and the path. Allow :/ in repository path, since CVS does. Fix typo: "pository" should be set from $CVSROOT, not $repository. This fixes a bug reported by Wolfgang Scherer in - , + , along with some related bugs I discovered by inspecting how CVS itself parses $CVSROOT. @@ -3752,7 +3752,7 @@ * rcs2log (rlog_options): Append -rbranchtag if CVS/Tag indicates a tag, and if the user has not specified an rlog option. Adapted from a suggestion by Martin Stjernholm in - . + . (Copyright): Update to 2003. 2003-12-24 Thien-Thi Nguyen diff --git a/lib-src/rcs2log b/lib-src/rcs2log index 5e5709201e..50276f245d 100755 --- a/lib-src/rcs2log +++ b/lib-src/rcs2log @@ -45,7 +45,7 @@ Each entry looks something like this: * rcs2log (Help): Clarify wording of the usage message. Problem reported by Alan Mackenzie in - . + . ChangeLog entries contain the current date, full name, email address including hostname, the name of the affected file, and commentary. diff --git a/lisp/ChangeLog.11 b/lisp/ChangeLog.11 index eda7603cb6..52f5189228 100644 --- a/lisp/ChangeLog.11 +++ b/lisp/ChangeLog.11 @@ -6395,7 +6395,7 @@ * vc-svn.el (vc-svn-checkin): Use `nconc' instead of `list*', because the latter is a CL-ism. This fixes the bug reported by Shawn Boyette in - http://lists.gnu.org/archive/html/emacs-devel/2004-05/msg00442.html. + https://lists.gnu.org/archive/html/emacs-devel/2004-05/msg00442.html. 2004-06-04 Miles Bader diff --git a/lisp/ChangeLog.13 b/lisp/ChangeLog.13 index 9c451a359e..8869cc46eb 100644 --- a/lisp/ChangeLog.13 +++ b/lisp/ChangeLog.13 @@ -4667,7 +4667,7 @@ 2008-01-02 Karl Fogel Change a return type, for greater extensibility. - See http://lists.gnu.org/archive/html/emacs-devel/2007-12/msg01077.html + See https://lists.gnu.org/archive/html/emacs-devel/2007-12/msg01077.html and its thread for discussion leading to this change. * bookmark.el (bookmark-jump-noselect): @@ -11475,7 +11475,7 @@ (fancy-about-text): Add links "Authors" and "Contributing". (fancy-splash-head): Add text "Welcome to " on the startup screen, and "This is " on the about screen. Add link to - "http://www.gnu.org/software/emacs/" for "GNU Emacs". + "https://www.gnu.org/software/emacs/" for "GNU Emacs". For the about screen move emacs version to the header from `fancy-splash-tail' (as it's done already for normal about screen). (fancy-splash-tail): Insert emacs version only for startup screen. @@ -14464,7 +14464,7 @@ * bookmark.el: Revert 2007-07-13T18:16:17Z!kfogel@red-bean.com, thus restoring bookmark bindings to three slots under C-x r. See - http://lists.gnu.org/archive/html/emacs-devel/2007-07/msg00705.html. + https://lists.gnu.org/archive/html/emacs-devel/2007-07/msg00705.html. 2007-07-15 Jeff Miller (tiny change) @@ -14511,7 +14511,7 @@ * bookmark.el (bookmark-jump-other-window): New function. (bookmark-map): Bind it to "o". - http://lists.gnu.org/archive/html/emacs-devel/2007-07/msg00633.html + https://lists.gnu.org/archive/html/emacs-devel/2007-07/msg00633.html and its thread contains discussion about this change. The original patch was slightly tweaked by Karl Fogel before committing. @@ -14525,7 +14525,7 @@ * bookmark.el: Don't define bookmark keys under the "C-xr" map; instead, make "C-xp" a prefix for bookmark-map. Patch by Drew Adams , mildly tweaked by me. See - http://lists.gnu.org/archive/html/emacs-devel/2007-07/msg00633.html. + https://lists.gnu.org/archive/html/emacs-devel/2007-07/msg00633.html. 2007-07-13 Carsten Dominik diff --git a/lisp/ChangeLog.15 b/lisp/ChangeLog.15 index 7bd44059ad..3e6d664aa6 100644 --- a/lisp/ChangeLog.15 +++ b/lisp/ChangeLog.15 @@ -9833,7 +9833,7 @@ * window.el (pop-to-buffer): Remove the conditional that compares new-window and old-window, so it will reselect the selected window unconditionally. - http://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00078.html + https://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00078.html 2010-06-07 Stefan Monnier @@ -9882,7 +9882,7 @@ of kill-ring: don't call menu-bar-update-yank-menu, don't push interprogram-paste strings to kill-ring, and don't push the input argument `string' to kill-ring. - http://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00072.html + https://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00072.html 2010-06-04 Juanma Barranquero @@ -10445,7 +10445,7 @@ * dired-x.el (dired-jump, dired-jump-other-window): Add arg FILE-NAME to read from the minibuffer when called interactively with prefix argument instead of using buffer-file-name. - http://lists.gnu.org/archive/html/emacs-devel/2010-05/msg00534.html + https://lists.gnu.org/archive/html/emacs-devel/2010-05/msg00534.html * dired.el: Update autoloads. @@ -11998,7 +11998,7 @@ 2010-04-05 Juri Linkov Scrolling commands which scroll a line instead of full screen. - http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01452.html + https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01452.html * simple.el (scroll-up-line, scroll-down-line): New commands. Put property isearch-scroll=t on them. @@ -12009,7 +12009,7 @@ 2010-04-05 Juri Linkov Scrolling commands which do not signal errors at top/bottom. - http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01452.html + https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01452.html * simple.el (scroll-up-command, scroll-down-command): New commands. Put property isearch-scroll=t on them. @@ -12063,7 +12063,7 @@ (electric-help-mode): Set it to original major-mode. Doc fix. (with-electric-help): Use `electric-help-orig-major-mode' instead of (default-value 'major-mode). Doc fix. - http://lists.gnu.org/archive/html/emacs-devel/2010-04/msg00069.html + https://lists.gnu.org/archive/html/emacs-devel/2010-04/msg00069.html 2010-04-02 Sam Steingold @@ -12089,13 +12089,13 @@ * simple.el (next-line, previous-line): Re-throw a signal with `signal' instead of using `ding'. - http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01432.html + https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01432.html 2010-03-31 Juri Linkov * simple.el (keyboard-escape-quit): Raise deselecting the active region higher than exiting the minibuffer. - http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00904.html + https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00904.html 2010-03-31 Juri Linkov @@ -12184,7 +12184,7 @@ 2010-03-30 Juri Linkov Make occur handle multi-line matches cleanly with context. - http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01280.html + https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01280.html * replace.el (occur-accumulate-lines): Add optional arg `pt'. (occur-engine): Add local variables `ret', `prev-after-lines', @@ -12379,7 +12379,7 @@ 2010-03-23 Juri Linkov Implement Occur multi-line matches. - http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01044.html + https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg01044.html * replace.el (occur): Doc fix. (occur-engine): Set `begpt' to the beginning of the first line. @@ -12456,7 +12456,7 @@ 2010-03-21 Juri Linkov Fix message of multi-line occur regexps and multi-buffer header lines. - http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00457.html + https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00457.html * replace.el (occur-1): Don't display regexp if it is longer than window-width. Use `query-replace-descr' to display regexp. @@ -12750,7 +12750,7 @@ 2010-03-10 Kim F. Storm Animated image API. - http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00211.html + https://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00211.html * image.el (image-animate-max-time): New defcustom. (image-animated-types): New defconst. @@ -13908,7 +13908,7 @@ positions by using `bookmark-bmenu-marks-width', instead of hardcoding. This fixes the `bookmark-bmenu-execute-deletions' bug reported here: - http://lists.gnu.org/archive/html/emacs-devel/2009-12/msg00819.html + https://lists.gnu.org/archive/html/emacs-devel/2009-12/msg00819.html From: Sun Yijiang To: emacs-devel {_AT_} gnu.org Subject: bookmark.el bug report @@ -19816,7 +19816,7 @@ * files.el (find-alternate-file): If the old buffer is modified and visiting a file, behave similarly to `kill-buffer' when killing it, thus reverting to the pre-1.878 behavior; see - http://lists.gnu.org/archive/html/emacs-devel/2009-09/msg00101.html + https://lists.gnu.org/archive/html/emacs-devel/2009-09/msg00101.html for discussion. Also, consult `buffer-file-name' as a variable not as a function, for consistency with the rest of the code. diff --git a/lisp/ChangeLog.16 b/lisp/ChangeLog.16 index 03e6115c57..f3d4874a1c 100644 --- a/lisp/ChangeLog.16 +++ b/lisp/ChangeLog.16 @@ -1371,7 +1371,7 @@ * frame.el (toggle-frame-maximized, toggle-frame-fullscreen): Use fullboth as an alias for fullscreen. Suggested by Jan Djärv in - . + . 2013-01-30 Stefan Monnier @@ -2422,7 +2422,7 @@ * epg.el: Support pinentry-curses. Suggested by Werner Koch in - . + . (epg-agent-file, epg-agent-mtime): New variable. (epg--start): Record the modified time of gpg-agent socket file, to restore Emacs frame after pinentry-curses termination. @@ -2448,7 +2448,7 @@ (toggle-frame-maximized): Rewrite and bind to M-. (toggle-frame-fullscreen): New command bound to instead of `toggle-frame-maximized'. - http://lists.gnu.org/archive/html/emacs-devel/2012-12/msg00703.html + https://lists.gnu.org/archive/html/emacs-devel/2012-12/msg00703.html 2012-12-27 Michael Albinus @@ -2686,7 +2686,7 @@ (isearch-insert-char-by-name): New command. * international/mule-cmds.el (read-char-by-name): Let-bind `enable-recursive-minibuffers' to t. - http://lists.gnu.org/archive/html/emacs-devel/2012-12/msg00234.html + https://lists.gnu.org/archive/html/emacs-devel/2012-12/msg00234.html 2012-12-15 Juri Linkov @@ -4831,7 +4831,7 @@ * progmodes/compile.el (compilation-error-regexp-alist-alist): Adjust the msft regexp to the output of Studio 2010, and move msft before edg-1. See the discussion on emacs-devel, - http://lists.gnu.org/archive/html/emacs-devel/2012-09/msg00579.html, + https://lists.gnu.org/archive/html/emacs-devel/2012-09/msg00579.html, for the details. 2012-10-14 Stefan Monnier @@ -5453,7 +5453,7 @@ * profiler.el (profiler-sampling-interval): Change default back to 1. See Stefan Monnier in - . + . 2012-10-01 Fabián Ezequiel Gallina @@ -6641,7 +6641,7 @@ search-whitespace-regexp if isearch-lax-whitespace or isearch-regexp-lax-whitespace is non-nil. (Info-mode): Don't set local variable search-whitespace-regexp. - http://lists.gnu.org/archive/html/emacs-devel/2012-08/msg00811.html + https://lists.gnu.org/archive/html/emacs-devel/2012-08/msg00811.html 2012-09-12 Stefan Monnier @@ -7037,7 +7037,7 @@ 2012-09-02 Juri Linkov Toggle whitespace matching mode with M-s SPC. - http://lists.gnu.org/archive/html/emacs-devel/2012-09/msg00008.html + https://lists.gnu.org/archive/html/emacs-devel/2012-09/msg00008.html * isearch.el (search-whitespace-regexp): Doc fix. Remove cons cell customization. @@ -8109,7 +8109,7 @@ * whitespace.el (whitespace-display-mappings): Use Unicode codepoints, instead of emacs-mule codepoints. See - http://lists.gnu.org/archive/html/help-gnu-emacs/2012-07/msg00366.html + https://lists.gnu.org/archive/html/help-gnu-emacs/2012-07/msg00366.html for the details. * files.el (file-truename): Don't skip symlink-chasing part on @@ -8164,7 +8164,7 @@ * international/mule-cmds.el: Create inactivate-current-input-method-function as an obsolete alias for deactivate-current-input-method-function. See Katsumi Yamaoka in - . + . 2012-08-01 Jay Belanger @@ -8513,7 +8513,7 @@ * startup.el (command-line): Don't display an empty user name in the error message about non-existent home directory, when init-file-user was set to an empty string. See - http://lists.gnu.org/archive/html/bug-gnu-emacs/2012-07/msg00835.html + https://lists.gnu.org/archive/html/bug-gnu-emacs/2012-07/msg00835.html for the details and context. 2012-07-22 Vincent Belaïche @@ -9419,7 +9419,7 @@ * calendar/calendar.el (calendar-exit): Don't try to delete or iconify last frame. See: - http://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00372.html + https://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00372.html 2012-06-25 Jim Diamond (tiny change) @@ -10430,7 +10430,7 @@ * descr-text.el (describe-char): Mention how to insert the character, if the current input method doesn't support it. See the discussion in this thread for the details: - http://lists.gnu.org/archive/html/emacs-devel/2012-05/msg00533.html. + https://lists.gnu.org/archive/html/emacs-devel/2012-05/msg00533.html. 2012-06-08 Sam Steingold @@ -13909,7 +13909,7 @@ Insert invisible LRM characters before each character in a keyboard layout cell, to prevent their reordering by bidi display engine. For details, see the discussion in - http://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00085.html. + https://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00085.html. 2012-03-08 Alan Mackenzie @@ -13927,7 +13927,7 @@ * international/quail.el (quail-help): Force bidi-paragraph-direction be left-to-right. See discussion in - http://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00062.html + https://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00062.html for the reason. 2012-03-07 Michael Albinus @@ -15004,7 +15004,7 @@ * descr-text.el (describe-char): Show the raw character, not only its display form at POS. Suggested by Kenichi Handa . - See http://lists.gnu.org/archive/html/emacs-devel/2012-01/msg00760.html + See https://lists.gnu.org/archive/html/emacs-devel/2012-01/msg00760.html for the reasons. 2012-01-28 Phil Hagelberg @@ -15362,7 +15362,7 @@ * time.el (display-time-load-average) (display-time-default-load-average): Doc fixes. See the thread starting at - http://lists.gnu.org/archive/html/help-gnu-emacs/2012-01/msg00059.html + https://lists.gnu.org/archive/html/help-gnu-emacs/2012-01/msg00059.html for the details. 2012-01-06 Glenn Morris @@ -15769,7 +15769,7 @@ (texinfo-insert-master-menu-list): Improve the error message displayed if there's no menu in the Top node. (Bug#2975) See also this thread: - http://lists.gnu.org/archive/html/emacs-devel/2011-12/msg00156.html. + https://lists.gnu.org/archive/html/emacs-devel/2011-12/msg00156.html. 2011-12-09 Manuel Gómez (tiny change) @@ -24381,7 +24381,7 @@ * help-fns.el (describe-variable): Complete all variables having documentation, including keywords. - http://lists.gnu.org/archive/html/emacs-devel/2011-04/msg00112.html + https://lists.gnu.org/archive/html/emacs-devel/2011-04/msg00112.html 2011-04-04 Juanma Barranquero diff --git a/lisp/ChangeLog.17 b/lisp/ChangeLog.17 index 789452351a..1bd882f0a2 100644 --- a/lisp/ChangeLog.17 +++ b/lisp/ChangeLog.17 @@ -3598,7 +3598,7 @@ * comint.el (comint-history-isearch-message): Use field-beginning instead of comint-line-beginning-position - that's more fixes for - http://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00305.html + https://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00305.html (comint-history-isearch-message): Fix args of isearch-message-prefix. 2014-12-29 Juri Linkov @@ -3698,7 +3698,7 @@ * language/misc-lang.el (composition-function-table): Add Syriac characters and also ZWJ/ZWNJ. - See http://lists.gnu.org/archive/html/help-gnu-emacs/2014-12/msg00248.html + See https://lists.gnu.org/archive/html/help-gnu-emacs/2014-12/msg00248.html for the details. 2014-12-27 Fabián Ezequiel Gallina @@ -4489,14 +4489,14 @@ comint-line-beginning-position. (comint-send-input): Go to the end of the field instead of the end of the line to accept whole multi-line input. - http://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00305.html + https://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00305.html 2014-12-05 Juri Linkov * minibuffer.el (minibuffer-completion-help): Compare selected-window with minibuffer-window to check whether completions should be displayed near the minibuffer. (Bug#17809) - http://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00311.html + https://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00311.html 2014-12-05 Michael Albinus @@ -4605,7 +4605,7 @@ the remote repository were unreachable, because the VC hooks tried to run "svn status -u" on the file, where the "-u" tells svn to get update information from the remote repository. - http://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00174.html + https://lists.gnu.org/archive/html/emacs-devel/2014-12/msg00174.html * vc/vc-svn.el (vc-svn-state): Remove optional `localp' argument and always pass "-v" to "svn status", never "-u". @@ -5306,7 +5306,7 @@ (query-replace-read-from): Call custom-reevaluate-setting on query-replace-from-to-separator to reevaluate the separator depending on the return value of char-displayable-p. - http://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00466.html + https://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00466.html 2014-11-18 Juri Linkov @@ -5316,7 +5316,7 @@ * simple.el (next-line-or-history-element) (previous-line-or-history-element): New commands. - http://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00822.html + https://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00822.html 2014-11-18 Leo Liu @@ -5441,7 +5441,7 @@ Improve time stamp handling, and be more consistent about it. This implements a suggestion made in: - http://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00587.html + https://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00587.html Among other things, this means timer.el no longer needs to autoload the time-date module. * allout-widgets.el (allout-elapsed-time-seconds): Doc fix. @@ -5682,7 +5682,7 @@ 2014-11-10 Sylvain Chouleur (tiny change) Allow VTIMEZONE where daylight and standard time zones are equal. - See: http://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00494.html + See: https://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00494.html * calendar/icalendar.el (icalendar--convert-tz-offset): Support timezone without daylight saving time. @@ -5813,7 +5813,7 @@ to the history variables. (query-replace-read-to): Add FROM-TO pairs to query-replace-defaults. (query-replace-regexp-eval): Let-bind query-replace-defaults to nil. - http://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00253.html + https://lists.gnu.org/archive/html/emacs-devel/2014-11/msg00253.html * isearch.el (isearch-text-char-description): Keep characters intact and put formatted strings with the `display' property. @@ -7565,7 +7565,7 @@ (lisp--form-quoted-p): New functions. (lisp-completion-at-point): Use them to see if we're completing a variable reference, a function name, or just any symbol. - http://lists.gnu.org/archive/html/emacs-devel/2014-02/msg00229.html + https://lists.gnu.org/archive/html/emacs-devel/2014-02/msg00229.html 2014-09-18 Ivan Kanis @@ -9937,7 +9937,7 @@ `window-configuration-change-hook'. (desktop-auto-save-set-timer): Change REPEAT arg of `run-with-idle-timer' from t to nil. - http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00147.html + https://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00147.html 2014-06-08 Santiago Payà i Miralta @@ -10488,7 +10488,7 @@ * emacs-lisp/package.el (package-generate-description-file): Output first-line comment to set buffer-local var `no-byte-compile'. Suggested by Dmitry Gutov: - . + . 2014-05-25 Thien-Thi Nguyen @@ -12170,7 +12170,7 @@ to `comment-start-skip' if not `comment-use-syntax'. (Bug#16971) (comment-beginning): Use `narrow-to-region' instead of moving back one character. - (http://lists.gnu.org/archive/html/emacs-devel/2014-03/msg00488.html) + (https://lists.gnu.org/archive/html/emacs-devel/2014-03/msg00488.html) (comment-start-skip): Update the docstring. 2014-03-18 Richard Stallman @@ -12467,7 +12467,7 @@ from `xterm-standard-colors' that look well on the default white background (and also on the black background) to avoid illegible color combinations like yellow-on-white and white-on-white. - http://lists.gnu.org/archive/html/emacs-devel/2014-02/msg00157.html + https://lists.gnu.org/archive/html/emacs-devel/2014-02/msg00157.html 2014-03-08 Juanma Barranquero @@ -13224,7 +13224,7 @@ 2014-02-12 Dmitry Gutov * progmodes/js.el (js-indent-line): Don't widen. - http://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00276.html + https://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00276.html 2014-02-12 Glenn Morris @@ -13965,7 +13965,7 @@ choices. (ruby-smie-rules): Instead of using a hardcoded list of alignable keywords, check against the value of `ruby-alignable-keywords' - (http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01439.html). + (https://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01439.html). 2014-01-17 Glenn Morris @@ -15408,7 +15408,7 @@ * simple.el (blink-matching--overlay): New variable. (blink-matching-open): Instead of moving point, highlight the matching paren with an overlay - (http://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00333.html). + (https://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00333.html). * faces.el (paren-showing-faces, show-paren-match) (show-paren-mismatch): Move from paren.el. @@ -16628,7 +16628,7 @@ * textmodes/ispell.el (ispell-lookup-words): When `look' is not available and the word has no wildcards, append one to the grep pattern. - http://lists.gnu.org/archive/html/emacs-devel/2013-11/msg00258.html + https://lists.gnu.org/archive/html/emacs-devel/2013-11/msg00258.html (ispell-complete-word): Call `ispell-lookup-words' with the value independent of `ispell-look-p'. @@ -18282,7 +18282,7 @@ * emacs-lisp/package.el (package-buffer-info, describe-package-1): Use :url instead of :homepage, as per - http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00622.html + https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00622.html * newcomment.el (comment-beginning): When `comment-use-syntax' is non-nil, use `syntax-ppss' (Bug#15251). @@ -21942,7 +21942,7 @@ 2013-06-25 Martin Rudalics * window.el (window--state-get-1): Workaround for bug#14527. - http://lists.gnu.org/archive/html/emacs-devel/2013-06/msg00941.html + https://lists.gnu.org/archive/html/emacs-devel/2013-06/msg00941.html 2013-06-25 Lars Magne Ingebrigtsen @@ -22055,7 +22055,7 @@ * progmodes/ruby-mode.el (ruby-font-lock-keywords): Move `catch', add some more keyword-like methods. - http://lists.gnu.org/archive/html/emacs-devel/2013-06/msg00911.html + https://lists.gnu.org/archive/html/emacs-devel/2013-06/msg00911.html 2013-06-22 Juanma Barranquero @@ -22674,7 +22674,7 @@ 2013-06-18 Matthias Meulien * tabify.el (untabify, tabify): With prefix, apply to entire buffer. - + 2013-06-18 Glenn Morris @@ -22704,7 +22704,7 @@ * emacs-lisp/package.el (package-load-descriptor): Remove `with-syntax-table' call, `read' doesn't need it. - http://lists.gnu.org/archive/html/emacs-devel/2013-06/msg00539.html + https://lists.gnu.org/archive/html/emacs-devel/2013-06/msg00539.html 2013-06-17 Juanma Barranquero @@ -25118,7 +25118,7 @@ (desktop-auto-save, desktop-auto-save-set-timer): New functions. (after-init-hook): Call `desktop-auto-save-set-timer'. Suggested by Reuben Thomas in - . + . 2013-04-27 Leo Liu @@ -25144,7 +25144,7 @@ * ls-lisp.el (ls-lisp-insert-directory): If no files are displayed, move point to after the totals line. - See http://lists.gnu.org/archive/html/emacs-devel/2013-04/msg00677.html + See https://lists.gnu.org/archive/html/emacs-devel/2013-04/msg00677.html for the details. 2013-04-27 Stefan Monnier @@ -25757,7 +25757,7 @@ Do not set x-display-name until X connection is established. This is needed to prevent from weird situation described at - . + . * frame.el (make-frame): Set x-display-name after call to window system initialization function, not before. * term/x-win.el (x-initialize-window-system): Add optional @@ -26079,7 +26079,7 @@ (batch-skkdic-convert): Suppress most of the chatter. It's not needed so much now that machines are faster, and its non-ASCII component was confusing; see Dmitry Gutov in - . + . 2013-03-20 Leo Liu @@ -26223,7 +26223,7 @@ * startup.el (command-line-normalize-file-name): Fix handling of backslashes in DOS and Windows file names. Reported by Xue Fuqiao in - http://lists.gnu.org/archive/html/help-gnu-emacs/2013-03/msg00245.html. + https://lists.gnu.org/archive/html/help-gnu-emacs/2013-03/msg00245.html. 2013-03-15 Michael Albinus diff --git a/lisp/Makefile.in b/lisp/Makefile.in index 913f937c57..b628444aad 100644 --- a/lisp/Makefile.in +++ b/lisp/Makefile.in @@ -132,13 +132,13 @@ PHONY_EXTRAS = # This could lead to problems in parallel builds if automatically # generated *.el files (eg loaddefs etc) were being changed at the same time. # One solution was to add autoloads as a prerequisite: -# http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-01/msg00469.html -# http://lists.gnu.org/archive/html/bug-gnu-emacs/2007-12/msg00171.html +# https://lists.gnu.org/archive/html/emacs-pretest-bug/2007-01/msg00469.html +# https://lists.gnu.org/archive/html/bug-gnu-emacs/2007-12/msg00171.html # However, this meant that running these targets modified loaddefs.el, # every time (due to time-stamping). Calling these rules from # bootstrap-after would modify loaddefs after src/emacs, resulting # in make install remaking src/emacs for no real reason: -# http://lists.gnu.org/archive/html/emacs-devel/2008-02/msg00311.html +# https://lists.gnu.org/archive/html/emacs-devel/2008-02/msg00311.html # Nowadays these commands don't scan automatically generated files, # since they will never contain any useful information # (see finder-no-scan-regexp and custom-dependencies-no-scan-regexp). diff --git a/lisp/calendar/cal-dst.el b/lisp/calendar/cal-dst.el index 659b6349bb..6a4054d73f 100644 --- a/lisp/calendar/cal-dst.el +++ b/lisp/calendar/cal-dst.el @@ -248,7 +248,7 @@ The result has the proper form for `calendar-daylight-savings-starts'." ;; TODO it might be better to extract this information directly from ;; the system timezone database. But cross-platform...? ;; See thread -;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2006-11/msg00060.html +;; https://lists.gnu.org/archive/html/emacs-pretest-bug/2006-11/msg00060.html (defun calendar-dst-find-data (&optional time) "Find data on the first daylight saving time transitions after TIME. TIME defaults to `current-time'. Return value is as described diff --git a/lisp/calendar/cal-menu.el b/lisp/calendar/cal-menu.el index 90cce2840c..32769332df 100644 --- a/lisp/calendar/cal-menu.el +++ b/lisp/calendar/cal-menu.el @@ -100,7 +100,7 @@ ;; Show 11 years--5 before, 5 after year of middle month. ;; We used to use :suffix rather than :label and bumped into ;; an easymenu bug: - ;; http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01813.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01813.html ;; The bug has since been fixed. (dotimes (i 11) (push (vector (format "hol-year-%d" i) diff --git a/lisp/calendar/diary-lib.el b/lisp/calendar/diary-lib.el index 520c730f5e..d8d2dd4aaf 100644 --- a/lisp/calendar/diary-lib.el +++ b/lisp/calendar/diary-lib.el @@ -425,7 +425,7 @@ Only used if `diary-header-line-flag' is non-nil." ;; display does not create the fancy buffer, nor does it set ;; diary-selective-display in the diary buffer. This means some ;; customizations will not take effect, eg: -;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-03/msg00466.html +;; https://lists.gnu.org/archive/html/emacs-pretest-bug/2007-03/msg00466.html ;; So the check for diary-selective-display was dropped. This means the ;; diary will be displayed if one customizes a diary variable while ;; just visiting the diary-file. This is i) unlikely, and ii) no great loss. @@ -814,8 +814,8 @@ LIST-ONLY is non-nil, in which case it just returns the list." ;; diary-header-line-flag after diary has been displayed ;; take effect. Unconditionally calling (diary-mode) ;; clobbers file local variables. - ;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-03/msg00363.html - ;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-04/msg00404.html + ;; https://lists.gnu.org/archive/html/emacs-pretest-bug/2007-03/msg00363.html + ;; https://lists.gnu.org/archive/html/emacs-pretest-bug/2007-04/msg00404.html (if (eq major-mode 'diary-mode) (setq header-line-format (and diary-header-line-flag diary-header-line-format))))) diff --git a/lisp/comint.el b/lisp/comint.el index 17f1ab4ca0..9bbb362d9c 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -678,7 +678,7 @@ Entry to this mode runs the hooks on `comint-mode-hook'." ;; comint-scroll-show-maximum-output is nil, and no-one can remember ;; what the original problem was. If there are problems with point ;; not going to the end, consider re-enabling this. - ;; http://lists.gnu.org/archive/html/emacs-devel/2007-08/msg00827.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2007-08/msg00827.html ;; ;; This makes it really work to keep point at the bottom. ;; (make-local-variable 'scroll-conservatively) diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el index 6b67555770..a87783850a 100644 --- a/lisp/cus-edit.el +++ b/lisp/cus-edit.el @@ -2799,7 +2799,7 @@ If STATE is nil, the value is computed by `custom-variable-state'." ;; init-file-user rather than user-init-file. This is in case ;; cus-edit is loaded by something in site-start.el, because ;; user-init-file is not set at that stage. - ;; http://lists.gnu.org/archive/html/emacs-devel/2007-10/msg00310.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2007-10/msg00310.html ,@(when (or custom-file init-file-user) '(("Save for Future Sessions" custom-variable-save (lambda (widget) diff --git a/lisp/dnd.el b/lisp/dnd.el index defd8a8e07..a0c91b1374 100644 --- a/lisp/dnd.el +++ b/lisp/dnd.el @@ -144,7 +144,7 @@ Return nil if URI is not a local file." str)) uri t t)) -;; http://lists.gnu.org/archive/html/emacs-devel/2006-05/msg01060.html +;; https://lists.gnu.org/archive/html/emacs-devel/2006-05/msg01060.html (defun dnd-get-local-file-name (uri &optional must-exist) "Return file name converted from file:/// or file: syntax. URI is the uri for the file. If MUST-EXIST is given and non-nil, diff --git a/lisp/emacs-lisp/lisp.el b/lisp/emacs-lisp/lisp.el index c3d62fd59b..c54828e7b4 100644 --- a/lisp/emacs-lisp/lisp.el +++ b/lisp/emacs-lisp/lisp.el @@ -402,7 +402,7 @@ is called as a function to find the defun's beginning." "Return non-nil if the point is in an \"emptyish\" line. This means a line that consists entirely of comments and/or whitespace." -;; See http://lists.gnu.org/archive/html/help-gnu-emacs/2016-08/msg00141.html +;; See https://lists.gnu.org/archive/html/help-gnu-emacs/2016-08/msg00141.html (save-excursion (forward-line 0) (< (line-end-position) diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index edba6550fa..5189cc4a6e 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -28,7 +28,7 @@ ;; in subr.el. ;; Do not document these functions in the lispref. -;; http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01006.html +;; https://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01006.html ;; NB If you want to use this library, it's almost always correct to use: ;; (eval-when-compile (require 'subr-x)) diff --git a/lisp/epg.el b/lisp/epg.el index 8a4696627e..407b0f5d5d 100644 --- a/lisp/epg.el +++ b/lisp/epg.el @@ -627,7 +627,7 @@ callback data (if any)." ;; Record modified time of gpg-agent socket to restore the Emacs ;; frame on text terminal in `epg-wait-for-completion'. ;; See - ;; + ;; ;; for more details. (when (and agent-info (string-match "\\(.*\\):[0-9]+:[0-9]+" agent-info)) (setq agent-file (match-string 1 agent-info) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index ab36371b9c..0e56b732d8 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -75,7 +75,7 @@ (require 'erc-compat) (defvar erc-official-location - "http://emacswiki.org/cgi-bin/wiki/ERC (mailing list: erc-discuss@gnu.org)" + "https://emacswiki.org/cgi-bin/wiki/ERC (mailing list: erc-discuss@gnu.org)" "Location of the ERC client on the Internet.") (defgroup erc nil diff --git a/lisp/eshell/em-unix.el b/lisp/eshell/em-unix.el index a86596953d..c486d2c51d 100644 --- a/lisp/eshell/em-unix.el +++ b/lisp/eshell/em-unix.el @@ -961,7 +961,7 @@ Show wall-clock time elapsed during execution of COMMAND.") ;; after setting (throw 'eshell-replace-command (eshell-parse-command (car time-args) -;;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2007-08/msg00205.html +;;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2007-08/msg00205.html (eshell-stringify-list (eshell-flatten-list (cdr time-args)))))))) diff --git a/lisp/faces.el b/lisp/faces.el index f85d31e6c8..24ab1fa4f0 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -2361,7 +2361,7 @@ If you set `term-file-prefix' to nil, this function does nothing." (defface variable-pitch '((((type w32)) ;; This is a workaround for an issue discussed in - ;; http://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00746.html. + ;; https://lists.gnu.org/archive/html/emacs-devel/2016-04/msg00746.html. ;; We need (a) the splash screen not to pick up bold-italics variant of ;; the font, and (b) still be able to request bold/italic/larger size ;; variants in the likes of EWW. diff --git a/lisp/ffap.el b/lisp/ffap.el index 2228aca081..810afd5739 100644 --- a/lisp/ffap.el +++ b/lisp/ffap.el @@ -786,7 +786,7 @@ This uses `ffap-file-exists-string', which may try adding suffixes from ("\\`~/" . ffap-lcd) ; |~/misc/ffap.el.Z| ;; This used to have a blank, but ffap-string-at-point doesn't ;; handle blanks. - ;; http://lists.gnu.org/archive/html/emacs-devel/2008-01/msg01058.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2008-01/msg01058.html ("\\`[Rr][Ff][Cc][-#]?\\([0-9]+\\)" ; no $ . ffap-rfc) ; "100% RFC2100 compliant" (dired-mode . ffap-dired) ; maybe in a subdirectory diff --git a/lisp/finder.el b/lisp/finder.el index b599c440fa..ab37ed7fc2 100644 --- a/lisp/finder.el +++ b/lisp/finder.el @@ -129,7 +129,7 @@ Keywords and package names both should be symbols.") ;; Skip autogenerated files, because they will never contain anything ;; useful, and because in parallel builds of Emacs they may get ;; modified while we are trying to read them. -;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-01/msg00469.html +;; https://lists.gnu.org/archive/html/emacs-pretest-bug/2007-01/msg00469.html ;; ldefs-boot is not auto-generated, but has nothing useful. (defvar finder-no-scan-regexp "\\(^\\.#\\|\\(loaddefs\\|ldefs-boot\\|\ cus-load\\|finder-inf\\|esh-groups\\|subdirs\\|leim-list\\)\\.el$\\)" diff --git a/lisp/frame.el b/lisp/frame.el index 76c1842455..e501daefb4 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -1071,7 +1071,7 @@ is given and non-nil, the unwanted frames are iconified instead." (when mini (setq parms (delq mini parms))) ;; Leave name in iff it was set explicitly. ;; This should fix the behavior reported in - ;; http://lists.gnu.org/archive/html/emacs-devel/2007-08/msg01632.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2007-08/msg01632.html (when (and name (not explicit-name)) (setq parms (delq name parms))) parms)) diff --git a/lisp/gnus/ChangeLog.3 b/lisp/gnus/ChangeLog.3 index 0fcb4a08e3..0d4b268f16 100644 --- a/lisp/gnus/ChangeLog.3 +++ b/lisp/gnus/ChangeLog.3 @@ -3819,7 +3819,7 @@ 2012-02-15 Paul Eggert * shr.el (shr-rescale-image): Undo previous change; see - . + . 2012-02-13 Lars Ingebrigtsen diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el index a9e66cede1..ed0b3cb44f 100644 --- a/lisp/gnus/message.el +++ b/lisp/gnus/message.el @@ -4094,7 +4094,7 @@ Instead, just auto-save the buffer and then bury it." "Bury this mail BUFFER." ;; Note that this is not quite the same as (bury-buffer buffer), ;; since bury-buffer does extra stuff with a nil argument. - ;; Eg http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg00539.html + ;; Eg https://lists.gnu.org/archive/html/emacs-devel/2014-01/msg00539.html (with-current-buffer buffer (bury-buffer)) (if message-return-action (apply (car message-return-action) (cdr message-return-action)))) @@ -6678,7 +6678,7 @@ is a function used to switch to and display the mail buffer." ;; C-h f compose-mail says that headers should be specified as ;; (string . value); however all the rest of message expects ;; headers to be symbols, not strings (eg message-header-format-alist). - ;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html ;; We need to convert any string input, eg from rmail-start-mail. (dolist (h other-headers other-headers) (if (stringp (car h)) (setcar h (intern (capitalize (car h))))))) diff --git a/lisp/info.el b/lisp/info.el index 993dc079a8..6f87adb04e 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -1347,7 +1347,7 @@ is non-nil)." ;; Shouldn't really happen, but sometimes does, ;; eg on Debian systems with buggy packages; ;; so may as well try it. - ;; http://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00005.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00005.html (progn (setq file (expand-file-name "dir.gz" truename)) (file-attributes file))))) (setq dirs-done diff --git a/lisp/international/quail.el b/lisp/international/quail.el index 448ea8e057..f2c7bcb1f7 100644 --- a/lisp/international/quail.el +++ b/lisp/international/quail.el @@ -2513,7 +2513,7 @@ package to describe." (setq buffer-read-only nil) ;; Without this, a keyboard layout with R2L characters might be ;; displayed reversed, right to left. See the thread starting at - ;; http://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00062.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00062.html ;; for a description of one such situation. (setq bidi-paragraph-direction 'left-to-right) (insert "Input method: " (quail-name) diff --git a/lisp/mail/emacsbug.el b/lisp/mail/emacsbug.el index 7bd9078342..dc25e8005c 100644 --- a/lisp/mail/emacsbug.el +++ b/lisp/mail/emacsbug.el @@ -179,7 +179,7 @@ Prompts for bug subject. Leaves you in a mail buffer." 'face 'link 'help-echo (concat "mouse-2, RET: Follow this link") 'action (lambda (button) - (browse-url "http://lists.gnu.org/archive/html/bug-gnu-emacs/")) + (browse-url "https://lists.gnu.org/archive/html/bug-gnu-emacs/")) 'follow-link t) (insert " mailing list\nand the GNU bug tracker at ") (insert-text-button diff --git a/lisp/mail/rmail.el b/lisp/mail/rmail.el index 12b1191e98..1a24d87add 100644 --- a/lisp/mail/rmail.el +++ b/lisp/mail/rmail.el @@ -278,7 +278,7 @@ Otherwise, look for `movemail' in the directories in ;; rmail-insert-inbox-text before r1.439 fell back to using ;; (expand-file-name "movemail" exec-directory) and just ;; assuming it would work. - ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2008-02/msg00087.html + ;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2008-02/msg00087.html (let ((progname (expand-file-name (concat "movemail" (if (memq system-type '(ms-dos windows-nt)) @@ -534,7 +534,7 @@ still the current message in the Rmail buffer.") ;; It's not clear what it should do now, since there is nothing that ;; records when a message is shown for the first time (unseen is not ;; necessarily the same thing). -;; See http://lists.gnu.org/archive/html/emacs-devel/2009-03/msg00013.html +;; See https://lists.gnu.org/archive/html/emacs-devel/2009-03/msg00013.html (defcustom rmail-message-filter nil "If non-nil, a filter function for new messages in RMAIL. Called with region narrowed to the message, including headers, diff --git a/lisp/mail/supercite.el b/lisp/mail/supercite.el index c0dd7aaf59..0cb8a1791f 100644 --- a/lisp/mail/supercite.el +++ b/lisp/mail/supercite.el @@ -713,7 +713,7 @@ the list should be unique." ;; regi functions -;; http://lists.gnu.org/archive/html/emacs-devel/2009-02/msg00691.html +;; https://lists.gnu.org/archive/html/emacs-devel/2009-02/msg00691.html ;; When rmail replies to a message with full headers visible, the "From " ;; line can be included. (defun sc-mail-check-from () diff --git a/lisp/net/rlogin.el b/lisp/net/rlogin.el index ab7135af56..297aa0230c 100644 --- a/lisp/net/rlogin.el +++ b/lisp/net/rlogin.el @@ -38,7 +38,7 @@ ;; FIXME? ;; Maybe this file should be obsolete. -;; http://lists.gnu.org/archive/html/emacs-devel/2013-02/msg00517.html +;; https://lists.gnu.org/archive/html/emacs-devel/2013-02/msg00517.html ;; It only adds rlogin-directory-tracking-mode. Is that useful? (require 'comint) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 3573eeb7d4..e253db0883 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -45,7 +45,7 @@ ;; There's a mailing list for this, as well. Its name is: ;; tramp-devel@gnu.org ;; You can use the Web to subscribe, under the following URL: -;; http://lists.gnu.org/mailman/listinfo/tramp-devel +;; https://lists.gnu.org/mailman/listinfo/tramp-devel ;; ;; For the adventurous, the current development sources are available ;; via Git. You can find instructions about this at the following URL: @@ -4642,7 +4642,7 @@ Only works for Bourne-like shells." ;; are. (Andrea Crotti) ;; ;; * Run emerge on two remote files. Bug is described here: -;; . +;; . ;; (Bug#6850) ;; ;; * Refactor code from different handlers. Start with diff --git a/lisp/obsolete/complete.el b/lisp/obsolete/complete.el index 7b33de9f60..40123ccaca 100644 --- a/lisp/obsolete/complete.el +++ b/lisp/obsolete/complete.el @@ -924,7 +924,7 @@ or properties are considered." (or (boundp sym) (fboundp sym) (symbol-plist sym)))))) (PC-not-minibuffer t)) - ;; http://lists.gnu.org/archive/html/emacs-devel/2007-03/msg01211.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2007-03/msg01211.html ;; ;; This deals with cases like running PC-l-c-s on "M-: (n-f". ;; The first call to PC-l-c-s expands this to "(ne-f", and moves diff --git a/lisp/org/ChangeLog.1 b/lisp/org/ChangeLog.1 index 7e27fb6b7e..ee5f01479a 100644 --- a/lisp/org/ChangeLog.1 +++ b/lisp/org/ChangeLog.1 @@ -11560,7 +11560,7 @@ break after the last footnote definition. This is an an implicit assumption made by the org-lparse.el library. With this change, footnote definitions can reliably be exported with ODT backend. - See http://lists.gnu.org/archive/html/emacs-orgmode/2012-02/msg01013.html. + See https://lists.gnu.org/archive/html/emacs-orgmode/2012-02/msg01013.html. 2012-04-01 Eric Schulte diff --git a/lisp/proced.el b/lisp/proced.el index f5ea10b8ad..c9e851b7e0 100644 --- a/lisp/proced.el +++ b/lisp/proced.el @@ -1803,7 +1803,7 @@ supported but discouraged. It will be removed in a future version of Emacs." (let (failures) ;; Why not always use `signal-process'? See - ;; http://lists.gnu.org/archive/html/emacs-devel/2008-03/msg02955.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2008-03/msg02955.html (if (functionp proced-signal-function) ;; use built-in `signal-process' (let ((signal (if (stringp signal) diff --git a/lisp/progmodes/cc-menus.el b/lisp/progmodes/cc-menus.el index 6746651f18..9339dcd83c 100644 --- a/lisp/progmodes/cc-menus.el +++ b/lisp/progmodes/cc-menus.el @@ -117,7 +117,7 @@ A sample value might look like: `\\(_P\\|_PROTO\\)'.") ,(concat "^\\<" ; line MUST start with word char ;; \n added to prevent overflow in regexp matcher. - ;; http://lists.gnu.org/archive/html/emacs-pretest-bug/2007-02/msg00021.html + ;; https://lists.gnu.org/archive/html/emacs-pretest-bug/2007-02/msg00021.html "[^()\n]*" ; no parentheses before "[^" c-alnum "_:<>~]" ; match any non-identifier char "\\([" c-alpha "_][" c-alnum "_:<>~]*\\)" ; match function name diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el index b3d090382d..e4b77ab050 100644 --- a/lisp/progmodes/compile.el +++ b/lisp/progmodes/compile.el @@ -1740,7 +1740,7 @@ Returns the compilation buffer created." (setq thisdir default-directory)) (set-buffer-modified-p nil)) ;; Pop up the compilation buffer. - ;; http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01638.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01638.html (setq outwin (display-buffer outbuf '(nil (allow-no-window . t)))) (with-current-buffer outbuf (let ((process-environment @@ -2855,7 +2855,7 @@ TRUE-DIRNAME is the `file-truename' of DIRNAME, if given." ;; The gethash used to not use spec-directory, but ;; this leads to errors when files in different ;; directories have the same name: - ;; http://lists.gnu.org/archive/html/emacs-devel/2007-08/msg00463.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2007-08/msg00463.html (or (gethash (cons filename spec-directory) compilation-locs) (puthash (cons filename spec-directory) (compilation--make-file-struct diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el index 7e91201784..103b6ce7c5 100644 --- a/lisp/progmodes/octave.el +++ b/lisp/progmodes/octave.el @@ -1497,7 +1497,7 @@ current buffer file unless called with a prefix arg \\[universal-argument]." (string (buffer-substring-no-properties beg end)) line) (with-current-buffer inferior-octave-buffer - ;; http://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00095.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00095.html (compilation-forget-errors) (setq inferior-octave-output-list nil) (while (not (string-equal string "")) diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el index b8ce326f17..db88563a3e 100644 --- a/lisp/progmodes/sql.el +++ b/lisp/progmodes/sql.el @@ -28,7 +28,7 @@ ;; Please send bug reports and bug fixes to the mailing list at ;; help-gnu-emacs@gnu.org. If you want to subscribe to the mailing ;; list, see the web page at -;; http://lists.gnu.org/mailman/listinfo/help-gnu-emacs for +;; https://lists.gnu.org/mailman/listinfo/help-gnu-emacs for ;; instructions. I monitor this list actively. If you send an e-mail ;; to Alex Schroeder it usually makes it to me when Alex has a chance ;; to forward them along (Thanks, Alex). diff --git a/lisp/ruler-mode.el b/lisp/ruler-mode.el index bfe205923e..7a23f4d03c 100644 --- a/lisp/ruler-mode.el +++ b/lisp/ruler-mode.el @@ -702,7 +702,7 @@ Optional argument PROPS specifies other text properties to apply." ;; FIXME: `make-string' returns a unibyte string if it's ASCII-only, ;; which prevents further `aset' from inserting non-ASCII chars, ;; hence the need for `string-to-multibyte'. - ;; http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00841.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00841.html (string-to-multibyte ;; Make the part of header-line corresponding to the ;; line-number display be blank, not filled with diff --git a/lisp/simple.el b/lisp/simple.el index 767a3f041e..5ef511ce0a 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -8813,7 +8813,7 @@ If it does not exist, create and it switch it to `messages-buffer-mode'." ;; rms says this should be done by specifying symbols that define ;; versions together with bad values. This is therefore not as ;; flexible as it could be. See the thread: -;; http://lists.gnu.org/archive/html/emacs-devel/2007-08/msg00300.html +;; https://lists.gnu.org/archive/html/emacs-devel/2007-08/msg00300.html (defconst bad-packages-alist ;; Not sure exactly which semantic versions have problems. ;; Definitely 2.0pre3, probably all 2.0pre's before this. diff --git a/lisp/startup.el b/lisp/startup.el index 7cf6fee425..4b538d130e 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -513,7 +513,7 @@ It is the default value of the variable `top-level'." (let ((default-directory dir)) (load (expand-file-name "subdirs.el") t t t)) ;; Do not scan standard directories that won't contain a leim-list.el. - ;; http://lists.gnu.org/archive/html/emacs-devel/2009-10/msg00502.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2009-10/msg00502.html ;; (Except the preloaded one in lisp/leim.) (or (string-prefix-p lispdir dir) (let ((default-directory dir)) @@ -1371,7 +1371,7 @@ the `--debug-init' option to view a complete error backtrace." ;; trying to load gnus could load the wrong file. ;; OK, it would not matter if .emacs.d were at the end of load-path. ;; but for the sake of simplicity, we discourage it full-stop. - ;; Ref eg http://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00056.html + ;; Ref eg https://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00056.html ;; ;; A bad element could come from user-emacs-file, the command line, ;; or EMACSLOADPATH, so we basically always have to check. diff --git a/lisp/subr.el b/lisp/subr.el index 64479a4b5b..d2fefe04f8 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -578,7 +578,7 @@ one is kept." (setq tail (cdr tail)))))) list) -;; See http://lists.gnu.org/archive/html/emacs-devel/2013-05/msg00204.html +;; See https://lists.gnu.org/archive/html/emacs-devel/2013-05/msg00204.html (defun delete-consecutive-dups (list &optional circular) "Destructively remove `equal' consecutive duplicates from LIST. First and last elements are considered consecutive if CIRCULAR is @@ -2431,7 +2431,7 @@ in milliseconds; this was useful when Emacs was built without floating point support." (declare (advertised-calling-convention (seconds &optional nodisp) "22.1")) ;; This used to be implemented in C until the following discussion: - ;; http://lists.gnu.org/archive/html/emacs-devel/2006-07/msg00401.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2006-07/msg00401.html ;; Then it was moved here using an implementation based on an idle timer, ;; which was then replaced by the use of read-event. (if (numberp nodisp) @@ -3103,7 +3103,7 @@ Do nothing if FACE is nil." (put-text-property start end 'face face))) ;; This removes `mouse-face' properties in *Help* buffer buttons: -;; http://lists.gnu.org/archive/html/emacs-devel/2002-04/msg00648.html +;; https://lists.gnu.org/archive/html/emacs-devel/2002-04/msg00648.html (defun yank-handle-category-property (category start end) "Apply property category CATEGORY's properties between START and END." (when category @@ -4218,7 +4218,7 @@ Used from `delayed-warnings-hook' (which see)." (setq delayed-warnings-list (nreverse collapsed)))) ;; At present this is only used for Emacs internals. -;; Ref http://lists.gnu.org/archive/html/emacs-devel/2012-02/msg00085.html +;; Ref https://lists.gnu.org/archive/html/emacs-devel/2012-02/msg00085.html (defvar delayed-warnings-hook '(collapse-delayed-warnings display-delayed-warnings) "Normal hook run to process and display delayed warnings. @@ -5230,7 +5230,7 @@ or \"gnus-article-toto-\".") ;; The following statement ought to be in print.c, but `provide' can't ;; be used there. -;; http://lists.gnu.org/archive/html/emacs-devel/2009-08/msg00236.html +;; https://lists.gnu.org/archive/html/emacs-devel/2009-08/msg00236.html (when (hash-table-p (car (read-from-string (prin1-to-string (make-hash-table))))) (provide 'hashtable-print-readable)) diff --git a/lisp/term/ns-win.el b/lisp/term/ns-win.el index 16633792e4..b8d1a43690 100644 --- a/lisp/term/ns-win.el +++ b/lisp/term/ns-win.el @@ -594,7 +594,7 @@ the last file dropped is selected." (declare-function tool-bar-mode "tool-bar" (&optional arg)) ;; Based on a function by David Reitter ; -;; see http://lists.gnu.org/archive/html/emacs-devel/2005-09/msg00681.html . +;; see https://lists.gnu.org/archive/html/emacs-devel/2005-09/msg00681.html . (defun ns-toggle-toolbar (&optional frame) "Switches the tool bar on and off in frame FRAME. If FRAME is nil, the change applies to the selected frame." @@ -878,7 +878,7 @@ See the documentation of `create-fontset-from-fontset-spec' for the format.") ;; Mac OS X Lion introduces PressAndHold, which is unsupported by this port. ;; See this thread for more details: - ;; http://lists.gnu.org/archive/html/emacs-devel/2011-06/msg00505.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2011-06/msg00505.html (ns-set-resource nil "ApplePressAndHoldEnabled" "NO") (x-apply-session-resources) diff --git a/lisp/term/x-win.el b/lisp/term/x-win.el index b769444671..62200bf2cb 100644 --- a/lisp/term/x-win.el +++ b/lisp/term/x-win.el @@ -1287,7 +1287,7 @@ This returns an error if any Emacs frames are X frames." ;; During initialization, we defer sending size hints to the window ;; manager, because that can induce a race condition: - ;; http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html ;; Send the size hints once initialization is done. (add-hook 'after-init-hook 'x-wm-set-size-hint) diff --git a/lisp/textmodes/nroff-mode.el b/lisp/textmodes/nroff-mode.el index ce4c155f52..eb7068f3d0 100644 --- a/lisp/textmodes/nroff-mode.el +++ b/lisp/textmodes/nroff-mode.el @@ -195,7 +195,7 @@ Puts a full-stop before comments on a line by themselves." 9) 8)))))) ; add 9 to ensure at least two blanks (goto-char pt)))) -;; http://lists.gnu.org/archive/html/emacs-devel/2007-10/msg01869.html +;; https://lists.gnu.org/archive/html/emacs-devel/2007-10/msg01869.html (defun nroff-insert-comment-function () "Function for `comment-insert-comment-function' in `nroff-mode'." (indent-to (nroff-comment-indent)) diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el index 5c4d540f7a..948743e8e5 100644 --- a/lisp/textmodes/tex-mode.el +++ b/lisp/textmodes/tex-mode.el @@ -1775,7 +1775,7 @@ Mark is left at original location." ;; Note this does not handle things like mismatched brackets inside ;; begin/end blocks. ;; Needs to handle escaped parens for tex-validate-*. -;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2007-09/msg00038.html +;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2007-09/msg00038.html ;; Does not handle escaped parens when latex-handle-escaped-parens is nil. (defun latex-forward-sexp-1 () "Like (forward-sexp 1) but aware of multi-char elements and escaped parens." diff --git a/lisp/url/ChangeLog.1 b/lisp/url/ChangeLog.1 index eb7982916c..5d6a68e563 100644 --- a/lisp/url/ChangeLog.1 +++ b/lisp/url/ChangeLog.1 @@ -2403,7 +2403,7 @@ (file-symlink-p): Ditto. (url-insert-file-contents): If `visit' is non-nil then make sure we set buffer-file-name. After these changes you can visit - http://www.gnu.org/ directly from the minibuffer. + https://www.gnu.org/ directly from the minibuffer. (url-insert-file-contents): When inserting the file contents, use a save-excursion so that we behave just like the original. diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index 1e835f6f37..9d56ed256a 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -432,7 +432,7 @@ and the face `diff-added' for added lines.") "If non-nil, empty lines are valid in unified diffs. Some versions of diff replace all-blank context lines in unified format with empty lines. This makes the format less robust, but is tolerated. -See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html") +See https://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html") (defconst diff-hunk-header-re (concat "^\\(?:" diff-hunk-header-re-unified ".*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$")) diff --git a/lisp/vc/smerge-mode.el b/lisp/vc/smerge-mode.el index 91be89b5dc..34fea232fe 100644 --- a/lisp/vc/smerge-mode.el +++ b/lisp/vc/smerge-mode.el @@ -1094,7 +1094,7 @@ used to replace chars to try and eliminate some spurious differences." ;; also and more importantly because otherwise it ;; may happen that diff doesn't behave like ;; smerge-refine-weight-hack expects it to. - ;; See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg00401.html + ;; See https://lists.gnu.org/archive/html/emacs-devel/2007-11/msg00401.html "-awd" "-ad") file1 file2)) ;; Process diff's output. diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 9d7a4d49b8..ed85603f82 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -978,7 +978,7 @@ This prompts for a branch to merge from." ;; FIXME ;; 1) the net result is to call git twice per file. ;; 2) v-g-c-f is documented to take a directory. - ;; http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01126.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01126.html (vc-git-conflicted-files buffer-file-name) (save-excursion (goto-char (point-min)) diff --git a/lisp/vc/vc-rcs.el b/lisp/vc/vc-rcs.el index 9dffc144c6..db1addbecd 100644 --- a/lisp/vc/vc-rcs.el +++ b/lisp/vc/vc-rcs.el @@ -849,7 +849,7 @@ and CVS." ;; You might think that this should be distributed with RCS, but ;; apparently not. CVS sometimes provides a version of it. -;; http://lists.gnu.org/archive/html/emacs-devel/2014-05/msg00288.html +;; https://lists.gnu.org/archive/html/emacs-devel/2014-05/msg00288.html (defvar vc-rcs-rcs2log-program (let (exe) (cond ((file-executable-p diff --git a/lisp/view.el b/lisp/view.el index 2d26a11a81..8e40cffce2 100644 --- a/lisp/view.el +++ b/lisp/view.el @@ -982,7 +982,7 @@ for highlighting the match that is found." ;; This is the dumb approach, looking at each line. The original ;; version of this function looked like it might have been trying to ;; do something clever, but not succeeding: -;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2007-09/msg00073.html +;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2007-09/msg00073.html (defun view-search-no-match-lines (times regexp) "Search for the TIMESth occurrence of a line with no match for REGEXP. If such a line is found, return non-nil and set the match-data to that line. diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el index f071c402c0..dca06ca359 100644 --- a/lisp/wid-edit.el +++ b/lisp/wid-edit.el @@ -832,7 +832,7 @@ button end points." ;; This alias exists only so that one can choose in doc-strings (e.g. ;; Custom-mode) which key-binding of widget-keymap one wants to refer to. -;; http://lists.gnu.org/archive/html/emacs-devel/2008-11/msg00480.html +;; https://lists.gnu.org/archive/html/emacs-devel/2008-11/msg00480.html (define-obsolete-function-alias 'advertised-widget-backward 'widget-backward "23.2") diff --git a/lisp/windmove.el b/lisp/windmove.el index 14656c98d1..b573000fd7 100644 --- a/lisp/windmove.el +++ b/lisp/windmove.el @@ -425,7 +425,7 @@ supplied, if ARG is greater or smaller than zero, respectively." top-left ;; Don't care whether window is horizontally scrolled - ;; `posn-at-point' handles that already. See also: - ;; http://lists.gnu.org/archive/html/emacs-devel/2012-01/msg00638.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2012-01/msg00638.html (posn-col-row (posn-at-point (window-point window) window)))))))) diff --git a/nt/ChangeLog.1 b/nt/ChangeLog.1 index adfdea286f..7502156b1f 100644 --- a/nt/ChangeLog.1 +++ b/nt/ChangeLog.1 @@ -297,8 +297,8 @@ to avoid gnulib replacement of 'struct timeval' and the resulting compilation of lib/gettimeofday.c with incompatible version of gettimeofday. Related discussions on emacs-devel: - http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00286.html - http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00361.html + https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00286.html + https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00361.html 2013-08-31 Glenn Morris @@ -373,7 +373,7 @@ * inc/sys/time.h (gettimeofday): Use '__restrict' instead of 'restrict', which is a C99 extension. See - http://lists.gnu.org/archive/html/emacs-devel/2013-05/msg00588.html + https://lists.gnu.org/archive/html/emacs-devel/2013-05/msg00588.html and the following discussion for the problem this caused in the old nt/configure.bat build. @@ -524,10 +524,10 @@ Fix more incompatibilities between MinGW.org and MinGW64 headers reported by Óscar Fuentes in - http://lists.gnu.org/archive/html/emacs-devel/2013-03/msg00733.html - http://lists.gnu.org/archive/html/emacs-devel/2013-03/msg00699.html + https://lists.gnu.org/archive/html/emacs-devel/2013-03/msg00733.html + https://lists.gnu.org/archive/html/emacs-devel/2013-03/msg00699.html and in - http://lists.gnu.org/archive/html/emacs-devel/2013-03/msg00707.html. + https://lists.gnu.org/archive/html/emacs-devel/2013-03/msg00707.html. * inc/ms-w32.h (USE_NO_MINGW_SETJMP_TWO_ARGS) [_W64]: Define to 1. For MinGW64, include sys/types.h and time.h. (_WIN32_WINNT) [!_W64]: Don't define for MinGW64. @@ -548,7 +548,7 @@ Fix incompatibilities between MinGW.org and MinGW64 headers reported by Óscar Fuentes in - http://lists.gnu.org/archive/html/emacs-devel/2013-03/msg00611.html. + https://lists.gnu.org/archive/html/emacs-devel/2013-03/msg00611.html. * inc/ms-w32.h (struct timespec): Don't define if _TIMESPEC_DEFINED is already defined. (sigset_t) [!_POSIX]: Typedef for MinGW64. @@ -724,7 +724,7 @@ 2012-11-21 Eli Zaretskii * nmake.defs: Use !if, not !ifdef. For the details, see - http://lists.gnu.org/archive/html/help-emacs-windows/2012-11/msg00027.html + https://lists.gnu.org/archive/html/help-emacs-windows/2012-11/msg00027.html * inc/stdint.h (INTPTR_MIN) (PTRDIFF_MIN) [!__GNUC__]: Define for MSVC. @@ -1184,7 +1184,7 @@ (dist): Depend on it. (install-shortcuts): Depend on install-addpm instead of copying addpm.exe as part of the recipe. See - http://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00171.html + https://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00171.html for the related problem and discussions. 2012-06-11 Glenn Morris diff --git a/nt/INSTALL b/nt/INSTALL index 662a30406f..30e14293f5 100644 --- a/nt/INSTALL +++ b/nt/INSTALL @@ -630,7 +630,7 @@ build will run on Windows 9X and newer systems). To support XPM images (required for color tool-bar icons), you will need the libXpm library. It is available from the ezwinports site, http://sourceforge.net/projects/ezwinports/files/ and from - http://ftp.gnu.org/gnu/emacs/windows/. + https://ftp.gnu.org/gnu/emacs/windows/. For PNG images, we recommend to use versions 1.4.x and later of libpng, because previous versions had security issues. You can find @@ -653,7 +653,7 @@ build will run on Windows 9X and newer systems). giflib, as it is much enhanced wrt previous versions. You can find precompiled binaries and headers for giflib on the ezwinports site, http://sourceforge.net/projects/ezwinports/files/ and on - http://ftp.gnu.org/gnu/emacs/windows/. + https://ftp.gnu.org/gnu/emacs/windows/. Version 5.0.0 and later of giflib are binary incompatible with previous versions (the signatures of several functions have @@ -694,7 +694,7 @@ build will run on Windows 9X and newer systems). because the compiler needs to see their header files when building Emacs. - http://ftp.gnu.org/gnu/emacs/windows/ + https://ftp.gnu.org/gnu/emacs/windows/ More fat ports, from the MSYS2 project. @@ -755,7 +755,7 @@ build will run on Windows 9X and newer systems). You can get pre-built binaries (including any required DLL and the header files) at http://sourceforge.net/projects/ezwinports/files/ - and on http://ftp.gnu.org/gnu/emacs/windows/. + and on https://ftp.gnu.org/gnu/emacs/windows/. * Optional libxml2 support @@ -777,7 +777,7 @@ build will run on Windows 9X and newer systems). (including any required DLL and the header files) is here: http://sourceforge.net/projects/ezwinports/files/ - http://ftp.gnu.org/gnu/emacs/windows/ + https://ftp.gnu.org/gnu/emacs/windows/ For runtime support of libxml2, you will also need to install the libiconv "development" tarball, because the libiconv headers need to diff --git a/nt/INSTALL.W64 b/nt/INSTALL.W64 index 6ebc1641de..841660bf0f 100644 --- a/nt/INSTALL.W64 +++ b/nt/INSTALL.W64 @@ -101,7 +101,7 @@ Savannah Emacs site, https://savannah.gnu.org/projects/emacs. ** From the FTP site -The Emacs ftp site is located at http://ftp.gnu.org/gnu/emacs/ - download the +The Emacs ftp site is located at https://ftp.gnu.org/gnu/emacs/ - download the version you want to build and put the file into a location like C:\emacs\, then uncompress it with tar. This will put the Emacs source into a folder like C:\emacs\emacs-24.5: diff --git a/nt/README b/nt/README index f4cca8efe7..c4d5b95340 100644 --- a/nt/README +++ b/nt/README @@ -92,12 +92,12 @@ issues related to the Windows port of Emacs. For information about the list, see this Web page: - http://mail.gnu.org/mailman/listinfo/help-emacs-windows + https://mail.gnu.org/mailman/listinfo/help-emacs-windows To ask questions on the mailing list, send email to help-emacs-windows@gnu.org. (You don't need to subscribe for that.) To subscribe to the list or unsubscribe from it, fill the form you - find at http://mail.gnu.org/mailman/listinfo/help-emacs-windows as + find at https://mail.gnu.org/mailman/listinfo/help-emacs-windows as explained there. Another valuable source of information and help which should not be diff --git a/nt/README.W32 b/nt/README.W32 index 89647588f4..0a15965f46 100644 --- a/nt/README.W32 +++ b/nt/README.W32 @@ -280,7 +280,7 @@ See the end of the file for license conditions. In addition to the manual, there is a mailing list for help with Emacs here: - http://lists.gnu.org/mailman/listinfo/help-gnu-emacs + https://lists.gnu.org/mailman/listinfo/help-gnu-emacs To ask questions on this mailing list, send email to help-gnu-emacs@gnu.org. @@ -288,7 +288,7 @@ See the end of the file for license conditions. A mailing list for issues specifically related to the MS-Windows port of Emacs is here: - http://lists.gnu.org/mailman/listinfo/help-emacs-windows + https://lists.gnu.org/mailman/listinfo/help-emacs-windows To ask questions on this mailing list, send email to help-emacs-windows@gnu.org. diff --git a/oldXMenu/ChangeLog.1 b/oldXMenu/ChangeLog.1 index 8ac7e184a7..2bc61ac4a8 100644 --- a/oldXMenu/ChangeLog.1 +++ b/oldXMenu/ChangeLog.1 @@ -67,7 +67,7 @@ * XLookAssoc.c, XMenuInt.h: Include . This avoids a build failure when configuring on Fedora 17 --with-x-toolkit=no, reported by Dmitry Andropov in - . + . 2012-10-06 Ulrich Müller diff --git a/src/ChangeLog.11 b/src/ChangeLog.11 index eb1aeb1eea..b26e54cd53 100644 --- a/src/ChangeLog.11 +++ b/src/ChangeLog.11 @@ -26,7 +26,7 @@ current_column: Now returns EMACS_INT, fixing some iftc that was introduced in the 2002-06-02 change "temporarily"; see - . + . * bytecode.c (Fbyte_code): Don't cast current_column () to int. * cmds.c (internal_self_insert): Likewise. * indent.c (Fcurrent_column): Likewise. @@ -91,7 +91,7 @@ * fileio.c (Fmake_symbolic_link): Treat ENOSYS specially, and generate a special message for it. Suggested by Eli Zaretskii in - . + . (Frename_file, Fmake_symbolic_link, Ffile_symlink_p): Simplify the code by assuming that the readlink and symlink calls exist, even if they always fail on this host. @@ -154,7 +154,7 @@ * dired.c (Ffile_attributes): Increase size of modes from 10 to 12 as per recent filemodestring API change. Reported by Jonas Öster in - . + . 2011-02-23 Ben Key @@ -7244,9 +7244,9 @@ * xdisp.c (try_scrolling): Compute the limit for searching point in forward scroll from scroll_max, instead of an arbitrary limit of 10 screen lines. - See http://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00766.html + See https://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00766.html and - http://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00773.html + https://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00773.html for details. 2010-06-16 Glenn Morris @@ -7346,7 +7346,7 @@ * window.c (Fselect_window): Move `record_buffer' up to the beginning of this function, so the buffer gets recorded even if the selected window does not change. - http://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00137.html + https://lists.gnu.org/archive/html/emacs-devel/2010-06/msg00137.html 2010-06-07 Juanma Barranquero @@ -7688,7 +7688,7 @@ * fileio.c (Fdelete_file): Change interactive spec to use `read-file-name' like in `find-file-read-args' where the default value is `default-directory' instead of `buffer-file-name'. - http://lists.gnu.org/archive/html/emacs-devel/2010-05/msg00533.html + https://lists.gnu.org/archive/html/emacs-devel/2010-05/msg00533.html 2010-05-20 Kevin Ryde @@ -7962,7 +7962,7 @@ * xdisp.c (init_iterator): Don't turn on bidi reordering in unibyte buffers. See - http://lists.gnu.org/archive/html/emacs-devel/2010-05/msg00263.html. + https://lists.gnu.org/archive/html/emacs-devel/2010-05/msg00263.html. 2010-05-10 Glenn Morris @@ -8910,9 +8910,9 @@ * xdisp.c (display_line): Don't write beyond the last glyph row in the desired matrix. Fixes a crash in "emacs -nw" (bug#5972), see - http://lists.gnu.org/archive/html/emacs-devel/2010-04/msg00075.html + https://lists.gnu.org/archive/html/emacs-devel/2010-04/msg00075.html and - http://lists.gnu.org/archive/html/emacs-devel/2010-04/msg00213.html + https://lists.gnu.org/archive/html/emacs-devel/2010-04/msg00213.html 2010-04-18 Stefan Monnier @@ -11510,7 +11510,7 @@ * frame.c (xrdb_get_resource): Return nil for empty string resources; some parts of Emacs code (like font selection) don't grok them. - See http://lists.gnu.org/archive/html/emacs-devel/2009-09/msg00528.html + See https://lists.gnu.org/archive/html/emacs-devel/2009-09/msg00528.html 2009-09-24 Andreas Schwab diff --git a/src/ChangeLog.12 b/src/ChangeLog.12 index 35993adb66..61251bfbb4 100644 --- a/src/ChangeLog.12 +++ b/src/ChangeLog.12 @@ -231,9 +231,9 @@ Make it a stub in this case; otherwise the build might fail, and this code hasn't been tested on such hosts anyway. Problem reported by Nelson H. F. Beebe in - + and analyzed by Jérémie Courrèges-Anglas in - . + . 2013-03-06 Dmitry Antipov @@ -332,7 +332,7 @@ because the locking mechanism was never reliable in that case). This patch fixes this and other bugs discovered by a code inspection that was prompted by - . + . Also, this patch switches to .#-FILE (not .#FILE) on MS-Windows, to avoid interoperability problems between the MS-Windows and non-MS-Windows implementations. MS-Windows and non-MS-Windows @@ -494,9 +494,9 @@ Fix regression introduced by July 10 filelock.c patch. * filelock.c (fill_in_lock_file_name): Fix crash caused by the 2012-07-10 patch to this file. Reported by Eli Zaretskii in - + and diagnosed by Andreas Schwab in - . + . 2013-02-22 Paul Eggert @@ -678,7 +678,7 @@ Improve AIX port some more (Bug#13650). With this, it should be as good as it was in 23.3, though it's still pretty bad: the dumped emacs does not run. See Mark Fleishman in - http://lists.gnu.org/archive/html/help-gnu-emacs/2011-04/msg00287.html + https://lists.gnu.org/archive/html/help-gnu-emacs/2011-04/msg00287.html * unexaix.c (start_of_text): Remove. (_data, _text): Declare as char[], not int, as AIX manual suggests. (bias, lnnoptr, text_scnptr, data_scnptr, load_scnptr) @@ -1087,7 +1087,7 @@ 2013-01-28 Dmitry Antipov Remove obsolete redisplay code. See the discussion at - http://lists.gnu.org/archive/html/emacs-devel/2013-01/msg00576.html. + https://lists.gnu.org/archive/html/emacs-devel/2013-01/msg00576.html. * dispnew.c (preemption_period, preemption_next_check): Remove. (Vredisplay_preemption_period): Likewise. (update_frame, update_single_window, update_window, update_frame_1): @@ -1132,7 +1132,7 @@ Drop async_visible and async_iconified fields of struct frame. This is possible because async input is gone; for details, see - http://lists.gnu.org/archive/html/emacs-devel/2012-12/msg00734.html. + https://lists.gnu.org/archive/html/emacs-devel/2012-12/msg00734.html. * frame.h (struct frame): Remove async_visible and async_iconified members, convert garbaged to unsigned bitfield. Adjust comments. (FRAME_SAMPLE_VISIBILITY): Remove. Adjust all users. @@ -1152,7 +1152,7 @@ 2013-01-24 Dmitry Antipov * insdel.c (prepare_to_modify_buffer): Revert last change as suggested - in http://lists.gnu.org/archive/html/emacs-devel/2013-01/msg00555.html. + in https://lists.gnu.org/archive/html/emacs-devel/2013-01/msg00555.html. 2013-01-23 Stefan Monnier @@ -1255,7 +1255,7 @@ * buffer.c (sort_overlays): Use SAFE_NALLOCA, to avoid segfault when there are lots of overlays. - See http://lists.gnu.org/archive/html/emacs-devel/2013-01/msg00421.html + See https://lists.gnu.org/archive/html/emacs-devel/2013-01/msg00421.html for the details and a way to reproduce. 2013-01-19 Paul Eggert @@ -1276,7 +1276,7 @@ Allow floating-point file offsets. Problem reported by Vitalie Spinu in - . + . * fileio.c (emacs_lseek): Remove. (file_offset): New function. (Finsert_file_contents, Fwrite_region): Use it. @@ -1314,7 +1314,7 @@ 2013-01-18 Dmitry Antipov Fix crash when inserting data from non-regular files. - See http://lists.gnu.org/archive/html/emacs-devel/2013-01/msg00406.html + See https://lists.gnu.org/archive/html/emacs-devel/2013-01/msg00406.html for the error description produced by valgrind. * fileio.c (read_non_regular): Rename to read_contents. Free Lisp_Save_Value object used to pass parameters. @@ -1331,7 +1331,7 @@ * fileio.c (Finsert_file_contents): Use open+fstat, not stat+open. This avoids a race if the file is renamed between stat and open. This race is not the problem originally noted in Bug#13149; - see and later messages in the thread. + see and later messages in the thread. 2013-01-17 Dmitry Antipov @@ -1642,7 +1642,7 @@ * w32.c (unsetenv): Set up the string passed to _putenv correctly. - See http://lists.gnu.org/archive/html/emacs-devel/2012-12/msg00863.html + See https://lists.gnu.org/archive/html/emacs-devel/2012-12/msg00863.html for the bug this caused. 2012-12-30 Paul Eggert @@ -1757,7 +1757,7 @@ * window.c (window_body_cols): Subtract display margins from the window body width on TTYs as well. See - http://lists.gnu.org/archive/html/help-gnu-emacs/2012-12/msg00317.html + https://lists.gnu.org/archive/html/help-gnu-emacs/2012-12/msg00317.html for the original report. 2012-12-25 Dmitry Antipov @@ -1954,7 +1954,7 @@ * sysdep.c (emacs_abort): Bump backtrace size to 40. Companion to the 2012-09-30 patch. Suggested by Eli Zaretskii in - . + . 2012-12-16 Romain Francoise @@ -2802,7 +2802,7 @@ dostounix_filename. Prevents crashes down the road, because dostounix_filename assumes it gets a unibyte string. Reported by Michel de Ruiter , see - http://lists.gnu.org/archive/html/help-emacs-windows/2012-11/msg00017.html + https://lists.gnu.org/archive/html/help-emacs-windows/2012-11/msg00017.html 2012-11-20 Stefan Monnier @@ -2983,7 +2983,7 @@ * eval.c (mark_backtrace) [BYTE_MARK_STACK]: Remove stray '*'. This follows up on the 2012-09-29 patch that removed indirection for the 'function' field. Reported by Sergey Vinokurov in - . + . 2012-11-14 Eli Zaretskii @@ -3192,7 +3192,7 @@ * alloc.c (struct Lisp_Vectorlike_Free): Special type to represent vectorlike object on the free list. This is introduced to avoid some (but not all) pointer casting and aliasing problems, see - http://lists.gnu.org/archive/html/emacs-devel/2012-11/msg00105.html. + https://lists.gnu.org/archive/html/emacs-devel/2012-11/msg00105.html. * .gdbinit (pvectype, pvecsize): New commands to examine vectorlike objects. (xvectype, xvecsize): Use them to examine Lisp_Object values. @@ -3346,7 +3346,7 @@ Restore some duplicate definitions (Bug#12814). This undoes part of the 2012-11-03 changes. Some people build with plain -g rather than with -g3, and they need the duplicate - definitions for .gdbinit to work; see . + definitions for .gdbinit to work; see . * lisp.h (GCTYPEBITS, ARRAY_MARK_FLAG, PSEUDOVECTOR_FLAG, VALMASK): Define as macros, as well as as enums or as constants. @@ -3368,7 +3368,7 @@ * window.c (Fwindow_combination_limit): Revert to the only required argument and adjust docstring as suggested in - http://lists.gnu.org/archive/html/emacs-diffs/2012-11/msg01082.html + https://lists.gnu.org/archive/html/emacs-diffs/2012-11/msg01082.html by Martin Rudalics . 2012-11-06 Dmitry Antipov @@ -3568,7 +3568,7 @@ * window.c (decode_next_window_args): Update window arg after calling decode_live_window and so fix crash reported at - http://lists.gnu.org/archive/html/emacs-devel/2012-11/msg00035.html + https://lists.gnu.org/archive/html/emacs-devel/2012-11/msg00035.html by Juanma Barranquero . (Fwindow_body_width, Fwindow_body_height): Simplify a bit. * font.c (Ffont_at): Likewise. @@ -3789,7 +3789,7 @@ 2012-10-19 Eli Zaretskii * puresize.h (BASE_PURESIZE): Bump the base value to 1700000. - See http://lists.gnu.org/archive/html/emacs-devel/2012-10/msg00593.html + See https://lists.gnu.org/archive/html/emacs-devel/2012-10/msg00593.html for the reasons. * alloc.c (NSTATICS): Decrease to 0x800. @@ -4603,7 +4603,7 @@ * xdisp.c (syms_of_xdisp): Default message-log-max to 1000, not 100. Suggested by Juri Linkov in - . + . Prefer plain 'static' to 'static inline' (Bug#12541). With static functions, modern compilers inline pretty well by @@ -4884,7 +4884,7 @@ * syssignal.h (PROFILER_CPU_SUPPORT): Don't define if PROFILING. Suggested by Eli Zaretskii in - . + . 2012-09-30 Eli Zaretskii @@ -4929,7 +4929,7 @@ * sysdep.c (handle_fatal_signal): Bump backtrace size to 40. Suggested by Eli Zaretskii in - . + . 2012-09-29 Juanma Barranquero @@ -5079,7 +5079,7 @@ * character.c (char_string, string_char): Remove calls to MAYBE_UNIFY_CHAR. See the discussion starting at - http://lists.gnu.org/archive/html/emacs-devel/2012-09/msg00433.html + https://lists.gnu.org/archive/html/emacs-devel/2012-09/msg00433.html for the details. 2012-09-25 Chong Yidong @@ -5140,7 +5140,7 @@ (interruptible_wait_for_termination): Move these decls from lisp.h to syswait.h, since they use pid_t. Needed on FreeBSD; see Herbert J. Skuhra in - . + . * callproc.c: Include syswait.h. gnutls.c, gtkutil.c: Use bool for boolean. @@ -5205,7 +5205,7 @@ Do not use SA_NODEFER. Problem reported by Dani Moncayo in - . + . * alloc.c (die): * sysdep.c (emacs_abort): Do not reset signal handler. * emacs.c (terminate_due_to_signal): Reset signal handler here. @@ -5385,7 +5385,7 @@ * .gdbinit: Just stop at fatal_error_backtrace. See Stefan Monnier's request in - . + . Remove no-longer-used query of system type. 2012-09-22 Chong Yidong @@ -5517,7 +5517,7 @@ * w32inevt.c (w32_console_read_socket): Return -1 on failure, not 0. Ignore 'expected'. See Eli Zaretskii in - (last line). + (last line). * frame.c (read_integer): Remove. All uses replaced by strtol/strtoul. (XParseGeometry): Now static. Substitute extremal values for @@ -5571,7 +5571,7 @@ I found that SYNC_INPUT has race conditions and would be a real pain to fix. Since it's an undocumented and deprecated configure-time option, now seems like a good time to remove it. - Also see . + Also see . * alloc.c (_bytes_used, __malloc_extra_blocks, _malloc_internal) (_free_internal) [!DOUG_LEA_MALLOC]: Remove decls. (alloc_mutex) [!SYSTEM_MALLOC && !SYNC_INPUT && HAVE_PTHREAD]: @@ -5743,7 +5743,7 @@ Better workaround for GNOME bug when --enable-gcc-warnings. * emacsgtkfixed.c (G_STATIC_ASSERT): Remove, undoing last change. Instead, disable -Wunused-local-typedefs. See Dmitry Antipov in - . + . Simplify SIGIO usage (Bug#12408). The code that dealt with SIGIO was crufty and confusing, e.g., it @@ -6040,7 +6040,7 @@ More signal-handler cleanup (Bug#12327). * emacs.c (main): Convert three 'signal' calls to 'sigaction' calls. Problem introduced when merging patches. Noted by Eli Zaretskii in - . + . * floatfns.c: Comment fix. * lisp.h (force_auto_save_soon): Declare regardless of SIGDANGER. SIGDANGER might not be in scope so "#ifdef SIGDANGER" is not right, @@ -6860,7 +6860,7 @@ It was meant to be temporary and it often doesn't work, because when IDX has side effects the behavior of IDX==IDX is undefined. See Stefan Monnier in - . + . 2012-08-26 Barry O'Reilly @@ -7314,7 +7314,7 @@ (set_char_table_contents): Rename from char_table_set_contents. (set_sub_char_table_contents): Rename from sub_char_table_set_contents. All uses changed. See the end of - . + . * lisp.h (CSET): Remove (Bug#12215). (set_char_table_ascii, set_char_table_defalt, set_char_table_parent) @@ -8062,7 +8062,7 @@ * lisp.h (ASET) [ENABLE_CHECKING]: Pay attention to ARRAY_MARK_FLAG when checking subscripts, because ASET is not supposed to be invoked from the garbage collector. - See Andreas Schwab in . + See Andreas Schwab in . (gc_aset): New function, which is like ASET but can be used in the garbage collector. (set_hash_key, set_hash_value, set_hash_next, set_hash_hash) @@ -8170,7 +8170,7 @@ Use "ASET (a, i, v)" rather than "AREF (a, i) = v". This how ASET and AREF are supposed to work, and makes it easier to think about future improvements. See - . + . * charset.h (set_charset_attr): New function. All lvalue-style uses of CHARSET_DECODER etc. changed to use it. * lisp.h (ASET): Rewrite so as not to use AREF in an lvalue style. @@ -8573,7 +8573,7 @@ Adjust GDB to reflect pvec_type changes (Bug#12036). * .gdbinit (xvectype, xpr, xbacktrace): Adjust to reflect the 2012-07-04 changes to pseudovector representation. - Problem reported by Eli Zaretskii in . + Problem reported by Eli Zaretskii in . 2012-07-27 Michael Albinus @@ -8614,7 +8614,7 @@ (xgetint): Simplify expression. * alloc.c (gdb_make_enums_visible): New constant. This ports to GCC 3.4.2 the export of symbols to GDB. Problem reported by Eli - Zaretskii in . + Zaretskii in . * lisp.h (PUBLISH_TO_GDB): Remove. All uses removed. No longer needed now that we have gdb_make_enums_visible. (enum CHECK_LISP_OBJECT_TYPE, enum Lisp_Bits, enum More_Lisp_Bits) @@ -8664,7 +8664,7 @@ (ARRAY_MARK_FLAG, PSEUDOVECTOR_FLAG, VALMASK): Move these here from emacs.c, as this is a more-suitable home. Had this been done earlier the fix for 12036 would have avoided some of the problems noted in - by Eli Zaretskii, as the scope problems + by Eli Zaretskii, as the scope problems would have been more obvious. * emacs.c: Do not include ; no longer needed. (gdb_CHECK_LISP_OBJECT_TYPE, gdb_DATA_SEG_BITS) @@ -8794,7 +8794,7 @@ Swap buffer text indirection counters in Fbuffer_swap_text. * buffer.c (Fbuffer_swap_text): Swap indirections too. This avoids crash reported by Christoph Scholtes at - http://lists.gnu.org/archive/html/bug-gnu-emacs/2012-07/msg00785.html. + https://lists.gnu.org/archive/html/bug-gnu-emacs/2012-07/msg00785.html. 2012-07-22 Jan Djärv @@ -8830,7 +8830,7 @@ * keyboard.c (keys_of_keyboard): Bind language-change to 'ignore' in special-event-map. See the discussion at - http://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00417.html + https://lists.gnu.org/archive/html/emacs-devel/2012-06/msg00417.html for the reasons. * w32menu.c (add_menu_item): Cast to ULONG_PTR when assigning @@ -8924,7 +8924,7 @@ Tweak the value returned from Fgarbage_collect again. * alloc.c (Fgarbage_collect): New return value, as confirmed in - http://lists.gnu.org/archive/html/emacs-devel/2012-07/msg00418.html. + https://lists.gnu.org/archive/html/emacs-devel/2012-07/msg00418.html. Adjust documentation. (total_vector_bytes): Rename to total_vector_slots, adjust accounting. @@ -8962,7 +8962,7 @@ * intervals.c (merge_interval_right, merge_interval_left): Do not zero out this interval if it is absorbed by its children, as this interval's total length doesn't change in that case. See - . + . 2012-07-18 Paul Eggert @@ -9009,7 +9009,7 @@ Return more descriptive data from Fgarbage_collect. Suggested by Stefan Monnier in - http://lists.gnu.org/archive/html/emacs-devel/2012-07/msg00369.html. + https://lists.gnu.org/archive/html/emacs-devel/2012-07/msg00369.html. * alloc.c (bounded_number): New function. (total_buffers, total_vectors): New variable. (total_string_size): Rename to total_string_bytes, adjust users. @@ -9034,7 +9034,7 @@ Restore old code in allocate_string_data to avoid Faset breakage. Reported by Julien Danjou in - http://lists.gnu.org/archive/html/emacs-devel/2012-07/msg00371.html. + https://lists.gnu.org/archive/html/emacs-devel/2012-07/msg00371.html. * alloc.c (allocate_string_data): Restore old code with minor adjustments, fix comment to explain this subtle issue. @@ -9392,7 +9392,7 @@ Fix typos that broke OS X build. Reported by Randal L. Schwartz in - . + . * nsterm.m (ns_timeout): Add missing local decl. (ns_get_color): snprintf -> sprintf, to fix typo. @@ -9768,7 +9768,7 @@ 2012-07-08 Paul Eggert * systime.h (EMACS_SUB_TIME): Clarify behavior with unsigned time_t. - See . + See . 2012-07-08 Eli Zaretskii @@ -10115,18 +10115,18 @@ * fileio.c (time_error_value): Check the right error number. Problem reported by Troels Nielsen in - . + . 2012-07-04 Paul Eggert * window.c (set_window_hscroll): Revert the 100000 hscroll limit. This should be fixed in a better way; see Eli Zaretskii in - . + . (HSCROLL_MAX): Remove; this is now internal to set_window_hscroll. * fileio.c (time_error_value): Rename from special_mtime. The old name's problems were noted by Eli Zaretskii in - . + . * emacs.c (gdb_pvec_type): Change it back to enum pvec_type. This variable's comment says Emacs needs at least one GDB-visible @@ -10710,7 +10710,7 @@ Use it to avoid bogus compiler warnings with obsolescent GCC versions. This improves on the previous patch, which introduced a bug when time_t is unsigned and as wide as intmax_t. - See . + See . 2012-06-23 Eli Zaretskii @@ -10995,7 +10995,7 @@ * bytecode.c (METER_CODE) [BYTE_CODE_METER]: Don't assume !CHECK_LISP_OBJECT_TYPE && !USE_LSB_TAG. Problem with CHECK_LISP_OBJECT_TYPE reported by Dmitry Antipov in - . + . (METER_1, METER_2): Simplify. 2012-06-18 Stefan Monnier @@ -11595,7 +11595,7 @@ * bidi.c (bidi_mirror_char): Don't possibly truncate the integer before checking whether it's out of range. Put the check inside eassert. See - . + . 2012-05-27 Ken Brown @@ -12700,7 +12700,7 @@ Untag more efficiently if USE_LSB_TAG. This is based on a proposal by YAMAMOTO Mitsuharu in - . + . For an admittedly artificial (nth 8000 longlist) benchmark on Fedora 15 x86-64, this yields a 25% CPU speedup. Also, it shrinks Emacs's overall text size by 1%. @@ -12726,7 +12726,7 @@ stack for each reader_thread, instead of defaulting to 8MB determined by the linker. This avoids failures in creating subprocesses on Windows 7, see the discussion in this thread: - http://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00119.html. + https://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00119.html. 2012-05-07 Jérémy Compostella @@ -13118,13 +13118,13 @@ Remove one incorrect comment and fix another. Fix minor ralloc.c problems found by static checking. - See http://lists.gnu.org/archive/html/emacs-devel/2011-12/msg00720.html + See https://lists.gnu.org/archive/html/emacs-devel/2011-12/msg00720.html * ralloc.c (ALIGNED, ROUND_TO_PAGE, HEAP_PTR_SIZE) (r_alloc_size_in_use, r_alloc_freeze, r_alloc_thaw): Remove; unused. (r_alloc_sbrk): Now static. Improve ralloc.c interface checking. - See http://lists.gnu.org/archive/html/emacs-devel/2011-12/msg00720.html + See https://lists.gnu.org/archive/html/emacs-devel/2011-12/msg00720.html * buffer.c (ralloc_reset_variable, r_alloc, r_re_alloc) (r_alloc_free) [REL_ALLOC]: Move decls from here ... * lisp.h (r_alloc, r_alloc_free, r_re_alloc, r_alloc_reset_variable) @@ -13335,7 +13335,7 @@ about subtle differences between FETCH_CHAR* and STRING_CHAR* macros related to unification of CJK characters. For the details, see the discussion following the message here: - http://debbugs.gnu.org/cgi/bugreport.cgi?bug=11073#14. + https://debbugs.gnu.org/cgi/bugreport.cgi?bug=11073#14. 2012-04-04 Chong Yidong @@ -13539,7 +13539,7 @@ Generalize fix for crash due to non-contiguous EMACS_INT (Bug#10780). Suggested by Stefan Monnier in - . + . * alloc.c (widen_to_Lisp_Object): New static function. (mark_memory): Also mark Lisp_Objects by fetching pointer words and widening them to Lisp_Objects. This would work even if @@ -13593,7 +13593,7 @@ It's useless in that case, and it can cause problems on hosts that allocate halves of EMACS_INT values separately. Reported by Dan Horák. Diagnosed by Andreas Schwab in - . + . * mem-limits.h (EXCEEDS_LISP_PTR): Define to 0 on hosts where UINTPTR_MAX >> VALBITS == 0. This is required by the above change; it avoids undefined behavior on hosts where shifting right by more @@ -13619,7 +13619,7 @@ 2012-02-15 Paul Eggert * image.c (MAX_IMAGE_SIZE): Increase from 6.0 to 10.0; see - . + . 2012-02-15 Chong Yidong @@ -14242,7 +14242,7 @@ Remove GCPRO-related macros that exist only to avoid shadowing locals. * lisp.h (GCPRO1_VAR, GCPRO2_VAR, GCPRO3_VAR, GCPRO4_VAR, GCPRO5_VAR) (GCPRO6_VAR, UNGCPRO_VAR): Remove. See - . + . All uses changed to use GCPRO1 etc. (GCPRO1, GCPRO2, GCPRO3, GCPRO4, GCPRO5, GCPRO6, UNGCPRO): Revert to old implementation (i.e., before 2011-03-11). @@ -14278,7 +14278,7 @@ 2011-11-26 Paul Eggert * fileio.c (Finsert_file_contents): Undo previous change; see - . + . 2011-11-26 Paul Eggert @@ -14379,7 +14379,7 @@ Standardize on VIRT_ADDR_VARIES behavior (Bug#10042). Otherwise, valgrind does not work on some platforms. Problem reported by Andreas Schwab in - . + . * puresize.h (pure, PURE_P): Always behave as if VIRT_ADDR_VARIES is set, removing the need for VIRT_ADDRESS_VARIES. (PURE_P): Use a more-efficient implementation that needs just one @@ -14545,7 +14545,7 @@ Avoid some portability problems by eschewing 'extern inline' functions. The trivial performance wins aren't worth the portability hassles; see - + et seq. * dispextern.h (window_box, window_box_height, window_text_bottom_y) (window_box_width, window_box_left, window_box_left_offset) @@ -14625,7 +14625,7 @@ (window_box_right, window_box_right_offset): Declare extern. Otherwise, these inline functions do not conform to C99 and are miscompiled by Microsoft compilers. Reported by Eli Zaretskii in - . + . * intervals.c (adjust_intervals_for_insertion) (adjust_intervals_for_deletion): Now extern, because otherwise the extern inline functions 'offset_intervals' couldn't refer to it. @@ -14959,7 +14959,7 @@ This doesn't change this function's behavior. (current-time-zone): Rewrite to use format_time_string. This fixes the bug reported by Michael Schierl in - . + . Jason Rumney's 2007-06-07 change worked around this bug, but didn't fix it. * systime.h (tzname, timezone): Remove no-longer-used declarations. @@ -15144,7 +15144,7 @@ * charset.c (charset_table_init): New static var. (syms_of_charset): Use it instead of xmalloc. This removes a dependency on glibc malloc internals. See Eli Zaretskii's comment in - . + . * lisp.h (XMALLOC_OVERRUN_CHECK_OVERHEAD, XMALLOC_OVERRUN_CHECK_SIZE): Move back to alloc.c. (XMALLOC_BASE_ALIGNMENT, COMMON_MULTIPLE, XMALLOC_HEADER_ALIGNMENT) @@ -15433,7 +15433,7 @@ signed integers, not unsigned. This is to be consistent with outgoing selection data, which was modified to use signed integers in as part of the fix to Bug#9196 in response to Jan D.'s comment - in that X11 + in that X11 expects long, not unsigned long. 2011-09-14 Eli Zaretskii @@ -15763,7 +15763,7 @@ (ccl_driver): Do not generate an out-of-range pointer. (Fccl_execute_on_string): Remove unnecessary check for integer overflow, noted by Stefan Monnier in - . + . Remove a FIXME that didn't need fixing. Simplify the newly-introduced buffer reallocation code. @@ -16712,11 +16712,11 @@ (bidi_dump_cached_states): Use ptrdiff_t, not int, to avoid overflow. (bidi_cache_ensure_space): Also check that the bidi cache size does not exceed that of the largest Lisp string or buffer. See Eli - Zaretskii in . + Zaretskii in . * alloc.c (__malloc_size_t): Remove. All uses replaced by size_t. See Andreas Schwab's note - . + . * image.c: Improve checking for integer overflow. (check_image_size): Assume that f is nonnull, since @@ -16749,7 +16749,7 @@ * dispnew.c (init_display): Use *_RANGE_OVERFLOW macros. The plain *_OVERFLOW macros run afoul of GCC bug 49705 - + and therefore cause GCC to emit a bogus diagnostic in some cases. * image.c: Integer signedness and overflow and related fixes. @@ -16990,7 +16990,7 @@ 2011-07-19 Paul Eggert Port to OpenBSD. - See http://lists.gnu.org/archive/html/emacs-devel/2011-07/msg00688.html + See https://lists.gnu.org/archive/html/emacs-devel/2011-07/msg00688.html and the surrounding thread. * minibuf.c (read_minibuf_noninteractive): Rewrite to use getchar rather than fgets, and retry after EINTR. Otherwise, 'emacs @@ -19316,7 +19316,7 @@ 2011-05-31 Paul Eggert Use 'inline', not 'INLINE'. - + * alloc.c, fontset.c (INLINE): Remove. * alloc.c, bidi.c, charset.c, coding.c, dispnew.c, fns.c, image.c: * intervals.c, keyboard.c, process.c, syntax.c, textprop.c, w32term.c: @@ -19620,7 +19620,7 @@ * systime.h (Time): Define only if emacs is defined. This is to allow ../lib-src/profile.c to be compiled on FreeBSD, where the include path doesn't have X11/X.h by default. See - . + . 2011-05-20 Kenichi Handa @@ -19885,7 +19885,7 @@ * dbusbind.c: Do not use XPNTR on a value that may be an integer. Reported by Stefan Monnier in - . + . (xd_remove_watch, Fdbus_init_bus, xd_read_queued_messages): Use SYMBOLP-guarded XSYMBOL, not XPNTR. @@ -20276,7 +20276,7 @@ * intervals.h (struct interval): Use EMACS_INT for members where EMACS_UINT might cause problems. See - . + . (CHECK_TOTAL_LENGTH): Remove cast to EMACS_INT; no longer needed. * intervals.c (interval_deletion_adjustment): Now returns EMACS_INT. All uses changed. @@ -20741,7 +20741,7 @@ * sysdep.c (emacs_read): Remove unnecessary check vs MAX_RW_COUNT. emacs_write: Accept and return EMACS_INT for sizes. - See http://lists.gnu.org/archive/html/emacs-devel/2011-04/msg00514.html + See https://lists.gnu.org/archive/html/emacs-devel/2011-04/msg00514.html et seq. * gnutls.c, gnutls.h (emacs_gnutls_read, emacs_gnutls_write): Accept and return EMACS_INT. @@ -20755,7 +20755,7 @@ * process.c (send_process): Adjust to the new signatures of emacs_write and emacs_gnutls_write. Do not attempt to store a byte offset into an 'int'; it might overflow. - See http://lists.gnu.org/archive/html/emacs-devel/2011-04/msg00483.html + See https://lists.gnu.org/archive/html/emacs-devel/2011-04/msg00483.html * sound.c: Don't assume sizes fit in 'int'. (struct sound_device.period_size, alsa_period_size): @@ -21225,7 +21225,7 @@ * sysdep.c (emacs_read, emacs_write): Check for negative sizes since callers should never pass a negative size. Change the signature to match that of plain 'read' and 'write'; see - . + . * lisp.h: Update prototypes of emacs_write and emacs_read. 2011-04-11 Eli Zaretskii @@ -22049,7 +22049,7 @@ This also avoids a (bogus) GCC warning with gcc -Wstrict-overflow. * scroll.c (do_scrolling): Work around GCC bug 48228. - See . + See . * frame.c (Fmodify_frame_parameters): Simplify loop counter. This also avoids a warning with gcc -Wstrict-overflow. @@ -22615,7 +22615,7 @@ 1152921504606846976) returns the obviously-bogus value (-948597 62170) on my RHEL 5.5 x86-64 host. With the patch, it correctly reports time overflow. See - . + . * deps.mk (editfns.o): Depend on ../lib/intprops.h. * editfns.c: Include limits.h and intprops.h. (TIME_T_MIN, TIME_T_MAX): New macros. diff --git a/src/ChangeLog.13 b/src/ChangeLog.13 index 6f5ea03626..e252acfbdb 100644 --- a/src/ChangeLog.13 +++ b/src/ChangeLog.13 @@ -48,7 +48,7 @@ Avoid some core dumps in X session management Derived from a bug report by Nicolas Richard in: - http://bugs.gnu.org/20191#20 + https://bugs.gnu.org/20191#20 * xsmfns.c (smc_save_yourself_CB): Don't dump core if invocation-name is not a string. Initialize user-login-name if it is not already initialized, and don't dump core if it is not a @@ -60,14 +60,14 @@ Port user-login-name initialization to Qnil == 0 Derived from a bug report by Nicolas Richard in: - http://bugs.gnu.org/20191#20 + https://bugs.gnu.org/20191#20 * editfns.c (Fuser_login_name, Fuser_real_login_name) (syms_of_editfns): Don't rely on all-bits-zero being an Elisp integer, as this is no longer true now that Qnil == 0. Assume !BROKEN_NON_BLOCKING_CONNECT From a suggestion by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00824.html + https://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00824.html * process.c (NON_BLOCKING_CONNECT): Simplify by assuming that BROKEN_NON_BLOCKING_CONNECT is not defined. (SELECT_CAN_DO_WRITE_MASK): Remove, and assume it's now true. @@ -77,12 +77,12 @@ * lread.c (substitute_object_recurse): For sub-char-tables, start the recursive SUBSTITUTE loop from index of 2, to skip the non-Lisp members of the sub-char-table. See the discussion at - http://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00520.html + https://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00520.html for the details. Support non-blocking connect on MS-Windows. Based on ideas from Kim F. Storm , see - http://lists.gnu.org/archive/html/emacs-devel/2006-12/msg00873.html. + https://lists.gnu.org/archive/html/emacs-devel/2006-12/msg00873.html. * w32proc.c (reader_thread): If the FILE_CONNECT flag is set, call '_sys_wait_connect'. If it returns STATUS_CONNECT_FAILED, exit @@ -1107,7 +1107,7 @@ Isolate NIL_IS_ZERO-assuming code better Suggested by Stefan Monnier in: - http://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00588.html + https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00588.html * alloc.c (allocate_pseudovector): Use memclear, not memsetnil, to remove a 'verify'. * callint.c (Fcall_interactively): @@ -1121,7 +1121,7 @@ Undo port to hypothetical nonzero Qnil case This mostly undoes the previous change in this area. See: - http://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00570.html + https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00570.html * alloc.c (allocate_pseudovector): * callint.c (Fcall_interactively): * dispnew.c (realloc_glyph_pool): @@ -1326,7 +1326,7 @@ Don't say Fnext_read_file_uses_dialog_p is const It's const only if a windowing system is not used; don't say it's const otherwise. See: - http://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00310.html + https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00310.html * fileio.c (next_read_file_uses_dialog_p): Remove. Move guts back to ... (Fnext_read_file_uses_dialog_p): ... here. @@ -1415,13 +1415,13 @@ * fileio.c (next_read_file_uses_dialog_p): New workaround ... (Fnext_read_file_uses_dialog_p): ... called from here to avoid ATTRIBUTE_CONST dependency from #ifdefs. For details, see - http://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00289.html. + https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00289.html. 2015-01-12 Paul Eggert Port to 32-bit MingGW --with-wide-int Problem reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00265.html + https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00265.html * lisp.h (struct Lisp_Sub_Char_Table): Check that offset matches what we think it is, rather than checking only its alignment (and doing so incorrectly on MinGW). @@ -1623,12 +1623,12 @@ Port Qnil==0 XUNTAG to clang clang has undefined behavior if the program subtracts an integer from (char *) 0. Problem reported by YAMAMOTO Mitsuharu in: - http://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00132.html + https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00132.html * lisp.h (lisp_h_XUNTAG) [USE_LSB_TAG]: (XUNTAG) [!USE_LSB_TAG]: Port to clang 3.5.0. Port GFileMonitor * hack to Qnil==0 platforms - Reported by Glenn Morris in: http://bugs.gnu.org/15880#112 + Reported by Glenn Morris in: https://bugs.gnu.org/15880#112 * gfilenotify.c (monitor_to_lisp, lisp_to_monitor): New functions. (dir_monitor_callback, Fgfile_add_watch, Fgfile_rm_watch): Use them. @@ -1842,7 +1842,7 @@ Instead of using gnutls_global_set_mem_functions, check every call to a GnuTLS function that might return an indication of memory exhaustion. Suggested by Dmitry Antipov in: - http://lists.gnu.org/archive/html/emacs-devel/2014-12/msg02056.html + https://lists.gnu.org/archive/html/emacs-devel/2014-12/msg02056.html * gnutls.c (gnutls_global_set_mem_functions) [WINDOWSNT]: Remove. (init_gnutls_functions): Do not load gnutls_global_set_mem_functions. (fn_gnutls_global_set_mem_functions) [!WINDOWSNT]: Remove. @@ -2369,7 +2369,7 @@ Improve clarity of USE_LSB_TAG definition. Reported by Lee Duhem. Suggested by Andreas Schwab in: - http://lists.gnu.org/archive/html/emacs-devel/2014-11/msg02222.html + https://lists.gnu.org/archive/html/emacs-devel/2014-11/msg02222.html * lisp.h (USE_LSB_TAG): Define in terms of the (simpler) VAL_MAX / 2 rather than in terms of the (more complicated) EMACS_INT_MAX >> GCTYPEBITS, and adjust commentary to match. @@ -3043,7 +3043,7 @@ * xterm.c (x_draw_hollow_cursor): Fix display of hollow cursor on 1-pixel R2L characters. Reported by Dmitry Antipov , see - http://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00518.html. + https://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00518.html. 2014-10-16 Eli Zaretskii @@ -3284,7 +3284,7 @@ , and more generally should fix a portability problem in Emacs. Reported by Stefan Monnier in: - http://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00261.html + https://lists.gnu.org/archive/html/emacs-devel/2014-10/msg00261.html 2014-10-08 Leo Liu @@ -3334,7 +3334,7 @@ * keyboard.c (Qleft, Qright): Remove duplicate definitions (Bug#9927). These were already defined in buffer.c, and the duplicate definitions cause problems on platforms like 'gcc -fno-common'. - Reported by Peter Dyballa in: http://bugs.gnu.org/9927#137 + Reported by Peter Dyballa in: https://bugs.gnu.org/9927#137 2014-10-05 Jan Djärv @@ -3475,7 +3475,7 @@ (my_create_window): Move the calculation of the coordinates of the frame's top-left edge here. Pass them to the input thread via the second parameter of the WM_EMACS_CREATEWINDOW message. - See http://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00892.html + See https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00892.html for the details. 2014-09-30 Eli Zaretskii @@ -3508,7 +3508,7 @@ * alloc.c: Remove now-unnecessary check. Suggested by Dmitry Antipov in: - http://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00891.html + https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00891.html * xterm.c (x_term_init): Allocate temps on stack, not on heap. @@ -3650,7 +3650,7 @@ Fix local_cons etc. to not exhaust the stack when in a loop. Problem reported in: - http://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00696.html + https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00696.html * buffer.c (Fother_buffer, other_buffer_safely, init_buffer): * charset.c (load_charset_map_from_file, Ffind_charset_region) (Ffind_charset_string): @@ -3800,7 +3800,7 @@ Fix SAFE_ALLOCA to not exhaust the stack when in a loop. Reported by Dmitry Antipov in thread leading to: - http://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00713.html + https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00713.html This patch fixes only SAFE_ALLOCA, SAFE_NALLOCA, and SAFE_ALLOCA_LISP; the experimental local_* macros enabled by USE_LOCAL_ALLOCATORS remain unfixed. @@ -4133,7 +4133,7 @@ Simplify lisp.h by removing the __COUNTER__ business. Reported by Dmitry Antipov in: - http://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00220.html + https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00220.html * lisp.h (make_local_vector, make_local_string) (build_local_string): Simplify by not bothering with __COUNTER__. The __COUNTER__ business wasn't working properly, and was needed @@ -4164,7 +4164,7 @@ These can generate a constant with the correct value but the wrong width, which doesn't work as a printf argument. All uses removed. Reported by Dmitry Antipov in: - http://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00213.html + https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00213.html (ENUMABLE): Remove; no longer needed. (ARRAY_MARK_FLAG_val, PSEUDOVECTOR_FLAG_val, VALMASK_val): Remove; no longer needed because of the above change. @@ -4275,7 +4275,7 @@ Use SAFE_ALLOCA etc. to avoid unbounded stack allocation (Bug#18410). This follows up on the recent thread in emacs-devel on alloca; see: - http://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00042.html + https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00042.html This patch also cleans up alloca-related glitches noted while examining the code looking for unbounded alloca. * alloc.c (listn): @@ -4492,7 +4492,7 @@ it's an unsigned data type). This can happen in R2L hscrolled glyph rows, and caused us to draw the cursor glyph on the fringe. For the details, see - http://lists.gnu.org/archive/html/emacs-devel/2014-08/msg00543.html. + https://lists.gnu.org/archive/html/emacs-devel/2014-08/msg00543.html. 2014-08-31 Ken Brown @@ -4654,7 +4654,7 @@ immediately following the newline on the previous line. Avoids setting the ends_at_zv_p flag on screen lines that are not at or beyond ZV, which causes infloop in redisplay. For the details, see - http://lists.gnu.org/archive/html/emacs-devel/2014-08/msg00368.html. + https://lists.gnu.org/archive/html/emacs-devel/2014-08/msg00368.html. * dispnew.c (buffer_posn_from_coords): Fix mirroring of X coordinate for hscrolled R2L screen lines. (Bug#18277) @@ -4697,7 +4697,7 @@ (init_sigsegv): Adjust accordingly. * keyboard.c (Vtop_level_message): Rename to Vinternal__top_level_message, as suggested by Stefan Monnier in - http://lists.gnu.org/archive/html/emacs-devel/2014-08/msg00493.html + https://lists.gnu.org/archive/html/emacs-devel/2014-08/msg00493.html All related users changed. 2014-08-26 Dmitry Antipov @@ -4854,7 +4854,7 @@ (Fset_window_new_total, Fset_window_new_normal) (Fwindow_resize_apply): Fix doc-strings (see Bug#18112). See also: - http://lists.gnu.org/archive/html/bug-gnu-emacs/2014-08/msg00287.html + https://lists.gnu.org/archive/html/bug-gnu-emacs/2014-08/msg00287.html 2014-08-11 Eli Zaretskii @@ -4943,7 +4943,7 @@ * keyboard.c (safe_run_hooks): Follow the convenient style to bind inhibit-quit to t and pass 2 args to safe_run_hook_funcall. See - . + . (safe_run_hook_funcall): Adjust accordingly. 2014-08-04 Martin Rudalics @@ -5027,7 +5027,7 @@ 2014-08-01 Eli Zaretskii Fix display of R2L lines when the last character fits only partially. - See http://lists.gnu.org/archive/html/emacs-devel/2014-07/msg00476.html + See https://lists.gnu.org/archive/html/emacs-devel/2014-07/msg00476.html for the details. * xdisp.c (extend_face_to_end_of_line): If the last glyph of an R2L row is visible only partially, give the row a negative x @@ -5090,7 +5090,7 @@ * xrdb.c (x_load_resources) [USE_MOTIF]: Although not strictly necessary, put horizontal scroll bar resources as well. See - . + . * xterm.c (x_sync_with_move): Really wait 0.5s, not 0.0005s. 2014-07-29 Dmitry Antipov @@ -5148,7 +5148,7 @@ (adjust_frame_size): Always declare prototype. Fix Gnus-related issues reported by David Kastrup in - . + . * atimer.c (timerfd_callback): Always read expiration data. Add comment. (turn_on_atimers) [HAVE_TIMERFD]: Disarm timerfd timer. @@ -5597,7 +5597,7 @@ 2014-07-24 Dmitry Antipov Fix error reported by Angelo Graziosi in - + and complete previous change. * frame.c (adjust_frame_height): New function. (Fset_frame_height, Fset_frame_size): Use it. @@ -5607,7 +5607,7 @@ * frame.c (Fset_frame_height): Take frame top margin into account. Incorrect behavior was reported by Martin Rudalics in - + 2014-07-22 Dmitry Antipov @@ -6223,7 +6223,7 @@ * fns.c (validate_subarray): Add prototype. (Fcompare_substring): Use validate_subarray to check ranges. Adjust comment to mention that the semantics was changed. Also see - http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00447.html. + https://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00447.html. 2014-06-24 Paul Eggert @@ -6320,7 +6320,7 @@ Omit redundant extern decls. Most of this patch is from Dmitry Antipov, in: - http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00263.html + https://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00263.html * commands.h (update_mode_lines): * frame.h (Qbackground_color, Qforeground_color) (x_set_menu_bar_lines): @@ -6372,7 +6372,7 @@ * Makefile.in (ns-app): Fix typo that broke build on OS X. Reported by David Caldwell in: - http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00251.html + https://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00251.html 2014-06-16 Dmitry Antipov @@ -6654,7 +6654,7 @@ * emacs.c: Include "sysselect.h", to define its inline functions. Reported by Glenn Morris in: - http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00077.html + https://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00077.html Do not require libXt-devel when building with gtk. * gtkutil.h, menu.h: Include lwlib-widget.h, not lwlib-h, to avoid @@ -7080,7 +7080,7 @@ (Fgarbage_collect): Calculate the end address of the stack portion that needs to be examined by mark_stack, and pass that address to garbage_collect_1, which will pass it to mark_stack. - See http://lists.gnu.org/archive/html/emacs-devel/2014-05/msg00270.html + See https://lists.gnu.org/archive/html/emacs-devel/2014-05/msg00270.html for more details about the underlying problems. In particular, this avoids dumping Emacs with the large hash-table whose value is held in purify-flag for most of the time loadup.el runs. @@ -7298,7 +7298,7 @@ * term.c (tty_menu_display): Move the cursor to the active menu item. (tty_menu_activate): Return the cursor to the active menu item after displaying the menu and after displaying help-echo. - See http://lists.gnu.org/archive/html/emacs-devel/2014-04/msg00402.html + See https://lists.gnu.org/archive/html/emacs-devel/2014-04/msg00402.html for the details of why this is needed by screen readers and Braille displays. @@ -8628,7 +8628,7 @@ * terminal.c (initial_free_frame_resources): New function. (init_initial_terminal): Install new hook to free face cache on initial frame and avoid memory leak. For details, see - . + . * xfaces.c (free_frame_faces): Adjust comment. 2014-01-26 Paul Eggert @@ -8689,7 +8689,7 @@ * xdisp.c (reseat_1, Fcurrent_bidi_paragraph_direction): Avoid undefined behavior by initializing display property bit of a string processed by the bidirectional iterator. For details, see - . + . 2014-01-23 Paul Eggert @@ -8732,7 +8732,7 @@ Avoid undefined behavior by initializing buffer redisplay bit. Reported by Dmitry Antipov in - . + . * buffer.c (Fget_buffer_create): Initialize redisplay bit. Revert some of the CANNOT_DUMP fix (Bug#16494). @@ -8780,7 +8780,7 @@ Fix MinGW64 porting problem with _setjmp. Reported by Eli Zaretskii in: - http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01297.html + https://lists.gnu.org/archive/html/emacs-devel/2014-01/msg01297.html * image.c (FAST_SETJMP, FAST_LONGJMP): New macros, replacing the old _setjmp and _longjmp. All uses changed. @@ -9756,7 +9756,7 @@ * xterm.c (x_make_frame_visible): Restore hack which is needed when input polling is used. This is still meaningful for Cygwin, see - http://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00351.html. + https://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00351.html. * keyboard.c (poll_for_input_1, input_polling_used): Define unconditionally. * dispextern.h (FACE_SUITABLE_FOR_CHAR_P): Remove unused macro. @@ -10573,7 +10573,7 @@ Fix some dependency problems that cause unnecessary recompiles. Reported by RMS in - . + . * Makefile.in (OLDXMENU_TARGET, OLDXMENU, OLDXMENU_DEPS) (really-lwlib, really-oldXMenu, stamp-oldxmenu) (../src/$(OLDXMENU), $(OLDXMENU)): Remove. @@ -10584,7 +10584,7 @@ Fix recently introduced bool vector overrun. This was due to an optimization that went awry. Reported by Glenn Morris in - . + . * alloc.c (make_uninit_bool_vector): Don't allocate a dummy word for empty vectors, undoing the 2013-11-18 change. * data.c (bool_vector_binop_driver): Rely on this. @@ -10635,7 +10635,7 @@ Always allocate at least one bits_word per bool vector. See Daniel Colascione in: - http://lists.gnu.org/archive/html/emacs-devel/2013-11/msg00518.html + https://lists.gnu.org/archive/html/emacs-devel/2013-11/msg00518.html * alloc.c (make_uninit_bool_vector): Always allocate at least one word. * data.c (bool_vector_binop_driver): Rely on this. Tune. * lisp.h (struct Lisp_Bool_vector): Document this. @@ -10672,7 +10672,7 @@ * data.c: Work around bogus GCC diagnostic about shift count. Reported by Eli Zaretskii in - . + . (pre_value): New function. (count_trailing_zero_bits): Use it. @@ -11049,7 +11049,7 @@ * buffer.c (init_buffer): Don't store default-directory of *scratch* in multibyte form. The original problem which led to that is described in - http://lists.gnu.org/archive/html/emacs-pretest-bug/2004-11/msg00532.html, + https://lists.gnu.org/archive/html/emacs-pretest-bug/2004-11/msg00532.html, but it was solved long ago. (Bug#15260) 2013-11-04 Paul Eggert @@ -11293,7 +11293,7 @@ the same font object. Perform font-specific cleanup when font object is swept by GC. See - http://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00740.html. + https://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00740.html. * alloc.c (cleanup_vector): New function. (sweep_vector): Call it for each reclaimed vector object. * font.h (struct font): Adjust comment. @@ -11347,7 +11347,7 @@ * keyboard.c (make_lispy_event): Remove GPM-specific code that handles mouse clicks. Instead, let GPM use the same code as all the other mice use. See the discussion starting at - http://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00521.html + https://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00521.html for the details of the problem with the menu bar this fixes. 2013-10-18 Dmitry Antipov @@ -11722,7 +11722,7 @@ flavors of 'eassert', one for where 'assume' is far more likely to help or to hurt; but that can be done later. Reported by Dmitry Antipov in - . + . Also, don't include ; no longer needed. 2013-10-09 Glenn Morris @@ -11891,7 +11891,7 @@ Do not allocate huge temporary memory areas and objects while encoding for file I/O, thus reducing an enormous memory usage for large buffers. - See http://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00180.html. + See https://lists.gnu.org/archive/html/emacs-devel/2013-10/msg00180.html. * coding.h (struct coding_system): New member raw_destination. * coding.c (setup_coding_system): Initialize it to zero. (encode_coding_object): If raw_destination is set, do not create @@ -12132,14 +12132,14 @@ * dispnew.c (clear_glyph_row, copy_row_except_pointers): Use enums instead of ints, as it's the usual style for offsetof constants. See: - http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00478.html + https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00478.html * data.c (POPCOUNT_STATIC_INLINE): New macro, as a hack for popcount. This is ugly, but it should fix the performance problem for older GCC versions in the short run. I'll look into integrating the Gnulib module for popcount, as a better fix. See the thread starting in: - http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00474.html + https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00474.html (popcount_size_t_generic) [NEED_GENERIC_POPCOUNT]: (popcount_size_t_msc) [USE_MSC_POPCOUNT]: (popcount_size_t_gcc) [USE_GCC_POPCOUNT]: @@ -12331,7 +12331,7 @@ which must have the same definition in all modules, because the defining code might be shared across modules, depending on the implementation. Symptoms reported by Martin Rudalics in: - http://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00414.html + https://lists.gnu.org/archive/html/emacs-devel/2013-09/msg00414.html * regex.c, syntax.c (SYNTAX_ENTRY_VIA_PROPERTY): Remove. (SYNTAX, SYNTAX_ENTRY, SYNTAX_WITH_FLAGS): New macros, overriding the corresponding functions in syntax.h. @@ -14498,7 +14498,7 @@ * w32uniscribe.c (uniscribe_list, uniscribe_match) (uniscribe_list_family): Adjust to match font API change. MS-Windows breakage reported by Juanma Barranquero - at http://lists.gnu.org/archive/html/emacs-devel/2013-08/msg00006.html. + at https://lists.gnu.org/archive/html/emacs-devel/2013-08/msg00006.html. 2013-08-01 Dmitry Antipov @@ -14691,7 +14691,7 @@ * eval.c (Fprogn): Do not check that BODY is a proper list. This undoes the previous change. The check slows down the interpreter, and is not needed to prevent a crash. See - . + . 2013-07-23 Glenn Morris @@ -15206,7 +15206,7 @@ * deps.mk (sysdep.o): Remove dependency on ../lib/ignore-value.h. Reported by Herbert J. Skuhra in - . + . Don't lose top specpdl entry when memory is exhausted. * eval.c (grow_specpdl): Increment specpdl top by 1 and check for @@ -15642,7 +15642,7 @@ Try again to fix FreeBSD bug re multithreaded memory alloc (Bug#14569). * emacs.c (main) [HAVE_PTHREAD && !SYSTEM_MALLOC && !DOUG_LEA_MALLOC]: Do not clear _malloc_thread_enabled_p, undoing the previous change, - which did not work (see ). + which did not work (see ). (main): Do not invoke malloc_enable_thread if (! CANNOT_DUMP && (!noninteractive || initialized)). This attempts to thread the needle between the Scylla of FreeBSD and the Charybdis of Cygwin. @@ -15657,7 +15657,7 @@ Try to fix FreeBSD bug re multithreaded memory allocation (Bug#14569). * emacs.c (main) [HAVE_PTHREAD && !SYSTEM_MALLOC && !DOUG_LEA_MALLOC]: Clear _malloc_thread_enabled_p at startup. Reported by Ashish SHUKLA in - . + . 2013-07-02 Paul Eggert @@ -15714,7 +15714,7 @@ * emacs.c (malloc_enable_thread): Hoist extern decl to top level. (main) [HAVE_PTHREAD && !SYSTEM_MALLOC && !DOUG_LEA_MALLOC]: Invoke malloc_enable_thread even when not interactive. - Reported by Ken Brown in . + Reported by Ken Brown in . * process.c (init_process_emacs) [CYGWIN]: Tickle glib even in this case, since the underlying bug has now been fixed. @@ -16084,7 +16084,7 @@ 2013-06-17 Paul Eggert Move functions from lisp.h to individual modules when possible. - From a suggestion by Andreas Schwab in . + From a suggestion by Andreas Schwab in . * alloc.c (XFLOAT_INIT, set_symbol_name): * buffer.c (CHECK_OVERLAY): * chartab.c (CHECK_CHAR_TABLE, set_char_table_ascii) @@ -16344,7 +16344,7 @@ A few porting etc. fixes for the new file monitor code. See the thread containing - . + . * gfilenotify.c (dir_monitor_callback, Fgfile_add_watch) (Fgfile_rm_watch): Don't assume EMACS_INT is the same width as a pointer. (dir_monitor_callback, Fgfile_rm_watch): @@ -17194,7 +17194,7 @@ more than one line when there's an overlay string with a display property at end of line. Reported by Karl Chen in - http://lists.gnu.org/archive/html/emacs-devel/2013-04/msg00362.html. + https://lists.gnu.org/archive/html/emacs-devel/2013-04/msg00362.html. 2013-04-12 Stefan Monnier diff --git a/src/alloc.c b/src/alloc.c index 87e9ef0059..2e6399e7f8 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -5902,7 +5902,7 @@ mark_pinned_symbols (void) where mark_stack finds values that look like live Lisp objects on portions of stack that couldn't possibly contain such live objects. For more details of this, see the discussion at - http://lists.gnu.org/archive/html/emacs-devel/2014-05/msg00270.html. */ + https://lists.gnu.org/archive/html/emacs-devel/2014-05/msg00270.html. */ static Lisp_Object garbage_collect_1 (void *end) { diff --git a/src/atimer.c b/src/atimer.c index 0abd6c19c3..0a43797756 100644 --- a/src/atimer.c +++ b/src/atimer.c @@ -419,7 +419,7 @@ timerfd_callback (int fd, void *arg) else if (nbytes < 0) /* For some not yet known reason, we may get weird event and no data on timer descriptor. This can break Gnus at least, see: - http://lists.gnu.org/archive/html/emacs-devel/2014-07/msg00503.html. */ + https://lists.gnu.org/archive/html/emacs-devel/2014-07/msg00503.html. */ eassert (errno == EAGAIN); else /* I don't know what else can happen with this descriptor. */ diff --git a/src/callproc.c b/src/callproc.c index 9375ce5312..8f13e98fd1 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -634,7 +634,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, #ifdef DARWIN_OS /* Work around a macOS bug, where SIGCHLD is apparently delivered to a vforked child instead of to its parent. See: - http://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00342.html + https://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00342.html */ signal (SIGCHLD, SIG_DFL); #endif diff --git a/src/dispnew.c b/src/dispnew.c index 4a319ccc11..ad59704a16 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -1699,7 +1699,7 @@ required_matrix_height (struct window *w) if (FRAME_WINDOW_P (f)) { - /* http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html */ + /* https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html */ int ch_height = max (FRAME_SMALLEST_FONT_HEIGHT (f), 1); int window_pixel_height = window_box_height (w) + eabs (w->vscroll); @@ -1726,7 +1726,7 @@ required_matrix_width (struct window *w) struct frame *f = XFRAME (w->frame); if (FRAME_WINDOW_P (f)) { - /* http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html */ + /* https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html */ int ch_width = max (FRAME_SMALLEST_CHAR_WIDTH (f), 1); /* Compute number of glyphs needed in a glyph row. */ diff --git a/src/eval.c b/src/eval.c index 39d78364d5..acda64e7f0 100644 --- a/src/eval.c +++ b/src/eval.c @@ -616,7 +616,7 @@ The return value is BASE-VARIABLE. */) emacs_abort (); } - /* http://lists.gnu.org/archive/html/emacs-devel/2008-04/msg00834.html + /* https://lists.gnu.org/archive/html/emacs-devel/2008-04/msg00834.html If n_a is bound, but b_v is not, set the value of b_v to n_a, so that old-code that affects n_a before the aliasing is setup still works. */ diff --git a/src/frame.c b/src/frame.c index 4ec54fa347..1aff3a007a 100644 --- a/src/frame.c +++ b/src/frame.c @@ -2029,7 +2029,7 @@ delete_frame (Lisp_Object frame, Lisp_Object force) #if defined (USE_X_TOOLKIT) || defined (USE_GTK) /* FIXME: Deleting the terminal crashes emacs because of a GTK bug. - http://lists.gnu.org/archive/html/emacs-devel/2011-10/msg00363.html */ + https://lists.gnu.org/archive/html/emacs-devel/2011-10/msg00363.html */ /* Since a similar behavior was observed on the Lucid and Motif builds (see Bug#5802, Bug#21509, Bug#23499, Bug#27816), we now diff --git a/src/gtkutil.c b/src/gtkutil.c index 9f05524738..a07ee4b1b0 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1367,7 +1367,7 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position) /* Don't set size hints during initialization; that apparently leads to a race condition. See the thread at - http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */ + https://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */ if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f) || FRAME_PARENT_FRAME (f)) diff --git a/src/lisp.h b/src/lisp.h index 0c3ca3ae06..680c25d4c4 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -492,7 +492,7 @@ enum Lisp_Fwd_Type /* If you want to define a new Lisp data type, here are some instructions. See the thread at - http://lists.gnu.org/archive/html/emacs-devel/2012-10/msg00561.html + https://lists.gnu.org/archive/html/emacs-devel/2012-10/msg00561.html for more info. First, there are already a couple of Lisp types that can be used if diff --git a/src/sysdep.c b/src/sysdep.c index 1e6e0d011b..26d381f579 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -463,7 +463,7 @@ child_setup_tty (int out) s.main.c_oflag |= OPOST; /* Enable output postprocessing */ s.main.c_oflag &= ~ONLCR; /* Disable map of NL to CR-NL on output */ #ifdef NLDLY - /* http://lists.gnu.org/archive/html/emacs-devel/2008-05/msg00406.html + /* https://lists.gnu.org/archive/html/emacs-devel/2008-05/msg00406.html Some versions of GNU Hurd do not have FFDLY? */ #ifdef FFDLY s.main.c_oflag &= ~(NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY); @@ -2056,7 +2056,7 @@ init_signals (bool dumping) thread_fatal_action.sa_flags = process_fatal_action.sa_flags; /* SIGINT may need special treatment on MS-Windows. See - http://lists.gnu.org/archive/html/emacs-devel/2010-09/msg01062.html + https://lists.gnu.org/archive/html/emacs-devel/2010-09/msg01062.html Please update the doc of kill-emacs, kill-emacs-hook, and NEWS if you change this. */ diff --git a/src/term.c b/src/term.c index 065bce45d3..06695d1ec6 100644 --- a/src/term.c +++ b/src/term.c @@ -2057,7 +2057,7 @@ TERMINAL does not refer to a text terminal. */) /* Declare here rather than in the function, as in the rest of Emacs, to work around an HPUX compiler bug (?). See - http://lists.gnu.org/archive/html/emacs-devel/2007-08/msg00410.html */ + https://lists.gnu.org/archive/html/emacs-devel/2007-08/msg00410.html */ static int default_max_colors; static int default_no_color_video; static char *default_orig_pair; diff --git a/src/w32term.c b/src/w32term.c index c15cbbfa84..e62d49dd94 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -417,7 +417,7 @@ w32_draw_rectangle (HDC hdc, XGCValues *gc, int x, int y, is 1 pixel wider and higher than its arguments WIDTH and HEIGHT. This allows us to keep the code that calls this function similar to the corresponding code in xterm.c. For the details, see - http://lists.gnu.org/archives/html/emacs-devel/2014-10/msg00546.html. */ + https://lists.gnu.org/archives/html/emacs-devel/2014-10/msg00546.html. */ Rectangle (hdc, x, y, x + width + 1, y + height + 1); SelectObject (hdc, oldhb); diff --git a/src/xfns.c b/src/xfns.c index 69955fe9a8..9022e4a967 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -6328,7 +6328,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) } /* FIXME - can this be done in a similar way to normal frames? - http://lists.gnu.org/archive/html/emacs-devel/2007-10/msg00641.html */ + https://lists.gnu.org/archive/html/emacs-devel/2007-10/msg00641.html */ /* Set the `display-type' frame parameter before setting up faces. */ { diff --git a/src/xsettings.c b/src/xsettings.c index f73b791d51..e35c61651c 100644 --- a/src/xsettings.c +++ b/src/xsettings.c @@ -675,8 +675,8 @@ apply_xft_settings (struct x_display_info *dpyinfo, of unrelated settings that override users' font customizations, among others. Compare: - http://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00557.html - http://lists.gnu.org/archive/html/bug-gnu-emacs/2016-12/msg00820.html + https://lists.gnu.org/archive/html/emacs-devel/2016-05/msg00557.html + https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-12/msg00820.html As soon as the dynamic-settings code has been tested and verified, this Emacs 25.2 workaround should be removed. */ diff --git a/src/xterm.c b/src/xterm.c index 90275763cb..d90654b101 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -3870,7 +3870,7 @@ static void x_shift_glyphs_for_insert (struct frame *f, int x, int y, int width, int height, int shift_by) { /* Never called on a GUI frame, see - http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html + https://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html */ XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, @@ -12524,7 +12524,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) dpyinfo->xcb_connection = xcb_conn; #endif - /* http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html */ + /* https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html */ dpyinfo->smallest_font_height = 1; dpyinfo->smallest_char_width = 1; diff --git a/test/ChangeLog.1 b/test/ChangeLog.1 index d244798038..7b228abd1d 100644 --- a/test/ChangeLog.1 +++ b/test/ChangeLog.1 @@ -1307,7 +1307,7 @@ * automated/undo-tests.el (undo-test-in-region-not-most-recent): Add new test of undo in region. (undo-test-in-region-eob): Add test case described at - http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16411 + https://debbugs.gnu.org/cgi/bugreport.cgi?bug=16411 2014-02-28 Michael Albinus diff --git a/test/lisp/replace-tests.el b/test/lisp/replace-tests.el index b98406d8ef..06b6dd8a0a 100644 --- a/test/lisp/replace-tests.el +++ b/test/lisp/replace-tests.el @@ -54,7 +54,7 @@ fx 6:fx ") ;; * Test multi-line matches, this is the first test from - ;; http://lists.gnu.org/archive/html/emacs-devel/2005-06/msg01008.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2005-06/msg01008.html ;; where numbers are replaced with letters. ("a\na" 0 "\ a @@ -70,7 +70,7 @@ a :a ") ;; * Test multi-line matches, this is the second test from - ;; http://lists.gnu.org/archive/html/emacs-devel/2005-06/msg01008.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2005-06/msg01008.html ;; where numbers are replaced with letters. ("a\nb" 0 "\ a diff --git a/test/lisp/vc/vc-bzr-tests.el b/test/lisp/vc/vc-bzr-tests.el index e751f56286..85f401eb37 100644 --- a/test/lisp/vc/vc-bzr-tests.el +++ b/test/lisp/vc/vc-bzr-tests.el @@ -106,7 +106,7 @@ (should (get-buffer "*vc-log*"))) (delete-directory homedir t)))) -;; http://lists.gnu.org/archive/html/help-gnu-emacs/2012-04/msg00145.html +;; https://lists.gnu.org/archive/html/help-gnu-emacs/2012-04/msg00145.html (ert-deftest vc-bzr-test-faulty-bzr-autoloads () "Test we can generate autoloads in a bzr directory when bzr is faulty." (skip-unless (executable-find vc-bzr-program)) diff --git a/test/manual/etags/c-src/emacs/src/lisp.h b/test/manual/etags/c-src/emacs/src/lisp.h index c4b78fc628..0c7da36651 100644 --- a/test/manual/etags/c-src/emacs/src/lisp.h +++ b/test/manual/etags/c-src/emacs/src/lisp.h @@ -510,7 +510,7 @@ enum Lisp_Fwd_Type /* If you want to define a new Lisp data type, here are some instructions. See the thread at - http://lists.gnu.org/archive/html/emacs-devel/2012-10/msg00561.html + https://lists.gnu.org/archive/html/emacs-devel/2012-10/msg00561.html for more info. First, there are already a couple of Lisp types that can be used if commit 8cdd8b920a80e4c61270b0a90f51fb4c8db85c6e Author: Paul Eggert Date: Sun Oct 1 18:30:44 2017 -0700 Merge from Gnulib This is mostly to change http: to https: in licenses. * COPYING, build-aux/config.guess, build-aux/config.sub: * doc/emacs/doclicense.texi, doc/emacs/gpl.texi: * doc/lispintro/doclicense.texi, doc/lispref/doclicense.texi: * doc/lispref/gpl.texi, doc/misc/doclicense.texi: * doc/misc/gpl.texi, etc/COPYING, leim/COPYING: * lib-src/COPYING, lib/COPYING, lisp/COPYING, lwlib/COPYING: * msdos/COPYING, nt/COPYING, src/COPYING: Copy from Gnulib. diff --git a/COPYING b/COPYING index 94a9ed024d..f288702d2f 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. 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 . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/build-aux/config.guess b/build-aux/config.guess index 8bd1095f11..ba1c1439a9 100755 --- a/build-aux/config.guess +++ b/build-aux/config.guess @@ -2,7 +2,7 @@ # Attempt to guess a canonical system name. # Copyright 1992-2017 Free Software Foundation, Inc. -timestamp='2017-09-16' +timestamp='2017-09-26' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -1400,8 +1400,20 @@ EOF exit ;; esac +echo "$0: unable to guess system type" >&2 + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}" in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 <. +. diff --git a/leim/COPYING b/leim/COPYING index 94a9ed024d..f288702d2f 100644 --- a/leim/COPYING +++ b/leim/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. 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 . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/lib-src/COPYING b/lib-src/COPYING index 94a9ed024d..f288702d2f 100644 --- a/lib-src/COPYING +++ b/lib-src/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. 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 . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/lib/COPYING b/lib/COPYING index 94a9ed024d..f288702d2f 100644 --- a/lib/COPYING +++ b/lib/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. 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 . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/lisp/COPYING b/lisp/COPYING index 94a9ed024d..f288702d2f 100644 --- a/lisp/COPYING +++ b/lisp/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. 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 . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/lwlib/COPYING b/lwlib/COPYING index 94a9ed024d..f288702d2f 100644 --- a/lwlib/COPYING +++ b/lwlib/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. 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 . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/msdos/COPYING b/msdos/COPYING index 94a9ed024d..f288702d2f 100644 --- a/msdos/COPYING +++ b/msdos/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. 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 . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/nt/COPYING b/nt/COPYING index 94a9ed024d..f288702d2f 100644 --- a/nt/COPYING +++ b/nt/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. 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 . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/src/COPYING b/src/COPYING index 94a9ed024d..f288702d2f 100644 --- a/src/COPYING +++ b/src/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. 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 . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. commit ee3024c70d2974b59ecdd04b75d18d7258262e70 Author: Stefan Monnier Date: Sun Oct 1 17:15:22 2017 -0400 * lisp/electric.el: Break recursive loading loop (electric-pair-text-pairs): Declare instead of requiring elec-pair. diff --git a/lisp/electric.el b/lisp/electric.el index 599b584c14..5f4304462d 100644 --- a/lisp/electric.el +++ b/lisp/electric.el @@ -39,8 +39,6 @@ ;;; Code: -(eval-when-compile (require 'elec-pair)) - ;; This loop is the guts for non-standard modes which retain control ;; until some event occurs. It is a `do-forever', the only way out is ;; to throw. It assumes that you have set up the keymap, window, and @@ -471,6 +469,8 @@ substitution is inhibited. The functions are called after the after the inserted character. The functions in this hook should not move point or change the current buffer.") +(defvar electric-pair-text-pairs) + (defun electric-quote-post-self-insert-function () "Function that `electric-quote-mode' adds to `post-self-insert-hook'. This requotes when a quoting key is typed." commit 60b7668b89ff00213dd8f66b54698b6ddecb427b Author: Simen Heggestøyl Date: Sun Oct 1 20:24:55 2017 +0200 Keep eww buffer current when looking up CSS on MDN * lisp/textmodes/css-mode.el (css-lookup-symbol): Keep the eww buffer current when looking up CSS documentation on MDN. This fixes a bug where the eww buffer's content sometimes get mangled when switching buffers mid-render. diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el index ce9bbf47e7..9022ab7c3f 100644 --- a/lisp/textmodes/css-mode.el +++ b/lisp/textmodes/css-mode.el @@ -1659,14 +1659,13 @@ on what is seen near point." (setq symbol (concat ":" symbol))) (let ((url (format css-lookup-url-format symbol)) (buffer (get-buffer-create "*MDN CSS*"))) - (save-selected-window - ;; Make sure to display the buffer before calling `eww', as - ;; that calls `pop-to-buffer-same-window'. - (switch-to-buffer-other-window buffer) - (with-current-buffer buffer - (eww-mode) - (add-hook 'eww-after-render-hook #'css--mdn-after-render nil t) - (eww url)))))) + ;; Make sure to display the buffer before calling `eww', as that + ;; calls `pop-to-buffer-same-window'. + (switch-to-buffer-other-window buffer) + (with-current-buffer buffer + (eww-mode) + (add-hook 'eww-after-render-hook #'css--mdn-after-render nil t) + (eww url))))) (provide 'css-mode) ;;; css-mode.el ends here commit bd49b6f1b39cffeaf6098bc7b0182552683b1c07 Author: Charles A. Roelli Date: Sat Sep 30 20:42:03 2017 +0200 Workaround for faulty localtime() under macOS 10.6 * lisp/org/org-clock.el (org-clock--oldest-date): Only execute 'decode-time' on times later than year -2**31 under macOS 10.6. See Bug#27706. diff --git a/lisp/org/org-clock.el b/lisp/org/org-clock.el index 8df185d2e9..2eec817735 100644 --- a/lisp/org/org-clock.el +++ b/lisp/org/org-clock.el @@ -478,7 +478,17 @@ to add an effort property.") (funcall dichotomy most-negative-fixnum 0 - (lambda (m) (ignore-errors (decode-time (list m 0)))))) + (lambda (m) + ;; libc in macOS 10.6 hangs when decoding times + ;; around year -2**31. Limit `high' not to go + ;; any earlier than that. + (unless (and (eq system-type 'darwin) + (string-match-p + "10\\.6\\.[[:digit:]]" + (shell-command-to-string + "sw_vers -productVersion")) + (<= m -1034058203136)) + (ignore-errors (decode-time (list m 0))))))) (low (funcall dichotomy most-negative-fixnum commit 66d37175ecac41dfb2f854dbb148dcc7ca87b345 Author: Philipp Stephani Date: Sat Sep 30 20:40:02 2017 +0200 * src/editfns.c (Fchar_after): Small optimization. diff --git a/src/editfns.c b/src/editfns.c index e326604467..4dcf7cbe6e 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -1256,10 +1256,10 @@ If POS is out of range, the value is nil. */) if (NILP (pos)) { pos_byte = PT_BYTE; - XSETFASTINT (pos, PT); + if (pos_byte < BEGV_BYTE || pos_byte >= ZV_BYTE) + return Qnil; } - - if (MARKERP (pos)) + else if (MARKERP (pos)) { pos_byte = marker_byte_position (pos); if (pos_byte < BEGV_BYTE || pos_byte >= ZV_BYTE) commit bb47c72de35312b742a1964b31ff315727baed00 Author: Eli Zaretskii Date: Sun Oct 1 19:12:30 2017 +0300 Avoid compilation warning in electric.el * lisp/electric.el: Require 'elec-pair' when compiling, to avoid a compiler warning. diff --git a/lisp/electric.el b/lisp/electric.el index 65e36b7a63..599b584c14 100644 --- a/lisp/electric.el +++ b/lisp/electric.el @@ -39,6 +39,8 @@ ;;; Code: +(eval-when-compile (require 'elec-pair)) + ;; This loop is the guts for non-standard modes which retain control ;; until some event occurs. It is a `do-forever', the only way out is ;; to throw. It assumes that you have set up the keymap, window, and commit 913808e224455dc3cd3d7ea0ff5d36849319954a Author: Alan Mackenzie Date: Sun Oct 1 16:08:20 2017 +0000 Doc amendment for syntax-ppss. * doc/elisp/syntax.texi (Position Parse): Note, twice, that syntax-ppss is equivalent to parse-partial-sexp from the beginning of THE VISIBLE PART OF the buffer. Final part of the fix for bug #22983. diff --git a/doc/lispref/syntax.texi b/doc/lispref/syntax.texi index e3ae53536f..b37f2b22b8 100644 --- a/doc/lispref/syntax.texi +++ b/doc/lispref/syntax.texi @@ -751,7 +751,8 @@ position. This function does that conveniently. @defun syntax-ppss &optional pos This function returns the parser state that the parser would reach at -position @var{pos} starting from the beginning of the buffer. +position @var{pos} starting from the beginning of the visible portion +of the buffer. @iftex See the next section for @end iftex @@ -762,11 +763,11 @@ for a description of the parser state. The return value is the same as if you call the low-level parsing function @code{parse-partial-sexp} to parse from the beginning of the -buffer to @var{pos} (@pxref{Low-Level Parsing}). However, -@code{syntax-ppss} uses a cache to speed up the computation. Due to -this optimization, the second value (previous complete subexpression) -and sixth value (minimum parenthesis depth) in the returned parser -state are not meaningful. +visible portion of the buffer to @var{pos} (@pxref{Low-Level +Parsing}). However, @code{syntax-ppss} uses caches to speed up the +computation. Due to this optimization, the second value (previous +complete subexpression) and sixth value (minimum parenthesis depth) in +the returned parser state are not meaningful. This function has a side effect: it adds a buffer-local entry to @code{before-change-functions} (@pxref{Change Hooks}) for commit 98dc91fda8d81e6b51e71fd4a034f6cb87537d52 Author: Charles A. Roelli Date: Sun Oct 1 17:41:49 2017 +0200 Remove incorrect NEWS entry about 'find-library' * etc/NEWS (Changes in Emacs 26.1): Remove an entry about 'find-library' taking a prefix argument to pop to a different window. This behavior was added in "Allow a prefix argument to find-library to pop to a different window" (commit e1f2d14a), and then removed in "New commands: find-library-other-window, find-library-other-frame" (commit 021430f4). diff --git a/etc/NEWS b/etc/NEWS index 51122b1152..b734e8dd19 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -299,10 +299,6 @@ header line. ** 'C-x h' ('mark-whole-buffer') will now avoid marking the prompt part of minibuffers. ---- -** 'find-library' now takes a prefix argument to pop to a different -window. - --- ** 'fill-paragraph' no longer marks the buffer as changed unless it actually changed something. commit 539d8626cda36957adc480d7f63c3557ad169f70 Author: Alan Mackenzie Date: Sun Oct 1 10:01:33 2017 +0000 Remove inadvertent changes to syntax.texi in last commit. * doc/lispref/syntax.texi (Position Parse): revert changes. diff --git a/doc/lispref/syntax.texi b/doc/lispref/syntax.texi index b37f2b22b8..e3ae53536f 100644 --- a/doc/lispref/syntax.texi +++ b/doc/lispref/syntax.texi @@ -751,8 +751,7 @@ position. This function does that conveniently. @defun syntax-ppss &optional pos This function returns the parser state that the parser would reach at -position @var{pos} starting from the beginning of the visible portion -of the buffer. +position @var{pos} starting from the beginning of the buffer. @iftex See the next section for @end iftex @@ -763,11 +762,11 @@ for a description of the parser state. The return value is the same as if you call the low-level parsing function @code{parse-partial-sexp} to parse from the beginning of the -visible portion of the buffer to @var{pos} (@pxref{Low-Level -Parsing}). However, @code{syntax-ppss} uses caches to speed up the -computation. Due to this optimization, the second value (previous -complete subexpression) and sixth value (minimum parenthesis depth) in -the returned parser state are not meaningful. +buffer to @var{pos} (@pxref{Low-Level Parsing}). However, +@code{syntax-ppss} uses a cache to speed up the computation. Due to +this optimization, the second value (previous complete subexpression) +and sixth value (minimum parenthesis depth) in the returned parser +state are not meaningful. This function has a side effect: it adds a buffer-local entry to @code{before-change-functions} (@pxref{Change Hooks}) for commit 8c18dcbc784a97196d6513e5556de48af4cea617 Author: Alan Mackenzie Date: Sat Sep 30 11:08:16 2017 +0000 Amend documentation for text-quoting-style becoming a user option. * doc/lispref/control.texi (Signaling Errors): * doc/lispref/display.texi (Displaying Messages): * doc/lispref/strings.texi (Formatting Strings): Edit for brevity, farming out the details to the new Text Quoting Style node. * doc/lispref/help.texi (Text Quoting Style): New section. Move detailed discussion of text-quoting-style here. Add discussion about how to output grave accent and apostrophe in documentation and messages. Adjust xrefs to point to this section when appropriate. * etc/NEWS: text-quoting-style semantics have not changed. diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index c39e035459..4eddbe9c12 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -1101,16 +1101,10 @@ These examples show typical uses of @code{error}: error symbol @code{error}, and a list containing the string returned by @code{format-message}. -The @code{text-quoting-style} variable controls what quotes are -generated; @xref{Keys in Documentation}. A call using a format like -@t{"Missing `%s'"} with grave accents and apostrophes typically -generates a message like @t{"Missing ‘foo’"} with matching curved -quotes. In contrast, a call using a format like @t{"Missing '%s'"} -with only apostrophes typically generates a message like @t{"Missing -’foo’"} with only closing curved quotes, an unusual style in English. -One way around this problem is to bind @code{text-quoting-style} to -the symbol @code{grave} around the call to @code{error}; this causes -@acronym{ASCII} quote characters to be output unchanged. +Typically grave accent and apostrophe in the format translate to +matching curved quotes, e.g., @t{"Missing `%s'"} might result in +@t{"Missing ‘foo’"}. @xref{Text Quoting Style}, for how to influence +or inhibit this translation. @strong{Warning:} If you want to use your own string as an error message verbatim, don't just write @code{(error @var{string})}. If @var{string} diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 62b136f6c6..afd09cfb33 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -265,16 +265,10 @@ properties, it is displayed with the specified faces (@pxref{Faces}). The string is also added to the @file{*Messages*} buffer, but without text properties (@pxref{Logging Messages}). -The @code{text-quoting-style} variable controls what quotes are -generated; @xref{Keys in Documentation}. A call using a format like -@t{"Missing `%s'"} with grave accents and apostrophes typically -generates a message like @t{"Missing ‘foo’"} with matching curved -quotes. In contrast, a call using a format like @t{"Missing '%s'"} -with only apostrophes typically generates a message like @t{"Missing -’foo’"} with only closing curved quotes, an unusual style in English. -One way around this problem is to bind @code{text-quoting-style} to -the symbol @code{grave} around calls to @code{message}; this causes -@acronym{ASCII} quote characters to be output unchanged. +Typically grave accent and apostrophe in the format translate to +matching curved quotes, e.g., @t{"Missing `%s'"} might result in +@t{"Missing ‘foo’"}. @xref{Text Quoting Style}, for how to influence +or inhibit this translation. In batch mode, the message is printed to the standard error stream, followed by a newline. @@ -7038,7 +7032,7 @@ window display table nor a buffer display table defined, or when Emacs is outputting text to the standard output or error streams. Although its default is typically @code{nil}, in an interactive session if the terminal cannot display curved quotes, its default maps curved quotes -to ASCII approximations. @xref{Keys in Documentation}. +to ASCII approximations. @xref{Text Quoting Style}. @end defvar The @file{disp-table} library defines several functions for changing diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index 4cbcdf855d..c752594584 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -940,6 +940,7 @@ Documentation * Documentation Basics:: Where doc strings are defined and stored. * Accessing Documentation:: How Lisp programs can access doc strings. * Keys in Documentation:: Substituting current key bindings. +* Text Quoting Style:: Quotation marks in doc strings and messages. * Describing Characters:: Making printable descriptions of non-printing characters and key sequences. * Help Functions:: Subroutines used by Emacs help facilities. diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi index 74dc6dac9c..4aa9b95180 100644 --- a/doc/lispref/help.texi +++ b/doc/lispref/help.texi @@ -33,6 +33,7 @@ Help, emacs, The GNU Emacs Manual}. * Documentation Basics:: Where doc strings are defined and stored. * Accessing Documentation:: How Lisp programs can access doc strings. * Keys in Documentation:: Substituting current key bindings. +* Text Quoting Style:: Quotation marks in doc strings and messages. * Describing Characters:: Making printable descriptions of non-printing characters and key sequences. * Help Functions:: Subroutines used by Emacs help facilities. @@ -336,6 +337,7 @@ specifies @var{mapvar}'s value as the keymap for any following (grave accent) stands for a left quote. This generates a left single quotation mark, an apostrophe, or a grave accent depending on the value of @code{text-quoting-style}. +@xref{Text Quoting Style}. @item ' (apostrophe) stands for a right quote. @@ -428,6 +430,53 @@ C-g abort-recursive-edit strings---for instance, you can refer to functions, variables, and sections of this manual. @xref{Documentation Tips}, for details. +@node Text Quoting Style +@section Text Quoting Style + + Typically, grave accents and apostrophes are treated specially in +documentation strings and diagnostic messages, and translate to matching +single quotation marks (also called ``curved quotes''). For example, +the documentation string @t{"Alias for `foo'."} and the function call +@code{(message "Alias for `foo'.")} both translate to @t{"Alias for +‘foo’."}. Less commonly, Emacs displays grave accents and apostrophes +as themselves, or as apostrophes only (e.g., @t{"Alias for 'foo'."}). +Documentation strings and message formats should be written so that +they display well with any of these styles. For example, the +documentation string @t{"Alias for 'foo'."} is probably not what you +want, as it can display as @t{"Alias for ’foo’."}, an unusual style in +English. + + Sometimes you may need to display a grave accent or apostrophe +without translation, regardless of text quoting style. In a +documentation string, you can do this with escapes. For example, in +the documentation string @t{"\\=`(a ,(sin 0)) ==> (a 0.0)"} the grave +accent is intended to denote Lisp code, so it is escaped and displays +as itself regardless of quoting style. In a call to @code{message} or +@code{error}, you can avoid translation by using a format @t{"%s"} +with an argument that is a call to @code{format}. For example, +@code{(message "%s" (format "`(a ,(sin %S)) ==> (a %S)" x (sin x)))} +displays a message that starts with grave accent regardless of text +quoting style. + +@defopt text-quoting-style +@cindex curved quotes +@cindex curly quotes +The value of this user option is a symbol that specifies the style +Emacs should use for single quotes in the wording of help and +messages. If the option's value is @code{curve}, the style is +@t{‘like this’} with curved single quotes. If the value is +@code{straight}, the style is @t{'like this'} with straight +apostrophes. If the value is @code{grave}, quotes are not translated +and the style is @t{`like this'} with grave accent and apostrophe, the +standard style before Emacs version 25. The default value @code{nil} +acts like @code{curve} if curved single quotes seem to be displayable, +and like @code{grave} otherwise. + +This option is useful on platforms that have problems with curved +quotes. You can customize it freely according to your personal +preference. +@end defopt + @node Describing Characters @section Describing Characters for Help Messages @cindex describe characters and events diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index 10385e0550..dd004927ca 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -826,20 +826,14 @@ to the produced string representations of the argument @var{objects}. @defun format-message string &rest objects @cindex curved quotes, in formatted messages @cindex curly quotes, in formatted messages -@cindex @code{text-quoting-style}, and formatting messages This function acts like @code{format}, except it also converts any grave accents (@t{`}) and apostrophes (@t{'}) in @var{string} as per the value of @code{text-quoting-style}. -A format that quotes with grave accents and apostrophes @t{`like -this'} typically generates curved quotes @t{‘like this’}. In -contrast, a format that quotes with only apostrophes @t{'like this'} -typically generates two closing curved quotes @t{’like this’}, an -unusual style in English. One way around such problems is to bind -@code{text-quoting-style} to the symbol @code{grave} around calls to -@code{format-message}; this causes @acronym{ASCII} quoting characters -to be output unchanged. @xref{Keys in Documentation}, for how the -@code{text-quoting-style} variable affects generated quotes. +Typically grave accent and apostrophe in the format translate to +matching curved quotes, e.g., @t{"Missing `%s'"} might result in +@t{"Missing ‘foo’"}. @xref{Text Quoting Style}, for how to influence +or inhibit this translation. @end defun @cindex @samp{%} in format diff --git a/doc/lispref/syntax.texi b/doc/lispref/syntax.texi index e3ae53536f..b37f2b22b8 100644 --- a/doc/lispref/syntax.texi +++ b/doc/lispref/syntax.texi @@ -751,7 +751,8 @@ position. This function does that conveniently. @defun syntax-ppss &optional pos This function returns the parser state that the parser would reach at -position @var{pos} starting from the beginning of the buffer. +position @var{pos} starting from the beginning of the visible portion +of the buffer. @iftex See the next section for @end iftex @@ -762,11 +763,11 @@ for a description of the parser state. The return value is the same as if you call the low-level parsing function @code{parse-partial-sexp} to parse from the beginning of the -buffer to @var{pos} (@pxref{Low-Level Parsing}). However, -@code{syntax-ppss} uses a cache to speed up the computation. Due to -this optimization, the second value (previous complete subexpression) -and sixth value (minimum parenthesis depth) in the returned parser -state are not meaningful. +visible portion of the buffer to @var{pos} (@pxref{Low-Level +Parsing}). However, @code{syntax-ppss} uses caches to speed up the +computation. Due to this optimization, the second value (previous +complete subexpression) and sixth value (minimum parenthesis depth) in +the returned parser state are not meaningful. This function has a side effect: it adds a buffer-local entry to @code{before-change-functions} (@pxref{Change Hooks}) for diff --git a/etc/NEWS b/etc/NEWS index 20182c1b81..51122b1152 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1247,10 +1247,8 @@ change FOO, respectively. The exhaustive list of removed variables is: ** The variable 'text-quoting-style' is now a customizable option. It controls whether to and how to translate ASCII quotes in messages and help output. Its possible values and their semantics remain unchanged -from Emacs 25, except that 'text-quoting-style' no longer affects the -treatment of curved quotes in format arguments to functions like -'message' and 'format-message'. In particular, when this variable's -value is 'grave', all quotes in formats are output as-is. +from Emacs 25. In particular, when this variable's value is 'grave', +all quotes in formats are output as-is. --- ** Functions like 'check-declare-file' and 'check-declare-directory' commit 5f76ac150a28e4de940790f96f0f751c8ee5d4c7 Author: Alan Mackenzie Date: Fri Sep 22 21:52:03 2017 +0000 Make the value nil in text-quoting-style mean what it does in Emacs 25. This is a partial reversion of yesterday's commit by the same author, which changed the meaning of nil and introduced the new value t. * src/doc.c (text_quoting_style, text-quoting-style) (internal--text-quoting-flag): Revert yesterday's changes. * lisp/cus-start.el: (top level): Amend the entry for text-quoting-style. * etc/NEWS: Amend the entry for text-quoting-style. * doc/lispref/control.texi (Signalling Errors) * doc/lispref/display.texi (Displaying Messages) * doc/lispref/strings.texi (Formatting Strings): Bind text-quoting-style to grave rather than nil to inhibit translation of quotes. * doc/lispref/help.texi (Keys in Documentation): Revert the description of the proposed new default, t. diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index 09435f5796..c39e035459 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -1109,7 +1109,7 @@ quotes. In contrast, a call using a format like @t{"Missing '%s'"} with only apostrophes typically generates a message like @t{"Missing ’foo’"} with only closing curved quotes, an unusual style in English. One way around this problem is to bind @code{text-quoting-style} to -@code{nil} around the call to @code{error}; this causes the +the symbol @code{grave} around the call to @code{error}; this causes @acronym{ASCII} quote characters to be output unchanged. @strong{Warning:} If you want to use your own string as an error message diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 8f58fca506..62b136f6c6 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -273,7 +273,7 @@ quotes. In contrast, a call using a format like @t{"Missing '%s'"} with only apostrophes typically generates a message like @t{"Missing ’foo’"} with only closing curved quotes, an unusual style in English. One way around this problem is to bind @code{text-quoting-style} to -@code{nil} around calls to @code{message}; this causes the +the symbol @code{grave} around calls to @code{message}; this causes @acronym{ASCII} quote characters to be output unchanged. In batch mode, the message is printed to the standard error stream, diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi index e1e98124e0..74dc6dac9c 100644 --- a/doc/lispref/help.texi +++ b/doc/lispref/help.texi @@ -359,11 +359,11 @@ should use for single quotes in the wording of help and messages. If the variable's value is @code{curve}, the style is @t{‘like this’} with curved single quotes. If the value is @code{straight}, the style is @t{'like this'} with straight apostrophes. If the value is -@code{nil} or @code{grave}, quotes are not translated and the style is -@t{`like this'} with grave accent and apostrophe, the standard style -before Emacs version 25. The default value @code{t} acts like -@code{curve} if curved single quotes seem to be displayable, and like -@code{nil} otherwise. +@code{grave}, quotes are not translated and the style is @t{`like +this'} with grave accent and apostrophe, the standard style before +Emacs version 25. The default value @code{nil} acts like @code{curve} +if curved single quotes seem to be displayable, and like @code{grave} +otherwise. This option is useful on platforms that have problems with curved quotes. You can customize it freely according to your personal diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index 117a373a19..10385e0550 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -836,10 +836,10 @@ this'} typically generates curved quotes @t{‘like this’}. In contrast, a format that quotes with only apostrophes @t{'like this'} typically generates two closing curved quotes @t{’like this’}, an unusual style in English. One way around such problems is to bind -@code{text-quoting-style} to @code{nil} around calls to -@code{format-message}; this causes the @acronym{ASCII} quoting -characters to be output unchanged. @xref{Keys in Documentation}, for -how the @code{text-quoting-style} variable affects generated quotes. +@code{text-quoting-style} to the symbol @code{grave} around calls to +@code{format-message}; this causes @acronym{ASCII} quoting characters +to be output unchanged. @xref{Keys in Documentation}, for how the +@code{text-quoting-style} variable affects generated quotes. @end defun @cindex @samp{%} in format diff --git a/etc/NEWS b/etc/NEWS index d518d5e277..20182c1b81 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1244,15 +1244,13 @@ change FOO, respectively. The exhaustive list of removed variables is: *** Many variables obsoleted in 22.1 referring to face symbols +++ -** The variable `text-quoting-style' is now a customizable option. It +** The variable 'text-quoting-style' is now a customizable option. It controls whether to and how to translate ASCII quotes in messages and -help output. The value nil now means "no translation", and the value -t, the default, means "use curved quotes if displayable" (as nil did -in Emacs 25). The other possible values remain unchanged. -`text-quoting-style' no longer affects the treatment of curved quotes -in format arguments to functions like `message' and `format-message'. -In particular, when this variable's value is `grave', all quotes in -formats are output as-is. +help output. Its possible values and their semantics remain unchanged +from Emacs 25, except that 'text-quoting-style' no longer affects the +treatment of curved quotes in format arguments to functions like +'message' and 'format-message'. In particular, when this variable's +value is 'grave', all quotes in formats are output as-is. --- ** Functions like 'check-declare-file' and 'check-declare-directory' diff --git a/lisp/cus-start.el b/lisp/cus-start.el index f5e1431f6b..a5ec223fe5 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -226,11 +226,10 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of ;; doc.c (text-quoting-style display (choice - (const :tag "No translation" nil) - (const :tag "Prefer \\=‘curved\\=’ quotes, if possible" t) + (const :tag "Prefer \\=‘curved\\=’ quotes, if possible" nil) (const :tag "\\=‘Curved\\=’ quotes" curved) (const :tag "\\='Straight\\=' quotes" straight) - (const :tag "\\=`Grave\\=' quotes" grave))) + (const :tag "\\=`Grave\\=' quotes (no translation)" grave))) ;; dosfns.c (dos-display-scancodes display boolean) diff --git a/src/doc.c b/src/doc.c index e2af39ef07..3286c12675 100644 --- a/src/doc.c +++ b/src/doc.c @@ -690,9 +690,8 @@ enum text_quoting_style text_quoting_style (void) { if (NILP (Vtext_quoting_style) - || (EQ (Vtext_quoting_style, Qt) - ? default_to_grave_quoting_style () - : EQ (Vtext_quoting_style, Qgrave))) + ? default_to_grave_quoting_style () + : EQ (Vtext_quoting_style, Qgrave)) return GRAVE_QUOTING_STYLE; else if (EQ (Vtext_quoting_style, Qstraight)) return STRAIGHT_QUOTING_STYLE; @@ -1019,25 +1018,22 @@ syms_of_doc (void) Vbuild_files = Qnil; DEFVAR_LISP ("text-quoting-style", Vtext_quoting_style, - doc: /* How to translate single quotes in help and messages. -Its value should be a symbol, and describes the style of quote -substituted for ASCII quote characters GRAVE ACCENT (\\=`, \\=\\x60) and -APOSTROPHE (\\=', \\=\\x27). This is done in help output and in functions -like `message' and `format-message'. It is not done in `format'. - -The value nil means do not translate the quotes at all. The value t -(the default) acts like `curve' if curved single quotes appear to be -displayable, and like nil otherwise. `curve' means quote with curved -single quotes ‘like this’. `straight' means quote with apostrophes -\\='like this\\='. `grave' means do not translate quote marks and is -now a synonym for nil. - -(The value t was newly introduced in Emacs 26, and in Emacs 25 nil -meant what t means now.) */); - Vtext_quoting_style = Qt; + doc: /* Style to use for single quotes in help and messages. +Its value should be a symbol. It works by substituting certain single +quotes for grave accent and apostrophe. This is done in help output +and in functions like `message' and `format-message'. It is not done +in `format'. + +`curve' means quote with curved single quotes ‘like this’. +`straight' means quote with straight apostrophes \\='like this\\='. +`grave' means quote with grave accent and apostrophe \\=`like this\\='; +i.e., do not alter quote marks. The default value nil acts like +`curve' if curved single quotes are displayable, and like `grave' +otherwise. */); + Vtext_quoting_style = Qnil; DEFVAR_BOOL ("internal--text-quoting-flag", text_quoting_flag, - doc: /* If nil, a `text-quoting-style' value t is treated as `nil'. */); + doc: /* If nil, a nil `text-quoting-style' is treated as `grave'. */); /* Initialized by ‘main’. */ defsubr (&Sdocumentation); commit d5e4e004fa134cb81989bcf40c5d6c79b837301f Author: Alan Mackenzie Date: Thu Sep 21 20:31:06 2017 +0000 Make text-quoting-style customizable. Introduce t and new meaning for nil. A value of nil for text-quoting-style now means "no translation". t means "Use curved quotes if displayable". * src/doc.c (text-quoting-style (function)): modify for new semantics. (text-quoting-style (variable)): Amend the doc string, set the default value to t. * lisp/cus-start.el: (top level): Create a customize entry for text-quoting-style in group display. * etc/NEWS: Amend the entry for text-quoting-style. * doc/emacs/display.texi (Text Display): Describe the translation of ASCII quotes to curved quotes, and how to influence or inhibit it. * doc/lispref/control.texi (Signalling Errors) * doc/lispref/display.texi (Displaying Messages) * doc/lispref/strings.texi (Formatting Strings): Describe binding text-quoting-style to nil to inhibit unwanted quote translation. * doc/lispref/help.texi (Keys in Documentation): Change text-quoting-style from a variable to a user option. Describe its changed set of values. State that it can be customized freely. diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index 6afd8366b2..5860bacb9d 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -1548,11 +1548,20 @@ for details. @cindex curly quotes @cindex curved quotes @cindex homoglyph face + +Emacs tries to determine if the curved quotes @samp{‘} and @samp{’} +can be displayed on the current display. By default, if this seems to +be so, then Emacs will translate the @acronym{ASCII} quotes (@samp{`} +and @samp{'}), when they appear in messages and help texts, to these +curved quotes. You can influence or inhibit this translation by +customizing the user option @code{text-quoting-style} (@pxref{Keys in +Documentation,,, elisp, The Emacs Lisp Reference Manual}). + If the curved quotes @samp{‘}, @samp{’}, @samp{“}, and @samp{”} are known to look just like @acronym{ASCII} characters, they are shown -with the @code{homoglyph} face. Curved quotes that cannot be -displayed are shown as their @acronym{ASCII} approximations @samp{`}, -@samp{'}, and @samp{"} with the @code{homoglyph} face. +with the @code{homoglyph} face. Curved quotes that are known not to +be displayable are shown as their @acronym{ASCII} approximations +@samp{`}, @samp{'}, and @samp{"} with the @code{homoglyph} face. @node Cursor Display @section Displaying the Cursor diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index 401a999cf2..09435f5796 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -1108,6 +1108,9 @@ generates a message like @t{"Missing ‘foo’"} with matching curved quotes. In contrast, a call using a format like @t{"Missing '%s'"} with only apostrophes typically generates a message like @t{"Missing ’foo’"} with only closing curved quotes, an unusual style in English. +One way around this problem is to bind @code{text-quoting-style} to +@code{nil} around the call to @code{error}; this causes the +@acronym{ASCII} quote characters to be output unchanged. @strong{Warning:} If you want to use your own string as an error message verbatim, don't just write @code{(error @var{string})}. If @var{string} diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 3dae984f33..8f58fca506 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -272,6 +272,9 @@ generates a message like @t{"Missing ‘foo’"} with matching curved quotes. In contrast, a call using a format like @t{"Missing '%s'"} with only apostrophes typically generates a message like @t{"Missing ’foo’"} with only closing curved quotes, an unusual style in English. +One way around this problem is to bind @code{text-quoting-style} to +@code{nil} around calls to @code{message}; this causes the +@acronym{ASCII} quote characters to be output unchanged. In batch mode, the message is printed to the standard error stream, followed by a newline. diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi index cb21411352..e1e98124e0 100644 --- a/doc/lispref/help.texi +++ b/doc/lispref/help.texi @@ -351,25 +351,24 @@ and @samp{\=\=} puts @samp{\=} into the output. @strong{Please note:} Each @samp{\} must be doubled when written in a string in Emacs Lisp. -@defvar text-quoting-style +@defopt text-quoting-style @cindex curved quotes @cindex curly quotes The value of this variable is a symbol that specifies the style Emacs -should use for single quotes in the wording of help and messages. -If the variable's value is @code{curve}, the style is -@t{‘like this’} with curved single quotes. If the value is -@code{straight}, the style is @t{'like this'} with straight -apostrophes. If the value is @code{grave}, -quotes are not translated and the style is @t{`like -this'} with grave accent and apostrophe, the standard style -before Emacs version 25. The default value @code{nil} -acts like @code{curve} if curved single quotes are displayable, and -like @code{grave} otherwise. - -This variable can be used by experts on platforms that have problems -with curved quotes. As it is not intended for casual use, it is not a -user option. -@end defvar +should use for single quotes in the wording of help and messages. If +the variable's value is @code{curve}, the style is @t{‘like this’} +with curved single quotes. If the value is @code{straight}, the style +is @t{'like this'} with straight apostrophes. If the value is +@code{nil} or @code{grave}, quotes are not translated and the style is +@t{`like this'} with grave accent and apostrophe, the standard style +before Emacs version 25. The default value @code{t} acts like +@code{curve} if curved single quotes seem to be displayable, and like +@code{nil} otherwise. + +This option is useful on platforms that have problems with curved +quotes. You can customize it freely according to your personal +preference. +@end defopt @defun substitute-command-keys string This function scans @var{string} for the above special sequences and diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index 219225d412..117a373a19 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -835,8 +835,11 @@ A format that quotes with grave accents and apostrophes @t{`like this'} typically generates curved quotes @t{‘like this’}. In contrast, a format that quotes with only apostrophes @t{'like this'} typically generates two closing curved quotes @t{’like this’}, an -unusual style in English. @xref{Keys in Documentation}, for how the -@code{text-quoting-style} variable affects generated quotes. +unusual style in English. One way around such problems is to bind +@code{text-quoting-style} to @code{nil} around calls to +@code{format-message}; this causes the @acronym{ASCII} quoting +characters to be output unchanged. @xref{Keys in Documentation}, for +how the @code{text-quoting-style} variable affects generated quotes. @end defun @cindex @samp{%} in format diff --git a/etc/NEWS b/etc/NEWS index 2216cfc18e..d518d5e277 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1244,10 +1244,15 @@ change FOO, respectively. The exhaustive list of removed variables is: *** Many variables obsoleted in 22.1 referring to face symbols +++ -** The variable 'text-quoting-style' no longer affects the treatment -of curved quotes in format arguments to functions like 'message' and -'format-message'. In particular, when this variable's value is -'grave', all quotes in formats are output as-is. +** The variable `text-quoting-style' is now a customizable option. It +controls whether to and how to translate ASCII quotes in messages and +help output. The value nil now means "no translation", and the value +t, the default, means "use curved quotes if displayable" (as nil did +in Emacs 25). The other possible values remain unchanged. +`text-quoting-style' no longer affects the treatment of curved quotes +in format arguments to functions like `message' and `format-message'. +In particular, when this variable's value is `grave', all quotes in +formats are output as-is. --- ** Functions like 'check-declare-file' and 'check-declare-directory' diff --git a/lisp/cus-start.el b/lisp/cus-start.el index fd015b70ca..f5e1431f6b 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -223,6 +223,15 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of (visible-bell display boolean) (no-redraw-on-reenter display boolean) + ;; doc.c + (text-quoting-style display + (choice + (const :tag "No translation" nil) + (const :tag "Prefer \\=‘curved\\=’ quotes, if possible" t) + (const :tag "\\=‘Curved\\=’ quotes" curved) + (const :tag "\\='Straight\\=' quotes" straight) + (const :tag "\\=`Grave\\=' quotes" grave))) + ;; dosfns.c (dos-display-scancodes display boolean) (dos-hyper-key keyboard integer) diff --git a/src/doc.c b/src/doc.c index 3286c12675..e2af39ef07 100644 --- a/src/doc.c +++ b/src/doc.c @@ -690,8 +690,9 @@ enum text_quoting_style text_quoting_style (void) { if (NILP (Vtext_quoting_style) - ? default_to_grave_quoting_style () - : EQ (Vtext_quoting_style, Qgrave)) + || (EQ (Vtext_quoting_style, Qt) + ? default_to_grave_quoting_style () + : EQ (Vtext_quoting_style, Qgrave))) return GRAVE_QUOTING_STYLE; else if (EQ (Vtext_quoting_style, Qstraight)) return STRAIGHT_QUOTING_STYLE; @@ -1018,22 +1019,25 @@ syms_of_doc (void) Vbuild_files = Qnil; DEFVAR_LISP ("text-quoting-style", Vtext_quoting_style, - doc: /* Style to use for single quotes in help and messages. -Its value should be a symbol. It works by substituting certain single -quotes for grave accent and apostrophe. This is done in help output -and in functions like `message' and `format-message'. It is not done -in `format'. - -`curve' means quote with curved single quotes ‘like this’. -`straight' means quote with straight apostrophes \\='like this\\='. -`grave' means quote with grave accent and apostrophe \\=`like this\\='; -i.e., do not alter quote marks. The default value nil acts like -`curve' if curved single quotes are displayable, and like `grave' -otherwise. */); - Vtext_quoting_style = Qnil; + doc: /* How to translate single quotes in help and messages. +Its value should be a symbol, and describes the style of quote +substituted for ASCII quote characters GRAVE ACCENT (\\=`, \\=\\x60) and +APOSTROPHE (\\=', \\=\\x27). This is done in help output and in functions +like `message' and `format-message'. It is not done in `format'. + +The value nil means do not translate the quotes at all. The value t +(the default) acts like `curve' if curved single quotes appear to be +displayable, and like nil otherwise. `curve' means quote with curved +single quotes ‘like this’. `straight' means quote with apostrophes +\\='like this\\='. `grave' means do not translate quote marks and is +now a synonym for nil. + +(The value t was newly introduced in Emacs 26, and in Emacs 25 nil +meant what t means now.) */); + Vtext_quoting_style = Qt; DEFVAR_BOOL ("internal--text-quoting-flag", text_quoting_flag, - doc: /* If nil, a nil `text-quoting-style' is treated as `grave'. */); + doc: /* If nil, a `text-quoting-style' value t is treated as `nil'. */); /* Initialized by ‘main’. */ defsubr (&Sdocumentation); commit 1ba3471b9b443f0617662f4a50439bec211162ba Author: Michael Albinus Date: Sun Oct 1 13:31:39 2017 +0200 eshell.texi improvements * doc/misc/eshell.texi (Built-ins): eshell/sudo is a compiled Lisp function in `em-tramp.el'. Mention also $*, $1, $2, ... (Aliases): Add $*, $1, $2, ... to the variable index. diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index 8a607ef770..73f9a9562b 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -239,7 +239,6 @@ especially for file names with special characters like pipe (@code{|}), which could be part of remote file names. @node Built-ins - @section Built-in commands Several commands are built-in in Eshell. In order to call the external variant of a built-in command @code{foo}, you could call @@ -258,7 +257,7 @@ alias, @ref{Aliases}. Example: @example ~ $ which sudo -eshell/sudo is a compiled Lisp function in `em-unix.el' +eshell/sudo is a compiled Lisp function in `em-tramp.el'. ~ $ alias sudo '*sudo $*' ~ $ which sudo sudo is an alias, defined as "*sudo $*" @@ -419,6 +418,9 @@ Lisp functions, based on successful completion). @end table +@ref{Aliases} for the built-in variables @samp{$*}, @samp{$1}, +@samp{$2}, @dots{}, in alias definitions. + @node Variables @section Variables Since Eshell is just an Emacs REPL@footnote{Read-Eval-Print Loop}, it @@ -429,6 +431,7 @@ would in an Elisp program. Eshell provides a command version of @node Aliases @section Aliases +@vindex $* Aliases are commands that expand to a longer input line. For example, @command{ll} is a common alias for @code{ls -l}, and would be defined with the command invocation @kbd{alias ll 'ls -l $*'}; with this defined, @@ -438,6 +441,7 @@ automatically written to the file named by @code{eshell-aliases-file}, which you can also edit directly (although you will have to manually reload it). +@vindex $1, $2, @dots{} Note that unlike aliases in Bash, arguments must be handled explicitly. Typically the alias definition would end in @samp{$*} to pass all arguments along. More selective use of arguments via commit 8136df6a8cbf071266eb38f5baef005f4e9241a3 Author: Paul Eggert Date: Sat Sep 30 15:36:52 2017 -0700 Make logcount act like CL on negative arg Behaving like Common Lisp is less likely to lead to surprises, as it yields the same answers on 32- vs 64-bit machines. * doc/lispref/numbers.texi (Bitwise Operations): Document behavior on negative integers. * src/data.c (Flogcount): Behave like Common Lisp for negative arguments. * test/src/data-tests.el (data-tests-popcnt) (data-tests-logcount): Test negative args too. diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi index 5058063af4..be74b0c611 100644 --- a/doc/lispref/numbers.texi +++ b/doc/lispref/numbers.texi @@ -1113,9 +1113,14 @@ bit is one in the result if, and only if, the @var{n}th bit is zero in @defun logcount integer This function returns the @dfn{Hamming weight} of @var{integer}: the number of ones in the binary representation of @var{integer}. +If @var{integer} is negative, it returns the number of zero bits in +its two's complement binary representation. The result is always +nonnegative. @example -(logcount 42) ; 42 = #b101010 +(logcount 43) ; 43 = #b101011 + @result{} 4 +(logcount -43) ; -43 = #b111...1010101 @result{} 3 @end example @end defun diff --git a/src/data.c b/src/data.c index fd8cdd19aa..2e7f3e017b 100644 --- a/src/data.c +++ b/src/data.c @@ -3071,11 +3071,13 @@ usage: (logxor &rest INTS-OR-MARKERS) */) DEFUN ("logcount", Flogcount, Slogcount, 1, 1, 0, doc: /* Return population count of VALUE. -If VALUE is negative, the count is of its two's complement representation. */) +This is the number of one bits in the two's complement representation +of VALUE. If VALUE is negative, return the number of zero bits in the +representation. */) (Lisp_Object value) { CHECK_NUMBER (value); - EMACS_UINT v = XUINT (value); + EMACS_INT v = XINT (value) < 0 ? -1 - XINT (value) : XINT (value); return make_number (EMACS_UINT_WIDTH <= UINT_WIDTH ? count_one_bits (v) : EMACS_UINT_WIDTH <= ULONG_WIDTH diff --git a/test/src/data-tests.el b/test/src/data-tests.el index d1154cc5c4..374d1689b9 100644 --- a/test/src/data-tests.el +++ b/test/src/data-tests.el @@ -109,12 +109,14 @@ (defun data-tests-popcnt (byte) "Calculate the Hamming weight of BYTE." + (if (< byte 0) + (setq byte (lognot byte))) (setq byte (- byte (logand (lsh byte -1) #x55555555))) (setq byte (+ (logand byte #x33333333) (logand (lsh byte -2) #x33333333))) (lsh (* (logand (+ byte (lsh byte -4)) #x0f0f0f0f) #x01010101) -24)) (ert-deftest data-tests-logcount () - (should (cl-loop for n in (number-sequence 0 255) + (should (cl-loop for n in (number-sequence -255 255) always (= (logcount n) (data-tests-popcnt n)))) ;; https://oeis.org/A000120 (should (= 11 (logcount 9727))) commit d247e1d30abcb77665f829ca98a5bdef69ff4bc3 Author: Philipp Stephani Date: Sun Jul 23 21:58:49 2017 +0200 Electric quote mode: Conditionally replace " (Bug#24710) * lisp/electric.el (electric-quote-replace-double): New user option. (electric-quote-post-self-insert-function): Use it. * test/lisp/electric-tests.el (electric-quote-replace-double-disabled) (electric-quote-replace-double-bob) (electric-quote-replace-double-bol) (electric-quote-replace-double-after-space) (electric-quote-replace-double-after-letter) (electric-quote-replace-double-after-paren): New unit tests. * doc/emacs/text.texi (Quotation Marks): Document 'electric-quote-replace-double'. diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi index 496b43ce1e..5aa0c77d34 100644 --- a/doc/emacs/text.texi +++ b/doc/emacs/text.texi @@ -443,6 +443,13 @@ non-@code{nil}, and in programming-language strings if @code{nil} for @code{electric-quote-string} and @code{t} for the other variables. +@vindex electric-quote-replace-double + You can also set the option @code{electric-quote-replace-double} to +a non-@code{nil} value. Then, typing @t{"} insert an appropriate +curved double quote depending on context: @t{“} at the beginning of +the buffer or after a line break, whitespace, opening parenthesis, or +quote character, and @t{”} otherwise. + Electric Quote mode is disabled by default. To toggle it, type @kbd{M-x electric-quote-mode}. To toggle it in a single buffer, use @kbd{M-x electric-quote-local-mode}. To suppress it for a single use, diff --git a/etc/NEWS b/etc/NEWS index 8fbc354fc0..42c1b04816 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -42,6 +42,11 @@ When you add a new item, use the appropriate mark if you are sure it applies, This controls how long Emacs will wait for updates to the graphical state to take effect (making a frame visible, for example). ++++ +** The new user option 'electric-quote-replace-double' controls +whether " is also replaced in 'electric-quote-mode'. If non-nil, " is +replaced by a double typographic quote. + * Changes in Specialized Modes and Packages in Emacs 27.1 diff --git a/lisp/electric.el b/lisp/electric.el index d7929945db..65e36b7a63 100644 --- a/lisp/electric.el +++ b/lisp/electric.el @@ -451,6 +451,14 @@ whitespace, opening parenthesis, or quote and leaves \\=` alone." :version "26.1" :type 'boolean :safe #'booleanp :group 'electricity) +(defcustom electric-quote-replace-double nil + "Non-nil means to replace \" with an electric double quote. +Emacs replaces \" with an opening double quote after a line +break, whitespace, opening parenthesis, or quote, and with a +closing double quote otherwise." + :version "26.1" + :type 'boolean :safe #'booleanp :group 'electricity) + (defvar electric-quote-inhibit-functions () "List of functions that should inhibit electric quoting. When the variable `electric-quote-mode' is non-nil, Emacs will @@ -467,7 +475,9 @@ This requotes when a quoting key is typed." (when (and electric-quote-mode (or (eq last-command-event ?\') (and (not electric-quote-context-sensitive) - (eq last-command-event ?\`))) + (eq last-command-event ?\`)) + (and electric-quote-replace-double + (eq last-command-event ?\"))) (not (run-hook-with-args-until-success 'electric-quote-inhibit-functions)) (if (derived-mode-p 'text-mode) @@ -488,7 +498,8 @@ This requotes when a quoting key is typed." (save-excursion (let ((backtick ?\`)) (if (or (eq last-command-event ?\`) - (and electric-quote-context-sensitive + (and (or electric-quote-context-sensitive + electric-quote-replace-double) (save-excursion (backward-char) (or (bobp) (bolp) @@ -506,13 +517,19 @@ This requotes when a quoting key is typed." (setq last-command-event q<<)) ((search-backward (string backtick) (1- (point)) t) (replace-match (string q<)) - (setq last-command-event q<))) + (setq last-command-event q<)) + ((search-backward "\"" (1- (point)) t) + (replace-match (string q<<)) + (setq last-command-event q<<))) (cond ((search-backward (string q> ?') (- (point) 2) t) (replace-match (string q>>)) (setq last-command-event q>>)) ((search-backward "'" (1- (point)) t) (replace-match (string q>)) - (setq last-command-event q>)))))))))) + (setq last-command-event q>)) + ((search-backward "\"" (1- (point)) t) + (replace-match (string q>>)) + (setq last-command-event q>>)))))))))) (put 'electric-quote-post-self-insert-function 'priority 10) diff --git a/test/lisp/electric-tests.el b/test/lisp/electric-tests.el index fc69919fbe..7df2449b9e 100644 --- a/test/lisp/electric-tests.el +++ b/test/lisp/electric-tests.el @@ -617,6 +617,12 @@ baz\"\"" :fixture-fn #'electric-quote-local-mode :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test electric-quote-replace-double-disabled + "" "\"" :expected-string "\"" :expected-point 2 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :test-in-comments nil :test-in-strings nil) + (define-electric-pair-test electric-quote-context-sensitive-backtick "" "`" :expected-string "`" :expected-point 2 :modes '(text-mode) @@ -638,6 +644,13 @@ baz\"\"" :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test electric-quote-replace-double-bob + "" "\"" :expected-string "“" :expected-point 2 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-replace-double . t)) + :test-in-comments nil :test-in-strings nil) + (define-electric-pair-test electric-quote-context-sensitive-bol-single "a\n" "--'" :expected-string "a\n‘" :expected-point 4 :modes '(text-mode) @@ -652,6 +665,13 @@ baz\"\"" :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test electric-quote-replace-double-bol + "a\n" "--\"" :expected-string "a\n“" :expected-point 4 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-replace-double . t)) + :test-in-comments nil :test-in-strings nil) + (define-electric-pair-test electric-quote-context-sensitive-after-space-single " " "-'" :expected-string " ‘" :expected-point 3 :modes '(text-mode) @@ -666,6 +686,13 @@ baz\"\"" :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test electric-quote-replace-double-after-space + " " "-\"" :expected-string " “" :expected-point 3 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-replace-double . t)) + :test-in-comments nil :test-in-strings nil) + (define-electric-pair-test electric-quote-context-sensitive-after-letter-single "a" "-'" :expected-string "a’" :expected-point 3 :modes '(text-mode) @@ -680,6 +707,13 @@ baz\"\"" :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test electric-quote-replace-double-after-letter + "a" "-\"" :expected-string "a”" :expected-point 3 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-replace-double . t)) + :test-in-comments nil :test-in-strings nil) + (define-electric-pair-test electric-quote-context-sensitive-after-paren-single "(" "-'" :expected-string "(‘" :expected-point 3 :modes '(text-mode) @@ -694,6 +728,13 @@ baz\"\"" :bindings '((electric-quote-context-sensitive . t)) :test-in-comments nil :test-in-strings nil) +(define-electric-pair-test electric-quote-replace-double-after-paren + "(" "-\"" :expected-string "(“" :expected-point 3 + :modes '(text-mode) + :fixture-fn #'electric-quote-local-mode + :bindings '((electric-quote-replace-double . t)) + :test-in-comments nil :test-in-strings nil) + ;; Simulate ‘markdown-mode’: it sets both ‘comment-start’ and ;; ‘comment-use-syntax’, but derives from ‘text-mode’. (define-electric-pair-test electric-quote-markdown-in-text commit d88a0f6554888643854ddb2c1f49b77b0bf8904c Author: Paul Eggert Date: Sat Sep 30 13:12:33 2017 -0700 Simplify logcount implementation * src/data.c (HAVE_BUILTIN_POPCOUNTLL, logcount32, logcount64): Remove. (Flogcount): Simplify by using count_one_bits. diff --git a/src/data.c b/src/data.c index b595e3fb1a..fd8cdd19aa 100644 --- a/src/data.c +++ b/src/data.c @@ -3069,64 +3069,18 @@ usage: (logxor &rest INTS-OR-MARKERS) */) return arith_driver (Alogxor, nargs, args); } -#if GNUC_PREREQ (4, 1, 0) -#define HAVE_BUILTIN_POPCOUNTLL -#endif - -#ifndef HAVE_BUILTIN_POPCOUNTLL -static uint32_t -logcount32 (uint32_t b) -{ - b -= (b >> 1) & 0x55555555; - b = (b & 0x33333333) + ((b >> 2) & 0x33333333); - b = (b + (b >> 4)) & 0x0f0f0f0f; - return (b * 0x01010101) >> 24; -} - -static uint64_t -logcount64 (uint64_t b) -{ - b -= (b >> 1) & 0x5555555555555555ULL; - b = (b & 0x3333333333333333ULL) + ((b >> 2) & 0x3333333333333333ULL); - b = (b + (b >> 4)) & 0x0f0f0f0f0f0f0f0fULL; - return (b * 0x0101010101010101ULL) >> 56; -} -#endif /* HAVE_BUILTIN_POPCOUNTLL */ - DEFUN ("logcount", Flogcount, Slogcount, 1, 1, 0, doc: /* Return population count of VALUE. If VALUE is negative, the count is of its two's complement representation. */) - (register Lisp_Object value) + (Lisp_Object value) { - Lisp_Object res; - EMACS_UINT v; - CHECK_NUMBER (value); - - v = XUINT (value); -#ifdef HAVE_BUILTIN_POPCOUNTLL - if (v <= UINT_MAX) - XSETINT (res, __builtin_popcount (v)); - else if (v <= ULONG_MAX) - XSETINT (res, __builtin_popcountl (v)); - else if (v <= ULONG_LONG_MAX) - XSETINT (res, __builtin_popcountll (v)); -#else /* HAVE_BUILTIN_POPCOUNTLL */ - if (v <= UINT_MAX) - XSETINT (res, logcount32 (v)); - else if (v <= ULONG_MAX || v <= ULONG_LONG_MAX) - XSETINT (res, logcount64 (v)); -#endif /* HAVE_BUILTIN_POPCOUNTLL */ - else - { - unsigned int count; - for (count = 0; v; count++) - { - v &= v - 1; - } - XSETINT (res, count); - } - return res; + EMACS_UINT v = XUINT (value); + return make_number (EMACS_UINT_WIDTH <= UINT_WIDTH + ? count_one_bits (v) + : EMACS_UINT_WIDTH <= ULONG_WIDTH + ? count_one_bits_l (v) + : count_one_bits_ll (v)); } static Lisp_Object commit 185f33340680d918a95ff704a8f7e2d9e1a6f0ca Author: Mark Oteiza Date: Sat Sep 30 14:14:12 2017 -0400 Add logcount (Bug#22689) * doc/lispref/numbers.texi (Bitwise Operations): Add documentation. * etc/NEWS: Mention. * src/data.c (logcount32, logcount64): New functions. (logcount): New Lisp function. (syms_of_data): Declare it. * test/src/data-tests.el (data-tests-popcnt, data-tests-logcount): New test. diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi index 3fdc94169b..5058063af4 100644 --- a/doc/lispref/numbers.texi +++ b/doc/lispref/numbers.texi @@ -1107,6 +1107,19 @@ bit is one in the result if, and only if, the @var{n}th bit is zero in @end example @end defun +@cindex popcount +@cindex Hamming weight +@cindex counting set bits +@defun logcount integer +This function returns the @dfn{Hamming weight} of @var{integer}: the +number of ones in the binary representation of @var{integer}. + +@example +(logcount 42) ; 42 = #b101010 + @result{} 3 +@end example +@end defun + @node Math Functions @section Standard Mathematical Functions @cindex transcendental functions diff --git a/etc/NEWS b/etc/NEWS index 238c7b7ea4..8fbc354fc0 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -31,6 +31,9 @@ When you add a new item, use the appropriate mark if you are sure it applies, * Changes in Emacs 27.1 ++++ +** New function 'logcount' calculates an integer's Hamming weight. + * Editing Changes in Emacs 27.1 diff --git a/src/data.c b/src/data.c index e070be6c20..b595e3fb1a 100644 --- a/src/data.c +++ b/src/data.c @@ -3069,6 +3069,66 @@ usage: (logxor &rest INTS-OR-MARKERS) */) return arith_driver (Alogxor, nargs, args); } +#if GNUC_PREREQ (4, 1, 0) +#define HAVE_BUILTIN_POPCOUNTLL +#endif + +#ifndef HAVE_BUILTIN_POPCOUNTLL +static uint32_t +logcount32 (uint32_t b) +{ + b -= (b >> 1) & 0x55555555; + b = (b & 0x33333333) + ((b >> 2) & 0x33333333); + b = (b + (b >> 4)) & 0x0f0f0f0f; + return (b * 0x01010101) >> 24; +} + +static uint64_t +logcount64 (uint64_t b) +{ + b -= (b >> 1) & 0x5555555555555555ULL; + b = (b & 0x3333333333333333ULL) + ((b >> 2) & 0x3333333333333333ULL); + b = (b + (b >> 4)) & 0x0f0f0f0f0f0f0f0fULL; + return (b * 0x0101010101010101ULL) >> 56; +} +#endif /* HAVE_BUILTIN_POPCOUNTLL */ + +DEFUN ("logcount", Flogcount, Slogcount, 1, 1, 0, + doc: /* Return population count of VALUE. +If VALUE is negative, the count is of its two's complement representation. */) + (register Lisp_Object value) +{ + Lisp_Object res; + EMACS_UINT v; + + CHECK_NUMBER (value); + + v = XUINT (value); +#ifdef HAVE_BUILTIN_POPCOUNTLL + if (v <= UINT_MAX) + XSETINT (res, __builtin_popcount (v)); + else if (v <= ULONG_MAX) + XSETINT (res, __builtin_popcountl (v)); + else if (v <= ULONG_LONG_MAX) + XSETINT (res, __builtin_popcountll (v)); +#else /* HAVE_BUILTIN_POPCOUNTLL */ + if (v <= UINT_MAX) + XSETINT (res, logcount32 (v)); + else if (v <= ULONG_MAX || v <= ULONG_LONG_MAX) + XSETINT (res, logcount64 (v)); +#endif /* HAVE_BUILTIN_POPCOUNTLL */ + else + { + unsigned int count; + for (count = 0; v; count++) + { + v &= v - 1; + } + XSETINT (res, count); + } + return res; +} + static Lisp_Object ash_lsh_impl (Lisp_Object value, Lisp_Object count, bool lsh) { @@ -3856,6 +3916,7 @@ syms_of_data (void) defsubr (&Slogand); defsubr (&Slogior); defsubr (&Slogxor); + defsubr (&Slogcount); defsubr (&Slsh); defsubr (&Sash); defsubr (&Sadd1); diff --git a/test/src/data-tests.el b/test/src/data-tests.el index 8de8c145d4..d1154cc5c4 100644 --- a/test/src/data-tests.el +++ b/test/src/data-tests.el @@ -107,6 +107,19 @@ (should (isnan (min 1.0 0.0e+NaN))) (should (isnan (min 1.0 0.0e+NaN 1.1)))) +(defun data-tests-popcnt (byte) + "Calculate the Hamming weight of BYTE." + (setq byte (- byte (logand (lsh byte -1) #x55555555))) + (setq byte (+ (logand byte #x33333333) (logand (lsh byte -2) #x33333333))) + (lsh (* (logand (+ byte (lsh byte -4)) #x0f0f0f0f) #x01010101) -24)) + +(ert-deftest data-tests-logcount () + (should (cl-loop for n in (number-sequence 0 255) + always (= (logcount n) (data-tests-popcnt n)))) + ;; https://oeis.org/A000120 + (should (= 11 (logcount 9727))) + (should (= 8 (logcount 9999)))) + ;; Bool vector tests. Compactly represent bool vectors as hex ;; strings. commit 20a09de953f437109a098fa8c4d380663d921481 Merge: e6fc3b147d 3ab2f9bbb9 Author: Paul Eggert Date: Fri Sep 29 22:06:33 2017 -0700 Merge from origin/emacs-26 3ab2f9bbb9 Merge from gnulib cbc8324488 Prefer HTTPS to HTTP for gnu.org bbda601d1d ; Spelling fixes 695cf5300b Wait for frame visibility with timeout in w32term too e1f6e3127a Bring back the busy wait after x_make_frame_visible (Bug#2... bccf635217 ; * src/gtkutil.c (xg_check_special_colors): Add another G... 26d58f0c58 ; Standardize license notices 61225964ed Revert "bug#28609: simple.el" a75ab3b3fb bug#28609: simple.el c7a21430c1 ; * etc/NEWS: Fix last change. 33401b26b1 Merge branch 'emacs-26' of git.savannah.gnu.org:/srv/git/e... d4b2bbdc73 Merge branch 'emacs-26' into scratch/org-mode-merge c1ac8c170f Merge branch 'emacs-26' of git.savannah.gnu.org:/srv/git/e... af130f900f Fix ert backtrace saving for non-`signal'ed errors (Bug#28... 7476eeaa23 Revert "Fix build on macOS (bug#28571)" fec63089d5 Fix build on macOS (bug#28571) 0f9a78e770 Add tests for `css-current-defun-name' 88a0dd71f1 In w32fullscreen_hook don't add decorations to undecorated... 18073beb14 Merge branch 'emacs-26' of git.savannah.gnu.org:/srv/git/e... 1eef11b7be Fix doc string of 'dired-listing-switches' eaefbc26d5 ; Add files missing in ab351d442d7 ab351d442d Update Org to v9.1.1 commit e6fc3b147dded8d0833853491ad5e3d7b22f390e Merge: 057a9ad7ef ce540f8a68 Author: Paul Eggert Date: Fri Sep 29 22:06:33 2017 -0700 ; Merge from origin/emacs-26 The following commits were skipped: ce540f8a68 Revert "Split flymake.el into flymake-proc.el and flymake-... 7cf59c6635 Revert "Add flymake-backends defcustom" commit 057a9ad7ef53c0588e64410d58f3ecea285c216a Merge: 226c112459 a3f647c5c8 Author: Paul Eggert Date: Fri Sep 29 22:06:33 2017 -0700 Merge from origin/emacs-26 a3f647c5c8 * src/editfns.c (styled_format): Fix typo in previous change. 0e82fa3416 Avoid some unnecessary copying in Fformat etc. commit 226c11245983fc0cc739808da0a68164bab0e37f Merge: 98ac36efe4 98a37e6014 Author: Paul Eggert Date: Fri Sep 29 22:06:33 2017 -0700 ; Merge from origin/emacs-26 The following commit was skipped: 98a37e6014 lisp/simple.el: Indicate when a list of pairs is meant in ... commit 98ac36efe4ce4bd3a0bca76fc73ce6c7abaa4371 Merge: 5406be4db6 1e5949642a Author: Paul Eggert Date: Fri Sep 29 22:06:14 2017 -0700 Merge from origin/emacs-26 1e5949642a ; * src/gtkutil.c (xg_create_frame_widgets): Add FIXME re.... e7c8da4d05 bug#28609: simple.el 827db6b559 Use a separate syntax-ppss cache for narrowed buffers a2244f417a Improve python3-compatibility of fallback completion (Bug#... 79162cb0db Fix subr-x-tests when running from elc 66d35ae49d * lisp/eshell/esh-util.el (eshell-condition-case): Add deb... f5e72b04d9 Make sh-indentation into an alias for sh-basic-offset (Bug... a58d0c590a Fix loading of smie-config rules (Bug#24848) 3a68dec327 ; Update NEWS for the change in eldoc-message 5a41dd0a1f Reset default-directory inside *xref-grep* buffer 49cd561dc6 * test/lisp/tramp-tests.el (tramp-test21-file-links): Spec... b719f6b20b Loosen strict parsing requirement for desktop files c7a0c13777 * lisp/xdg.el (xdg-thumb-uri): Fix doc string. dc6b3560e5 Fix documentation of `make-frame' and related variables an... 3d3778d82a Accept new `always' value for option `buffer-offer-save' 638f64c40a Improve new NS scrolling variable names d93301242f Document 'replace-buffer-contents' in the manual. 00e4e3e9d2 Fix undecorated frame resizing issues on NS (bug#28512) 820739bbb5 ; * doc/emacs/display.texi (Display Custom): Fix wording. f2b2201594 ; Spelling and URL fixes 0e143b1fc5 Documentation improvements for 'display-line-numbers' f656ccdb43 ; Fix typo d64da52d57 Fix last change in bat-mode.el 908af46abd Fix restoring in GUI sessions desktop saved in TTY sessions 51cbd85454 Improve syntax highlighting in bat-mode 0273916618 Document the 'list-FOO' convention d24ec58540 Expose viewing conditions in CAM02-UCS metric a81d5a3d3f Revert "Set frame size to actual requested size (bug#18215)" 0bf066d4b2 Add tests for Edebug 68baca3ee1 Catch more messages in ert-with-message-capture 28e0c410c9 ; * lisp/mouse.el (secondary-selection-exist-p): Doc fix. 31e1d9ef2f Support setting region from secondary selection and vice v... 047f02f00f Fix new copy-directory bug with empty dirs fbd15836af * doc/lispref/strings.texi (Formatting Strings): Improve i... f16a8d5dbd Fix 2 testsuite tests for MS-Windows 965cffd89c Rename timer-list to list-timers a5fec62b51 Provide native touchpad scrolling on macOS 7b3d1c6beb Fix MinGW64 build broken by recent MinGW64 import libraries c83d0c5fdf Fix crashes in 'move-point-visually' in minibuffer windows 7f3d5f929d * src/emacs.c (usage_message): Don't mention 'find-file'. 6845282200 Fix a minor inaccuracy in the Emacs manual 74d7bb9498 Fix errors in flyspell-post-command-hook 40fdbb01d0 Work on Tramp's file-truename 1a01423b3c Fix bug with make-directory on MS-Windows root 066efb8666 Fix log-view-diff-common when point is after last entry 3f006b56cd Adapt fileio-tests--symlink-failure to Cygwin ee512e9a82 Ignore buffers whose name begins with a space in save-some... 9e1b5bd92c Improve tramp-interrupt-process robustness 8d4223e61b Minor Tramp doc update 331d0e520f Fix gensym 466df76f7d Cleanup in files-tests.el 6359fe630a Remove old cl-assert calls in 'newline' 059184e645 Avoid crash with C-g C-g in GC 541006c536 Fix format-time-string %Z bug with negative tz 679e05eeb9 message-citation-line-format %Z is now tz name 4e8888d438 Use doc-view or pdf-tools on any window-system 5f28f0db73 Fix bug with min and max and NaNs 37b5e661d2 Fix recently-introduced copy-directory bug 6bbbc38b34 Merge from Gnulib 57249fb297 Fix compatibility problem in Tramp 411bec82c4 Avoid GCC 7 compilation warning in eval.c 34a6774daa ; Partially revert c3445aed5194 3003ac0469 Adapt Tramp version. Do not merge 48d39c39e8 Search for Syntax section when viewing MDN 9d101376b4 Allow smerge-keep-current to work for empty hunks 13aba24add Call vc-setup-buffer in vc-git-log-{in,out}going 1d599df5e0 Fix last change to textmodes/page-ext.el a726e09a9a * test/src/lcms-tests.el (lcms-cri-cam02-ucs): Skip if lcm... 546413e1ac * test/src/lcms-tests.el (lcms-whitepoint): Skip if lcms2 ... 96aaeaaffa ; * src/lcms.c: Minor stylistic changes in comments. c3df816585 Fix compilation warning in etags.c commit 3ab2f9bbb96d0425a9396e08b2f462de3fd7818d Author: Paul Eggert Date: Fri Sep 29 21:55:32 2017 -0700 Merge from gnulib This incorporates: 2017-09-28 string: code style 2017-09-25 sys_types: update URL 2017-09-23 install-sh: do not assume / = // 2017-09-21 mktime: port to OpenVMS * build-aux/install-sh, m4/mktime.m4, m4/string_h.m4: * m4/sys_types_h.m4: Copy from Gnulib. * lib/gnulib.mk.in: Regenerate. diff --git a/build-aux/install-sh b/build-aux/install-sh index 0360b79e7d..ac159ceda4 100755 --- a/build-aux/install-sh +++ b/build-aux/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2016-01-11.22; # UTC +scriptversion=2017-09-23.17; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -271,15 +271,18 @@ do fi dst=$dst_arg - # If destination is a directory, append the input filename; won't work - # if double slashes aren't ignored. + # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst - dst=$dstdir/`basename "$src"` + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac dstdir_status=0 else dstdir=`dirname "$dst"` @@ -288,6 +291,11 @@ do fi fi + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + obsolete_mkdir_used=false if test $dstdir_status != 0; then @@ -427,8 +435,8 @@ do else # Make a couple of temp file names in the proper directory. - dsttmp=$dstdir/_inst.$$_ - rmtmp=$dstdir/_rm.$$_ + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index d8afec40bc..0f795b3d82 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -2504,20 +2504,20 @@ string.h: string.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's|@''HAVE_DECL_STRERROR_R''@|$(HAVE_DECL_STRERROR_R)|g' \ -e 's|@''HAVE_DECL_STRSIGNAL''@|$(HAVE_DECL_STRSIGNAL)|g' \ -e 's|@''HAVE_STRVERSCMP''@|$(HAVE_STRVERSCMP)|g' \ - -e 's|@''REPLACE_STPNCPY''@|$(REPLACE_STPNCPY)|g' \ -e 's|@''REPLACE_MEMCHR''@|$(REPLACE_MEMCHR)|g' \ -e 's|@''REPLACE_MEMMEM''@|$(REPLACE_MEMMEM)|g' \ - -e 's|@''REPLACE_STRCASESTR''@|$(REPLACE_STRCASESTR)|g' \ + -e 's|@''REPLACE_STPNCPY''@|$(REPLACE_STPNCPY)|g' \ -e 's|@''REPLACE_STRCHRNUL''@|$(REPLACE_STRCHRNUL)|g' \ -e 's|@''REPLACE_STRDUP''@|$(REPLACE_STRDUP)|g' \ - -e 's|@''REPLACE_STRSTR''@|$(REPLACE_STRSTR)|g' \ - -e 's|@''REPLACE_STRERROR''@|$(REPLACE_STRERROR)|g' \ - -e 's|@''REPLACE_STRERROR_R''@|$(REPLACE_STRERROR_R)|g' \ -e 's|@''REPLACE_STRNCAT''@|$(REPLACE_STRNCAT)|g' \ -e 's|@''REPLACE_STRNDUP''@|$(REPLACE_STRNDUP)|g' \ -e 's|@''REPLACE_STRNLEN''@|$(REPLACE_STRNLEN)|g' \ - -e 's|@''REPLACE_STRSIGNAL''@|$(REPLACE_STRSIGNAL)|g' \ + -e 's|@''REPLACE_STRSTR''@|$(REPLACE_STRSTR)|g' \ + -e 's|@''REPLACE_STRCASESTR''@|$(REPLACE_STRCASESTR)|g' \ -e 's|@''REPLACE_STRTOK_R''@|$(REPLACE_STRTOK_R)|g' \ + -e 's|@''REPLACE_STRERROR''@|$(REPLACE_STRERROR)|g' \ + -e 's|@''REPLACE_STRERROR_R''@|$(REPLACE_STRERROR_R)|g' \ + -e 's|@''REPLACE_STRSIGNAL''@|$(REPLACE_STRSIGNAL)|g' \ -e 's|@''UNDEFINE_STRTOK_R''@|$(UNDEFINE_STRTOK_R)|g' \ -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ diff --git a/m4/mktime.m4 b/m4/mktime.m4 index 85666844e2..1461905fb9 100644 --- a/m4/mktime.m4 +++ b/m4/mktime.m4 @@ -1,4 +1,4 @@ -# serial 29 +# serial 30 dnl Copyright (C) 2002-2003, 2005-2007, 2009-2017 Free Software Foundation, dnl Inc. dnl This file is free software; the Free Software Foundation @@ -55,6 +55,10 @@ AC_DEFUN([gl_FUNC_MKTIME_WORKS], # include #endif +#ifndef TIME_T_IS_SIGNED +# define TIME_T_IS_SIGNED 0 +#endif + /* Work around redefinition to rpl_putenv by other config tests. */ #undef putenv diff --git a/m4/string_h.m4 b/m4/string_h.m4 index ac6311fba0..8c42cf1b85 100644 --- a/m4/string_h.m4 +++ b/m4/string_h.m4 @@ -5,7 +5,7 @@ # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 21 +# serial 22 # Written by Paul Eggert. @@ -107,16 +107,16 @@ AC_DEFUN([gl_HEADER_STRING_H_DEFAULTS], REPLACE_MEMCHR=0; AC_SUBST([REPLACE_MEMCHR]) REPLACE_MEMMEM=0; AC_SUBST([REPLACE_MEMMEM]) REPLACE_STPNCPY=0; AC_SUBST([REPLACE_STPNCPY]) + REPLACE_STRCHRNUL=0; AC_SUBST([REPLACE_STRCHRNUL]) REPLACE_STRDUP=0; AC_SUBST([REPLACE_STRDUP]) + REPLACE_STRNCAT=0; AC_SUBST([REPLACE_STRNCAT]) + REPLACE_STRNDUP=0; AC_SUBST([REPLACE_STRNDUP]) + REPLACE_STRNLEN=0; AC_SUBST([REPLACE_STRNLEN]) REPLACE_STRSTR=0; AC_SUBST([REPLACE_STRSTR]) REPLACE_STRCASESTR=0; AC_SUBST([REPLACE_STRCASESTR]) - REPLACE_STRCHRNUL=0; AC_SUBST([REPLACE_STRCHRNUL]) + REPLACE_STRTOK_R=0; AC_SUBST([REPLACE_STRTOK_R]) REPLACE_STRERROR=0; AC_SUBST([REPLACE_STRERROR]) REPLACE_STRERROR_R=0; AC_SUBST([REPLACE_STRERROR_R]) - REPLACE_STRNCAT=0; AC_SUBST([REPLACE_STRNCAT]) - REPLACE_STRNDUP=0; AC_SUBST([REPLACE_STRNDUP]) - REPLACE_STRNLEN=0; AC_SUBST([REPLACE_STRNLEN]) REPLACE_STRSIGNAL=0; AC_SUBST([REPLACE_STRSIGNAL]) - REPLACE_STRTOK_R=0; AC_SUBST([REPLACE_STRTOK_R]) UNDEFINE_STRTOK_R=0; AC_SUBST([UNDEFINE_STRTOK_R]) ]) diff --git a/m4/sys_types_h.m4 b/m4/sys_types_h.m4 index 06268cfb2d..de56d04fc1 100644 --- a/m4/sys_types_h.m4 +++ b/m4/sys_types_h.m4 @@ -1,4 +1,4 @@ -# sys_types_h.m4 serial 8 +# sys_types_h.m4 serial 9 dnl Copyright (C) 2011-2017 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -40,7 +40,7 @@ AC_DEFUN([gl_SYS_TYPES_H_DEFAULTS], m4_version_prereq([2.70], [], [ # This is taken from the following Autoconf patch: -# http://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commitdiff;h=e17a30e987d7ee695fb4294a82d987ec3dc9b974 +# https://git.savannah.gnu.org/cgit/autoconf.git/commit/?id=e17a30e987d7ee695fb4294a82d987ec3dc9b974 m4_undefine([AC_HEADER_MAJOR]) AC_DEFUN([AC_HEADER_MAJOR], commit cbc832448878f7bc7b226243abb8d8b1ae68a937 Author: Paul Eggert Date: Fri Sep 29 17:44:23 2017 -0700 Prefer HTTPS to HTTP for gnu.org This catches some URLs I missed in my previous scan, or perhaps were added after the scan. diff --git a/ChangeLog.1 b/ChangeLog.1 index eeb6da4265..00c66d6059 100644 --- a/ChangeLog.1 +++ b/ChangeLog.1 @@ -14715,4 +14715,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/ChangeLog.2 b/ChangeLog.2 index e789722a4d..289cc2be1d 100644 --- a/ChangeLog.2 +++ b/ChangeLog.2 @@ -35802,4 +35802,4 @@ See ChangeLog.1 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/ChangeLog.3 b/ChangeLog.3 index 9e622cef90..c74aede8cd 100644 --- a/ChangeLog.3 +++ b/ChangeLog.3 @@ -22619,4 +22619,4 @@ See ChangeLog.1 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/admin/ChangeLog.1 b/admin/ChangeLog.1 index b1aaee7cb6..bc3dba7171 100644 --- a/admin/ChangeLog.1 +++ b/admin/ChangeLog.1 @@ -2592,4 +2592,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/admin/nt/README-UNDUMP.W32 b/admin/nt/README-UNDUMP.W32 index aa91633dd4..b6ed8eee7e 100644 --- a/admin/nt/README-UNDUMP.W32 +++ b/admin/nt/README-UNDUMP.W32 @@ -55,4 +55,4 @@ 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 . +along with GNU Emacs. If not, see . diff --git a/admin/nt/README-ftp-server b/admin/nt/README-ftp-server index 5fd363c2eb..4f156b9c0c 100644 --- a/admin/nt/README-ftp-server +++ b/admin/nt/README-ftp-server @@ -227,7 +227,7 @@ See the end of the file for license conditions. The Emacs on MS Windows FAQ is distributed with Emacs (info manual "efaq-w32"), and at - http://www.gnu.org/software/emacs/manual/efaq-w32.html + https://www.gnu.org/software/emacs/manual/efaq-w32.html In addition to the FAQ, there is a mailing list for discussing issues related to the Windows port of Emacs. For information about the @@ -274,4 +274,4 @@ 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/. +along with GNU Emacs. If not, see https://www.gnu.org/licenses/. diff --git a/config.bat b/config.bat index d1f2702d35..d0251df560 100644 --- a/config.bat +++ b/config.bat @@ -16,7 +16,7 @@ rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the rem GNU General Public License for more details. rem You should have received a copy of the GNU General Public License -rem along with GNU Emacs. If not, see http://www.gnu.org/licenses/. +rem along with GNU Emacs. If not, see https://www.gnu.org/licenses/. rem ---------------------------------------------------------------------- rem YOU'LL NEED THE FOLLOWING UTILITIES TO MAKE EMACS: diff --git a/doc/emacs/ChangeLog.1 b/doc/emacs/ChangeLog.1 index 3c7aeb0c1d..169a4b4793 100644 --- a/doc/emacs/ChangeLog.1 +++ b/doc/emacs/ChangeLog.1 @@ -1121,7 +1121,7 @@ Convert some TeX accents (e.g., '@l{}') to UTF-8 (e.g., 'ł'). Apparently the TeX accents cause problems when generating gnu.org web pages, e.g., @l{} is rendered as '/l' on - . 2013-03-16 Glenn Morris @@ -10934,4 +10934,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/doc/lispintro/ChangeLog.1 b/doc/lispintro/ChangeLog.1 index 7e5b629164..de24c8e2bb 100644 --- a/doc/lispintro/ChangeLog.1 +++ b/doc/lispintro/ChangeLog.1 @@ -797,4 +797,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/doc/lispref/ChangeLog.1 b/doc/lispref/ChangeLog.1 index 1044ad7370..490f216b4c 100644 --- a/doc/lispref/ChangeLog.1 +++ b/doc/lispref/ChangeLog.1 @@ -14004,4 +14004,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/doc/man/ChangeLog.1 b/doc/man/ChangeLog.1 index aa863ff72b..68498c64c0 100644 --- a/doc/man/ChangeLog.1 +++ b/doc/man/ChangeLog.1 @@ -191,4 +191,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/doc/misc/ChangeLog.1 b/doc/misc/ChangeLog.1 index 2b1571c0ab..bc2c184d41 100644 --- a/doc/misc/ChangeLog.1 +++ b/doc/misc/ChangeLog.1 @@ -12131,4 +12131,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/etc/ChangeLog.1 b/etc/ChangeLog.1 index e502c6539d..9514ea284d 100644 --- a/etc/ChangeLog.1 +++ b/etc/ChangeLog.1 @@ -6906,4 +6906,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/leim/ChangeLog.1 b/leim/ChangeLog.1 index e7f8a46a86..db91ac24e2 100644 --- a/leim/ChangeLog.1 +++ b/leim/ChangeLog.1 @@ -2593,4 +2593,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/lib-src/ChangeLog.1 b/lib-src/ChangeLog.1 index a9783e9f60..417e57f3bb 100644 --- a/lib-src/ChangeLog.1 +++ b/lib-src/ChangeLog.1 @@ -8624,4 +8624,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/lib-src/ntlib.c b/lib-src/ntlib.c index 9908f0fa45..3754f914e3 100644 --- a/lib-src/ntlib.c +++ b/lib-src/ntlib.c @@ -18,7 +18,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include #include diff --git a/lib-src/ntlib.h b/lib-src/ntlib.h index b69a40b4f0..f7ee305e86 100644 --- a/lib-src/ntlib.h +++ b/lib-src/ntlib.h @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include #include diff --git a/lisp/ChangeLog.1 b/lisp/ChangeLog.1 index 65997e189f..b44f640dd5 100644 --- a/lisp/ChangeLog.1 +++ b/lisp/ChangeLog.1 @@ -3259,4 +3259,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.10 b/lisp/ChangeLog.10 index 918825e6ac..d654291739 100644 --- a/lisp/ChangeLog.10 +++ b/lisp/ChangeLog.10 @@ -23556,4 +23556,4 @@ See ChangeLog.9 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.11 b/lisp/ChangeLog.11 index f3d9840c0d..eda7603cb6 100644 --- a/lisp/ChangeLog.11 +++ b/lisp/ChangeLog.11 @@ -14336,4 +14336,4 @@ See ChangeLog.10 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.12 b/lisp/ChangeLog.12 index 04c5d8138d..0d3bd88f3e 100644 --- a/lisp/ChangeLog.12 +++ b/lisp/ChangeLog.12 @@ -33349,4 +33349,4 @@ See ChangeLog.11 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.13 b/lisp/ChangeLog.13 index f86590bf27..9c451a359e 100644 --- a/lisp/ChangeLog.13 +++ b/lisp/ChangeLog.13 @@ -16712,4 +16712,4 @@ See ChangeLog.12 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.14 b/lisp/ChangeLog.14 index a3397b1e47..48f5c07b18 100644 --- a/lisp/ChangeLog.14 +++ b/lisp/ChangeLog.14 @@ -20562,4 +20562,4 @@ See ChangeLog.13 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.15 b/lisp/ChangeLog.15 index 2512d35564..7bd44059ad 100644 --- a/lisp/ChangeLog.15 +++ b/lisp/ChangeLog.15 @@ -22817,4 +22817,4 @@ See ChangeLog.14 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.16 b/lisp/ChangeLog.16 index fb5aee17a7..03e6115c57 100644 --- a/lisp/ChangeLog.16 +++ b/lisp/ChangeLog.16 @@ -25238,4 +25238,4 @@ See ChangeLog.15 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.17 b/lisp/ChangeLog.17 index 5c415de047..789452351a 100644 --- a/lisp/ChangeLog.17 +++ b/lisp/ChangeLog.17 @@ -26309,4 +26309,4 @@ See ChangeLog.16 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.2 b/lisp/ChangeLog.2 index 7a4845374f..5087b943e3 100644 --- a/lisp/ChangeLog.2 +++ b/lisp/ChangeLog.2 @@ -4007,4 +4007,4 @@ See ChangeLog.1 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.3 b/lisp/ChangeLog.3 index 3bac72a69d..1ba33b1085 100644 --- a/lisp/ChangeLog.3 +++ b/lisp/ChangeLog.3 @@ -12448,4 +12448,4 @@ See ChangeLog.2 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.4 b/lisp/ChangeLog.4 index 00ce74e515..00798e590c 100644 --- a/lisp/ChangeLog.4 +++ b/lisp/ChangeLog.4 @@ -8949,4 +8949,4 @@ See ChangeLog.3 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.5 b/lisp/ChangeLog.5 index 800277b123..64abfe988f 100644 --- a/lisp/ChangeLog.5 +++ b/lisp/ChangeLog.5 @@ -9283,4 +9283,4 @@ See ChangeLog.4 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.6 b/lisp/ChangeLog.6 index 8482637989..8c985fbfb7 100644 --- a/lisp/ChangeLog.6 +++ b/lisp/ChangeLog.6 @@ -8036,4 +8036,4 @@ See ChangeLog.5 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.7 b/lisp/ChangeLog.7 index 52a0180c63..62ee295b89 100644 --- a/lisp/ChangeLog.7 +++ b/lisp/ChangeLog.7 @@ -23126,4 +23126,4 @@ See ChangeLog.6 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.8 b/lisp/ChangeLog.8 index 7e4522f53f..57b5584ebe 100644 --- a/lisp/ChangeLog.8 +++ b/lisp/ChangeLog.8 @@ -10007,4 +10007,4 @@ See ChangeLog.7 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/ChangeLog.9 b/lisp/ChangeLog.9 index e51c0c5dad..376589ff90 100644 --- a/lisp/ChangeLog.9 +++ b/lisp/ChangeLog.9 @@ -20700,4 +20700,4 @@ See ChangeLog.8 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/cedet/ChangeLog.1 b/lisp/cedet/ChangeLog.1 index c0223cbc78..a17e53a1c5 100644 --- a/lisp/cedet/ChangeLog.1 +++ b/lisp/cedet/ChangeLog.1 @@ -3475,4 +3475,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/dos-fns.el b/lisp/dos-fns.el index 1d48371912..f69335d2c2 100644 --- a/lisp/dos-fns.el +++ b/lisp/dos-fns.el @@ -20,7 +20,7 @@ ;; 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 . +;; along with GNU Emacs. If not, see . ;;; Commentary: diff --git a/lisp/dos-vars.el b/lisp/dos-vars.el index d552d518a0..90052ce028 100644 --- a/lisp/dos-vars.el +++ b/lisp/dos-vars.el @@ -19,7 +19,7 @@ ;; 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 . +;; along with GNU Emacs. If not, see . ;;; Commentary: diff --git a/lisp/dos-w32.el b/lisp/dos-w32.el index ff5310e1fb..affadee2fe 100644 --- a/lisp/dos-w32.el +++ b/lisp/dos-w32.el @@ -19,7 +19,7 @@ ;; 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 . +;; along with GNU Emacs. If not, see . ;;; Commentary: diff --git a/lisp/erc/ChangeLog.1 b/lisp/erc/ChangeLog.1 index 2d5403fdc1..eefbbe924b 100644 --- a/lisp/erc/ChangeLog.1 +++ b/lisp/erc/ChangeLog.1 @@ -11717,7 +11717,7 @@ 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 . + along with GNU Emacs. If not, see . ;; Local Variables: ;; coding: utf-8 diff --git a/lisp/erc/ChangeLog.2 b/lisp/erc/ChangeLog.2 index 6d789e4c93..36b01e235c 100644 --- a/lisp/erc/ChangeLog.2 +++ b/lisp/erc/ChangeLog.2 @@ -772,7 +772,7 @@ See ChangeLog.1 for earlier changes. 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 . + along with GNU Emacs. If not, see . ;; Local Variables: ;; coding: utf-8 diff --git a/lisp/gnus/ChangeLog.1 b/lisp/gnus/ChangeLog.1 index 4cf5129dcd..c21d59bf70 100644 --- a/lisp/gnus/ChangeLog.1 +++ b/lisp/gnus/ChangeLog.1 @@ -3717,7 +3717,7 @@ 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 . + along with GNU Emacs. If not, see . ;; Local Variables: ;; coding: utf-8 diff --git a/lisp/gnus/ChangeLog.2 b/lisp/gnus/ChangeLog.2 index d7ff3b6205..f163338924 100644 --- a/lisp/gnus/ChangeLog.2 +++ b/lisp/gnus/ChangeLog.2 @@ -18553,7 +18553,7 @@ See ChangeLog.1 for earlier changes. 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 . + along with GNU Emacs. If not, see . ;; Local Variables: ;; coding: utf-8 diff --git a/lisp/gnus/ChangeLog.3 b/lisp/gnus/ChangeLog.3 index a799f73f58..0fcb4a08e3 100644 --- a/lisp/gnus/ChangeLog.3 +++ b/lisp/gnus/ChangeLog.3 @@ -26340,7 +26340,7 @@ See ChangeLog.2 for earlier changes. 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 . + along with GNU Emacs. If not, see . ;; Local Variables: ;; coding: utf-8 diff --git a/lisp/mh-e/ChangeLog.1 b/lisp/mh-e/ChangeLog.1 index 31a9ea7651..9bf28b0f13 100644 --- a/lisp/mh-e/ChangeLog.1 +++ b/lisp/mh-e/ChangeLog.1 @@ -11434,7 +11434,7 @@ 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 . + along with GNU Emacs. If not, see . ;; Local Variables: ;; coding: utf-8 diff --git a/lisp/mh-e/ChangeLog.2 b/lisp/mh-e/ChangeLog.2 index 487198663e..c3f28ae816 100644 --- a/lisp/mh-e/ChangeLog.2 +++ b/lisp/mh-e/ChangeLog.2 @@ -3688,7 +3688,7 @@ See ChangeLog.1 for earlier changes. 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 . + along with GNU Emacs. If not, see . ;; Local Variables: ;; coding: utf-8 diff --git a/lisp/org/ChangeLog.1 b/lisp/org/ChangeLog.1 index ee50f6fb04..7e27fb6b7e 100644 --- a/lisp/org/ChangeLog.1 +++ b/lisp/org/ChangeLog.1 @@ -32848,4 +32848,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/lisp/thingatpt.el b/lisp/thingatpt.el index 13f761e69e..485cc97555 100644 --- a/lisp/thingatpt.el +++ b/lisp/thingatpt.el @@ -388,7 +388,7 @@ the bounds of a possible ill-formed URI (one lacking a scheme)." ;; Ensure PT is actually within BOUNDARY. Check the following ;; example with point on the beginning of the line: ;; - ;; 3,1406710489,http://gnu.org,0,"0" + ;; 3,1406710489,https://gnu.org,0,"0" (and (<= url-beg pt end) (cons url-beg end)))))) (put 'url 'thing-at-point 'thing-at-point-url-at-point) diff --git a/lisp/url/ChangeLog.1 b/lisp/url/ChangeLog.1 index 75be6af62a..eb7982916c 100644 --- a/lisp/url/ChangeLog.1 +++ b/lisp/url/ChangeLog.1 @@ -3084,4 +3084,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/lwlib/ChangeLog.1 b/lwlib/ChangeLog.1 index 56259498a9..623b798cc7 100644 --- a/lwlib/ChangeLog.1 +++ b/lwlib/ChangeLog.1 @@ -1979,4 +1979,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/msdos/ChangeLog.1 b/msdos/ChangeLog.1 index e8e6ec3af5..6fe88d119b 100644 --- a/msdos/ChangeLog.1 +++ b/msdos/ChangeLog.1 @@ -1565,4 +1565,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/msdos/INSTALL b/msdos/INSTALL index ca4ab85f1d..41e36545f7 100644 --- a/msdos/INSTALL +++ b/msdos/INSTALL @@ -151,4 +151,4 @@ 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 . +along with GNU Emacs. If not, see . diff --git a/msdos/README b/msdos/README index 122e8150e4..1add1c46e9 100644 --- a/msdos/README +++ b/msdos/README @@ -38,4 +38,4 @@ 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 . +along with GNU Emacs. If not, see . diff --git a/msdos/depfiles.bat b/msdos/depfiles.bat index b2c7bc8230..31c8622f79 100644 --- a/msdos/depfiles.bat +++ b/msdos/depfiles.bat @@ -16,7 +16,7 @@ rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the rem GNU General Public License for more details. rem You should have received a copy of the GNU General Public License -rem along with GNU Emacs. If not, see http://www.gnu.org/licenses/. +rem along with GNU Emacs. If not, see https://www.gnu.org/licenses/. rem ---------------------------------------------------------------------- diff --git a/msdos/inttypes.h b/msdos/inttypes.h index 7996d05658..ce7797a933 100644 --- a/msdos/inttypes.h +++ b/msdos/inttypes.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef _REPL_INTTYPES_H #define _REPL_INTTYPES_H diff --git a/msdos/mainmake.v2 b/msdos/mainmake.v2 index dc2b0b6e8a..e8391bcf91 100644 --- a/msdos/mainmake.v2 +++ b/msdos/mainmake.v2 @@ -15,7 +15,7 @@ # 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 . +# along with GNU Emacs. If not, see . # make all to compile and build Emacs. # make install to install it (installs in-place, in `bin' subdir of top dir). diff --git a/nextstep/ChangeLog.1 b/nextstep/ChangeLog.1 index f84779d9de..0eb4a14671 100644 --- a/nextstep/ChangeLog.1 +++ b/nextstep/ChangeLog.1 @@ -327,4 +327,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/nt/ChangeLog.1 b/nt/ChangeLog.1 index 0117639a8f..adfdea286f 100644 --- a/nt/ChangeLog.1 +++ b/nt/ChangeLog.1 @@ -3563,4 +3563,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/nt/INSTALL b/nt/INSTALL index c6182c22ce..662a30406f 100644 --- a/nt/INSTALL +++ b/nt/INSTALL @@ -820,4 +820,4 @@ 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 . +along with GNU Emacs. If not, see . diff --git a/nt/INSTALL.W64 b/nt/INSTALL.W64 index cb13473573..6ebc1641de 100644 --- a/nt/INSTALL.W64 +++ b/nt/INSTALL.W64 @@ -224,4 +224,4 @@ 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 . +along with GNU Emacs. If not, see . diff --git a/nt/Makefile.in b/nt/Makefile.in index 7e911db7aa..8215823630 100644 --- a/nt/Makefile.in +++ b/nt/Makefile.in @@ -15,7 +15,7 @@ # 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 . +# along with GNU Emacs. If not, see . # Avoid trouble on systems where the `SHELL' variable might be # inherited from the environment. diff --git a/nt/README b/nt/README index 19ffd50f90..f4cca8efe7 100644 --- a/nt/README +++ b/nt/README @@ -86,7 +86,7 @@ This appendix is also available (as part of the entire manual) at - http://www.gnu.org/software/emacs/manual/html_mono/emacs.html#Microsoft-Windows + https://www.gnu.org/software/emacs/manual/html_mono/emacs.html#Microsoft-Windows In addition to the manual, there is a mailing list for discussing issues related to the Windows port of Emacs. For information about @@ -140,4 +140,4 @@ 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 . +along with GNU Emacs. If not, see . diff --git a/nt/README.W32 b/nt/README.W32 index bec0b66ac5..89647588f4 100644 --- a/nt/README.W32 +++ b/nt/README.W32 @@ -275,7 +275,7 @@ See the end of the file for license conditions. This appendix is also available (as part of the entire manual) at - http://www.gnu.org/software/emacs/manual/html_mono/emacs.html#Microsoft-Windows + https://www.gnu.org/software/emacs/manual/html_mono/emacs.html#Microsoft-Windows In addition to the manual, there is a mailing list for help with Emacs here: @@ -325,4 +325,4 @@ 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 . +along with GNU Emacs. If not, see . diff --git a/nt/addpm.c b/nt/addpm.c index b034fffe29..51f2510682 100644 --- a/nt/addpm.c +++ b/nt/addpm.c @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /**************************************************************************** * diff --git a/nt/cmdproxy.c b/nt/cmdproxy.c index 93e0097392..0b4d437589 100644 --- a/nt/cmdproxy.c +++ b/nt/cmdproxy.c @@ -25,7 +25,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include diff --git a/nt/configure.bat b/nt/configure.bat index cd2a8f4f28..9705c66faa 100755 --- a/nt/configure.bat +++ b/nt/configure.bat @@ -16,7 +16,7 @@ rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the rem GNU General Public License for more details. rem You should have received a copy of the GNU General Public License -rem along with GNU Emacs. If not, see http://www.gnu.org/licenses/. +rem along with GNU Emacs. If not, see https://www.gnu.org/licenses/. rem ---------------------------------------------------------------------- echo **************************************************************** diff --git a/nt/ddeclient.c b/nt/ddeclient.c index 15aeb842fc..c370ef83ac 100644 --- a/nt/ddeclient.c +++ b/nt/ddeclient.c @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include #include diff --git a/nt/epaths.nt b/nt/epaths.nt index ebb4ccf641..4f4f86a01a 100644 --- a/nt/epaths.nt +++ b/nt/epaths.nt @@ -28,7 +28,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Together with PATH_SITELOADSEARCH, this gives the default value of @@ -85,4 +85,3 @@ along with GNU Emacs. If not, see . */ /* Where Emacs should look for the application default file. */ #define PATH_X_DEFAULTS "" - diff --git a/nt/gnulib-cfg.mk b/nt/gnulib-cfg.mk index b75e36f5aa..419099ece3 100644 --- a/nt/gnulib-cfg.mk +++ b/nt/gnulib-cfg.mk @@ -13,7 +13,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this file. If not, see . +# along with this file. If not, see . # Gnulib modules to be omitted from Emacs. diff --git a/nt/inc/grp.h b/nt/inc/grp.h index 82a8bab227..0c3a8ecd64 100644 --- a/nt/inc/grp.h +++ b/nt/inc/grp.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef _GRP_H #define _GRP_H diff --git a/nt/inc/inttypes.h b/nt/inc/inttypes.h index e5037b1fed..e0905b74d7 100644 --- a/nt/inc/inttypes.h +++ b/nt/inc/inttypes.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef _REPL_INTTYPES_H #define _REPL_INTTYPES_H diff --git a/nt/inc/langinfo.h b/nt/inc/langinfo.h index a20e59bee0..88e12b59e8 100644 --- a/nt/inc/langinfo.h +++ b/nt/inc/langinfo.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef _LANGINFO_H #define _LANGINFO_H diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h index e1dbe29bbb..89aa94323d 100644 --- a/nt/inc/ms-w32.h +++ b/nt/inc/ms-w32.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Define symbols to identify the version of Unix this is. Define all the symbols that apply correctly. */ diff --git a/nt/inc/nl_types.h b/nt/inc/nl_types.h index 8236a6dba9..6ed0994c59 100644 --- a/nt/inc/nl_types.h +++ b/nt/inc/nl_types.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef _NL_TYPES_H #define _NL_TYPES_H diff --git a/nt/inc/stdint.h b/nt/inc/stdint.h index 1e41ddb637..c4fb98d2f9 100644 --- a/nt/inc/stdint.h +++ b/nt/inc/stdint.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef _NT_STDINT_H_ #define _NT_STDINT_H_ diff --git a/nt/inc/sys/resource.h b/nt/inc/sys/resource.h index 2964a643d3..de10aaee06 100644 --- a/nt/inc/sys/resource.h +++ b/nt/inc/sys/resource.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef INC_SYS_RESOURCE_H_ #define INC_SYS_RESOURCE_H_ diff --git a/nt/inc/sys/socket.h b/nt/inc/sys/socket.h index 32f6a1db6f..b7c1103f21 100644 --- a/nt/inc/sys/socket.h +++ b/nt/inc/sys/socket.h @@ -13,7 +13,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Workable version of based on winsock.h */ diff --git a/nt/inc/sys/stat.h b/nt/inc/sys/stat.h index d686af1bc1..2f1cf46873 100644 --- a/nt/inc/sys/stat.h +++ b/nt/inc/sys/stat.h @@ -16,7 +16,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef INC_SYS_STAT_H_ #define INC_SYS_STAT_H_ diff --git a/nt/inc/sys/wait.h b/nt/inc/sys/wait.h index 6be7fd3244..51eae821b5 100644 --- a/nt/inc/sys/wait.h +++ b/nt/inc/sys/wait.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef INC_SYS_WAIT_H_ #define INC_SYS_WAIT_H_ diff --git a/nt/preprep.c b/nt/preprep.c index 73660351a0..fc91628226 100644 --- a/nt/preprep.c +++ b/nt/preprep.c @@ -14,7 +14,7 @@ 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 . +along with GNU Emacs. If not, see . Andrew Innes 16-Jan-1999 diff --git a/nt/runemacs.c b/nt/runemacs.c index a98ff4be52..d6e02b248d 100644 --- a/nt/runemacs.c +++ b/nt/runemacs.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* diff --git a/oldXMenu/ChangeLog.1 b/oldXMenu/ChangeLog.1 index 8fa3794a36..8ac7e184a7 100644 --- a/oldXMenu/ChangeLog.1 +++ b/oldXMenu/ChangeLog.1 @@ -727,4 +727,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.1 b/src/ChangeLog.1 index e51b4addc0..74a5012552 100644 --- a/src/ChangeLog.1 +++ b/src/ChangeLog.1 @@ -3536,4 +3536,4 @@ 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.10 b/src/ChangeLog.10 index 65a8587bf9..1bd822d492 100644 --- a/src/ChangeLog.10 +++ b/src/ChangeLog.10 @@ -27927,4 +27927,4 @@ See ChangeLog.9 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.11 b/src/ChangeLog.11 index 365fc277a9..eb1aeb1eea 100644 --- a/src/ChangeLog.11 +++ b/src/ChangeLog.11 @@ -31400,4 +31400,4 @@ See ChangeLog.10 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.12 b/src/ChangeLog.12 index 367e0d61d8..35993adb66 100644 --- a/src/ChangeLog.12 +++ b/src/ChangeLog.12 @@ -22951,4 +22951,4 @@ See ChangeLog.11 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.13 b/src/ChangeLog.13 index 66f062d3d3..6f5ea03626 100644 --- a/src/ChangeLog.13 +++ b/src/ChangeLog.13 @@ -17920,4 +17920,4 @@ See ChangeLog.12 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.2 b/src/ChangeLog.2 index 56015d5617..42e1e8345d 100644 --- a/src/ChangeLog.2 +++ b/src/ChangeLog.2 @@ -4786,4 +4786,4 @@ See ChangeLog.1 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.3 b/src/ChangeLog.3 index 8f9b38e25d..17fd69c9f1 100644 --- a/src/ChangeLog.3 +++ b/src/ChangeLog.3 @@ -16518,4 +16518,4 @@ See ChangeLog.2 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.4 b/src/ChangeLog.4 index bb13c9a5f6..2935ee52cc 100644 --- a/src/ChangeLog.4 +++ b/src/ChangeLog.4 @@ -6921,4 +6921,4 @@ See ChangeLog.3 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.5 b/src/ChangeLog.5 index f0fde023ff..489ccfa532 100644 --- a/src/ChangeLog.5 +++ b/src/ChangeLog.5 @@ -7163,4 +7163,4 @@ See ChangeLog.4 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.6 b/src/ChangeLog.6 index 2282916205..f2b9e609b1 100644 --- a/src/ChangeLog.6 +++ b/src/ChangeLog.6 @@ -5373,4 +5373,4 @@ See ChangeLog.5 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.7 b/src/ChangeLog.7 index eb4833ccb0..32472ac074 100644 --- a/src/ChangeLog.7 +++ b/src/ChangeLog.7 @@ -11106,4 +11106,4 @@ See ChangeLog.6 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.8 b/src/ChangeLog.8 index ae971b52e1..cf7c926cc2 100644 --- a/src/ChangeLog.8 +++ b/src/ChangeLog.8 @@ -13994,4 +13994,4 @@ See ChangeLog.7 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/ChangeLog.9 b/src/ChangeLog.9 index 7b8f500b60..6de10c493f 100644 --- a/src/ChangeLog.9 +++ b/src/ChangeLog.9 @@ -13309,4 +13309,4 @@ See ChangeLog.8 for earlier changes. 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 . + along with GNU Emacs. If not, see . diff --git a/src/cygw32.c b/src/cygw32.c index 962b6a2f8b..724363d64c 100644 --- a/src/cygw32.c +++ b/src/cygw32.c @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include "cygw32.h" diff --git a/src/cygw32.h b/src/cygw32.h index a10b830e6b..f006c112d8 100644 --- a/src/cygw32.h +++ b/src/cygw32.h @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef CYGW32_H #define CYGW32_H diff --git a/src/dosfns.c b/src/dosfns.c index 7bf1dee587..8687049639 100644 --- a/src/dosfns.c +++ b/src/dosfns.c @@ -16,7 +16,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include diff --git a/src/dosfns.h b/src/dosfns.h index 266430d71e..2846010c22 100644 --- a/src/dosfns.h +++ b/src/dosfns.h @@ -18,7 +18,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #define DOS_COUNTRY_INFO 34 /* no of bytes returned by dos int 38h */ extern unsigned char dos_country_info[DOS_COUNTRY_INFO]; diff --git a/src/msdos.c b/src/msdos.c index 5b025753d9..68daa10fdb 100644 --- a/src/msdos.c +++ b/src/msdos.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Contributed by Morten Welinder */ /* New display, keyboard, and mouse control by Kim F. Storm */ diff --git a/src/msdos.h b/src/msdos.h index f4312c5c86..16292c551d 100644 --- a/src/msdos.h +++ b/src/msdos.h @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef EMACS_MSDOS_H #define EMACS_MSDOS_H diff --git a/src/w16select.c b/src/w16select.c index 70037f3ca7..0ecd39b7af 100644 --- a/src/w16select.c +++ b/src/w16select.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* These functions work by using WinOldAp interface. WinOldAp (WINOLDAP.MOD) is a Microsoft Windows extension supporting diff --git a/src/w32.c b/src/w32.c index eb531aa60c..fb13bd7d07 100644 --- a/src/w32.c +++ b/src/w32.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Geoff Voelker (voelker@cs.washington.edu) 7-29-94 diff --git a/src/w32.h b/src/w32.h index 1727f8bc62..cd782883c6 100644 --- a/src/w32.h +++ b/src/w32.h @@ -17,7 +17,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifdef CYGWIN #error "w32.h is not compatible with Cygwin" diff --git a/src/w32common.h b/src/w32common.h index 30718e0074..6de4ab4bfd 100644 --- a/src/w32common.h +++ b/src/w32common.h @@ -14,7 +14,7 @@ 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 . +along with GNU Emacs. If not, see . */ diff --git a/src/w32console.c b/src/w32console.c index a4c089fa96..15d11d56ad 100644 --- a/src/w32console.c +++ b/src/w32console.c @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Tim Fleehart (apollo@online.com) 1-17-92 diff --git a/src/w32fns.c b/src/w32fns.c index a77464465e..efbd81b22d 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Added by Kevin Gallo */ diff --git a/src/w32font.c b/src/w32font.c index 9881119202..d6bd7d6a2b 100644 --- a/src/w32font.c +++ b/src/w32font.c @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include #include diff --git a/src/w32font.h b/src/w32font.h index 42b425f35f..2d84950f0d 100644 --- a/src/w32font.h +++ b/src/w32font.h @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef EMACS_W32FONT_H #define EMACS_W32FONT_H diff --git a/src/w32gui.h b/src/w32gui.h index 4f142b09cc..00d5d1f57c 100644 --- a/src/w32gui.h +++ b/src/w32gui.h @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef EMACS_W32GUI_H #define EMACS_W32GUI_H diff --git a/src/w32heap.c b/src/w32heap.c index 510f6762bb..85ed050d99 100644 --- a/src/w32heap.c +++ b/src/w32heap.c @@ -14,7 +14,7 @@ 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 . */ + along with GNU Emacs. If not, see . */ /* Geoff Voelker (voelker@cs.washington.edu) 7-29-94 diff --git a/src/w32heap.h b/src/w32heap.h index 0b3e9dd888..1cabbd84df 100644 --- a/src/w32heap.h +++ b/src/w32heap.h @@ -14,7 +14,7 @@ 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 . +along with GNU Emacs. If not, see . Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */ diff --git a/src/w32inevt.c b/src/w32inevt.c index ed1f1d2e9a..0b0f3f9e66 100644 --- a/src/w32inevt.c +++ b/src/w32inevt.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Drew Bliss 01-Oct-93 diff --git a/src/w32inevt.h b/src/w32inevt.h index 87442cd5f3..b761d952eb 100644 --- a/src/w32inevt.h +++ b/src/w32inevt.h @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef EMACS_W32INEVT_H #define EMACS_W32INEVT_H diff --git a/src/w32menu.c b/src/w32menu.c index de5c4b46b5..d394628521 100644 --- a/src/w32menu.c +++ b/src/w32menu.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include diff --git a/src/w32notify.c b/src/w32notify.c index 7987d9f656..4e0e5804a5 100644 --- a/src/w32notify.c +++ b/src/w32notify.c @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Written by Eli Zaretskii . diff --git a/src/w32proc.c b/src/w32proc.c index 4459ebe324..ca59f995e6 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Drew Bliss Oct 14, 1993 diff --git a/src/w32reg.c b/src/w32reg.c index de19ae1485..040857e87b 100644 --- a/src/w32reg.c +++ b/src/w32reg.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Written by Kevin Gallo */ diff --git a/src/w32select.c b/src/w32select.c index 03bcc1c21d..003bef2dda 100644 --- a/src/w32select.c +++ b/src/w32select.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Written by Kevin Gallo, Benjamin Riefenstahl */ diff --git a/src/w32select.h b/src/w32select.h index 5cf2d6f638..da2057a0e8 100644 --- a/src/w32select.h +++ b/src/w32select.h @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #ifndef W32SELECT_H #define W32SELECT_H diff --git a/src/w32term.c b/src/w32term.c index 0a44a8fb22..c15cbbfa84 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include #include diff --git a/src/w32term.h b/src/w32term.h index 9956682c5c..16b44b0ca2 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ /* Added by Kevin Gallo */ diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c index e4055638cc..ca030ad5ae 100644 --- a/src/w32uniscribe.c +++ b/src/w32uniscribe.c @@ -14,7 +14,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include diff --git a/src/w32xfns.c b/src/w32xfns.c index 587a24125b..39a69d14db 100644 --- a/src/w32xfns.c +++ b/src/w32xfns.c @@ -15,7 +15,7 @@ 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 . */ +along with GNU Emacs. If not, see . */ #include #include diff --git a/test/ChangeLog.1 b/test/ChangeLog.1 index 4491eb82d6..d244798038 100644 --- a/test/ChangeLog.1 +++ b/test/ChangeLog.1 @@ -2967,4 +2967,4 @@ 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 . + along with GNU Emacs. If not, see . commit bbda601d1d4e125c9d3c374b56eee3e2e9623f1d Author: Paul Eggert Date: Fri Sep 29 16:40:18 2017 -0700 ; Spelling fixes diff --git a/doc/misc/efaq.texi b/doc/misc/efaq.texi index a8ece771fc..7809cfe98a 100644 --- a/doc/misc/efaq.texi +++ b/doc/misc/efaq.texi @@ -3501,7 +3501,7 @@ The most up-to-date official GNU software is normally kept at A list of sites mirroring @samp{ftp.gnu.org} can be found at -@uref{httpss://www.gnu.org/prep/ftp} +@uref{https://www.gnu.org/prep/ftp} @node Difference between Emacs and XEmacs @section What is the difference between Emacs and XEmacs (formerly Lucid Emacs)? diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index d1e476267b..b50ac7f05e 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -50,8 +50,8 @@ You can now find them here : - https://github.com/org-mime/org-mime *** Change ~org-texinfo-classes~ value -The value cannot support functions to create sectionning commands -anymore. Also, the sectionning commands should include commands for +The value cannot support functions to create sectioning commands +anymore. Also, the sectioning commands should include commands for appendices. See the docstring for more information. *** Removal of ~:sitemap-sans-extension~ @@ -159,7 +159,7 @@ developers to prepend code to the scheme block being processed. Multiple ~:prologue~ headers can be added each of them using a string with the content to be added. -The scheme blocks are prepared by surronding the code in the block +The scheme blocks are prepared by surrounding the code in the block with a let form. The content of the ~:prologue~ headers are prepended before this let form. @@ -280,7 +280,7 @@ argument for the scope of the clock table. Global table of contents are generated using vanilla Markdown syntax instead of HTML. Also #+TOC keyword, including local table of contents, are now supported. -**** Add Slovanian translations +**** Add Slovenian translations **** Implement ~org-export-insert-image-links~ This new function is meant to be used in back-ends supporting images as descriptions of links, a.k.a. image links. See its docstring for @@ -467,7 +467,7 @@ far away in the future. *** Save point before opening a file with an unknown search option When following a file link with a search option (e.g., =::#custom-id=) -that doesn't exist in the target file, save positon before raising an +that doesn't exist in the target file, save position before raising an error. As a consequence, it is possible to jump back to the original document with ~org-mark-ring-goto~ (default binding =C-c &=). @@ -479,7 +479,7 @@ See docstring for details. This variable is a ~defcustom~ and replaces the variable ~org-babel-capitalize-example-region-markers~, which is a ~defvar~ and -is now obselete. +is now obsolete. *** =INCLUDE= keywords in commented trees are now ignored. *** Default value for ~org-texinfo-text-markup-alist~ changed. diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el index 127290e598..5c785daa8a 100644 --- a/lisp/net/rcirc.el +++ b/lisp/net/rcirc.el @@ -20,7 +20,7 @@ ;; 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 . +;; along with GNU Emacs. If not, see . ;;; Commentary: diff --git a/lisp/org/ob-sql.el b/lisp/org/ob-sql.el index 9250825d4e..1b1d2dc09d 100644 --- a/lisp/org/ob-sql.el +++ b/lisp/org/ob-sql.el @@ -127,7 +127,7 @@ SQL Server on Windows and Linux platform." " ")) (defun org-babel-sql-dbstring-sqsh (host user password database) - "Make sqsh commmand line args for database connection. + "Make sqsh command line args for database connection. \"sqsh\" is one method to access Sybase or MS SQL via Linux platform" (mapconcat #'identity (delq nil diff --git a/lisp/org/org-datetree.el b/lisp/org/org-datetree.el index 308f42ff6c..6d1926bc15 100644 --- a/lisp/org/org-datetree.el +++ b/lisp/org/org-datetree.el @@ -54,7 +54,7 @@ Added time stamp is active unless value is `inactive'." "Find or create an entry for date D. If KEEP-RESTRICTION is non-nil, do not widen the buffer. When it is nil, the buffer will be widened to make sure an existing date -tree can be found. If it is the sympol `subtree-at-point', then the tree +tree can be found. If it is the symbol `subtree-at-point', then the tree will be built under the headline at point." (setq-local org-datetree-base-level 1) (save-restriction @@ -94,7 +94,7 @@ will be built under the headline at point." Compared to `org-datetree-find-date-create' this function creates entries ordered by week instead of months. When it is nil, the buffer will be widened to make sure an existing date -tree can be found. If it is the sympol `subtree-at-point', then the tree +tree can be found. If it is the symbol `subtree-at-point', then the tree will be built under the headline at point." (setq-local org-datetree-base-level 1) (save-restriction diff --git a/lisp/org/org-table.el b/lisp/org/org-table.el index ae43790864..66907e2cd9 100644 --- a/lisp/org/org-table.el +++ b/lisp/org/org-table.el @@ -5131,7 +5131,7 @@ information." (column ;; Call costly `org-export-table-cell-address' only if ;; absolutely necessary, i.e., if one - ;; of :fmt :efmt :hmft has a "plist type" value. + ;; of :fmt :efmt :hfmt has a "plist type" value. ,(and (cl-some (lambda (v) (integerp (car-safe v))) (list efmt hfmt fmt)) '(1+ (cdr (org-export-table-cell-address cell info)))))) diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index 14598bcafb..2a867bb365 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el @@ -3592,7 +3592,7 @@ so that `occur-next' and `occur-prev' will work." If `sh-use-smie' is non-nil, call `smie-config-guess'. Otherwise, run the sh-script specific indent learning command, as -decribed below. +described below. Output in buffer \"*indent*\" shows any lines which have conflicting values of a variable, and the final value of all variables learned. diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index bfdc301780..d430caec8a 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -2720,7 +2720,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (file-symlink-p tmp-name5))) ;; `smbclient' does not show symlinks in directories, so ;; we cannot delete a non-empty directory. We delete the - ;; file explicitely. + ;; file explicitly. (delete-file tmp-name5)) ;; Cleanup. commit 695cf5300b4f5b0a8f3bd615b3259a99c5532b5e Author: Noam Postavsky Date: Mon Sep 25 21:58:55 2017 -0400 Wait for frame visibility with timeout in w32term too * src/w32term.c (syms_of_w32term) [x-wait-for-event-timeout]: New variable. (x_make_frame_visible): Wait for frame to become visible according to its value. (input_signal_count): Remove. diff --git a/src/w32term.c b/src/w32term.c index d7ec40118f..0a44a8fb22 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -163,10 +163,6 @@ int last_scroll_bar_drag_pos; /* Keyboard code page - may be changed by language-change events. */ int w32_keyboard_codepage; -/* Incremented by w32_read_socket whenever it really tries to read - events. */ -static int volatile input_signal_count; - #ifdef CYGWIN int w32_message_fd = -1; #endif /* CYGWIN */ @@ -4658,9 +4654,6 @@ w32_read_socket (struct terminal *terminal, block_input (); - /* So people can tell when we have read the available input. */ - input_signal_count++; - /* Process any incoming thread messages. */ drain_message_queue (); @@ -6614,7 +6607,8 @@ w32_frame_raise_lower (struct frame *f, bool raise_flag) /* Change of visibility. */ -/* This tries to wait until the frame is really visible. +/* This tries to wait until the frame is really visible, depending on + the value of Vx_visible_frame_timeout. However, if the window manager asks the user where to position the frame, this will return before the user finishes doing that. The frame will not actually be visible at that time, @@ -6673,12 +6667,16 @@ x_make_frame_visible (struct frame *f) : SW_SHOWNORMAL); } + if (!FLOATP (Vx_wait_for_event_timeout)) + return; + /* Synchronize to ensure Emacs knows the frame is visible before we do anything else. We do this loop with input not blocked so that incoming events are handled. */ { Lisp_Object frame; - int count; + double timeout = XFLOAT_DATA (Vx_wait_for_event_timeout); + double start_time = XFLOAT_DATA (Ffloat_time (Qnil)); /* This must come after we set COUNT. */ unblock_input (); @@ -6688,8 +6686,8 @@ x_make_frame_visible (struct frame *f) /* Wait until the frame is visible. Process X events until a MapNotify event has been seen, or until we think we won't get a MapNotify at all.. */ - for (count = input_signal_count + 10; - input_signal_count < count && !FRAME_VISIBLE_P (f);) + while (timeout > (XFLOAT_DATA (Ffloat_time (Qnil)) - start_time) && + !FRAME_VISIBLE_P (f)) { /* Force processing of queued events. */ /* TODO: x_sync equivalent? */ @@ -7321,6 +7319,17 @@ syms_of_w32term (void) DEFSYM (Qrenamed_from, "renamed-from"); DEFSYM (Qrenamed_to, "renamed-to"); + DEFVAR_LISP ("x-wait-for-event-timeout", Vx_wait_for_event_timeout, + doc: /* How long to wait for X events. + +Emacs will wait up to this many seconds to receive X events after +making changes which affect the state of the graphical interface. +Under some window managers this can take an indefinite amount of time, +so it is important to limit the wait. + +If set to a non-float value, there will be no wait at all. */); + Vx_wait_for_event_timeout = make_float (0.1); + DEFVAR_INT ("w32-num-mouse-buttons", w32_num_mouse_buttons, doc: /* Number of physical mouse buttons. */); commit e1f6e3127a292e6ba66d27c49ddda4fe949569f5 Author: Noam Postavsky Date: Wed Aug 30 23:12:22 2017 -0400 Bring back the busy wait after x_make_frame_visible (Bug#25521) But wait specfically for a MapNotify event, and only for a configurable amount of time. * src/xterm.c (syms_of_xterm) [x-wait-for-event-timeout]: New variable. (x_wait_for_event): Use it instead of hardcoding the wait to 0.1s. (x_make_frame_visible): Call x_wait_for_event at the end. * etc/NEWS: Announce x_wait_for_event. diff --git a/etc/NEWS b/etc/NEWS index 922dfbdc24..ab9a2a5f32 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -600,6 +600,11 @@ The two new variables, 'bidi-paragraph-start-re' and 'bidi-paragraph-separate-re', allow customization of what exactly are paragraphs, for the purposes of bidirectional display. +--- +** New variable 'x-wait-for-event-timeout'. +This controls how long Emacs will wait for updates to the graphical +state to take effect (making a frame visible, for example). + * Changes in Specialized Modes and Packages in Emacs 26.1 diff --git a/src/xterm.c b/src/xterm.c index 0b321909c8..90275763cb 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -11029,17 +11029,22 @@ x_sync_with_move (struct frame *f, int left, int top, bool fuzzy) void x_wait_for_event (struct frame *f, int eventtype) { - int level = interrupt_input_blocked; + if (!FLOATP (Vx_wait_for_event_timeout)) + return; + int level = interrupt_input_blocked; fd_set fds; struct timespec tmo, tmo_at, time_now; int fd = ConnectionNumber (FRAME_X_DISPLAY (f)); f->wait_event_type = eventtype; - /* Set timeout to 0.1 second. Hopefully not noticeable. - Maybe it should be configurable. */ - tmo = make_timespec (0, 100 * 1000 * 1000); + /* Default timeout is 0.1 second. Hopefully not noticeable. */ + double timeout = XFLOAT_DATA (Vx_wait_for_event_timeout); + time_t timeout_seconds = (time_t) timeout; + tmo = make_timespec + (timeout_seconds, (long int) ((timeout - timeout_seconds) + * 1000 * 1000 * 1000)); tmo_at = timespec_add (current_timespec (), tmo); while (f->wait_event_type) @@ -11365,8 +11370,13 @@ xembed_send_message (struct frame *f, Time t, enum xembed_message msg, /* Change of visibility. */ -/* This function sends the request to make the frame visible, but may - return before it the frame's visibility is changed. */ +/* This tries to wait until the frame is really visible, depending on + the value of Vx_wait_for_event_timeout. + However, if the window manager asks the user where to position + the frame, this will return before the user finishes doing that. + The frame will not actually be visible at that time, + but it will become visible later when the window manager + finishes with it. */ void x_make_frame_visible (struct frame *f) @@ -11437,11 +11447,14 @@ x_make_frame_visible (struct frame *f) before we do anything else. We do this loop with input not blocked so that incoming events are handled. */ { + Lisp_Object frame; /* This must be before UNBLOCK_INPUT since events that arrive in response to the actions above will set it when they are handled. */ bool previously_visible = f->output_data.x->has_been_visible; + XSETFRAME (frame, f); + int original_left = f->left_pos; int original_top = f->top_pos; @@ -11488,6 +11501,10 @@ x_make_frame_visible (struct frame *f) unblock_input (); } + + /* Try to wait for a MapNotify event (that is what tells us when a + frame becomes visible). */ + x_wait_for_event (f, MapNotify); } } @@ -13283,6 +13300,17 @@ This should be one of the symbols `ctrl', `alt', `hyper', `meta', keysyms. The default is nil, which is the same as `super'. */); Vx_super_keysym = Qnil; + DEFVAR_LISP ("x-wait-for-event-timeout", Vx_wait_for_event_timeout, + doc: /* How long to wait for X events. + +Emacs will wait up to this many seconds to receive X events after +making changes which affect the state of the graphical interface. +Under some window managers this can take an indefinite amount of time, +so it is important to limit the wait. + +If set to a non-float value, there will be no wait at all. */); + Vx_wait_for_event_timeout = make_float (0.1); + DEFVAR_LISP ("x-keysym-table", Vx_keysym_table, doc: /* Hash table of character codes indexed by X keysym codes. */); Vx_keysym_table = make_hash_table (hashtest_eql, 900, commit bccf635217b0ba887d95b429f7d5d6903007a7b1 Author: Philipp Stephani Date: Fri Sep 29 22:47:33 2017 +0200 ; * src/gtkutil.c (xg_check_special_colors): Add another GTK+ FIXME. diff --git a/src/gtkutil.c b/src/gtkutil.c index 0da7039919..9f05524738 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -577,6 +577,10 @@ xg_check_special_colors (struct frame *f, if (get_fg) gtk_style_context_get_color (gsty, state, &col); else + /* FIXME: gtk_style_context_get_background_color is deprecated + in GTK+ 3.16. New versions of GTK+ don’t use the concept of + a single background color any more, so we shouldn’t query for + it. */ gtk_style_context_get_background_color (gsty, state, &col); unsigned short commit 5406be4db6528095b5e5b8bf94b06b2c06610340 Author: Philipp Stephani Date: Fri Sep 29 22:43:19 2017 +0200 Revert "GTK+: Stop querying for background colors." This reverts commit f6818e761eaafe095e07249180dc8f9a329f1473. diff --git a/src/gtkutil.c b/src/gtkutil.c index b98b0d08e7..f3e89c82c6 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -566,14 +566,6 @@ xg_check_special_colors (struct frame *f, if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg)) return success_p; -#if GTK_CHECK_VERSION (3, 16, 0) - if (get_bg) - /* gtk_style_context_get_background_color is deprecated in - GTK+ 3.16. New versions of GTK+ don't use the concept of a - single background color any more, so we can't query for it. */ - return false; -#endif - block_input (); { #ifdef HAVE_GTK3 @@ -585,12 +577,7 @@ xg_check_special_colors (struct frame *f, if (get_fg) gtk_style_context_get_color (gsty, state, &col); else -#if GTK_CHECK_VERSION (3, 16, 0) - /* We can't get here. */ - emacs_abort (); -#else gtk_style_context_get_background_color (gsty, state, &col); -#endif unsigned short r = col.red * 65535, commit 26d58f0c5865c9132e2fc559e061ef704a086d30 Author: Glenn Morris Date: Fri Sep 29 12:49:19 2017 -0400 ; Standardize license notices diff --git a/lisp/org/ob-vala.el b/lisp/org/ob-vala.el index 3998e2d4e2..580e27246d 100644 --- a/lisp/org/ob-vala.el +++ b/lisp/org/ob-vala.el @@ -8,6 +8,8 @@ ;;; License: +;; 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 diff --git a/lisp/org/org-duration.el b/lisp/org/org-duration.el index 3e5f0f56a5..096e973d34 100644 --- a/lisp/org/org-duration.el +++ b/lisp/org/org-duration.el @@ -1,22 +1,24 @@ ;;; org-duration.el --- Library handling durations -*- lexical-binding: t; -*- -;; Copyright (C) 2017 Free Software Foundation, Inc. +;; Copyright (C) 2017 Free Software Foundation, Inc. ;; Author: Nicolas Goaziou ;; Keywords: outlines, hypermedia, calendar, wp -;; This program is free software; you can redistribute it and/or modify +;; 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. -;; This program is distributed in the hope that it will be useful, +;; 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 this program. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: commit 61225964edbaa01e49a6e776af00502ab31767b5 Author: Eli Zaretskii Date: Fri Sep 29 15:53:27 2017 +0300 Revert "bug#28609: simple.el" This reverts commit a75ab3b3fb8ab69ef38a94403d061f88f3b5b63e. diff --git a/lisp/simple.el b/lisp/simple.el index 44728fd566..469557713d 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -1003,7 +1003,7 @@ Called with one argument METHOD. If METHOD is `delete-only', then delete the region; the return value is undefined. If METHOD is nil, then return the content as a string. If METHOD is `bounds', then return the boundaries of the region -as a pair of the form (START . END) positions. +as a list of pairs of (START . END) positions. If METHOD is anything else, delete the region and return its content as a string, after filtering it with `filter-buffer-substring', which is called with METHOD as its 3rd argument.") @@ -5473,8 +5473,7 @@ also checks the value of `use-empty-active-region'." (progn (cl-assert (mark)) t))) (defun region-bounds () - "Return the boundaries of the region as a pair of positions. -Value is of the form (START . END)." + "Return the boundaries of the region as a list of pairs of (START . END) positions." (funcall region-extract-function 'bounds)) (defun region-noncontiguous-p () commit a75ab3b3fb8ab69ef38a94403d061f88f3b5b63e Author: Devon Sean McCullough Date: Tue Sep 26 10:51:04 2017 -0400 bug#28609: simple.el Correct grammar; also, call a pair a pair. (cherry picked from commit 25ef543a97a80718cc4eb33734d393420a43f41e) diff --git a/lisp/simple.el b/lisp/simple.el index 469557713d..44728fd566 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -1003,7 +1003,7 @@ Called with one argument METHOD. If METHOD is `delete-only', then delete the region; the return value is undefined. If METHOD is nil, then return the content as a string. If METHOD is `bounds', then return the boundaries of the region -as a list of pairs of (START . END) positions. +as a pair of the form (START . END) positions. If METHOD is anything else, delete the region and return its content as a string, after filtering it with `filter-buffer-substring', which is called with METHOD as its 3rd argument.") @@ -5473,7 +5473,8 @@ also checks the value of `use-empty-active-region'." (progn (cl-assert (mark)) t))) (defun region-bounds () - "Return the boundaries of the region as a list of pairs of (START . END) positions." + "Return the boundaries of the region as a pair of positions. +Value is of the form (START . END)." (funcall region-extract-function 'bounds)) (defun region-noncontiguous-p () commit c7a21430c199dca0db30db67125f38bc1795f68b Author: Eli Zaretskii Date: Fri Sep 29 13:27:42 2017 +0300 ; * etc/NEWS: Fix last change. diff --git a/etc/NEWS b/etc/NEWS index 1b5ae658f6..922dfbdc24 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1354,11 +1354,11 @@ priority instead. Now the code does what the documentation says it should do. --- -** 'eldoc-message' only accepts one argument now. Programs that -called it with multiple arguments before should pass them through -'format' first. Even that is discouraged: for ElDoc support, you -should set 'eldoc-documentation-function' instead of calling -'eldoc-message' directly. +** The function 'eldoc-message' now accepts a single argument. +Programs that called it with multiple arguments before should pass +them through 'format' first. Even that is discouraged: for ElDoc +support, you should set 'eldoc-documentation-function' instead of +calling 'eldoc-message' directly. * Lisp Changes in Emacs 26.1 commit 33401b26b1aecf61adfd77fa7c4e29913c0bb3d7 Merge: c1ac8c170f d4b2bbdc73 Author: Eli Zaretskii Date: Fri Sep 29 13:24:05 2017 +0300 Merge branch 'emacs-26' of git.savannah.gnu.org:/srv/git/emacs into emacs-26 commit d4b2bbdc73ace5cb0971a32a75941486489d1cc5 Merge: eaefbc26d5 af130f900f Author: Rasmus Date: Fri Sep 29 10:41:51 2017 +0200 Merge branch 'emacs-26' into scratch/org-mode-merge commit c1ac8c170f17a98b7e5d6e098f707daeb71ea27d Merge: 18073beb14 af130f900f Author: Eli Zaretskii Date: Fri Sep 29 10:02:33 2017 +0300 Merge branch 'emacs-26' of git.savannah.gnu.org:/srv/git/emacs into emacs-26 commit af130f900fc499f71ea22f10ba055a75ce35ed4e Author: Noam Postavsky Date: Sat Sep 23 11:40:14 2017 -0400 Fix ert backtrace saving for non-`signal'ed errors (Bug#28333) * lisp/emacs-lisp/ert.el (ert--run-test-debugger): Take the frames above the `debugger' frame, rather than assuming there will be a `signal' frame. diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el index d4276221ba..83acbacb88 100644 --- a/lisp/emacs-lisp/ert.el +++ b/lisp/emacs-lisp/ert.el @@ -742,9 +742,8 @@ run. ARGS are the arguments to `debugger'." ;; backtrace ready for printing is important for batch ;; use. ;; - ;; Grab the frames starting from `signal', frames below - ;; that are all from the debugger. - (backtrace (backtrace-frames 'signal)) + ;; Grab the frames above the debugger. + (backtrace (cdr (backtrace-frames debugger))) (infos (reverse ert--infos))) (setf (ert--test-execution-info-result info) (cl-ecase type commit 7476eeaa236039b8ebd09aad6bd977d26646ace6 Author: Alan Third Date: Thu Sep 28 22:27:02 2017 +0100 Revert "Fix build on macOS (bug#28571)" This reverts commit fec63089d53d2196b0348086aeed70277fbc02c0. Prematurely pushed. diff --git a/src/conf_post.h b/src/conf_post.h index af946082ee..febdb8b8bf 100644 --- a/src/conf_post.h +++ b/src/conf_post.h @@ -397,12 +397,3 @@ extern int emacs_setenv_TZ (char const *); #else # define UNINIT /* empty */ #endif - -/* macOS 10.13 supports futimens, futimesat and utimensat, older - versions don't but can appear as though they do. Disable them - entirely to avoid breaking cross-version builds on macOS. */ -#ifdef DARWIN_OS -# undef HAVE_FUTIMENS -# undef HAVE_FUTIMESAT -# undef HAVE_UTIMENSAT -#endif commit 122ffe521b2b34caa6a1f2583878c569ea395cb3 Author: Charles A. Roelli Date: Thu Sep 28 20:17:35 2017 +0200 Fix 'point-to-register' prompt with prefix arg * lisp/register.el (point-to-register): Fix prompt when a prefix argument is given. diff --git a/lisp/register.el b/lisp/register.el index 913380763c..23eefd08b8 100644 --- a/lisp/register.el +++ b/lisp/register.el @@ -182,8 +182,11 @@ Use \\[jump-to-register] to go to that location or restore that configuration. Argument is a character, naming the register. Interactively, reads the register using `register-read-with-preview'." - (interactive (list (register-read-with-preview "Point to register: ") - current-prefix-arg)) + (interactive (list (register-read-with-preview + (if current-prefix-arg + "Frame configuration to register: " + "Point to register: ")) + current-prefix-arg)) ;; Turn the marker into a file-ref if the buffer is killed. (add-hook 'kill-buffer-hook 'register-swap-out nil t) (set-register register commit fec63089d53d2196b0348086aeed70277fbc02c0 Author: Alan Third Date: Sun Sep 24 12:01:03 2017 +0100 Fix build on macOS (bug#28571) * src/conf_post.h (HAVE_FUTIMENS, HAVE_FUTIMESAT, HAVE_UTIMENSAT) [DARWIN_OS]: Undefine. diff --git a/src/conf_post.h b/src/conf_post.h index febdb8b8bf..af946082ee 100644 --- a/src/conf_post.h +++ b/src/conf_post.h @@ -397,3 +397,12 @@ extern int emacs_setenv_TZ (char const *); #else # define UNINIT /* empty */ #endif + +/* macOS 10.13 supports futimens, futimesat and utimensat, older + versions don't but can appear as though they do. Disable them + entirely to avoid breaking cross-version builds on macOS. */ +#ifdef DARWIN_OS +# undef HAVE_FUTIMENS +# undef HAVE_FUTIMESAT +# undef HAVE_UTIMENSAT +#endif commit 0f9a78e7700ab3eed370c2f616d7932d953dd100 Author: Simen Heggestøyl Date: Thu Sep 28 18:47:07 2017 +0200 Add tests for `css-current-defun-name' * test/lisp/textmodes/css-mode-tests.el (css-test-current-defun-name) (css-test-current-defun-name-nested) (css-test-current-defun-name-complex): New tests for `css-current-defun-name'. diff --git a/test/lisp/textmodes/css-mode-tests.el b/test/lisp/textmodes/css-mode-tests.el index f93fdbbc5a..47cf5f9244 100644 --- a/test/lisp/textmodes/css-mode-tests.el +++ b/test/lisp/textmodes/css-mode-tests.el @@ -80,6 +80,27 @@ (equal (seq-sort #'string-lessp (css--value-class-lookup 'position)) '("bottom" "calc()" "center" "left" "right" "top")))) +(ert-deftest css-test-current-defun-name () + (with-temp-buffer + (insert "body { top: 0; }") + (goto-char 7) + (should (equal (css-current-defun-name) "body")) + (goto-char 18) + (should (equal (css-current-defun-name) "body")))) + +(ert-deftest css-test-current-defun-name-nested () + (with-temp-buffer + (insert "body > .main a { top: 0; }") + (goto-char 20) + (should (equal (css-current-defun-name) "body > .main a")))) + +(ert-deftest css-test-current-defun-name-complex () + (with-temp-buffer + (insert "input[type=submit]:hover { color: red; }") + (goto-char 30) + (should (equal (css-current-defun-name) + "input[type=submit]:hover")))) + ;;; Completion (defun css-mode-tests--completions () commit 551594e951642a043862dabb987facfc0253f7d3 Author: Mark Oteiza Date: Thu Sep 28 07:52:00 2017 -0400 Add indent spec to easy-mmode macros Ideally these macros should expand to the proper code instead of relegating all the work to a function call. * lisp/emacs-lisp/easy-mmode.el (easy-mmode-defmap): (easy-mmode-define-syntax): Add indent spec. diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index bf087fc2e9..643a65f48d 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -543,6 +543,7 @@ Valid keywords and arguments are: "Define a constant M whose value is the result of `easy-mmode-define-keymap'. The M, BS, and ARGS arguments are as per that function. DOC is the constant's documentation." + (declare (indent 1)) `(defconst ,m (easy-mmode-define-keymap ,bs nil (if (boundp ',m) ,m) ,(cons 'list args)) ,doc)) @@ -569,6 +570,7 @@ the constant's documentation." (defmacro easy-mmode-defsyntax (st css doc &rest args) "Define variable ST as a syntax-table. CSS contains a list of syntax specifications of the form (CHAR . SYNTAX)." + (declare (indent 1)) `(progn (autoload 'easy-mmode-define-syntax "easy-mmode") (defconst ,st (easy-mmode-define-syntax ,css ,(cons 'list args)) ,doc))) commit 88a0dd71f10ffb63fba08c062e948551c3e876c2 Author: Martin Rudalics Date: Thu Sep 28 10:10:21 2017 +0200 In w32fullscreen_hook don't add decorations to undecorated frames * src/w32term.c (w32fullscreen_hook): Do not add (or try to remove) decorations for undecorated frames. diff --git a/src/w32term.c b/src/w32term.c index a7a510b9ec..d7ec40118f 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -6252,7 +6252,8 @@ w32fullscreen_hook (struct frame *f) if (FRAME_PREV_FSMODE (f) == FULLSCREEN_BOTH) { - SetWindowLong (hwnd, GWL_STYLE, dwStyle | WS_OVERLAPPEDWINDOW); + if (!FRAME_UNDECORATED (f)) + SetWindowLong (hwnd, GWL_STYLE, dwStyle | WS_OVERLAPPEDWINDOW); SetWindowPlacement (hwnd, &FRAME_NORMAL_PLACEMENT (f)); } else if (FRAME_PREV_FSMODE (f) == FULLSCREEN_HEIGHT @@ -6278,7 +6279,8 @@ w32fullscreen_hook (struct frame *f) w32_fullscreen_rect (hwnd, f->want_fullscreen, FRAME_NORMAL_PLACEMENT (f).rcNormalPosition, &rect); - SetWindowLong (hwnd, GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW); + if (!FRAME_UNDECORATED (f)) + SetWindowLong (hwnd, GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW); SetWindowPos (hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOOWNERZORDER | SWP_FRAMECHANGED); commit ce540f8a687672fade6eb91e64ddf86e1e868784 Author: João Távora Date: Wed Sep 27 22:35:49 2017 +0100 Revert "Split flymake.el into flymake-proc.el and flymake-ui.el" In other words, re-coalesce the two files, lisp/progmodes/flymake-proc.el and lisp/progmodes/flymake-ui.el, back into a single one, lisp/progmodes/flymake.el. The changesets "Prefer HTTPS to FTP and HTTP in documentation" and "allow nil init in flymake-allowed-file-name-masks to disable flymake" are kept in place in the new lisp/progmodes/flymake.el. This reverts Git commit eb34f7f5a29e7bf62326ecb6e693f28878be28cd. Don't merge this back to master as development happening there builds upon this work. See also https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00932.html. diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el deleted file mode 100644 index 0cbf3e1c67..0000000000 --- a/lisp/progmodes/flymake-proc.el +++ /dev/null @@ -1,1091 +0,0 @@ -;;; flymake-proc.el --- Flymake for external syntax checker processes -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2017 Free Software Foundation, Inc. - -;; Author: Pavel Kobyakov -;; Maintainer: Leo Liu -;; Version: 0.3 -;; Keywords: c languages tools - -;; 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 . - -;;; Commentary: -;; -;; Flymake is a minor Emacs mode performing on-the-fly syntax checks. -;; -;; This file contains the most original implementation of flymake's -;; main source of on-the-fly diagnostic info, the external syntax -;; checker backend. -;; -;;; Bugs/todo: - -;; - Only uses "Makefile", not "makefile" or "GNUmakefile" -;; (from http://bugs.debian.org/337339). - -;;; Code: - -(require 'flymake-ui) - -(defcustom flymake-compilation-prevents-syntax-check t - "If non-nil, don't start syntax check if compilation is running." - :group 'flymake - :type 'boolean) - -(defcustom flymake-xml-program - (if (executable-find "xmlstarlet") "xmlstarlet" "xml") - "Program to use for XML validation." - :type 'file - :group 'flymake - :version "24.4") - -(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest") - "Dirs where to look for master files." - :group 'flymake - :type '(repeat (string))) - -(defcustom flymake-master-file-count-limit 32 - "Max number of master files to check." - :group 'flymake - :type 'integer) - -(defcustom flymake-allowed-file-name-masks - '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-simple-make-init) - ("\\.xml\\'" flymake-xml-init) - ("\\.html?\\'" flymake-xml-init) - ("\\.cs\\'" flymake-simple-make-init) - ("\\.p[ml]\\'" flymake-perl-init) - ("\\.php[345]?\\'" flymake-php-init) - ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup) - ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup) - ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup) - ("\\.tex\\'" flymake-simple-tex-init) - ("\\.idl\\'" flymake-simple-make-init) - ;; ("\\.cpp\\'" 1) - ;; ("\\.java\\'" 3) - ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'") - ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2)) - ;; ("\\.idl\\'" 1) - ;; ("\\.odl\\'" 1) - ;; ("[0-9]+\\.tex\\'" 2 ("\\.tex\\'") - ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 )) - ;; ("\\.tex\\'" 1) - ) - "Files syntax checking is allowed for. -This is an alist with elements of the form: - REGEXP [INIT [CLEANUP [NAME]]] -REGEXP is a regular expression that matches a file name. -INIT is the init function to use, missing means disable `flymake-mode'. -CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'. -NAME is the file name function to use, default `flymake-get-real-file-name'." - :group 'flymake - :type '(alist :key-type (regexp :tag "File regexp") - :value-type - (list :tag "Handler functions" - (choice :tag "Init function" - (const :tag "disable" nil) - function) - (choice :tag "Cleanup function" - (const :tag "flymake-simple-cleanup" nil) - function) - (choice :tag "Name function" - (const :tag "flymake-get-real-file-name" nil) - function)))) - -(defvar flymake-processes nil - "List of currently active flymake processes.") - -(defvar-local flymake-output-residual nil) - -(defun flymake-get-file-name-mode-and-masks (file-name) - "Return the corresponding entry from `flymake-allowed-file-name-masks'." - (unless (stringp file-name) - (error "Invalid file-name")) - (let ((fnm flymake-allowed-file-name-masks) - (mode-and-masks nil)) - (while (and (not mode-and-masks) fnm) - (let ((item (pop fnm))) - (when (string-match (car item) file-name) - (setq mode-and-masks item)))) ; (cdr item) may be nil - (setq mode-and-masks (cdr mode-and-masks)) - (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks)) - mode-and-masks)) - -(defun flymake-can-syntax-check-file (file-name) - "Determine whether we can syntax check FILE-NAME. -Return nil if we cannot, non-nil if we can." - (if (flymake-get-init-function file-name) t nil)) - -(defun flymake-get-init-function (file-name) - "Return init function to be used for the file." - (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name)))) - ;;(flymake-log 0 "calling %s" init-f) - ;;(funcall init-f (current-buffer)) - init-f)) - -(defun flymake-get-cleanup-function (file-name) - "Return cleanup function to be used for the file." - (or (nth 1 (flymake-get-file-name-mode-and-masks file-name)) - 'flymake-simple-cleanup)) - -(defun flymake-get-real-file-name-function (file-name) - (or (nth 2 (flymake-get-file-name-mode-and-masks file-name)) - 'flymake-get-real-file-name)) - -(defvar flymake-find-buildfile-cache (make-hash-table :test #'equal)) - -(defun flymake-get-buildfile-from-cache (dir-name) - "Look up DIR-NAME in cache and return its associated value. -If DIR-NAME is not found, return nil." - (gethash dir-name flymake-find-buildfile-cache)) - -(defun flymake-add-buildfile-to-cache (dir-name buildfile) - "Associate DIR-NAME with BUILDFILE in the buildfile cache." - (puthash dir-name buildfile flymake-find-buildfile-cache)) - -(defun flymake-clear-buildfile-cache () - "Clear the buildfile cache." - (clrhash flymake-find-buildfile-cache)) - -(defun flymake-find-buildfile (buildfile-name source-dir-name) - "Find buildfile starting from current directory. -Buildfile includes Makefile, build.xml etc. -Return its file name if found, or nil if not found." - (or (flymake-get-buildfile-from-cache source-dir-name) - (let* ((file (locate-dominating-file source-dir-name buildfile-name))) - (if file - (progn - (flymake-log 3 "found buildfile at %s" file) - (flymake-add-buildfile-to-cache source-dir-name file) - file) - (progn - (flymake-log 3 "buildfile for %s not found" source-dir-name) - nil))))) - -(defun flymake-fix-file-name (name) - "Replace all occurrences of `\\' with `/'." - (when name - (setq name (expand-file-name name)) - (setq name (abbreviate-file-name name)) - (setq name (directory-file-name name)) - name)) - -(defun flymake-same-files (file-name-one file-name-two) - "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file. -Return t if so, nil if not." - (equal (flymake-fix-file-name file-name-one) - (flymake-fix-file-name file-name-two))) - -;; This is bound dynamically to pass a parameter to a sort predicate below -(defvar flymake-included-file-name) - -(defun flymake-find-possible-master-files (file-name master-file-dirs masks) - "Find (by name and location) all possible master files. - -Name is specified by FILE-NAME and location is specified by -MASTER-FILE-DIRS. Master files include .cpp and .c for .h. -Files are searched for starting from the .h directory and max -max-level parent dirs. File contents are not checked." - (let* ((dirs master-file-dirs) - (files nil) - (done nil)) - - (while (and (not done) dirs) - (let* ((dir (expand-file-name (car dirs) (file-name-directory file-name))) - (masks masks)) - (while (and (file-exists-p dir) (not done) masks) - (let* ((mask (car masks)) - (dir-files (directory-files dir t mask))) - - (flymake-log 3 "dir %s, %d file(s) for mask %s" - dir (length dir-files) mask) - (while (and (not done) dir-files) - (when (not (file-directory-p (car dir-files))) - (setq files (cons (car dir-files) files)) - (when (>= (length files) flymake-master-file-count-limit) - (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit) - (setq done t))) - (setq dir-files (cdr dir-files)))) - (setq masks (cdr masks)))) - (setq dirs (cdr dirs))) - (when files - (let ((flymake-included-file-name (file-name-nondirectory file-name))) - (setq files (sort files 'flymake-master-file-compare)))) - (flymake-log 3 "found %d possible master file(s)" (length files)) - files)) - -(defun flymake-master-file-compare (file-one file-two) - "Compare two files specified by FILE-ONE and FILE-TWO. -This function is used in sort to move most possible file names -to the beginning of the list (File.h -> File.cpp moved to top)." - (and (equal (file-name-sans-extension flymake-included-file-name) - (file-name-base file-one)) - (not (equal file-one file-two)))) - -(defvar flymake-check-file-limit 8192 - "Maximum number of chars to look at when checking possible master file. -Nil means search the entire file.") - -(defun flymake-check-patch-master-file-buffer - (master-file-temp-buffer - master-file-name patched-master-file-name - source-file-name patched-source-file-name - include-dirs regexp) - "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME. -If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME -instead of SOURCE-FILE-NAME. - -For example, foo.cpp is a master file if it includes foo.h. - -When a buffer for MASTER-FILE-NAME exists, use it as a source -instead of reading master file from disk." - (let* ((source-file-nondir (file-name-nondirectory source-file-name)) - (source-file-extension (file-name-extension source-file-nondir)) - (source-file-nonext (file-name-sans-extension source-file-nondir)) - (found nil) - (inc-name nil) - (search-limit flymake-check-file-limit)) - (setq regexp - (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\"" - ;; Hack for tex files, where \include often excludes .tex. - ;; Maybe this is safe generally. - (if (and (> (length source-file-extension) 1) - (string-equal source-file-extension "tex")) - (format "%s\\(?:\\.%s\\)?" - (regexp-quote source-file-nonext) - (regexp-quote source-file-extension)) - (regexp-quote source-file-nondir)))) - (unwind-protect - (with-current-buffer master-file-temp-buffer - (if (or (not search-limit) - (> search-limit (point-max))) - (setq search-limit (point-max))) - (flymake-log 3 "checking %s against regexp %s" - master-file-name regexp) - (goto-char (point-min)) - (while (and (< (point) search-limit) - (re-search-forward regexp search-limit t)) - (let ((match-beg (match-beginning 1)) - (match-end (match-end 1))) - - (flymake-log 3 "found possible match for %s" source-file-nondir) - (setq inc-name (match-string 1)) - (and (> (length source-file-extension) 1) - (string-equal source-file-extension "tex") - (not (string-match (format "\\.%s\\'" source-file-extension) - inc-name)) - (setq inc-name (concat inc-name "." source-file-extension))) - (when (eq t (compare-strings - source-file-nondir nil nil - inc-name (- (length inc-name) - (length source-file-nondir)) nil)) - (flymake-log 3 "inc-name=%s" inc-name) - (when (flymake-check-include source-file-name inc-name - include-dirs) - (setq found t) - ;; replace-match is not used here as it fails in - ;; XEmacs with 'last match not a buffer' error as - ;; check-includes calls replace-in-string - (flymake-replace-region - match-beg match-end - (file-name-nondirectory patched-source-file-name)))) - (forward-line 1))) - (when found - (flymake-save-buffer-in-file patched-master-file-name))) - ;;+(flymake-log 3 "killing buffer %s" - ;; (buffer-name master-file-temp-buffer)) - (kill-buffer master-file-temp-buffer)) - ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found) - (when found - (flymake-log 2 "found master file %s" master-file-name)) - found)) - -;;; XXX: remove -(defun flymake-replace-region (beg end rep) - "Replace text in BUFFER in region (BEG END) with REP." - (save-excursion - (goto-char end) - ;; Insert before deleting, so as to better preserve markers's positions. - (insert rep) - (delete-region beg end))) - -(defun flymake-read-file-to-temp-buffer (file-name) - "Insert contents of FILE-NAME into newly created temp buffer." - (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name)))))) - (with-current-buffer temp-buffer - (insert-file-contents file-name)) - temp-buffer)) - -(defun flymake-copy-buffer-to-temp-buffer (buffer) - "Copy contents of BUFFER into newly created temp buffer." - (with-current-buffer - (get-buffer-create (generate-new-buffer-name - (concat "flymake:" (buffer-name buffer)))) - (insert-buffer-substring buffer) - (current-buffer))) - -(defun flymake-check-include (source-file-name inc-name include-dirs) - "Check if SOURCE-FILE-NAME can be found in include path. -Return t if it can be found via include path using INC-NAME." - (if (file-name-absolute-p inc-name) - (flymake-same-files source-file-name inc-name) - (while (and include-dirs - (not (flymake-same-files - source-file-name - (concat (file-name-directory source-file-name) - "/" (car include-dirs) - "/" inc-name)))) - (setq include-dirs (cdr include-dirs))) - include-dirs)) - -(defun flymake-find-buffer-for-file (file-name) - "Check if there exists a buffer visiting FILE-NAME. -Return t if so, nil if not." - (let ((buffer-name (get-file-buffer file-name))) - (if buffer-name - (get-buffer buffer-name)))) - -(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp) - "Save SOURCE-FILE-NAME with a different name. -Find master file, patch and save it." - (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks)) - (master-file-count (length possible-master-files)) - (idx 0) - (temp-buffer nil) - (master-file-name nil) - (patched-master-file-name nil) - (found nil)) - - (while (and (not found) (< idx master-file-count)) - (setq master-file-name (nth idx possible-master-files)) - (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master")) - (if (flymake-find-buffer-for-file master-file-name) - (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name))) - (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name))) - (setq found - (flymake-check-patch-master-file-buffer - temp-buffer - master-file-name - patched-master-file-name - source-file-name - patched-source-file-name - (funcall get-incl-dirs-f (file-name-directory master-file-name)) - include-regexp)) - (setq idx (1+ idx))) - (if found - (list master-file-name patched-master-file-name) - (progn - (flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count - (file-name-nondirectory source-file-name)) - nil)))) - -(defun flymake-save-buffer-in-file (file-name) - "Save the entire buffer contents into file FILE-NAME. -Create parent directories as needed." - (make-directory (file-name-directory file-name) 1) - (write-region nil nil file-name nil 566) - (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name)) - -(defun flymake-process-filter (process output) - "Parse OUTPUT and highlight error lines. -It's flymake process filter." - (let ((source-buffer (process-buffer process))) - - (flymake-log 3 "received %d byte(s) of output from process %d" - (length output) (process-id process)) - (when (buffer-live-p source-buffer) - (with-current-buffer source-buffer - (flymake-parse-output-and-residual output))))) - -(defun flymake-process-sentinel (process _event) - "Sentinel for syntax check buffers." - (when (memq (process-status process) '(signal exit)) - (let* ((exit-status (process-exit-status process)) - (command (process-command process)) - (source-buffer (process-buffer process)) - (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer)))) - - (flymake-log 2 "process %d exited with code %d" - (process-id process) exit-status) - (condition-case err - (progn - (flymake-log 3 "cleaning up using %s" cleanup-f) - (when (buffer-live-p source-buffer) - (with-current-buffer source-buffer - (funcall cleanup-f))) - - (delete-process process) - (setq flymake-processes (delq process flymake-processes)) - - (when (buffer-live-p source-buffer) - (with-current-buffer source-buffer - - (flymake-parse-residual) - (flymake-post-syntax-check exit-status command) - (setq flymake-is-running nil)))) - (error - (let ((err-str (format "Error in process sentinel for buffer %s: %s" - source-buffer (error-message-string err)))) - (flymake-log 0 err-str) - (with-current-buffer source-buffer - (setq flymake-is-running nil)))))))) - -(defun flymake-post-syntax-check (exit-status command) - (save-restriction - (widen) - (setq flymake-err-info flymake-new-err-info) - (setq flymake-new-err-info nil) - (setq flymake-err-info - (flymake-fix-line-numbers - flymake-err-info 1 (count-lines (point-min) (point-max)))) - (flymake-delete-own-overlays) - (flymake-highlight-err-lines flymake-err-info) - (let (err-count warn-count) - (setq err-count (flymake-get-err-count flymake-err-info "e")) - (setq warn-count (flymake-get-err-count flymake-err-info "w")) - (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)" - (buffer-name) err-count warn-count - (- (float-time) flymake-check-start-time)) - (setq flymake-check-start-time nil) - - (if (and (equal 0 err-count) (equal 0 warn-count)) - (if (equal 0 exit-status) - (flymake-report-status "" "") ; PASSED - (if (not flymake-check-was-interrupted) - (flymake-report-fatal-status "CFGERR" - (format "Configuration error has occurred while running %s" command)) - (flymake-report-status nil ""))) ; "STOPPED" - (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) - -(defun flymake-parse-output-and-residual (output) - "Split OUTPUT into lines, merge in residual if necessary." - (let* ((buffer-residual flymake-output-residual) - (total-output (if buffer-residual (concat buffer-residual output) output)) - (lines-and-residual (flymake-split-output total-output)) - (lines (nth 0 lines-and-residual)) - (new-residual (nth 1 lines-and-residual))) - (setq flymake-output-residual new-residual) - (setq flymake-new-err-info - (flymake-parse-err-lines - flymake-new-err-info lines)))) - -(defun flymake-parse-residual () - "Parse residual if it's non empty." - (when flymake-output-residual - (setq flymake-new-err-info - (flymake-parse-err-lines - flymake-new-err-info - (list flymake-output-residual))) - (setq flymake-output-residual nil))) - -(defun flymake-fix-line-numbers (err-info-list min-line max-line) - "Replace line numbers with fixed value. -If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE. -If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE. -The reason for this fix is because some compilers might report -line number outside the file being compiled." - (let* ((count (length err-info-list)) - (err-info nil) - (line 0)) - (while (> count 0) - (setq err-info (nth (1- count) err-info-list)) - (setq line (flymake-er-get-line err-info)) - (when (or (< line min-line) (> line max-line)) - (setq line (if (< line min-line) min-line max-line)) - (setq err-info-list (flymake-set-at err-info-list (1- count) - (flymake-er-make-er line - (flymake-er-get-line-err-info-list err-info))))) - (setq count (1- count)))) - err-info-list) - -(defun flymake-parse-err-lines (err-info-list lines) - "Parse err LINES, store info in ERR-INFO-LIST." - (let* ((count (length lines)) - (idx 0) - (line-err-info nil) - (real-file-name nil) - (source-file-name buffer-file-name) - (get-real-file-name-f (flymake-get-real-file-name-function source-file-name))) - - (while (< idx count) - (setq line-err-info (flymake-parse-line (nth idx lines))) - (when line-err-info - (setq real-file-name (funcall get-real-file-name-f - (flymake-ler-file line-err-info))) - (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name)) - - (when (flymake-same-files real-file-name source-file-name) - (setq line-err-info (flymake-ler-set-file line-err-info nil)) - (setq err-info-list (flymake-add-err-info err-info-list line-err-info)))) - (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no")) - (setq idx (1+ idx))) - err-info-list)) - -(defun flymake-split-output (output) - "Split OUTPUT into lines. -Return last one as residual if it does not end with newline char. -Returns ((LINES) RESIDUAL)." - (when (and output (> (length output) 0)) - (let* ((lines (split-string output "[\n\r]+" t)) - (complete (equal "\n" (char-to-string (aref output (1- (length output)))))) - (residual nil)) - (when (not complete) - (setq residual (car (last lines))) - (setq lines (butlast lines))) - (list lines residual)))) - -(defun flymake-reformat-err-line-patterns-from-compile-el (original-list) - "Grab error line patterns from ORIGINAL-LIST in compile.el format. -Convert it to flymake internal format." - (let* ((converted-list '())) - (dolist (item original-list) - (setq item (cdr item)) - (let ((regexp (nth 0 item)) - (file (nth 1 item)) - (line (nth 2 item)) - (col (nth 3 item))) - (if (consp file) (setq file (car file))) - (if (consp line) (setq line (car line))) - (if (consp col) (setq col (car col))) - - (when (not (functionp line)) - (setq converted-list (cons (list regexp file line col) converted-list))))) - converted-list)) - -(require 'compile) - -(defvar flymake-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text - (append - '( - ;; MS Visual C++ 6.0 - ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)" - 1 3 nil 4) - ;; jikes - ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)" - 1 3 nil 4) - ;; MS midl - ("midl[ ]*:[ ]*\\(command line error .*\\)" - nil nil nil 1) - ;; MS C# - ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)" - 1 3 nil 4) - ;; perl - ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1) - ;; PHP - ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1) - ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1) - ;; ant/javac. Note this also matches gcc warnings! - (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)" - 2 4 nil 5)) - ;; compilation-error-regexp-alist) - (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist)) - "Patterns for matching error/warning lines. Each pattern has the form -\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). -Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns -from compile.el") - -(define-obsolete-variable-alias 'flymake-warning-re 'flymake-warning-predicate "24.4") -(defvar flymake-warning-predicate "^[wW]arning" - "Predicate matching against error text to detect a warning. -Takes a single argument, the error's text and should return non-nil -if it's a warning. -Instead of a function, it can also be a regular expression.") - -(defun flymake-parse-line (line) - "Parse LINE to see if it is an error or warning. -Return its components if so, nil otherwise." - (let ((raw-file-name nil) - (line-no 0) - (err-type "e") - (err-text nil) - (patterns flymake-err-line-patterns) - (matched nil)) - (while (and patterns (not matched)) - (when (string-match (car (car patterns)) line) - (let* ((file-idx (nth 1 (car patterns))) - (line-idx (nth 2 (car patterns)))) - - (setq raw-file-name (if file-idx (match-string file-idx line) nil)) - (setq line-no (if line-idx (string-to-number - (match-string line-idx line)) 0)) - (setq err-text (if (> (length (car patterns)) 4) - (match-string (nth 4 (car patterns)) line) - (flymake-patch-err-text - (substring line (match-end 0))))) - (if (null err-text) - (setq err-text "") - (when (cond ((stringp flymake-warning-predicate) - (string-match flymake-warning-predicate err-text)) - ((functionp flymake-warning-predicate) - (funcall flymake-warning-predicate err-text))) - (setq err-type "w"))) - (flymake-log - 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s" - file-idx line-idx raw-file-name line-no err-text) - (setq matched t))) - (setq patterns (cdr patterns))) - (if matched - (flymake-ler-make-ler raw-file-name line-no err-type err-text) - ()))) - -(defun flymake-get-project-include-dirs-imp (basedir) - "Include dirs for the project current file belongs to." - (if (flymake-get-project-include-dirs-from-cache basedir) - (progn - (flymake-get-project-include-dirs-from-cache basedir)) - ;;else - (let* ((command-line (concat "make -C " - (shell-quote-argument basedir) - " DUMPVARS=INCLUDE_DIRS dumpvars")) - (output (shell-command-to-string command-line)) - (lines (split-string output "\n" t)) - (count (length lines)) - (idx 0) - (inc-dirs nil)) - (while (and (< idx count) (not (string-match "^INCLUDE_DIRS=.*" (nth idx lines)))) - (setq idx (1+ idx))) - (when (< idx count) - (let* ((inc-lines (split-string (nth idx lines) " *-I" t)) - (inc-count (length inc-lines))) - (while (> inc-count 0) - (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines))) - (push (replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs)) - (setq inc-count (1- inc-count))))) - (flymake-add-project-include-dirs-to-cache basedir inc-dirs) - inc-dirs))) - -(defvar flymake-get-project-include-dirs-function #'flymake-get-project-include-dirs-imp - "Function used to get project include dirs, one parameter: basedir name.") - -(defun flymake-get-project-include-dirs (basedir) - (funcall flymake-get-project-include-dirs-function basedir)) - -(defun flymake-get-system-include-dirs () - "System include dirs - from the `INCLUDE' env setting." - (let* ((includes (getenv "INCLUDE"))) - (if includes (split-string includes path-separator t) nil))) - -(defvar flymake-project-include-dirs-cache (make-hash-table :test #'equal)) - -(defun flymake-get-project-include-dirs-from-cache (base-dir) - (gethash base-dir flymake-project-include-dirs-cache)) - -(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs) - (puthash base-dir include-dirs flymake-project-include-dirs-cache)) - -(defun flymake-clear-project-include-dirs-cache () - (clrhash flymake-project-include-dirs-cache)) - -(defun flymake-get-include-dirs (base-dir) - "Get dirs to use when resolving local file names." - (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs)))) - include-dirs)) - -;; (defun flymake-restore-formatting () -;; "Remove any formatting made by flymake." -;; ) - -;; (defun flymake-get-program-dir (buffer) -;; "Get dir to start program in." -;; (unless (bufferp buffer) -;; (error "Invalid buffer")) -;; (with-current-buffer buffer -;; default-directory)) - -(defun flymake-safe-delete-file (file-name) - (when (and file-name (file-exists-p file-name)) - (delete-file file-name) - (flymake-log 1 "deleted file %s" file-name))) - -(defun flymake-safe-delete-directory (dir-name) - (condition-case nil - (progn - (delete-directory dir-name) - (flymake-log 1 "deleted dir %s" dir-name)) - (error - (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) - -(defun flymake-start-syntax-check () - "Start syntax checking for current buffer." - (interactive) - (flymake-log 3 "flymake is running: %s" flymake-is-running) - (when (and (not flymake-is-running) - (flymake-can-syntax-check-file buffer-file-name)) - (when (or (not flymake-compilation-prevents-syntax-check) - (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP") - (flymake-clear-buildfile-cache) - (flymake-clear-project-include-dirs-cache) - - (setq flymake-check-was-interrupted nil) - - (let* ((source-file-name buffer-file-name) - (init-f (flymake-get-init-function source-file-name)) - (cleanup-f (flymake-get-cleanup-function source-file-name)) - (cmd-and-args (funcall init-f)) - (cmd (nth 0 cmd-and-args)) - (args (nth 1 cmd-and-args)) - (dir (nth 2 cmd-and-args))) - (if (not cmd-and-args) - (progn - (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name) - (funcall cleanup-f)) - (progn - (setq flymake-last-change-time nil) - (flymake-start-syntax-check-process cmd args dir))))))) - -(defun flymake-start-syntax-check-process (cmd args dir) - "Start syntax check process." - (condition-case err - (let* ((process - (let ((default-directory (or dir default-directory))) - (when dir - (flymake-log 3 "starting process on dir %s" dir)) - (apply 'start-file-process - "flymake-proc" (current-buffer) cmd args)))) - (set-process-sentinel process 'flymake-process-sentinel) - (set-process-filter process 'flymake-process-filter) - (set-process-query-on-exit-flag process nil) - (push process flymake-processes) - - (setq flymake-is-running t) - (setq flymake-last-change-time nil) - (setq flymake-check-start-time (float-time)) - - (flymake-report-status nil "*") - (flymake-log 2 "started process %d, command=%s, dir=%s" - (process-id process) (process-command process) - default-directory) - process) - (error - (let* ((err-str - (format-message - "Failed to launch syntax check process `%s' with args %s: %s" - cmd args (error-message-string err))) - (source-file-name buffer-file-name) - (cleanup-f (flymake-get-cleanup-function source-file-name))) - (flymake-log 0 err-str) - (funcall cleanup-f) - (flymake-report-fatal-status "PROCERR" err-str))))) - -(defun flymake-kill-process (proc) - "Kill process PROC." - (kill-process proc) - (let* ((buf (process-buffer proc))) - (when (buffer-live-p buf) - (with-current-buffer buf - (setq flymake-check-was-interrupted t)))) - (flymake-log 1 "killed process %d" (process-id proc))) - -(defun flymake-stop-all-syntax-checks () - "Kill all syntax check processes." - (interactive) - (while flymake-processes - (flymake-kill-process (pop flymake-processes)))) - -(defun flymake-compilation-is-running () - (and (boundp 'compilation-in-progress) - compilation-in-progress)) - -(defun flymake-compile () - "Kill all flymake syntax checks, start compilation." - (interactive) - (flymake-stop-all-syntax-checks) - (call-interactively 'compile)) - -;;;; general init-cleanup and helper routines -(defun flymake-create-temp-inplace (file-name prefix) - (unless (stringp file-name) - (error "Invalid file-name")) - (or prefix - (setq prefix "flymake")) - (let* ((ext (file-name-extension file-name)) - (temp-name (file-truename - (concat (file-name-sans-extension file-name) - "_" prefix - (and ext (concat "." ext)))))) - (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name) - temp-name)) - -(defun flymake-create-temp-with-folder-structure (file-name _prefix) - (unless (stringp file-name) - (error "Invalid file-name")) - - (let* ((dir (file-name-directory file-name)) - ;; Not sure what this slash-pos is all about, but I guess it's just - ;; trying to remove the leading / of absolute file names. - (slash-pos (string-match "/" dir)) - (temp-dir (expand-file-name (substring dir (1+ slash-pos)) - temporary-file-directory))) - - (file-truename (expand-file-name (file-name-nondirectory file-name) - temp-dir)))) - -(defun flymake-delete-temp-directory (dir-name) - "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error." - (let* ((temp-dir temporary-file-directory) - (suffix (substring dir-name (1+ (length temp-dir))))) - - (while (> (length suffix) 0) - (setq suffix (directory-file-name suffix)) - ;;+(flymake-log 0 "suffix=%s" suffix) - (flymake-safe-delete-directory - (file-truename (expand-file-name suffix temp-dir))) - (setq suffix (file-name-directory suffix))))) - -(defvar-local flymake-temp-source-file-name nil) -(defvar-local flymake-master-file-name nil) -(defvar-local flymake-temp-master-file-name nil) -(defvar-local flymake-base-dir nil) - -(defun flymake-init-create-temp-buffer-copy (create-temp-f) - "Make a temporary copy of the current buffer, save its name in buffer data and return the name." - (let* ((source-file-name buffer-file-name) - (temp-source-file-name (funcall create-temp-f source-file-name "flymake"))) - - (flymake-save-buffer-in-file temp-source-file-name) - (setq flymake-temp-source-file-name temp-source-file-name) - temp-source-file-name)) - -(defun flymake-simple-cleanup () - "Do cleanup after `flymake-init-create-temp-buffer-copy'. -Delete temp file." - (flymake-safe-delete-file flymake-temp-source-file-name) - (setq flymake-last-change-time nil)) - -(defun flymake-get-real-file-name (file-name-from-err-msg) - "Translate file name from error message to \"real\" file name. -Return full-name. Names are real, not patched." - (let* ((real-name nil) - (source-file-name buffer-file-name) - (master-file-name flymake-master-file-name) - (temp-source-file-name flymake-temp-source-file-name) - (temp-master-file-name flymake-temp-master-file-name) - (base-dirs - (list flymake-base-dir - (file-name-directory source-file-name) - (if master-file-name (file-name-directory master-file-name)))) - (files (list (list source-file-name source-file-name) - (list temp-source-file-name source-file-name) - (list master-file-name master-file-name) - (list temp-master-file-name master-file-name)))) - - (when (equal 0 (length file-name-from-err-msg)) - (setq file-name-from-err-msg source-file-name)) - - (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files)) - ;; if real-name is nil, than file name from err msg is none of the files we've patched - (if (not real-name) - (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs))) - (if (not real-name) - (setq real-name file-name-from-err-msg)) - (setq real-name (flymake-fix-file-name real-name)) - (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name) - real-name)) - -(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files) - (let* ((base-dirs-count (length base-dirs)) - (file-count (length files)) - (real-name nil)) - - (while (and (not real-name) (> base-dirs-count 0)) - (setq file-count (length files)) - (while (and (not real-name) (> file-count 0)) - (let* ((this-dir (nth (1- base-dirs-count) base-dirs)) - (this-file (nth 0 (nth (1- file-count) files))) - (this-real-name (nth 1 (nth (1- file-count) files)))) - ;;+(flymake-log 0 "this-dir=%s this-file=%s this-real=%s msg-file=%s" this-dir this-file this-real-name file-name-from-err-msg) - (when (and this-dir this-file (flymake-same-files - (expand-file-name file-name-from-err-msg this-dir) - this-file)) - (setq real-name this-real-name))) - (setq file-count (1- file-count))) - (setq base-dirs-count (1- base-dirs-count))) - real-name)) - -(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs) - (let* ((real-name nil)) - (if (file-name-absolute-p file-name-from-err-msg) - (setq real-name file-name-from-err-msg) - (let* ((base-dirs-count (length base-dirs))) - (while (and (not real-name) (> base-dirs-count 0)) - (let* ((full-name (expand-file-name file-name-from-err-msg - (nth (1- base-dirs-count) base-dirs)))) - (if (file-exists-p full-name) - (setq real-name full-name)) - (setq base-dirs-count (1- base-dirs-count)))))) - real-name)) - -(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name) - "Find buildfile, store its dir in buffer data and return its dir, if found." - (let* ((buildfile-dir - (flymake-find-buildfile buildfile-name - (file-name-directory source-file-name)))) - (if buildfile-dir - (setq flymake-base-dir buildfile-dir) - (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name) - (flymake-report-fatal-status - "NOMK" (format "No buildfile (%s) found for %s" - buildfile-name source-file-name))))) - -(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp) - "Find master file (or buffer), create its copy along with a copy of the source file." - (let* ((source-file-name buffer-file-name) - (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)) - (master-and-temp-master (flymake-create-master-file - source-file-name temp-source-file-name - get-incl-dirs-f create-temp-f - master-file-masks include-regexp))) - - (if (not master-and-temp-master) - (progn - (flymake-log 1 "cannot find master file for %s" source-file-name) - (flymake-report-status "!" "") ; NOMASTER - nil) - (setq flymake-master-file-name (nth 0 master-and-temp-master)) - (setq flymake-temp-master-file-name (nth 1 master-and-temp-master))))) - -(defun flymake-master-cleanup () - (flymake-simple-cleanup) - (flymake-safe-delete-file flymake-temp-master-file-name)) - -;;;; make-specific init-cleanup routines -(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f) - "Create a command line for syntax check using GET-CMD-LINE-F." - (funcall get-cmd-line-f - (if use-relative-source - (file-relative-name source-file-name base-dir) - source-file-name) - (if use-relative-base-dir - (file-relative-name base-dir - (file-name-directory source-file-name)) - base-dir))) - -(defun flymake-get-make-cmdline (source base-dir) - (list "make" - (list "-s" - "-C" - base-dir - (concat "CHK_SOURCES=" source) - "SYNTAX_CHECK_MODE=1" - "check-syntax"))) - -(defun flymake-get-ant-cmdline (source base-dir) - (list "ant" - (list "-buildfile" - (concat base-dir "/" "build.xml") - (concat "-DCHK_SOURCES=" source) - "check-syntax"))) - -(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f) - "Create syntax check command line for a directly checked source file. -Use CREATE-TEMP-F for creating temp copy." - (let* ((args nil) - (source-file-name buffer-file-name) - (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name))) - (if buildfile-dir - (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))) - (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir - use-relative-base-dir use-relative-source - get-cmdline-f)))) - args)) - -(defun flymake-simple-make-init () - (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline)) - -(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp) - "Create make command line for a source file checked via master file compilation." - (let* ((make-args nil) - (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy - get-incl-dirs-f 'flymake-create-temp-inplace - master-file-masks include-regexp))) - (when temp-master-file-name - (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile"))) - (if buildfile-dir - (setq make-args (flymake-get-syntax-check-program-args - temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline))))) - make-args)) - -(defun flymake-find-make-buildfile (source-dir) - (flymake-find-buildfile "Makefile" source-dir)) - -;;;; .h/make specific -(defun flymake-master-make-header-init () - (flymake-master-make-init - 'flymake-get-include-dirs - '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'") - "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\"")) - -;;;; .java/make specific -(defun flymake-simple-make-java-init () - (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline)) - -(defun flymake-simple-ant-java-init () - (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline)) - -(defun flymake-simple-java-cleanup () - "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs." - (flymake-safe-delete-file flymake-temp-source-file-name) - (when flymake-temp-source-file-name - (flymake-delete-temp-directory - (file-name-directory flymake-temp-source-file-name)))) - -;;;; perl-specific init-cleanup routines -(defun flymake-perl-init () - (let* ((temp-file (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)) - (local-file (file-relative-name - temp-file - (file-name-directory buffer-file-name)))) - (list "perl" (list "-wc " local-file)))) - -;;;; php-specific init-cleanup routines -(defun flymake-php-init () - (let* ((temp-file (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)) - (local-file (file-relative-name - temp-file - (file-name-directory buffer-file-name)))) - (list "php" (list "-f" local-file "-l")))) - -;;;; tex-specific init-cleanup routines -(defun flymake-get-tex-args (file-name) - ;;(list "latex" (list "-c-style-errors" file-name)) - (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name))) - -(defun flymake-simple-tex-init () - (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))) - -;; Perhaps there should be a buffer-local variable flymake-master-file -;; that people can set to override this stuff. Could inherit from -;; the similar AUCTeX variable. -(defun flymake-master-tex-init () - (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy - 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace - '("\\.tex\\'") - "[ \t]*\\in\\(?:put\\|clude\\)[ \t]*{\\(.*%s\\)}"))) - (when temp-master-file-name - (flymake-get-tex-args temp-master-file-name)))) - -(defun flymake-get-include-dirs-dot (_base-dir) - '(".")) - -;;;; xml-specific init-cleanup routines -(defun flymake-xml-init () - (list flymake-xml-program - (list "val" (flymake-init-create-temp-buffer-copy - 'flymake-create-temp-inplace)))) - -(provide 'flymake-proc) -;;; flymake-proc.el ends here diff --git a/lisp/progmodes/flymake-ui.el b/lisp/progmodes/flymake-ui.el deleted file mode 100644 index 2a15a497d8..0000000000 --- a/lisp/progmodes/flymake-ui.el +++ /dev/null @@ -1,604 +0,0 @@ -;;; flymake-ui.el --- A universal on-the-fly syntax checker -*- lexical-binding: t; -*- - -;; Copyright (C) 2003-2017 Free Software Foundation, Inc. - -;; Author: Pavel Kobyakov -;; Maintainer: Leo Liu -;; Version: 0.3 -;; Keywords: c languages tools - -;; 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 . - -;;; Commentary: -;; -;; Flymake is a minor Emacs mode performing on-the-fly syntax checks.xo -;; -;; This file contains the UI for displaying and interacting with the -;; results of such checks, as well as entry points for backends to -;; hook on to. Backends are sources of diagnostic info. -;; -;;; Code: - -(eval-when-compile (require 'cl-lib)) - -(defgroup flymake nil - "Universal on-the-fly syntax checker." - :version "23.1" - :link '(custom-manual "(flymake) Top") - :group 'tools) - -(defcustom flymake-error-bitmap '(exclamation-mark error) - "Bitmap (a symbol) used in the fringe for indicating errors. -The value may also be a list of two elements where the second -element specifies the face for the bitmap. For possible bitmap -symbols, see `fringe-bitmaps'. See also `flymake-warning-bitmap'. - -The option `flymake-fringe-indicator-position' controls how and where -this is used." - :group 'flymake - :version "24.3" - :type '(choice (symbol :tag "Bitmap") - (list :tag "Bitmap and face" - (symbol :tag "Bitmap") - (face :tag "Face")))) - -(defcustom flymake-warning-bitmap 'question-mark - "Bitmap (a symbol) used in the fringe for indicating warnings. -The value may also be a list of two elements where the second -element specifies the face for the bitmap. For possible bitmap -symbols, see `fringe-bitmaps'. See also `flymake-error-bitmap'. - -The option `flymake-fringe-indicator-position' controls how and where -this is used." - :group 'flymake - :version "24.3" - :type '(choice (symbol :tag "Bitmap") - (list :tag "Bitmap and face" - (symbol :tag "Bitmap") - (face :tag "Face")))) - -(defcustom flymake-fringe-indicator-position 'left-fringe - "The position to put flymake fringe indicator. -The value can be nil (do not use indicators), `left-fringe' or `right-fringe'. -See `flymake-error-bitmap' and `flymake-warning-bitmap'." - :group 'flymake - :version "24.3" - :type '(choice (const left-fringe) - (const right-fringe) - (const :tag "No fringe indicators" nil))) - -(defcustom flymake-start-syntax-check-on-newline t - "Start syntax check if newline char was added/removed from the buffer." - :group 'flymake - :type 'boolean) - -(defcustom flymake-no-changes-timeout 0.5 - "Time to wait after last change before starting compilation." - :group 'flymake - :type 'number) - -(defcustom flymake-gui-warnings-enabled t - "Enables/disables GUI warnings." - :group 'flymake - :type 'boolean) -(make-obsolete-variable 'flymake-gui-warnings-enabled - "it no longer has any effect." "26.1") - -(defcustom flymake-start-syntax-check-on-find-file t - "Start syntax check on find file." - :group 'flymake - :type 'boolean) - -(defcustom flymake-log-level -1 - "Logging level, only messages with level lower or equal will be logged. --1 = NONE, 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG" - :group 'flymake - :type 'integer) - -(defvar-local flymake-timer nil - "Timer for starting syntax check.") - -(defvar-local flymake-last-change-time nil - "Time of last buffer change.") - -(defvar-local flymake-check-start-time nil - "Time at which syntax check was started.") - -(defvar-local flymake-check-was-interrupted nil - "Non-nil if syntax check was killed by `flymake-compile'.") - -(defvar-local flymake-err-info nil - "Sorted list of line numbers and lists of err info in the form (file, err-text).") - -(defvar-local flymake-new-err-info nil - "Same as `flymake-err-info', effective when a syntax check is in progress.") - -(defun flymake-log (level text &rest args) - "Log a message at level LEVEL. -If LEVEL is higher than `flymake-log-level', the message is -ignored. Otherwise, it is printed using `message'. -TEXT is a format control string, and the remaining arguments ARGS -are the string substitutions (see the function `format')." - (if (<= level flymake-log-level) - (let* ((msg (apply #'format-message text args))) - (message "%s" msg)))) - -(defun flymake-ins-after (list pos val) - "Insert VAL into LIST after position POS. -POS counts from zero." - (let ((tmp (copy-sequence list))) - (setcdr (nthcdr pos tmp) (cons val (nthcdr (1+ pos) tmp))) - tmp)) - -(defun flymake-set-at (list pos val) - "Set VAL at position POS in LIST. -POS counts from zero." - (let ((tmp (copy-sequence list))) - (setcar (nthcdr pos tmp) val) - tmp)) - -(defun flymake-er-make-er (line-no line-err-info-list) - (list line-no line-err-info-list)) - -(defun flymake-er-get-line (err-info) - (nth 0 err-info)) - -(defun flymake-er-get-line-err-info-list (err-info) - (nth 1 err-info)) - -(cl-defstruct (flymake-ler - (:constructor nil) - (:constructor flymake-ler-make-ler (file line type text &optional full-file))) - file line type text full-file) - -(defun flymake-ler-set-file (line-err-info file) - (flymake-ler-make-ler file - (flymake-ler-line line-err-info) - (flymake-ler-type line-err-info) - (flymake-ler-text line-err-info) - (flymake-ler-full-file line-err-info))) - -(defun flymake-ler-set-full-file (line-err-info full-file) - (flymake-ler-make-ler (flymake-ler-file line-err-info) - (flymake-ler-line line-err-info) - (flymake-ler-type line-err-info) - (flymake-ler-text line-err-info) - full-file)) - -(defun flymake-ler-set-line (line-err-info line) - (flymake-ler-make-ler (flymake-ler-file line-err-info) - line - (flymake-ler-type line-err-info) - (flymake-ler-text line-err-info) - (flymake-ler-full-file line-err-info))) - -(defun flymake-get-line-err-count (line-err-info-list type) - "Return number of errors of specified TYPE. -Value of TYPE is either \"e\" or \"w\"." - (let* ((idx 0) - (count (length line-err-info-list)) - (err-count 0)) - - (while (< idx count) - (when (equal type (flymake-ler-type (nth idx line-err-info-list))) - (setq err-count (1+ err-count))) - (setq idx (1+ idx))) - err-count)) - -(defun flymake-get-err-count (err-info-list type) - "Return number of errors of specified TYPE for ERR-INFO-LIST." - (let* ((idx 0) - (count (length err-info-list)) - (err-count 0)) - (while (< idx count) - (setq err-count (+ err-count (flymake-get-line-err-count (nth 1 (nth idx err-info-list)) type))) - (setq idx (1+ idx))) - err-count)) - -(defun flymake-highlight-err-lines (err-info-list) - "Highlight error lines in BUFFER using info from ERR-INFO-LIST." - (save-excursion - (dolist (err err-info-list) - (flymake-highlight-line (car err) (nth 1 err))))) - -(defun flymake-overlay-p (ov) - "Determine whether overlay OV was created by flymake." - (and (overlayp ov) (overlay-get ov 'flymake-overlay))) - -(defun flymake-make-overlay (beg end tooltip-text face bitmap) - "Allocate a flymake overlay in range BEG and END." - (when (not (flymake-region-has-flymake-overlays beg end)) - (let ((ov (make-overlay beg end nil t)) - (fringe (and flymake-fringe-indicator-position - (propertize "!" 'display - (cons flymake-fringe-indicator-position - (if (listp bitmap) - bitmap - (list bitmap))))))) - (overlay-put ov 'face face) - (overlay-put ov 'help-echo tooltip-text) - (overlay-put ov 'flymake-overlay t) - (overlay-put ov 'priority 100) - (overlay-put ov 'evaporate t) - (overlay-put ov 'before-string fringe) - ;;+(flymake-log 3 "created overlay %s" ov) - ov) - (flymake-log 3 "created an overlay at (%d-%d)" beg end))) - -(defun flymake-delete-own-overlays () - "Delete all flymake overlays in BUFFER." - (dolist (ol (overlays-in (point-min) (point-max))) - (when (flymake-overlay-p ol) - (delete-overlay ol) - ;;+(flymake-log 3 "deleted overlay %s" ol) - ))) - -(defun flymake-region-has-flymake-overlays (beg end) - "Check if region specified by BEG and END has overlay. -Return t if it has at least one flymake overlay, nil if no overlay." - (let ((ov (overlays-in beg end)) - (has-flymake-overlays nil)) - (while (consp ov) - (when (flymake-overlay-p (car ov)) - (setq has-flymake-overlays t)) - (setq ov (cdr ov))) - has-flymake-overlays)) - -(defface flymake-errline - '((((supports :underline (:style wave))) - :underline (:style wave :color "Red1")) - (t - :inherit error)) - "Face used for marking error lines." - :version "24.4" - :group 'flymake) - -(defface flymake-warnline - '((((supports :underline (:style wave))) - :underline (:style wave :color "DarkOrange")) - (t - :inherit warning)) - "Face used for marking warning lines." - :version "24.4" - :group 'flymake) - -(defun flymake-highlight-line (line-no line-err-info-list) - "Highlight line LINE-NO in current buffer. -Perhaps use text from LINE-ERR-INFO-LIST to enhance highlighting." - (goto-char (point-min)) - (forward-line (1- line-no)) - (pcase-let* ((beg (progn (back-to-indentation) (point))) - (end (progn - (end-of-line) - (skip-chars-backward " \t\f\t\n" beg) - (if (eq (point) beg) - (line-beginning-position 2) - (point)))) - (tooltip-text (mapconcat #'flymake-ler-text line-err-info-list "\n")) - (`(,face ,bitmap) - (if (> (flymake-get-line-err-count line-err-info-list "e") 0) - (list 'flymake-errline flymake-error-bitmap) - (list 'flymake-warnline flymake-warning-bitmap)))) - (flymake-make-overlay beg end tooltip-text face bitmap))) - -(defun flymake-find-err-info (err-info-list line-no) - "Find (line-err-info-list pos) for specified LINE-NO." - (if err-info-list - (let* ((line-err-info-list nil) - (pos 0) - (count (length err-info-list))) - - (while (and (< pos count) (< (car (nth pos err-info-list)) line-no)) - (setq pos (1+ pos))) - (when (and (< pos count) (equal (car (nth pos err-info-list)) line-no)) - (setq line-err-info-list (flymake-er-get-line-err-info-list (nth pos err-info-list)))) - (list line-err-info-list pos)) - '(nil 0))) - -(defun flymake-line-err-info-is-less-or-equal (line-one line-two) - (or (string< (flymake-ler-type line-one) (flymake-ler-type line-two)) - (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two)) - (not (flymake-ler-file line-one)) (flymake-ler-file line-two)) - (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two)) - (or (and (flymake-ler-file line-one) (flymake-ler-file line-two)) - (and (not (flymake-ler-file line-one)) (not (flymake-ler-file line-two))))))) - -(defun flymake-add-line-err-info (line-err-info-list line-err-info) - "Update LINE-ERR-INFO-LIST with the error LINE-ERR-INFO. -For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'. -The new element is inserted in the proper position, according to -the predicate `flymake-line-err-info-is-less-or-equal'. -The updated value of LINE-ERR-INFO-LIST is returned." - (if (not line-err-info-list) - (list line-err-info) - (let* ((count (length line-err-info-list)) - (idx 0)) - (while (and (< idx count) (flymake-line-err-info-is-less-or-equal (nth idx line-err-info-list) line-err-info)) - (setq idx (1+ idx))) - (cond ((equal 0 idx) (setq line-err-info-list (cons line-err-info line-err-info-list))) - (t (setq line-err-info-list (flymake-ins-after line-err-info-list (1- idx) line-err-info)))) - line-err-info-list))) - -(defun flymake-add-err-info (err-info-list line-err-info) - "Update ERR-INFO-LIST with the error LINE-ERR-INFO, preserving sort order. -Returns the updated value of ERR-INFO-LIST. -For the format of ERR-INFO-LIST, see `flymake-err-info'. -For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." - (let* ((line-no (if (flymake-ler-file line-err-info) 1 (flymake-ler-line line-err-info))) - (info-and-pos (flymake-find-err-info err-info-list line-no)) - (exists (car info-and-pos)) - (pos (nth 1 info-and-pos)) - (line-err-info-list nil) - (err-info nil)) - - (if exists - (setq line-err-info-list (flymake-er-get-line-err-info-list (car (nthcdr pos err-info-list))))) - (setq line-err-info-list (flymake-add-line-err-info line-err-info-list line-err-info)) - - (setq err-info (flymake-er-make-er line-no line-err-info-list)) - (cond (exists (setq err-info-list (flymake-set-at err-info-list pos err-info))) - ((equal 0 pos) (setq err-info-list (cons err-info err-info-list))) - (t (setq err-info-list (flymake-ins-after err-info-list (1- pos) err-info)))) - err-info-list)) - -(defvar-local flymake-is-running nil - "If t, flymake syntax check process is running for the current buffer.") - -(defun flymake-on-timer-event (buffer) - "Start a syntax check for buffer BUFFER if necessary." - (when (buffer-live-p buffer) - (with-current-buffer buffer - (when (and (not flymake-is-running) - flymake-last-change-time - (> (- (float-time) flymake-last-change-time) - flymake-no-changes-timeout)) - - (setq flymake-last-change-time nil) - (flymake-log 3 "starting syntax check as more than 1 second passed since last change") - (flymake-start-syntax-check))))) - -(define-obsolete-function-alias 'flymake-display-err-menu-for-current-line - 'flymake-popup-current-error-menu "24.4") - -(defun flymake-popup-current-error-menu (&optional event) - "Pop up a menu with errors/warnings for current line." - (interactive (list last-nonmenu-event)) - (let* ((line-no (line-number-at-pos)) - (errors (or (car (flymake-find-err-info flymake-err-info line-no)) - (user-error "No errors for current line"))) - (menu (mapcar (lambda (x) - (if (flymake-ler-file x) - (cons (format "%s - %s(%d)" - (flymake-ler-text x) - (flymake-ler-file x) - (flymake-ler-line x)) - x) - (list (flymake-ler-text x)))) - errors)) - (event (if (mouse-event-p event) - event - (list 'mouse-1 (posn-at-point)))) - (title (format "Line %d: %d error(s), %d warning(s)" - line-no - (flymake-get-line-err-count errors "e") - (flymake-get-line-err-count errors "w"))) - (choice (x-popup-menu event (list title (cons "" menu))))) - (flymake-log 3 "choice=%s" choice) - (when choice - (flymake-goto-file-and-line (flymake-ler-full-file choice) - (flymake-ler-line choice))))) - -(defun flymake-goto-file-and-line (file line) - "Try to get buffer for FILE and goto line LINE in it." - (if (not (file-exists-p file)) - (flymake-log 1 "File %s does not exist" file) - (find-file file) - (goto-char (point-min)) - (forward-line (1- line)))) - -;; flymake minor mode declarations -(defvar-local flymake-mode-line nil) -(defvar-local flymake-mode-line-e-w nil) -(defvar-local flymake-mode-line-status nil) - -(defun flymake-report-status (e-w &optional status) - "Show status in mode line." - (when e-w - (setq flymake-mode-line-e-w e-w)) - (when status - (setq flymake-mode-line-status status)) - (let* ((mode-line " Flymake")) - (when (> (length flymake-mode-line-e-w) 0) - (setq mode-line (concat mode-line ":" flymake-mode-line-e-w))) - (setq mode-line (concat mode-line flymake-mode-line-status)) - (setq flymake-mode-line mode-line) - (force-mode-line-update))) - -;; Nothing in flymake uses this at all any more, so this is just for -;; third-party compatibility. -(define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") - -(defun flymake-report-fatal-status (status warning) - "Display a warning and switch flymake mode off." - ;; This first message was always shown by default, and flymake-log - ;; does nothing by default, hence the use of message. - ;; Another option is display-warning. - (if (< flymake-log-level 0) - (message "Flymake: %s. Flymake will be switched OFF" warning)) - (flymake-mode 0) - (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s" - (buffer-name) status warning)) - -;;;###autoload -(define-minor-mode flymake-mode nil - :group 'flymake :lighter flymake-mode-line - (cond - - ;; Turning the mode ON. - (flymake-mode - (cond - ((not buffer-file-name) - (message "Flymake unable to run without a buffer file name")) - ((not (flymake-can-syntax-check-file buffer-file-name)) - (flymake-log 2 "flymake cannot check syntax in buffer %s" (buffer-name))) - (t - (add-hook 'after-change-functions 'flymake-after-change-function nil t) - (add-hook 'after-save-hook 'flymake-after-save-hook nil t) - (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) - ;;+(add-hook 'find-file-hook 'flymake-find-file-hook) - - (flymake-report-status "" "") - - (setq flymake-timer - (run-at-time nil 1 'flymake-on-timer-event (current-buffer))) - - (when (and flymake-start-syntax-check-on-find-file - ;; Since we write temp files in current dir, there's no point - ;; trying if the directory is read-only (bug#8954). - (file-writable-p (file-name-directory buffer-file-name))) - (with-demoted-errors - (flymake-start-syntax-check)))))) - - ;; Turning the mode OFF. - (t - (remove-hook 'after-change-functions 'flymake-after-change-function t) - (remove-hook 'after-save-hook 'flymake-after-save-hook t) - (remove-hook 'kill-buffer-hook 'flymake-kill-buffer-hook t) - ;;+(remove-hook 'find-file-hook (function flymake-find-file-hook) t) - - (flymake-delete-own-overlays) - - (when flymake-timer - (cancel-timer flymake-timer) - (setq flymake-timer nil)) - - (setq flymake-is-running nil)))) - -;; disabling flymake-mode is safe, enabling - not necessarily so -(put 'flymake-mode 'safe-local-variable 'null) - -;;;###autoload -(defun flymake-mode-on () - "Turn flymake mode on." - (flymake-mode 1) - (flymake-log 1 "flymake mode turned ON for buffer %s" (buffer-name))) - -;;;###autoload -(defun flymake-mode-off () - "Turn flymake mode off." - (flymake-mode 0) - (flymake-log 1 "flymake mode turned OFF for buffer %s" (buffer-name))) - -(defun flymake-after-change-function (start stop _len) - "Start syntax check for current buffer if it isn't already running." - ;;+(flymake-log 0 "setting change time to %s" (float-time)) - (let((new-text (buffer-substring start stop))) - (when (and flymake-start-syntax-check-on-newline (equal new-text "\n")) - (flymake-log 3 "starting syntax check as new-line has been seen") - (flymake-start-syntax-check)) - (setq flymake-last-change-time (float-time)))) - -(defun flymake-after-save-hook () - (if (local-variable-p 'flymake-mode (current-buffer)) ; (???) other way to determine whether flymake is active in buffer being saved? - (progn - (flymake-log 3 "starting syntax check as buffer was saved") - (flymake-start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???) - -(defun flymake-kill-buffer-hook () - (when flymake-timer - (cancel-timer flymake-timer) - (setq flymake-timer nil))) - -;;;###autoload -(defun flymake-find-file-hook () - ;;+(when flymake-start-syntax-check-on-find-file - ;;+ (flymake-log 3 "starting syntax check on file open") - ;;+ (flymake-start-syntax-check) - ;;+) - (when (and (not (local-variable-p 'flymake-mode (current-buffer))) - (flymake-can-syntax-check-file buffer-file-name)) - (flymake-mode) - (flymake-log 3 "automatically turned ON flymake mode"))) - -(defun flymake-get-first-err-line-no (err-info-list) - "Return first line with error." - (when err-info-list - (flymake-er-get-line (car err-info-list)))) - -(defun flymake-get-last-err-line-no (err-info-list) - "Return last line with error." - (when err-info-list - (flymake-er-get-line (nth (1- (length err-info-list)) err-info-list)))) - -(defun flymake-get-next-err-line-no (err-info-list line-no) - "Return next line with error." - (when err-info-list - (let* ((count (length err-info-list)) - (idx 0)) - (while (and (< idx count) (>= line-no (flymake-er-get-line (nth idx err-info-list)))) - (setq idx (1+ idx))) - (if (< idx count) - (flymake-er-get-line (nth idx err-info-list)))))) - -(defun flymake-get-prev-err-line-no (err-info-list line-no) - "Return previous line with error." - (when err-info-list - (let* ((count (length err-info-list))) - (while (and (> count 0) (<= line-no (flymake-er-get-line (nth (1- count) err-info-list)))) - (setq count (1- count))) - (if (> count 0) - (flymake-er-get-line (nth (1- count) err-info-list)))))) - -(defun flymake-skip-whitespace () - "Move forward until non-whitespace is reached." - (while (looking-at "[ \t]") - (forward-char))) - -(defun flymake-goto-line (line-no) - "Go to line LINE-NO, then skip whitespace." - (goto-char (point-min)) - (forward-line (1- line-no)) - (flymake-skip-whitespace)) - -(defun flymake-goto-next-error () - "Go to next error in err ring." - (interactive) - (let ((line-no (flymake-get-next-err-line-no flymake-err-info (line-number-at-pos)))) - (when (not line-no) - (setq line-no (flymake-get-first-err-line-no flymake-err-info)) - (flymake-log 1 "passed end of file")) - (if line-no - (flymake-goto-line line-no) - (flymake-log 1 "no errors in current buffer")))) - -(defun flymake-goto-prev-error () - "Go to previous error in err ring." - (interactive) - (let ((line-no (flymake-get-prev-err-line-no flymake-err-info (line-number-at-pos)))) - (when (not line-no) - (setq line-no (flymake-get-last-err-line-no flymake-err-info)) - (flymake-log 1 "passed beginning of file")) - (if line-no - (flymake-goto-line line-no) - (flymake-log 1 "no errors in current buffer")))) - -(defun flymake-patch-err-text (string) - (if (string-match "^[\n\t :0-9]*\\(.*\\)$" string) - (match-string 1 string) - string)) - -(provide 'flymake-ui) -;;; flymake-ui.el ends here diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 059bce95ee..866116fbec 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -24,18 +24,1629 @@ ;;; Commentary: ;; -;; Flymake is a minor Emacs mode performing on-the-fly syntax checks. -;; -;; It collects diagnostic information for multiple sources and -;; visually annotates the relevant lines in the buffer. -;; -;; This file is just a stub for that loads the UI and backends, which -;; could also be loaded separately. +;; Flymake is a minor Emacs mode performing on-the-fly syntax checks +;; using the external syntax check tool (for C/C++ this is usually the +;; compiler). + +;;; Bugs/todo: + +;; - Only uses "Makefile", not "makefile" or "GNUmakefile" +;; (from http://bugs.debian.org/337339). ;;; Code: -(require 'flymake-ui) -(require 'flymake-proc) +(eval-when-compile (require 'cl-lib)) + +(defgroup flymake nil + "Universal on-the-fly syntax checker." + :version "23.1" + :link '(custom-manual "(flymake) Top") + :group 'tools) + +(defcustom flymake-error-bitmap '(exclamation-mark error) + "Bitmap (a symbol) used in the fringe for indicating errors. +The value may also be a list of two elements where the second +element specifies the face for the bitmap. For possible bitmap +symbols, see `fringe-bitmaps'. See also `flymake-warning-bitmap'. + +The option `flymake-fringe-indicator-position' controls how and where +this is used." + :group 'flymake + :version "24.3" + :type '(choice (symbol :tag "Bitmap") + (list :tag "Bitmap and face" + (symbol :tag "Bitmap") + (face :tag "Face")))) + +(defcustom flymake-warning-bitmap 'question-mark + "Bitmap (a symbol) used in the fringe for indicating warnings. +The value may also be a list of two elements where the second +element specifies the face for the bitmap. For possible bitmap +symbols, see `fringe-bitmaps'. See also `flymake-error-bitmap'. + +The option `flymake-fringe-indicator-position' controls how and where +this is used." + :group 'flymake + :version "24.3" + :type '(choice (symbol :tag "Bitmap") + (list :tag "Bitmap and face" + (symbol :tag "Bitmap") + (face :tag "Face")))) + +(defcustom flymake-fringe-indicator-position 'left-fringe + "The position to put flymake fringe indicator. +The value can be nil (do not use indicators), `left-fringe' or `right-fringe'. +See `flymake-error-bitmap' and `flymake-warning-bitmap'." + :group 'flymake + :version "24.3" + :type '(choice (const left-fringe) + (const right-fringe) + (const :tag "No fringe indicators" nil))) + +(defcustom flymake-compilation-prevents-syntax-check t + "If non-nil, don't start syntax check if compilation is running." + :group 'flymake + :type 'boolean) + +(defcustom flymake-start-syntax-check-on-newline t + "Start syntax check if newline char was added/removed from the buffer." + :group 'flymake + :type 'boolean) + +(defcustom flymake-no-changes-timeout 0.5 + "Time to wait after last change before starting compilation." + :group 'flymake + :type 'number) + +(defcustom flymake-gui-warnings-enabled t + "Enables/disables GUI warnings." + :group 'flymake + :type 'boolean) +(make-obsolete-variable 'flymake-gui-warnings-enabled + "it no longer has any effect." "26.1") + +(defcustom flymake-start-syntax-check-on-find-file t + "Start syntax check on find file." + :group 'flymake + :type 'boolean) + +(defcustom flymake-log-level -1 + "Logging level, only messages with level lower or equal will be logged. +-1 = NONE, 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG" + :group 'flymake + :type 'integer) + +(defcustom flymake-xml-program + (if (executable-find "xmlstarlet") "xmlstarlet" "xml") + "Program to use for XML validation." + :type 'file + :group 'flymake + :version "24.4") + +(defcustom flymake-master-file-dirs '("." "./src" "./UnitTest") + "Dirs where to look for master files." + :group 'flymake + :type '(repeat (string))) + +(defcustom flymake-master-file-count-limit 32 + "Max number of master files to check." + :group 'flymake + :type 'integer) + +(defcustom flymake-allowed-file-name-masks + '(("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'" flymake-simple-make-init) + ("\\.xml\\'" flymake-xml-init) + ("\\.html?\\'" flymake-xml-init) + ("\\.cs\\'" flymake-simple-make-init) + ("\\.p[ml]\\'" flymake-perl-init) + ("\\.php[345]?\\'" flymake-php-init) + ("\\.h\\'" flymake-master-make-header-init flymake-master-cleanup) + ("\\.java\\'" flymake-simple-make-java-init flymake-simple-java-cleanup) + ("[0-9]+\\.tex\\'" flymake-master-tex-init flymake-master-cleanup) + ("\\.tex\\'" flymake-simple-tex-init) + ("\\.idl\\'" flymake-simple-make-init) + ;; ("\\.cpp\\'" 1) + ;; ("\\.java\\'" 3) + ;; ("\\.h\\'" 2 ("\\.cpp\\'" "\\.c\\'") + ;; ("[ \t]*#[ \t]*include[ \t]*\"\\([\w0-9/\\_\.]*[/\\]*\\)\\(%s\\)\"" 1 2)) + ;; ("\\.idl\\'" 1) + ;; ("\\.odl\\'" 1) + ;; ("[0-9]+\\.tex\\'" 2 ("\\.tex\\'") + ;; ("[ \t]*\\input[ \t]*{\\(.*\\)\\(%s\\)}" 1 2 )) + ;; ("\\.tex\\'" 1) + ) + "Files syntax checking is allowed for. +This is an alist with elements of the form: + REGEXP INIT [CLEANUP [NAME]] +REGEXP is a regular expression that matches a file name. +INIT is the init function to use, missing means disable `flymake-mode'. +CLEANUP is the cleanup function to use, default `flymake-simple-cleanup'. +NAME is the file name function to use, default `flymake-get-real-file-name'." + :group 'flymake + :type '(alist :key-type (regexp :tag "File regexp") + :value-type + (list :tag "Handler functions" + (choice :tag "Init function" + (const :tag "disable" nil) + function) + (choice :tag "Cleanup function" + (const :tag "flymake-simple-cleanup" nil) + function) + (choice :tag "Name function" + (const :tag "flymake-get-real-file-name" nil) + function)))) + +(defvar-local flymake-is-running nil + "If t, flymake syntax check process is running for the current buffer.") + +(defvar-local flymake-timer nil + "Timer for starting syntax check.") + +(defvar-local flymake-last-change-time nil + "Time of last buffer change.") + +(defvar-local flymake-check-start-time nil + "Time at which syntax check was started.") + +(defvar-local flymake-check-was-interrupted nil + "Non-nil if syntax check was killed by `flymake-compile'.") + +(defvar-local flymake-err-info nil + "Sorted list of line numbers and lists of err info in the form (file, err-text).") + +(defvar-local flymake-new-err-info nil + "Same as `flymake-err-info', effective when a syntax check is in progress.") + +(defun flymake-log (level text &rest args) + "Log a message at level LEVEL. +If LEVEL is higher than `flymake-log-level', the message is +ignored. Otherwise, it is printed using `message'. +TEXT is a format control string, and the remaining arguments ARGS +are the string substitutions (see the function `format')." + (if (<= level flymake-log-level) + (let* ((msg (apply #'format-message text args))) + (message "%s" msg)))) + +(defun flymake-ins-after (list pos val) + "Insert VAL into LIST after position POS. +POS counts from zero." + (let ((tmp (copy-sequence list))) + (setcdr (nthcdr pos tmp) (cons val (nthcdr (1+ pos) tmp))) + tmp)) + +(defun flymake-set-at (list pos val) + "Set VAL at position POS in LIST. +POS counts from zero." + (let ((tmp (copy-sequence list))) + (setcar (nthcdr pos tmp) val) + tmp)) + +(defvar flymake-processes nil + "List of currently active flymake processes.") + +(defvar-local flymake-output-residual nil) + +(defun flymake-get-file-name-mode-and-masks (file-name) + "Return the corresponding entry from `flymake-allowed-file-name-masks'." + (unless (stringp file-name) + (error "Invalid file-name")) + (let ((fnm flymake-allowed-file-name-masks) + (mode-and-masks nil)) + (while (and (not mode-and-masks) fnm) + (let ((item (pop fnm))) + (when (string-match (car item) file-name) + (setq mode-and-masks item)))) ; (cdr item) may be nil + (setq mode-and-masks (cdr mode-and-masks)) + (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks)) + mode-and-masks)) + +(defun flymake-can-syntax-check-file (file-name) + "Determine whether we can syntax check FILE-NAME. +Return nil if we cannot, non-nil if we can." + (if (flymake-get-init-function file-name) t nil)) + +(defun flymake-get-init-function (file-name) + "Return init function to be used for the file." + (let* ((init-f (nth 0 (flymake-get-file-name-mode-and-masks file-name)))) + ;;(flymake-log 0 "calling %s" init-f) + ;;(funcall init-f (current-buffer)) + init-f)) + +(defun flymake-get-cleanup-function (file-name) + "Return cleanup function to be used for the file." + (or (nth 1 (flymake-get-file-name-mode-and-masks file-name)) + 'flymake-simple-cleanup)) + +(defun flymake-get-real-file-name-function (file-name) + (or (nth 2 (flymake-get-file-name-mode-and-masks file-name)) + 'flymake-get-real-file-name)) + +(defvar flymake-find-buildfile-cache (make-hash-table :test #'equal)) + +(defun flymake-get-buildfile-from-cache (dir-name) + "Look up DIR-NAME in cache and return its associated value. +If DIR-NAME is not found, return nil." + (gethash dir-name flymake-find-buildfile-cache)) + +(defun flymake-add-buildfile-to-cache (dir-name buildfile) + "Associate DIR-NAME with BUILDFILE in the buildfile cache." + (puthash dir-name buildfile flymake-find-buildfile-cache)) + +(defun flymake-clear-buildfile-cache () + "Clear the buildfile cache." + (clrhash flymake-find-buildfile-cache)) + +(defun flymake-find-buildfile (buildfile-name source-dir-name) + "Find buildfile starting from current directory. +Buildfile includes Makefile, build.xml etc. +Return its file name if found, or nil if not found." + (or (flymake-get-buildfile-from-cache source-dir-name) + (let* ((file (locate-dominating-file source-dir-name buildfile-name))) + (if file + (progn + (flymake-log 3 "found buildfile at %s" file) + (flymake-add-buildfile-to-cache source-dir-name file) + file) + (progn + (flymake-log 3 "buildfile for %s not found" source-dir-name) + nil))))) + +(defun flymake-fix-file-name (name) + "Replace all occurrences of `\\' with `/'." + (when name + (setq name (expand-file-name name)) + (setq name (abbreviate-file-name name)) + (setq name (directory-file-name name)) + name)) + +(defun flymake-same-files (file-name-one file-name-two) + "Check if FILE-NAME-ONE and FILE-NAME-TWO point to same file. +Return t if so, nil if not." + (equal (flymake-fix-file-name file-name-one) + (flymake-fix-file-name file-name-two))) + +;; This is bound dynamically to pass a parameter to a sort predicate below +(defvar flymake-included-file-name) + +(defun flymake-find-possible-master-files (file-name master-file-dirs masks) + "Find (by name and location) all possible master files. + +Name is specified by FILE-NAME and location is specified by +MASTER-FILE-DIRS. Master files include .cpp and .c for .h. +Files are searched for starting from the .h directory and max +max-level parent dirs. File contents are not checked." + (let* ((dirs master-file-dirs) + (files nil) + (done nil)) + + (while (and (not done) dirs) + (let* ((dir (expand-file-name (car dirs) (file-name-directory file-name))) + (masks masks)) + (while (and (file-exists-p dir) (not done) masks) + (let* ((mask (car masks)) + (dir-files (directory-files dir t mask))) + + (flymake-log 3 "dir %s, %d file(s) for mask %s" + dir (length dir-files) mask) + (while (and (not done) dir-files) + (when (not (file-directory-p (car dir-files))) + (setq files (cons (car dir-files) files)) + (when (>= (length files) flymake-master-file-count-limit) + (flymake-log 3 "master file count limit (%d) reached" flymake-master-file-count-limit) + (setq done t))) + (setq dir-files (cdr dir-files)))) + (setq masks (cdr masks)))) + (setq dirs (cdr dirs))) + (when files + (let ((flymake-included-file-name (file-name-nondirectory file-name))) + (setq files (sort files 'flymake-master-file-compare)))) + (flymake-log 3 "found %d possible master file(s)" (length files)) + files)) + +(defun flymake-master-file-compare (file-one file-two) + "Compare two files specified by FILE-ONE and FILE-TWO. +This function is used in sort to move most possible file names +to the beginning of the list (File.h -> File.cpp moved to top)." + (and (equal (file-name-sans-extension flymake-included-file-name) + (file-name-base file-one)) + (not (equal file-one file-two)))) + +(defvar flymake-check-file-limit 8192 + "Maximum number of chars to look at when checking possible master file. +Nil means search the entire file.") + +(defun flymake-check-patch-master-file-buffer + (master-file-temp-buffer + master-file-name patched-master-file-name + source-file-name patched-source-file-name + include-dirs regexp) + "Check if MASTER-FILE-NAME is a master file for SOURCE-FILE-NAME. +If yes, patch a copy of MASTER-FILE-NAME to include PATCHED-SOURCE-FILE-NAME +instead of SOURCE-FILE-NAME. + +For example, foo.cpp is a master file if it includes foo.h. + +When a buffer for MASTER-FILE-NAME exists, use it as a source +instead of reading master file from disk." + (let* ((source-file-nondir (file-name-nondirectory source-file-name)) + (source-file-extension (file-name-extension source-file-nondir)) + (source-file-nonext (file-name-sans-extension source-file-nondir)) + (found nil) + (inc-name nil) + (search-limit flymake-check-file-limit)) + (setq regexp + (format regexp ; "[ \t]*#[ \t]*include[ \t]*\"\\(.*%s\\)\"" + ;; Hack for tex files, where \include often excludes .tex. + ;; Maybe this is safe generally. + (if (and (> (length source-file-extension) 1) + (string-equal source-file-extension "tex")) + (format "%s\\(?:\\.%s\\)?" + (regexp-quote source-file-nonext) + (regexp-quote source-file-extension)) + (regexp-quote source-file-nondir)))) + (unwind-protect + (with-current-buffer master-file-temp-buffer + (if (or (not search-limit) + (> search-limit (point-max))) + (setq search-limit (point-max))) + (flymake-log 3 "checking %s against regexp %s" + master-file-name regexp) + (goto-char (point-min)) + (while (and (< (point) search-limit) + (re-search-forward regexp search-limit t)) + (let ((match-beg (match-beginning 1)) + (match-end (match-end 1))) + + (flymake-log 3 "found possible match for %s" source-file-nondir) + (setq inc-name (match-string 1)) + (and (> (length source-file-extension) 1) + (string-equal source-file-extension "tex") + (not (string-match (format "\\.%s\\'" source-file-extension) + inc-name)) + (setq inc-name (concat inc-name "." source-file-extension))) + (when (eq t (compare-strings + source-file-nondir nil nil + inc-name (- (length inc-name) + (length source-file-nondir)) nil)) + (flymake-log 3 "inc-name=%s" inc-name) + (when (flymake-check-include source-file-name inc-name + include-dirs) + (setq found t) + ;; replace-match is not used here as it fails in + ;; XEmacs with 'last match not a buffer' error as + ;; check-includes calls replace-in-string + (flymake-replace-region + match-beg match-end + (file-name-nondirectory patched-source-file-name)))) + (forward-line 1))) + (when found + (flymake-save-buffer-in-file patched-master-file-name))) + ;;+(flymake-log 3 "killing buffer %s" + ;; (buffer-name master-file-temp-buffer)) + (kill-buffer master-file-temp-buffer)) + ;;+(flymake-log 3 "check-patch master file %s: %s" master-file-name found) + (when found + (flymake-log 2 "found master file %s" master-file-name)) + found)) + +;;; XXX: remove +(defun flymake-replace-region (beg end rep) + "Replace text in BUFFER in region (BEG END) with REP." + (save-excursion + (goto-char end) + ;; Insert before deleting, so as to better preserve markers's positions. + (insert rep) + (delete-region beg end))) + +(defun flymake-read-file-to-temp-buffer (file-name) + "Insert contents of FILE-NAME into newly created temp buffer." + (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name)))))) + (with-current-buffer temp-buffer + (insert-file-contents file-name)) + temp-buffer)) + +(defun flymake-copy-buffer-to-temp-buffer (buffer) + "Copy contents of BUFFER into newly created temp buffer." + (with-current-buffer + (get-buffer-create (generate-new-buffer-name + (concat "flymake:" (buffer-name buffer)))) + (insert-buffer-substring buffer) + (current-buffer))) + +(defun flymake-check-include (source-file-name inc-name include-dirs) + "Check if SOURCE-FILE-NAME can be found in include path. +Return t if it can be found via include path using INC-NAME." + (if (file-name-absolute-p inc-name) + (flymake-same-files source-file-name inc-name) + (while (and include-dirs + (not (flymake-same-files + source-file-name + (concat (file-name-directory source-file-name) + "/" (car include-dirs) + "/" inc-name)))) + (setq include-dirs (cdr include-dirs))) + include-dirs)) + +(defun flymake-find-buffer-for-file (file-name) + "Check if there exists a buffer visiting FILE-NAME. +Return t if so, nil if not." + (let ((buffer-name (get-file-buffer file-name))) + (if buffer-name + (get-buffer buffer-name)))) + +(defun flymake-create-master-file (source-file-name patched-source-file-name get-incl-dirs-f create-temp-f masks include-regexp) + "Save SOURCE-FILE-NAME with a different name. +Find master file, patch and save it." + (let* ((possible-master-files (flymake-find-possible-master-files source-file-name flymake-master-file-dirs masks)) + (master-file-count (length possible-master-files)) + (idx 0) + (temp-buffer nil) + (master-file-name nil) + (patched-master-file-name nil) + (found nil)) + + (while (and (not found) (< idx master-file-count)) + (setq master-file-name (nth idx possible-master-files)) + (setq patched-master-file-name (funcall create-temp-f master-file-name "flymake_master")) + (if (flymake-find-buffer-for-file master-file-name) + (setq temp-buffer (flymake-copy-buffer-to-temp-buffer (flymake-find-buffer-for-file master-file-name))) + (setq temp-buffer (flymake-read-file-to-temp-buffer master-file-name))) + (setq found + (flymake-check-patch-master-file-buffer + temp-buffer + master-file-name + patched-master-file-name + source-file-name + patched-source-file-name + (funcall get-incl-dirs-f (file-name-directory master-file-name)) + include-regexp)) + (setq idx (1+ idx))) + (if found + (list master-file-name patched-master-file-name) + (progn + (flymake-log 3 "none of %d master file(s) checked includes %s" master-file-count + (file-name-nondirectory source-file-name)) + nil)))) + +(defun flymake-save-buffer-in-file (file-name) + "Save the entire buffer contents into file FILE-NAME. +Create parent directories as needed." + (make-directory (file-name-directory file-name) 1) + (write-region nil nil file-name nil 566) + (flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name)) + +(defun flymake-process-filter (process output) + "Parse OUTPUT and highlight error lines. +It's flymake process filter." + (let ((source-buffer (process-buffer process))) + + (flymake-log 3 "received %d byte(s) of output from process %d" + (length output) (process-id process)) + (when (buffer-live-p source-buffer) + (with-current-buffer source-buffer + (flymake-parse-output-and-residual output))))) + +(defun flymake-process-sentinel (process _event) + "Sentinel for syntax check buffers." + (when (memq (process-status process) '(signal exit)) + (let* ((exit-status (process-exit-status process)) + (command (process-command process)) + (source-buffer (process-buffer process)) + (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer)))) + + (flymake-log 2 "process %d exited with code %d" + (process-id process) exit-status) + (condition-case err + (progn + (flymake-log 3 "cleaning up using %s" cleanup-f) + (when (buffer-live-p source-buffer) + (with-current-buffer source-buffer + (funcall cleanup-f))) + + (delete-process process) + (setq flymake-processes (delq process flymake-processes)) + + (when (buffer-live-p source-buffer) + (with-current-buffer source-buffer + + (flymake-parse-residual) + (flymake-post-syntax-check exit-status command) + (setq flymake-is-running nil)))) + (error + (let ((err-str (format "Error in process sentinel for buffer %s: %s" + source-buffer (error-message-string err)))) + (flymake-log 0 err-str) + (with-current-buffer source-buffer + (setq flymake-is-running nil)))))))) + +(defun flymake-post-syntax-check (exit-status command) + (save-restriction + (widen) + (setq flymake-err-info flymake-new-err-info) + (setq flymake-new-err-info nil) + (setq flymake-err-info + (flymake-fix-line-numbers + flymake-err-info 1 (count-lines (point-min) (point-max)))) + (flymake-delete-own-overlays) + (flymake-highlight-err-lines flymake-err-info) + (let (err-count warn-count) + (setq err-count (flymake-get-err-count flymake-err-info "e")) + (setq warn-count (flymake-get-err-count flymake-err-info "w")) + (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)" + (buffer-name) err-count warn-count + (- (float-time) flymake-check-start-time)) + (setq flymake-check-start-time nil) + + (if (and (equal 0 err-count) (equal 0 warn-count)) + (if (equal 0 exit-status) + (flymake-report-status "" "") ; PASSED + (if (not flymake-check-was-interrupted) + (flymake-report-fatal-status "CFGERR" + (format "Configuration error has occurred while running %s" command)) + (flymake-report-status nil ""))) ; "STOPPED" + (flymake-report-status (format "%d/%d" err-count warn-count) ""))))) + +(defun flymake-parse-output-and-residual (output) + "Split OUTPUT into lines, merge in residual if necessary." + (let* ((buffer-residual flymake-output-residual) + (total-output (if buffer-residual (concat buffer-residual output) output)) + (lines-and-residual (flymake-split-output total-output)) + (lines (nth 0 lines-and-residual)) + (new-residual (nth 1 lines-and-residual))) + (setq flymake-output-residual new-residual) + (setq flymake-new-err-info + (flymake-parse-err-lines + flymake-new-err-info lines)))) + +(defun flymake-parse-residual () + "Parse residual if it's non empty." + (when flymake-output-residual + (setq flymake-new-err-info + (flymake-parse-err-lines + flymake-new-err-info + (list flymake-output-residual))) + (setq flymake-output-residual nil))) + +(defun flymake-er-make-er (line-no line-err-info-list) + (list line-no line-err-info-list)) + +(defun flymake-er-get-line (err-info) + (nth 0 err-info)) + +(defun flymake-er-get-line-err-info-list (err-info) + (nth 1 err-info)) + +(cl-defstruct (flymake-ler + (:constructor nil) + (:constructor flymake-ler-make-ler (file line type text &optional full-file))) + file line type text full-file) + +(defun flymake-ler-set-file (line-err-info file) + (flymake-ler-make-ler file + (flymake-ler-line line-err-info) + (flymake-ler-type line-err-info) + (flymake-ler-text line-err-info) + (flymake-ler-full-file line-err-info))) + +(defun flymake-ler-set-full-file (line-err-info full-file) + (flymake-ler-make-ler (flymake-ler-file line-err-info) + (flymake-ler-line line-err-info) + (flymake-ler-type line-err-info) + (flymake-ler-text line-err-info) + full-file)) + +(defun flymake-ler-set-line (line-err-info line) + (flymake-ler-make-ler (flymake-ler-file line-err-info) + line + (flymake-ler-type line-err-info) + (flymake-ler-text line-err-info) + (flymake-ler-full-file line-err-info))) + +(defun flymake-get-line-err-count (line-err-info-list type) + "Return number of errors of specified TYPE. +Value of TYPE is either \"e\" or \"w\"." + (let* ((idx 0) + (count (length line-err-info-list)) + (err-count 0)) + + (while (< idx count) + (when (equal type (flymake-ler-type (nth idx line-err-info-list))) + (setq err-count (1+ err-count))) + (setq idx (1+ idx))) + err-count)) + +(defun flymake-get-err-count (err-info-list type) + "Return number of errors of specified TYPE for ERR-INFO-LIST." + (let* ((idx 0) + (count (length err-info-list)) + (err-count 0)) + (while (< idx count) + (setq err-count (+ err-count (flymake-get-line-err-count (nth 1 (nth idx err-info-list)) type))) + (setq idx (1+ idx))) + err-count)) + +(defun flymake-fix-line-numbers (err-info-list min-line max-line) + "Replace line numbers with fixed value. +If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE. +If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE. +The reason for this fix is because some compilers might report +line number outside the file being compiled." + (let* ((count (length err-info-list)) + (err-info nil) + (line 0)) + (while (> count 0) + (setq err-info (nth (1- count) err-info-list)) + (setq line (flymake-er-get-line err-info)) + (when (or (< line min-line) (> line max-line)) + (setq line (if (< line min-line) min-line max-line)) + (setq err-info-list (flymake-set-at err-info-list (1- count) + (flymake-er-make-er line + (flymake-er-get-line-err-info-list err-info))))) + (setq count (1- count)))) + err-info-list) + +(defun flymake-highlight-err-lines (err-info-list) + "Highlight error lines in BUFFER using info from ERR-INFO-LIST." + (save-excursion + (dolist (err err-info-list) + (flymake-highlight-line (car err) (nth 1 err))))) + +(defun flymake-overlay-p (ov) + "Determine whether overlay OV was created by flymake." + (and (overlayp ov) (overlay-get ov 'flymake-overlay))) + +(defun flymake-make-overlay (beg end tooltip-text face bitmap) + "Allocate a flymake overlay in range BEG and END." + (when (not (flymake-region-has-flymake-overlays beg end)) + (let ((ov (make-overlay beg end nil t)) + (fringe (and flymake-fringe-indicator-position + (propertize "!" 'display + (cons flymake-fringe-indicator-position + (if (listp bitmap) + bitmap + (list bitmap))))))) + (overlay-put ov 'face face) + (overlay-put ov 'help-echo tooltip-text) + (overlay-put ov 'flymake-overlay t) + (overlay-put ov 'priority 100) + (overlay-put ov 'evaporate t) + (overlay-put ov 'before-string fringe) + ;;+(flymake-log 3 "created overlay %s" ov) + ov) + (flymake-log 3 "created an overlay at (%d-%d)" beg end))) + +(defun flymake-delete-own-overlays () + "Delete all flymake overlays in BUFFER." + (dolist (ol (overlays-in (point-min) (point-max))) + (when (flymake-overlay-p ol) + (delete-overlay ol) + ;;+(flymake-log 3 "deleted overlay %s" ol) + ))) + +(defun flymake-region-has-flymake-overlays (beg end) + "Check if region specified by BEG and END has overlay. +Return t if it has at least one flymake overlay, nil if no overlay." + (let ((ov (overlays-in beg end)) + (has-flymake-overlays nil)) + (while (consp ov) + (when (flymake-overlay-p (car ov)) + (setq has-flymake-overlays t)) + (setq ov (cdr ov))) + has-flymake-overlays)) + +(defface flymake-errline + '((((supports :underline (:style wave))) + :underline (:style wave :color "Red1")) + (t + :inherit error)) + "Face used for marking error lines." + :version "24.4" + :group 'flymake) + +(defface flymake-warnline + '((((supports :underline (:style wave))) + :underline (:style wave :color "DarkOrange")) + (t + :inherit warning)) + "Face used for marking warning lines." + :version "24.4" + :group 'flymake) + +(defun flymake-highlight-line (line-no line-err-info-list) + "Highlight line LINE-NO in current buffer. +Perhaps use text from LINE-ERR-INFO-LIST to enhance highlighting." + (goto-char (point-min)) + (forward-line (1- line-no)) + (pcase-let* ((beg (progn (back-to-indentation) (point))) + (end (progn + (end-of-line) + (skip-chars-backward " \t\f\t\n" beg) + (if (eq (point) beg) + (line-beginning-position 2) + (point)))) + (tooltip-text (mapconcat #'flymake-ler-text line-err-info-list "\n")) + (`(,face ,bitmap) + (if (> (flymake-get-line-err-count line-err-info-list "e") 0) + (list 'flymake-errline flymake-error-bitmap) + (list 'flymake-warnline flymake-warning-bitmap)))) + (flymake-make-overlay beg end tooltip-text face bitmap))) + +(defun flymake-parse-err-lines (err-info-list lines) + "Parse err LINES, store info in ERR-INFO-LIST." + (let* ((count (length lines)) + (idx 0) + (line-err-info nil) + (real-file-name nil) + (source-file-name buffer-file-name) + (get-real-file-name-f (flymake-get-real-file-name-function source-file-name))) + + (while (< idx count) + (setq line-err-info (flymake-parse-line (nth idx lines))) + (when line-err-info + (setq real-file-name (funcall get-real-file-name-f + (flymake-ler-file line-err-info))) + (setq line-err-info (flymake-ler-set-full-file line-err-info real-file-name)) + + (when (flymake-same-files real-file-name source-file-name) + (setq line-err-info (flymake-ler-set-file line-err-info nil)) + (setq err-info-list (flymake-add-err-info err-info-list line-err-info)))) + (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if line-err-info "got" "no")) + (setq idx (1+ idx))) + err-info-list)) + +(defun flymake-split-output (output) + "Split OUTPUT into lines. +Return last one as residual if it does not end with newline char. +Returns ((LINES) RESIDUAL)." + (when (and output (> (length output) 0)) + (let* ((lines (split-string output "[\n\r]+" t)) + (complete (equal "\n" (char-to-string (aref output (1- (length output)))))) + (residual nil)) + (when (not complete) + (setq residual (car (last lines))) + (setq lines (butlast lines))) + (list lines residual)))) + +(defun flymake-reformat-err-line-patterns-from-compile-el (original-list) + "Grab error line patterns from ORIGINAL-LIST in compile.el format. +Convert it to flymake internal format." + (let* ((converted-list '())) + (dolist (item original-list) + (setq item (cdr item)) + (let ((regexp (nth 0 item)) + (file (nth 1 item)) + (line (nth 2 item)) + (col (nth 3 item))) + (if (consp file) (setq file (car file))) + (if (consp line) (setq line (car line))) + (if (consp col) (setq col (car col))) + + (when (not (functionp line)) + (setq converted-list (cons (list regexp file line col) converted-list))))) + converted-list)) + +(require 'compile) + +(defvar flymake-err-line-patterns ; regexp file-idx line-idx col-idx (optional) text-idx(optional), match-end to end of string is error text + (append + '( + ;; MS Visual C++ 6.0 + ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\|fatal error\\) \\(C[0-9]+\\):[ \t\n]*\\(.+\\)\\)" + 1 3 nil 4) + ;; jikes + ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\):[0-9]+:[0-9]+:[0-9]+: \\(\\(Error\\|Warning\\|Caution\\|Semantic Error\\):[ \t\n]*\\(.+\\)\\)" + 1 3 nil 4) + ;; MS midl + ("midl[ ]*:[ ]*\\(command line error .*\\)" + nil nil nil 1) + ;; MS C# + ("\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\)(\\([0-9]+\\),[0-9]+): \\(\\(error\\|warning\\|fatal error\\) \\(CS[0-9]+\\):[ \t\n]*\\(.+\\)\\)" + 1 3 nil 4) + ;; perl + ("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1) + ;; PHP + ("\\(?:Parse\\|Fatal\\) error: \\(.*\\) in \\(.*\\) on line \\([0-9]+\\)" 2 3 nil 1) + ;; LaTeX warnings (fileless) ("\\(LaTeX \\(Warning\\|Error\\): .*\\) on input line \\([0-9]+\\)" 20 3 nil 1) + ;; ant/javac. Note this also matches gcc warnings! + (" *\\(\\[javac\\] *\\)?\\(\\([a-zA-Z]:\\)?[^:(\t\n]+\\):\\([0-9]+\\)\\(?::[0-9]+\\)?:[ \t\n]*\\(.+\\)" + 2 4 nil 5)) + ;; compilation-error-regexp-alist) + (flymake-reformat-err-line-patterns-from-compile-el compilation-error-regexp-alist-alist)) + "Patterns for matching error/warning lines. Each pattern has the form +\(REGEXP FILE-IDX LINE-IDX COL-IDX ERR-TEXT-IDX). +Use `flymake-reformat-err-line-patterns-from-compile-el' to add patterns +from compile.el") + +(define-obsolete-variable-alias 'flymake-warning-re 'flymake-warning-predicate "24.4") +(defvar flymake-warning-predicate "^[wW]arning" + "Predicate matching against error text to detect a warning. +Takes a single argument, the error's text and should return non-nil +if it's a warning. +Instead of a function, it can also be a regular expression.") + +(defun flymake-parse-line (line) + "Parse LINE to see if it is an error or warning. +Return its components if so, nil otherwise." + (let ((raw-file-name nil) + (line-no 0) + (err-type "e") + (err-text nil) + (patterns flymake-err-line-patterns) + (matched nil)) + (while (and patterns (not matched)) + (when (string-match (car (car patterns)) line) + (let* ((file-idx (nth 1 (car patterns))) + (line-idx (nth 2 (car patterns)))) + + (setq raw-file-name (if file-idx (match-string file-idx line) nil)) + (setq line-no (if line-idx (string-to-number + (match-string line-idx line)) 0)) + (setq err-text (if (> (length (car patterns)) 4) + (match-string (nth 4 (car patterns)) line) + (flymake-patch-err-text + (substring line (match-end 0))))) + (if (null err-text) + (setq err-text "") + (when (cond ((stringp flymake-warning-predicate) + (string-match flymake-warning-predicate err-text)) + ((functionp flymake-warning-predicate) + (funcall flymake-warning-predicate err-text))) + (setq err-type "w"))) + (flymake-log + 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s" + file-idx line-idx raw-file-name line-no err-text) + (setq matched t))) + (setq patterns (cdr patterns))) + (if matched + (flymake-ler-make-ler raw-file-name line-no err-type err-text) + ()))) + +(defun flymake-find-err-info (err-info-list line-no) + "Find (line-err-info-list pos) for specified LINE-NO." + (if err-info-list + (let* ((line-err-info-list nil) + (pos 0) + (count (length err-info-list))) + + (while (and (< pos count) (< (car (nth pos err-info-list)) line-no)) + (setq pos (1+ pos))) + (when (and (< pos count) (equal (car (nth pos err-info-list)) line-no)) + (setq line-err-info-list (flymake-er-get-line-err-info-list (nth pos err-info-list)))) + (list line-err-info-list pos)) + '(nil 0))) + +(defun flymake-line-err-info-is-less-or-equal (line-one line-two) + (or (string< (flymake-ler-type line-one) (flymake-ler-type line-two)) + (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two)) + (not (flymake-ler-file line-one)) (flymake-ler-file line-two)) + (and (string= (flymake-ler-type line-one) (flymake-ler-type line-two)) + (or (and (flymake-ler-file line-one) (flymake-ler-file line-two)) + (and (not (flymake-ler-file line-one)) (not (flymake-ler-file line-two))))))) + +(defun flymake-add-line-err-info (line-err-info-list line-err-info) + "Update LINE-ERR-INFO-LIST with the error LINE-ERR-INFO. +For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'. +The new element is inserted in the proper position, according to +the predicate `flymake-line-err-info-is-less-or-equal'. +The updated value of LINE-ERR-INFO-LIST is returned." + (if (not line-err-info-list) + (list line-err-info) + (let* ((count (length line-err-info-list)) + (idx 0)) + (while (and (< idx count) (flymake-line-err-info-is-less-or-equal (nth idx line-err-info-list) line-err-info)) + (setq idx (1+ idx))) + (cond ((equal 0 idx) (setq line-err-info-list (cons line-err-info line-err-info-list))) + (t (setq line-err-info-list (flymake-ins-after line-err-info-list (1- idx) line-err-info)))) + line-err-info-list))) + +(defun flymake-add-err-info (err-info-list line-err-info) + "Update ERR-INFO-LIST with the error LINE-ERR-INFO, preserving sort order. +Returns the updated value of ERR-INFO-LIST. +For the format of ERR-INFO-LIST, see `flymake-err-info'. +For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." + (let* ((line-no (if (flymake-ler-file line-err-info) 1 (flymake-ler-line line-err-info))) + (info-and-pos (flymake-find-err-info err-info-list line-no)) + (exists (car info-and-pos)) + (pos (nth 1 info-and-pos)) + (line-err-info-list nil) + (err-info nil)) + + (if exists + (setq line-err-info-list (flymake-er-get-line-err-info-list (car (nthcdr pos err-info-list))))) + (setq line-err-info-list (flymake-add-line-err-info line-err-info-list line-err-info)) + + (setq err-info (flymake-er-make-er line-no line-err-info-list)) + (cond (exists (setq err-info-list (flymake-set-at err-info-list pos err-info))) + ((equal 0 pos) (setq err-info-list (cons err-info err-info-list))) + (t (setq err-info-list (flymake-ins-after err-info-list (1- pos) err-info)))) + err-info-list)) + +(defun flymake-get-project-include-dirs-imp (basedir) + "Include dirs for the project current file belongs to." + (if (flymake-get-project-include-dirs-from-cache basedir) + (progn + (flymake-get-project-include-dirs-from-cache basedir)) + ;;else + (let* ((command-line (concat "make -C " + (shell-quote-argument basedir) + " DUMPVARS=INCLUDE_DIRS dumpvars")) + (output (shell-command-to-string command-line)) + (lines (split-string output "\n" t)) + (count (length lines)) + (idx 0) + (inc-dirs nil)) + (while (and (< idx count) (not (string-match "^INCLUDE_DIRS=.*" (nth idx lines)))) + (setq idx (1+ idx))) + (when (< idx count) + (let* ((inc-lines (split-string (nth idx lines) " *-I" t)) + (inc-count (length inc-lines))) + (while (> inc-count 0) + (when (not (string-match "^INCLUDE_DIRS=.*" (nth (1- inc-count) inc-lines))) + (push (replace-regexp-in-string "\"" "" (nth (1- inc-count) inc-lines)) inc-dirs)) + (setq inc-count (1- inc-count))))) + (flymake-add-project-include-dirs-to-cache basedir inc-dirs) + inc-dirs))) + +(defvar flymake-get-project-include-dirs-function #'flymake-get-project-include-dirs-imp + "Function used to get project include dirs, one parameter: basedir name.") + +(defun flymake-get-project-include-dirs (basedir) + (funcall flymake-get-project-include-dirs-function basedir)) + +(defun flymake-get-system-include-dirs () + "System include dirs - from the `INCLUDE' env setting." + (let* ((includes (getenv "INCLUDE"))) + (if includes (split-string includes path-separator t) nil))) + +(defvar flymake-project-include-dirs-cache (make-hash-table :test #'equal)) + +(defun flymake-get-project-include-dirs-from-cache (base-dir) + (gethash base-dir flymake-project-include-dirs-cache)) + +(defun flymake-add-project-include-dirs-to-cache (base-dir include-dirs) + (puthash base-dir include-dirs flymake-project-include-dirs-cache)) + +(defun flymake-clear-project-include-dirs-cache () + (clrhash flymake-project-include-dirs-cache)) + +(defun flymake-get-include-dirs (base-dir) + "Get dirs to use when resolving local file names." + (let* ((include-dirs (append '(".") (flymake-get-project-include-dirs base-dir) (flymake-get-system-include-dirs)))) + include-dirs)) + +;; (defun flymake-restore-formatting () +;; "Remove any formatting made by flymake." +;; ) + +;; (defun flymake-get-program-dir (buffer) +;; "Get dir to start program in." +;; (unless (bufferp buffer) +;; (error "Invalid buffer")) +;; (with-current-buffer buffer +;; default-directory)) + +(defun flymake-safe-delete-file (file-name) + (when (and file-name (file-exists-p file-name)) + (delete-file file-name) + (flymake-log 1 "deleted file %s" file-name))) + +(defun flymake-safe-delete-directory (dir-name) + (condition-case nil + (progn + (delete-directory dir-name) + (flymake-log 1 "deleted dir %s" dir-name)) + (error + (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) + +(defun flymake-start-syntax-check () + "Start syntax checking for current buffer." + (interactive) + (flymake-log 3 "flymake is running: %s" flymake-is-running) + (when (and (not flymake-is-running) + (flymake-can-syntax-check-file buffer-file-name)) + (when (or (not flymake-compilation-prevents-syntax-check) + (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP") + (flymake-clear-buildfile-cache) + (flymake-clear-project-include-dirs-cache) + + (setq flymake-check-was-interrupted nil) + + (let* ((source-file-name buffer-file-name) + (init-f (flymake-get-init-function source-file-name)) + (cleanup-f (flymake-get-cleanup-function source-file-name)) + (cmd-and-args (funcall init-f)) + (cmd (nth 0 cmd-and-args)) + (args (nth 1 cmd-and-args)) + (dir (nth 2 cmd-and-args))) + (if (not cmd-and-args) + (progn + (flymake-log 0 "init function %s for %s failed, cleaning up" init-f source-file-name) + (funcall cleanup-f)) + (progn + (setq flymake-last-change-time nil) + (flymake-start-syntax-check-process cmd args dir))))))) + +(defun flymake-start-syntax-check-process (cmd args dir) + "Start syntax check process." + (condition-case err + (let* ((process + (let ((default-directory (or dir default-directory))) + (when dir + (flymake-log 3 "starting process on dir %s" dir)) + (apply 'start-file-process + "flymake-proc" (current-buffer) cmd args)))) + (set-process-sentinel process 'flymake-process-sentinel) + (set-process-filter process 'flymake-process-filter) + (set-process-query-on-exit-flag process nil) + (push process flymake-processes) + + (setq flymake-is-running t) + (setq flymake-last-change-time nil) + (setq flymake-check-start-time (float-time)) + + (flymake-report-status nil "*") + (flymake-log 2 "started process %d, command=%s, dir=%s" + (process-id process) (process-command process) + default-directory) + process) + (error + (let* ((err-str + (format-message + "Failed to launch syntax check process `%s' with args %s: %s" + cmd args (error-message-string err))) + (source-file-name buffer-file-name) + (cleanup-f (flymake-get-cleanup-function source-file-name))) + (flymake-log 0 err-str) + (funcall cleanup-f) + (flymake-report-fatal-status "PROCERR" err-str))))) + +(defun flymake-kill-process (proc) + "Kill process PROC." + (kill-process proc) + (let* ((buf (process-buffer proc))) + (when (buffer-live-p buf) + (with-current-buffer buf + (setq flymake-check-was-interrupted t)))) + (flymake-log 1 "killed process %d" (process-id proc))) + +(defun flymake-stop-all-syntax-checks () + "Kill all syntax check processes." + (interactive) + (while flymake-processes + (flymake-kill-process (pop flymake-processes)))) + +(defun flymake-compilation-is-running () + (and (boundp 'compilation-in-progress) + compilation-in-progress)) + +(defun flymake-compile () + "Kill all flymake syntax checks, start compilation." + (interactive) + (flymake-stop-all-syntax-checks) + (call-interactively 'compile)) + +(defun flymake-on-timer-event (buffer) + "Start a syntax check for buffer BUFFER if necessary." + (when (buffer-live-p buffer) + (with-current-buffer buffer + (when (and (not flymake-is-running) + flymake-last-change-time + (> (- (float-time) flymake-last-change-time) + flymake-no-changes-timeout)) + + (setq flymake-last-change-time nil) + (flymake-log 3 "starting syntax check as more than 1 second passed since last change") + (flymake-start-syntax-check))))) + +(define-obsolete-function-alias 'flymake-display-err-menu-for-current-line + 'flymake-popup-current-error-menu "24.4") + +(defun flymake-popup-current-error-menu (&optional event) + "Pop up a menu with errors/warnings for current line." + (interactive (list last-nonmenu-event)) + (let* ((line-no (line-number-at-pos)) + (errors (or (car (flymake-find-err-info flymake-err-info line-no)) + (user-error "No errors for current line"))) + (menu (mapcar (lambda (x) + (if (flymake-ler-file x) + (cons (format "%s - %s(%d)" + (flymake-ler-text x) + (flymake-ler-file x) + (flymake-ler-line x)) + x) + (list (flymake-ler-text x)))) + errors)) + (event (if (mouse-event-p event) + event + (list 'mouse-1 (posn-at-point)))) + (title (format "Line %d: %d error(s), %d warning(s)" + line-no + (flymake-get-line-err-count errors "e") + (flymake-get-line-err-count errors "w"))) + (choice (x-popup-menu event (list title (cons "" menu))))) + (flymake-log 3 "choice=%s" choice) + (when choice + (flymake-goto-file-and-line (flymake-ler-full-file choice) + (flymake-ler-line choice))))) + +(defun flymake-goto-file-and-line (file line) + "Try to get buffer for FILE and goto line LINE in it." + (if (not (file-exists-p file)) + (flymake-log 1 "File %s does not exist" file) + (find-file file) + (goto-char (point-min)) + (forward-line (1- line)))) + +;; flymake minor mode declarations +(defvar-local flymake-mode-line nil) +(defvar-local flymake-mode-line-e-w nil) +(defvar-local flymake-mode-line-status nil) + +(defun flymake-report-status (e-w &optional status) + "Show status in mode line." + (when e-w + (setq flymake-mode-line-e-w e-w)) + (when status + (setq flymake-mode-line-status status)) + (let* ((mode-line " Flymake")) + (when (> (length flymake-mode-line-e-w) 0) + (setq mode-line (concat mode-line ":" flymake-mode-line-e-w))) + (setq mode-line (concat mode-line flymake-mode-line-status)) + (setq flymake-mode-line mode-line) + (force-mode-line-update))) + +;; Nothing in flymake uses this at all any more, so this is just for +;; third-party compatibility. +(define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") + +(defun flymake-report-fatal-status (status warning) + "Display a warning and switch flymake mode off." + ;; This first message was always shown by default, and flymake-log + ;; does nothing by default, hence the use of message. + ;; Another option is display-warning. + (if (< flymake-log-level 0) + (message "Flymake: %s. Flymake will be switched OFF" warning)) + (flymake-mode 0) + (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s" + (buffer-name) status warning)) + +;;;###autoload +(define-minor-mode flymake-mode nil + :group 'flymake :lighter flymake-mode-line + (cond + + ;; Turning the mode ON. + (flymake-mode + (cond + ((not buffer-file-name) + (message "Flymake unable to run without a buffer file name")) + ((not (flymake-can-syntax-check-file buffer-file-name)) + (flymake-log 2 "flymake cannot check syntax in buffer %s" (buffer-name))) + (t + (add-hook 'after-change-functions 'flymake-after-change-function nil t) + (add-hook 'after-save-hook 'flymake-after-save-hook nil t) + (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) + ;;+(add-hook 'find-file-hook 'flymake-find-file-hook) + + (flymake-report-status "" "") + + (setq flymake-timer + (run-at-time nil 1 'flymake-on-timer-event (current-buffer))) + + (when (and flymake-start-syntax-check-on-find-file + ;; Since we write temp files in current dir, there's no point + ;; trying if the directory is read-only (bug#8954). + (file-writable-p (file-name-directory buffer-file-name))) + (with-demoted-errors + (flymake-start-syntax-check)))))) + + ;; Turning the mode OFF. + (t + (remove-hook 'after-change-functions 'flymake-after-change-function t) + (remove-hook 'after-save-hook 'flymake-after-save-hook t) + (remove-hook 'kill-buffer-hook 'flymake-kill-buffer-hook t) + ;;+(remove-hook 'find-file-hook (function flymake-find-file-hook) t) + + (flymake-delete-own-overlays) + + (when flymake-timer + (cancel-timer flymake-timer) + (setq flymake-timer nil)) + + (setq flymake-is-running nil)))) + +;;;###autoload +(defun flymake-mode-on () + "Turn flymake mode on." + (flymake-mode 1) + (flymake-log 1 "flymake mode turned ON for buffer %s" (buffer-name))) + +;;;###autoload +(defun flymake-mode-off () + "Turn flymake mode off." + (flymake-mode 0) + (flymake-log 1 "flymake mode turned OFF for buffer %s" (buffer-name))) + +(defun flymake-after-change-function (start stop _len) + "Start syntax check for current buffer if it isn't already running." + ;;+(flymake-log 0 "setting change time to %s" (float-time)) + (let((new-text (buffer-substring start stop))) + (when (and flymake-start-syntax-check-on-newline (equal new-text "\n")) + (flymake-log 3 "starting syntax check as new-line has been seen") + (flymake-start-syntax-check)) + (setq flymake-last-change-time (float-time)))) + +(defun flymake-after-save-hook () + (if (local-variable-p 'flymake-mode (current-buffer)) ; (???) other way to determine whether flymake is active in buffer being saved? + (progn + (flymake-log 3 "starting syntax check as buffer was saved") + (flymake-start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???) + +(defun flymake-kill-buffer-hook () + (when flymake-timer + (cancel-timer flymake-timer) + (setq flymake-timer nil))) + +;;;###autoload +(defun flymake-find-file-hook () + ;;+(when flymake-start-syntax-check-on-find-file + ;;+ (flymake-log 3 "starting syntax check on file open") + ;;+ (flymake-start-syntax-check) + ;;+) + (when (and (not (local-variable-p 'flymake-mode (current-buffer))) + (flymake-can-syntax-check-file buffer-file-name)) + (flymake-mode) + (flymake-log 3 "automatically turned ON flymake mode"))) + +(defun flymake-get-first-err-line-no (err-info-list) + "Return first line with error." + (when err-info-list + (flymake-er-get-line (car err-info-list)))) + +(defun flymake-get-last-err-line-no (err-info-list) + "Return last line with error." + (when err-info-list + (flymake-er-get-line (nth (1- (length err-info-list)) err-info-list)))) + +(defun flymake-get-next-err-line-no (err-info-list line-no) + "Return next line with error." + (when err-info-list + (let* ((count (length err-info-list)) + (idx 0)) + (while (and (< idx count) (>= line-no (flymake-er-get-line (nth idx err-info-list)))) + (setq idx (1+ idx))) + (if (< idx count) + (flymake-er-get-line (nth idx err-info-list)))))) + +(defun flymake-get-prev-err-line-no (err-info-list line-no) + "Return previous line with error." + (when err-info-list + (let* ((count (length err-info-list))) + (while (and (> count 0) (<= line-no (flymake-er-get-line (nth (1- count) err-info-list)))) + (setq count (1- count))) + (if (> count 0) + (flymake-er-get-line (nth (1- count) err-info-list)))))) + +(defun flymake-skip-whitespace () + "Move forward until non-whitespace is reached." + (while (looking-at "[ \t]") + (forward-char))) + +(defun flymake-goto-line (line-no) + "Go to line LINE-NO, then skip whitespace." + (goto-char (point-min)) + (forward-line (1- line-no)) + (flymake-skip-whitespace)) + +(defun flymake-goto-next-error () + "Go to next error in err ring." + (interactive) + (let ((line-no (flymake-get-next-err-line-no flymake-err-info (line-number-at-pos)))) + (when (not line-no) + (setq line-no (flymake-get-first-err-line-no flymake-err-info)) + (flymake-log 1 "passed end of file")) + (if line-no + (flymake-goto-line line-no) + (flymake-log 1 "no errors in current buffer")))) + +(defun flymake-goto-prev-error () + "Go to previous error in err ring." + (interactive) + (let ((line-no (flymake-get-prev-err-line-no flymake-err-info (line-number-at-pos)))) + (when (not line-no) + (setq line-no (flymake-get-last-err-line-no flymake-err-info)) + (flymake-log 1 "passed beginning of file")) + (if line-no + (flymake-goto-line line-no) + (flymake-log 1 "no errors in current buffer")))) + +(defun flymake-patch-err-text (string) + (if (string-match "^[\n\t :0-9]*\\(.*\\)$" string) + (match-string 1 string) + string)) + +;;;; general init-cleanup and helper routines +(defun flymake-create-temp-inplace (file-name prefix) + (unless (stringp file-name) + (error "Invalid file-name")) + (or prefix + (setq prefix "flymake")) + (let* ((ext (file-name-extension file-name)) + (temp-name (file-truename + (concat (file-name-sans-extension file-name) + "_" prefix + (and ext (concat "." ext)))))) + (flymake-log 3 "create-temp-inplace: file=%s temp=%s" file-name temp-name) + temp-name)) + +(defun flymake-create-temp-with-folder-structure (file-name _prefix) + (unless (stringp file-name) + (error "Invalid file-name")) + + (let* ((dir (file-name-directory file-name)) + ;; Not sure what this slash-pos is all about, but I guess it's just + ;; trying to remove the leading / of absolute file names. + (slash-pos (string-match "/" dir)) + (temp-dir (expand-file-name (substring dir (1+ slash-pos)) + temporary-file-directory))) + + (file-truename (expand-file-name (file-name-nondirectory file-name) + temp-dir)))) + +(defun flymake-delete-temp-directory (dir-name) + "Attempt to delete temp dir created by `flymake-create-temp-with-folder-structure', do not fail on error." + (let* ((temp-dir temporary-file-directory) + (suffix (substring dir-name (1+ (length temp-dir))))) + + (while (> (length suffix) 0) + (setq suffix (directory-file-name suffix)) + ;;+(flymake-log 0 "suffix=%s" suffix) + (flymake-safe-delete-directory + (file-truename (expand-file-name suffix temp-dir))) + (setq suffix (file-name-directory suffix))))) + +(defvar-local flymake-temp-source-file-name nil) +(defvar-local flymake-master-file-name nil) +(defvar-local flymake-temp-master-file-name nil) +(defvar-local flymake-base-dir nil) + +(defun flymake-init-create-temp-buffer-copy (create-temp-f) + "Make a temporary copy of the current buffer, save its name in buffer data and return the name." + (let* ((source-file-name buffer-file-name) + (temp-source-file-name (funcall create-temp-f source-file-name "flymake"))) + + (flymake-save-buffer-in-file temp-source-file-name) + (setq flymake-temp-source-file-name temp-source-file-name) + temp-source-file-name)) + +(defun flymake-simple-cleanup () + "Do cleanup after `flymake-init-create-temp-buffer-copy'. +Delete temp file." + (flymake-safe-delete-file flymake-temp-source-file-name) + (setq flymake-last-change-time nil)) + +(defun flymake-get-real-file-name (file-name-from-err-msg) + "Translate file name from error message to \"real\" file name. +Return full-name. Names are real, not patched." + (let* ((real-name nil) + (source-file-name buffer-file-name) + (master-file-name flymake-master-file-name) + (temp-source-file-name flymake-temp-source-file-name) + (temp-master-file-name flymake-temp-master-file-name) + (base-dirs + (list flymake-base-dir + (file-name-directory source-file-name) + (if master-file-name (file-name-directory master-file-name)))) + (files (list (list source-file-name source-file-name) + (list temp-source-file-name source-file-name) + (list master-file-name master-file-name) + (list temp-master-file-name master-file-name)))) + + (when (equal 0 (length file-name-from-err-msg)) + (setq file-name-from-err-msg source-file-name)) + + (setq real-name (flymake-get-full-patched-file-name file-name-from-err-msg base-dirs files)) + ;; if real-name is nil, than file name from err msg is none of the files we've patched + (if (not real-name) + (setq real-name (flymake-get-full-nonpatched-file-name file-name-from-err-msg base-dirs))) + (if (not real-name) + (setq real-name file-name-from-err-msg)) + (setq real-name (flymake-fix-file-name real-name)) + (flymake-log 3 "get-real-file-name: file-name=%s real-name=%s" file-name-from-err-msg real-name) + real-name)) + +(defun flymake-get-full-patched-file-name (file-name-from-err-msg base-dirs files) + (let* ((base-dirs-count (length base-dirs)) + (file-count (length files)) + (real-name nil)) + + (while (and (not real-name) (> base-dirs-count 0)) + (setq file-count (length files)) + (while (and (not real-name) (> file-count 0)) + (let* ((this-dir (nth (1- base-dirs-count) base-dirs)) + (this-file (nth 0 (nth (1- file-count) files))) + (this-real-name (nth 1 (nth (1- file-count) files)))) + ;;+(flymake-log 0 "this-dir=%s this-file=%s this-real=%s msg-file=%s" this-dir this-file this-real-name file-name-from-err-msg) + (when (and this-dir this-file (flymake-same-files + (expand-file-name file-name-from-err-msg this-dir) + this-file)) + (setq real-name this-real-name))) + (setq file-count (1- file-count))) + (setq base-dirs-count (1- base-dirs-count))) + real-name)) + +(defun flymake-get-full-nonpatched-file-name (file-name-from-err-msg base-dirs) + (let* ((real-name nil)) + (if (file-name-absolute-p file-name-from-err-msg) + (setq real-name file-name-from-err-msg) + (let* ((base-dirs-count (length base-dirs))) + (while (and (not real-name) (> base-dirs-count 0)) + (let* ((full-name (expand-file-name file-name-from-err-msg + (nth (1- base-dirs-count) base-dirs)))) + (if (file-exists-p full-name) + (setq real-name full-name)) + (setq base-dirs-count (1- base-dirs-count)))))) + real-name)) + +(defun flymake-init-find-buildfile-dir (source-file-name buildfile-name) + "Find buildfile, store its dir in buffer data and return its dir, if found." + (let* ((buildfile-dir + (flymake-find-buildfile buildfile-name + (file-name-directory source-file-name)))) + (if buildfile-dir + (setq flymake-base-dir buildfile-dir) + (flymake-log 1 "no buildfile (%s) for %s" buildfile-name source-file-name) + (flymake-report-fatal-status + "NOMK" (format "No buildfile (%s) found for %s" + buildfile-name source-file-name))))) + +(defun flymake-init-create-temp-source-and-master-buffer-copy (get-incl-dirs-f create-temp-f master-file-masks include-regexp) + "Find master file (or buffer), create its copy along with a copy of the source file." + (let* ((source-file-name buffer-file-name) + (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)) + (master-and-temp-master (flymake-create-master-file + source-file-name temp-source-file-name + get-incl-dirs-f create-temp-f + master-file-masks include-regexp))) + + (if (not master-and-temp-master) + (progn + (flymake-log 1 "cannot find master file for %s" source-file-name) + (flymake-report-status "!" "") ; NOMASTER + nil) + (setq flymake-master-file-name (nth 0 master-and-temp-master)) + (setq flymake-temp-master-file-name (nth 1 master-and-temp-master))))) + +(defun flymake-master-cleanup () + (flymake-simple-cleanup) + (flymake-safe-delete-file flymake-temp-master-file-name)) + +;;;; make-specific init-cleanup routines +(defun flymake-get-syntax-check-program-args (source-file-name base-dir use-relative-base-dir use-relative-source get-cmd-line-f) + "Create a command line for syntax check using GET-CMD-LINE-F." + (funcall get-cmd-line-f + (if use-relative-source + (file-relative-name source-file-name base-dir) + source-file-name) + (if use-relative-base-dir + (file-relative-name base-dir + (file-name-directory source-file-name)) + base-dir))) + +(defun flymake-get-make-cmdline (source base-dir) + (list "make" + (list "-s" + "-C" + base-dir + (concat "CHK_SOURCES=" source) + "SYNTAX_CHECK_MODE=1" + "check-syntax"))) + +(defun flymake-get-ant-cmdline (source base-dir) + (list "ant" + (list "-buildfile" + (concat base-dir "/" "build.xml") + (concat "-DCHK_SOURCES=" source) + "check-syntax"))) + +(defun flymake-simple-make-init-impl (create-temp-f use-relative-base-dir use-relative-source build-file-name get-cmdline-f) + "Create syntax check command line for a directly checked source file. +Use CREATE-TEMP-F for creating temp copy." + (let* ((args nil) + (source-file-name buffer-file-name) + (buildfile-dir (flymake-init-find-buildfile-dir source-file-name build-file-name))) + (if buildfile-dir + (let* ((temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f))) + (setq args (flymake-get-syntax-check-program-args temp-source-file-name buildfile-dir + use-relative-base-dir use-relative-source + get-cmdline-f)))) + args)) + +(defun flymake-simple-make-init () + (flymake-simple-make-init-impl 'flymake-create-temp-inplace t t "Makefile" 'flymake-get-make-cmdline)) + +(defun flymake-master-make-init (get-incl-dirs-f master-file-masks include-regexp) + "Create make command line for a source file checked via master file compilation." + (let* ((make-args nil) + (temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy + get-incl-dirs-f 'flymake-create-temp-inplace + master-file-masks include-regexp))) + (when temp-master-file-name + (let* ((buildfile-dir (flymake-init-find-buildfile-dir temp-master-file-name "Makefile"))) + (if buildfile-dir + (setq make-args (flymake-get-syntax-check-program-args + temp-master-file-name buildfile-dir nil nil 'flymake-get-make-cmdline))))) + make-args)) + +(defun flymake-find-make-buildfile (source-dir) + (flymake-find-buildfile "Makefile" source-dir)) + +;;;; .h/make specific +(defun flymake-master-make-header-init () + (flymake-master-make-init + 'flymake-get-include-dirs + '("\\.\\(?:c\\(?:pp\\|xx\\|\\+\\+\\)?\\|CC\\)\\'") + "[ \t]*#[ \t]*include[ \t]*\"\\([[:word:]0-9/\\_.]*%s\\)\"")) + +;;;; .java/make specific +(defun flymake-simple-make-java-init () + (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "Makefile" 'flymake-get-make-cmdline)) + +(defun flymake-simple-ant-java-init () + (flymake-simple-make-init-impl 'flymake-create-temp-with-folder-structure nil nil "build.xml" 'flymake-get-ant-cmdline)) + +(defun flymake-simple-java-cleanup () + "Cleanup after `flymake-simple-make-java-init' -- delete temp file and dirs." + (flymake-safe-delete-file flymake-temp-source-file-name) + (when flymake-temp-source-file-name + (flymake-delete-temp-directory + (file-name-directory flymake-temp-source-file-name)))) + +;;;; perl-specific init-cleanup routines +(defun flymake-perl-init () + (let* ((temp-file (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-inplace)) + (local-file (file-relative-name + temp-file + (file-name-directory buffer-file-name)))) + (list "perl" (list "-wc " local-file)))) + +;;;; php-specific init-cleanup routines +(defun flymake-php-init () + (let* ((temp-file (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-inplace)) + (local-file (file-relative-name + temp-file + (file-name-directory buffer-file-name)))) + (list "php" (list "-f" local-file "-l")))) + +;;;; tex-specific init-cleanup routines +(defun flymake-get-tex-args (file-name) + ;;(list "latex" (list "-c-style-errors" file-name)) + (list "texify" (list "--pdf" "--tex-option=-c-style-errors" file-name))) + +(defun flymake-simple-tex-init () + (flymake-get-tex-args (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))) + +;; Perhaps there should be a buffer-local variable flymake-master-file +;; that people can set to override this stuff. Could inherit from +;; the similar AUCTeX variable. +(defun flymake-master-tex-init () + (let* ((temp-master-file-name (flymake-init-create-temp-source-and-master-buffer-copy + 'flymake-get-include-dirs-dot 'flymake-create-temp-inplace + '("\\.tex\\'") + "[ \t]*\\in\\(?:put\\|clude\\)[ \t]*{\\(.*%s\\)}"))) + (when temp-master-file-name + (flymake-get-tex-args temp-master-file-name)))) + +(defun flymake-get-include-dirs-dot (_base-dir) + '(".")) + +;;;; xml-specific init-cleanup routines +(defun flymake-xml-init () + (list flymake-xml-program + (list "val" (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-inplace)))) (provide 'flymake) ;;; flymake.el ends here commit 7cf59c6635f747fe2d974b92be1fd937d3821681 Author: João Távora Date: Wed Sep 27 22:15:19 2017 +0100 Revert "Add flymake-backends defcustom" This reverts Git commit 13993c46a21495167517f76d2e36b6c09ac5e89e. Don't merge this back to master as development happening there builds upon this work. See also https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00932.html diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index df1a0750cf..0cbf3e1c67 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -123,12 +123,10 @@ NAME is the file name function to use, default `flymake-get-real-file-name'." (flymake-log 3 "file %s, init=%s" file-name (car mode-and-masks)) mode-and-masks)) -(defun flymake-proc-can-syntax-check-buffer () - "Determine whether we can syntax check current buffer. -Return nil if we cannot, non-nil if -we can." - (and buffer-file-name - (if (flymake-get-init-function buffer-file-name) t nil))) +(defun flymake-can-syntax-check-file (file-name) + "Determine whether we can syntax check FILE-NAME. +Return nil if we cannot, non-nil if we can." + (if (flymake-get-init-function file-name) t nil)) (defun flymake-get-init-function (file-name) "Return init function to be used for the file." @@ -719,11 +717,12 @@ Return its components if so, nil otherwise." (error (flymake-log 1 "Failed to delete dir %s, error ignored" dir-name)))) -(defun flymake-proc-start-syntax-check () +(defun flymake-start-syntax-check () "Start syntax checking for current buffer." (interactive) (flymake-log 3 "flymake is running: %s" flymake-is-running) - (when (not flymake-is-running) + (when (and (not flymake-is-running) + (flymake-can-syntax-check-file buffer-file-name)) (when (or (not flymake-compilation-prevents-syntax-check) (not (flymake-compilation-is-running))) ;+ (flymake-rep-ort-status buffer "COMP") (flymake-clear-buildfile-cache) @@ -1088,13 +1087,5 @@ Use CREATE-TEMP-F for creating temp copy." (list "val" (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace)))) - -;;;; Hook onto flymake-ui - -(add-to-list 'flymake-backends - `(flymake-proc-can-syntax-check-buffer - . - flymake-proc-start-syntax-check)) - (provide 'flymake-proc) ;;; flymake-proc.el ends here diff --git a/lisp/progmodes/flymake-ui.el b/lisp/progmodes/flymake-ui.el index bf5218c41d..2a15a497d8 100644 --- a/lisp/progmodes/flymake-ui.el +++ b/lisp/progmodes/flymake-ui.el @@ -108,17 +108,6 @@ See `flymake-error-bitmap' and `flymake-warning-bitmap'." :group 'flymake :type 'integer) -(defcustom flymake-backends '() - "Ordered list of backends providing syntax check information for a buffer. -Value is an alist of conses (PREDICATE . CHECKER). Both PREDICATE -and CHECKER are functions called with a single argument, the -buffer in which `flymake-mode' was enabled. PREDICATE is expected -to (quickly) return t or nil if the buffer can be syntax checked -by CHECKER, which in can performs more morose operations, -possibly asynchronously." - :group 'flymake - :type 'alist) - (defvar-local flymake-timer nil "Timer for starting syntax check.") @@ -379,7 +368,7 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (setq flymake-last-change-time nil) (flymake-log 3 "starting syntax check as more than 1 second passed since last change") - (flymake--start-syntax-check))))) + (flymake-start-syntax-check))))) (define-obsolete-function-alias 'flymake-display-err-menu-for-current-line 'flymake-popup-current-error-menu "24.4") @@ -453,20 +442,6 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status %s, warning %s" (buffer-name) status warning)) -(defvar-local flymake--backend nil - "The currently active backend selected by `flymake-mode'") - -(defun flymake--can-syntax-check-buffer (buffer) - (let ((all flymake-backends) - (candidate)) - (catch 'done - (while (setq candidate (pop all)) - (when (with-current-buffer buffer (funcall (car candidate))) - (throw 'done (cdr candidate))))))) - -(defun flymake--start-syntax-check () - (funcall flymake--backend)) - ;;;###autoload (define-minor-mode flymake-mode nil :group 'flymake :lighter flymake-mode-line @@ -474,36 +449,31 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." ;; Turning the mode ON. (flymake-mode - (let* ((backend (flymake--can-syntax-check-buffer (current-buffer)))) - (cond - ((not backend) - (flymake-log 2 "flymake cannot check syntax in buffer %s" (buffer-name))) - (t - (setq flymake--backend backend) - - (add-hook 'after-change-functions 'flymake-after-change-function nil t) - (add-hook 'after-save-hook 'flymake-after-save-hook nil t) - (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) - ;;+(add-hook 'find-file-hook 'flymake-find-file-hook) - - (flymake-report-status "" "") - - (setq flymake-timer - (run-at-time nil 1 'flymake-on-timer-event (current-buffer))) - - (when (and flymake-start-syntax-check-on-find-file - ;; Since we write temp files in current dir, there's no point - ;; trying if the directory is read-only (bug#8954). - (file-writable-p (file-name-directory buffer-file-name))) - (with-demoted-errors - (flymake--start-syntax-check))))) - ) - ) + (cond + ((not buffer-file-name) + (message "Flymake unable to run without a buffer file name")) + ((not (flymake-can-syntax-check-file buffer-file-name)) + (flymake-log 2 "flymake cannot check syntax in buffer %s" (buffer-name))) + (t + (add-hook 'after-change-functions 'flymake-after-change-function nil t) + (add-hook 'after-save-hook 'flymake-after-save-hook nil t) + (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) + ;;+(add-hook 'find-file-hook 'flymake-find-file-hook) + + (flymake-report-status "" "") + + (setq flymake-timer + (run-at-time nil 1 'flymake-on-timer-event (current-buffer))) + + (when (and flymake-start-syntax-check-on-find-file + ;; Since we write temp files in current dir, there's no point + ;; trying if the directory is read-only (bug#8954). + (file-writable-p (file-name-directory buffer-file-name))) + (with-demoted-errors + (flymake-start-syntax-check)))))) ;; Turning the mode OFF. (t - (setq flymake--backend nil) - (remove-hook 'after-change-functions 'flymake-after-change-function t) (remove-hook 'after-save-hook 'flymake-after-save-hook t) (remove-hook 'kill-buffer-hook 'flymake-kill-buffer-hook t) @@ -538,14 +508,14 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (let((new-text (buffer-substring start stop))) (when (and flymake-start-syntax-check-on-newline (equal new-text "\n")) (flymake-log 3 "starting syntax check as new-line has been seen") - (flymake--start-syntax-check)) + (flymake-start-syntax-check)) (setq flymake-last-change-time (float-time)))) (defun flymake-after-save-hook () (if (local-variable-p 'flymake-mode (current-buffer)) ; (???) other way to determine whether flymake is active in buffer being saved? (progn (flymake-log 3 "starting syntax check as buffer was saved") - (flymake--start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???) + (flymake-start-syntax-check)))) ; no more mode 3. cannot start check if mode 3 (to temp copies) is active - (???) (defun flymake-kill-buffer-hook () (when flymake-timer @@ -556,10 +526,10 @@ For the format of LINE-ERR-INFO, see `flymake-ler-make-ler'." (defun flymake-find-file-hook () ;;+(when flymake-start-syntax-check-on-find-file ;;+ (flymake-log 3 "starting syntax check on file open") - ;;+ (flymake--start-syntax-check) + ;;+ (flymake-start-syntax-check) ;;+) (when (and (not (local-variable-p 'flymake-mode (current-buffer))) - (flymake--can-syntax-check-buffer (current-buffer))) + (flymake-can-syntax-check-file buffer-file-name)) (flymake-mode) (flymake-log 3 "automatically turned ON flymake mode"))) commit 1f02ae39310f15bf683642b9aee1cf162bd391e6 Author: Mark Oteiza Date: Wed Sep 27 16:32:07 2017 -0400 Mark some functions as pure * lisp/emacs-lisp/byte-opt.el: Add some functions that return integral values to the builtin list of pure functions. diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 69f03c5166..623985f44f 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -1281,7 +1281,10 @@ ;; errors to compile time. (let ((pure-fns - '(concat symbol-name regexp-opt regexp-quote string-to-syntax))) + '(concat symbol-name regexp-opt regexp-quote string-to-syntax + string-to-char + ash lsh logb lognot logior logxor + ceiling floor))) (while pure-fns (put (car pure-fns) 'pure t) (setq pure-fns (cdr pure-fns))) commit 56f7e4c2024608b2a263dc8505c49996cda0ec4f Author: Stefan Monnier Date: Wed Sep 27 13:00:44 2017 -0400 * lisp/textmodes/page-ext.el: Misc cleanup, add RET binding Use lexical-binding. Remove redundant :group args. (pages-directory-mode-map): Bind RET. (pages-buffer, pages-pos-list): Define as buffer-local. (pages-directory-map): Define as alias *before* the defvar. Mark as obsolete. (pages-buffer-original-position, pages-buffer-original-page): Move declaration to before the first use. (pages-directory): Remove unused var `linenum`. (pages-directory-goto): Add optional `event` arg and make it work when bound to mouse events. (pages-directory-goto-with-mouse): Make it an obsolete alias. diff --git a/lisp/textmodes/page-ext.el b/lisp/textmodes/page-ext.el index 94b68decfb..bf1e33bf0f 100644 --- a/lisp/textmodes/page-ext.el +++ b/lisp/textmodes/page-ext.el @@ -1,4 +1,4 @@ -;;; page-ext.el --- extended page handling commands +;;; page-ext.el --- extended page handling commands -*- lexical-binding:t -*- ;; Copyright (C) 1990-1991, 1993-1994, 2001-2017 Free Software ;; Foundation, Inc. @@ -243,18 +243,15 @@ (defcustom pages-directory-buffer-narrowing-p t "If non-nil, `pages-directory-goto' narrows pages buffer to entry." - :type 'boolean - :group 'pages) + :type 'boolean) (defcustom pages-directory-for-adding-page-narrowing-p t "If non-nil, `add-new-page' narrows page buffer to new entry." - :type 'boolean - :group 'pages) + :type 'boolean) (defcustom pages-directory-for-adding-new-page-before-current-page-p t "If non-nil, `add-new-page' inserts new page before current page." - :type 'boolean - :group 'pages) + :type 'boolean) ;;; Addresses related variables @@ -262,23 +259,19 @@ (defcustom pages-addresses-file-name "~/addresses" "Standard name for file of addresses. Entries separated by page-delimiter. Used by `pages-directory-for-addresses' function." - :type 'file - :group 'pages) + :type 'file) (defcustom pages-directory-for-addresses-goto-narrowing-p t "If non-nil, `pages-directory-goto' narrows addresses buffer to entry." - :type 'boolean - :group 'pages) + :type 'boolean) (defcustom pages-directory-for-addresses-buffer-keep-windows-p t "If nil, `pages-directory-for-addresses' deletes other windows." - :type 'boolean - :group 'pages) + :type 'boolean) (defcustom pages-directory-for-adding-addresses-narrowing-p t "If non-nil, `add-new-page' narrows addresses buffer to new entry." - :type 'boolean - :group 'pages) + :type 'boolean) ;;; Key bindings for page handling functions @@ -415,9 +408,9 @@ Point is left in the body of page." Called from a program, there are three arguments: REVERSE (non-nil means reverse order), BEG and END (region to sort)." -;;; This sort function handles ends of pages differently than -;;; `sort-pages' and works better with lists of addresses and similar -;;; files. + ;; This sort function handles ends of pages differently than + ;; `sort-pages' and works better with lists of addresses and similar + ;; files. (interactive "P\nr") (save-restriction @@ -463,25 +456,27 @@ REVERSE (non-nil means reverse order), BEG and END (region to sort)." \(This regular expression may be used to select only those pages that contain matches to the regexp.)") -(defvar pages-buffer nil +(defvar-local pages-buffer nil "The buffer for which the pages-directory function creates the directory.") (defvar pages-directory-prefix "*Directory for:" "Prefix of name of temporary buffer for pages-directory.") -(defvar pages-pos-list nil +(defvar-local pages-pos-list nil "List containing the positions of the pages in the pages-buffer.") (defvar pages-target-buffer) +(define-obsolete-variable-alias 'pages-directory-map + 'pages-directory-mode-map "26.1") (defvar pages-directory-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-c\C-c" 'pages-directory-goto) + (define-key map "\C-m" 'pages-directory-goto) (define-key map "\C-c\C-p\C-a" 'add-new-page) - (define-key map [mouse-2] 'pages-directory-goto-with-mouse) + (define-key map [mouse-2] 'pages-directory-goto) map) "Keymap for the pages-directory-buffer.") -(defvaralias 'pages-directory-map 'pages-directory-mode-map) (defvar original-page-delimiter "^\f" "Default page delimiter.") @@ -512,6 +507,9 @@ resets the page-delimiter to the original value." ;;; Pages directory main definitions +(defvar pages-buffer-original-position) +(defvar pages-buffer-original-page) + (defun pages-directory (pages-list-all-headers-p count-lines-p &optional regexp) "Display a directory of the page headers in a temporary buffer. @@ -573,7 +571,6 @@ directory for only the accessible portion of the buffer." (let ((pages-target-buffer (current-buffer)) (pages-directory-buffer (concat pages-directory-prefix " " (buffer-name))) - (linenum 1) (pages-buffer-original-position (point)) (pages-buffer-original-page 0)) @@ -644,10 +641,6 @@ directory for only the accessible portion of the buffer." 1 pages-buffer-original-page)))) -(defvar pages-buffer-original-position) -(defvar pages-buffer-original-page) -(defvar pages-buffer-original-page) - (defun pages-copy-header-and-position (count-lines-p) "Copy page header and its position to the Pages Directory. Only arg non-nil, count lines in page and insert before header. @@ -701,16 +694,13 @@ Used by `pages-directory' function." Move point to one of the lines in this buffer, then use \\[pages-directory-goto] to go to the same line in the pages buffer." - (make-local-variable 'pages-buffer) - (make-local-variable 'pages-pos-list) (make-local-variable 'pages-directory-buffer-narrowing-p)) -(defun pages-directory-goto () +(defun pages-directory-goto (&optional event) "Go to the corresponding line in the pages buffer." - -;;; This function is mostly a copy of `occur-mode-goto-occurrence' - - (interactive) + ;; This function is mostly a copy of `occur-mode-goto-occurrence' + (interactive "@e") + (if event (mouse-set-point event)) (if (or (not pages-buffer) (not (buffer-name pages-buffer))) (progn @@ -724,18 +714,13 @@ to the same line in the pages buffer." (narrowing-p pages-directory-buffer-narrowing-p)) (pop-to-buffer pages-buffer) (widen) - (if end-of-directory-p - (goto-char (point-max)) - (goto-char (marker-position pos))) + (goto-char (if end-of-directory-p + (point-max) + (marker-position pos))) (if narrowing-p (narrow-to-page)))) -(defun pages-directory-goto-with-mouse (event) - "Go to the corresponding line under the mouse pointer in the pages buffer." - (interactive "e") - (with-current-buffer (window-buffer (posn-window (event-end event))) - (save-excursion - (goto-char (posn-point (event-end event))) - (pages-directory-goto)))) +(define-obsolete-function-alias 'pages-directory-goto-with-mouse + #'pages-directory-goto "26.1") ;;; The `pages-directory-for-addresses' function and ancillary code commit a3f647c5c810e8be5321bf99cb21d565590a7cf8 Author: Paul Eggert Date: Tue Sep 26 17:15:56 2017 -0700 * src/editfns.c (styled_format): Fix typo in previous change. diff --git a/src/editfns.c b/src/editfns.c index ef0374199c..e326604467 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -4384,7 +4384,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, multibyte = true; goto retry; } - new_result = false; } conversion = 's'; } commit 0e82fa34163dba21121e3a9cffa7f896c81c4d93 Author: Paul Eggert Date: Tue Sep 26 16:31:57 2017 -0700 Avoid some unnecessary copying in Fformat etc. This patch is just for performance; it should not affect behavior. On my platform, it made the microbenchmark (format "%S" load-path) run about 45% faster. It should also speed up calls like (message "%s" STRING). * src/callint.c (Fcall_interactively): * src/dbusbind.c (XD_OBJECT_TO_STRING): * src/editfns.c (Fmessage, Fmessage_box): * src/xdisp.c (vadd_to_log, Ftrace_to_stderr): Use styled_format instead of Fformat or Fformat_message, to avoid unnecessary copying. * src/editfns.c (styled_format): New arg NEW_RESULT. All uses changed. Reuse an input string if it has the right value and if !NEW_RESULT. * src/lisp.h (style_format): New decl. diff --git a/src/callint.c b/src/callint.c index 105ec071d0..469205cc38 100644 --- a/src/callint.c +++ b/src/callint.c @@ -272,7 +272,7 @@ invoke it. If KEYS is omitted or nil, the return value of { /* `args' will contain the array of arguments to pass to the function. `visargs' will contain the same list but in a nicer form, so that if we - pass it to `Fformat_message' it will be understandable to a human. */ + pass it to styled_format it will be understandable to a human. */ Lisp_Object *args, *visargs; Lisp_Object specs; Lisp_Object filter_specs; @@ -502,10 +502,7 @@ invoke it. If KEYS is omitted or nil, the return value of for (i = 2; *tem; i++) { visargs[1] = make_string (tem + 1, strcspn (tem + 1, "\n")); - if (strchr (SSDATA (visargs[1]), '%')) - callint_message = Fformat_message (i - 1, visargs + 1); - else - callint_message = visargs[1]; + callint_message = styled_format (i - 1, visargs + 1, true, false); switch (*tem) { diff --git a/src/dbusbind.c b/src/dbusbind.c index 4a7068416f..789aa00861 100644 --- a/src/dbusbind.c +++ b/src/dbusbind.c @@ -237,7 +237,8 @@ static char * XD_OBJECT_TO_STRING (Lisp_Object object) { AUTO_STRING (format, "%s"); - return SSDATA (CALLN (Fformat, format, object)); + Lisp_Object args[] = { format, object }; + return SSDATA (styled_format (ARRAYELTS (args), args, false, false)); } #define XD_DBUS_VALIDATE_BUS_ADDRESS(bus) \ diff --git a/src/editfns.c b/src/editfns.c index 2f8b075817..ef0374199c 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -74,7 +74,6 @@ static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec, static long int tm_gmtoff (struct tm *); static int tm_diff (struct tm *, struct tm *); static void update_buffer_properties (ptrdiff_t, ptrdiff_t); -static Lisp_Object styled_format (ptrdiff_t, Lisp_Object *, bool); #ifndef HAVE_TM_GMTOFF # define HAVE_TM_GMTOFF false @@ -3959,7 +3958,7 @@ usage: (message FORMAT-STRING &rest ARGS) */) } else { - Lisp_Object val = Fformat_message (nargs, args); + Lisp_Object val = styled_format (nargs, args, true, false); message3 (val); return val; } @@ -3985,7 +3984,7 @@ usage: (message-box FORMAT-STRING &rest ARGS) */) } else { - Lisp_Object val = Fformat_message (nargs, args); + Lisp_Object val = styled_format (nargs, args, true, false); Lisp_Object pane, menu; pane = list1 (Fcons (build_string ("OK"), Qt)); @@ -4141,7 +4140,7 @@ produced text. usage: (format STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { - return styled_format (nargs, args, false); + return styled_format (nargs, args, false, true); } DEFUN ("format-message", Fformat_message, Sformat_message, 1, MANY, 0, @@ -4157,13 +4156,16 @@ and right quote replacement characters are specified by usage: (format-message STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { - return styled_format (nargs, args, true); + return styled_format (nargs, args, true, true); } -/* Implement ‘format-message’ if MESSAGE is true, ‘format’ otherwise. */ +/* Implement ‘format-message’ if MESSAGE is true, ‘format’ otherwise. + If NEW_RESULT, the result is a new string; otherwise, the result + may be one of the arguments. */ -static Lisp_Object -styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) +Lisp_Object +styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message, + bool new_result) { ptrdiff_t n; /* The number of the next arg to substitute. */ char initial_buffer[4000]; @@ -4193,6 +4195,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) /* The start and end bytepos in the output string. */ ptrdiff_t start, end; + /* Whether the argument is a newly created string. */ + bool_bf new_string : 1; + /* Whether the argument is a string with intervals. */ bool_bf intervals : 1; } *info; @@ -4342,7 +4347,10 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) memset (&discarded[format0 - format_start], 1, format - format0 - (conversion == '%')); if (conversion == '%') - goto copy_char; + { + new_result = true; + goto copy_char; + } ++n; if (! (n < nargs)) @@ -4352,6 +4360,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (nspec < ispec) { spec->argument = args[n]; + spec->new_string = false; spec->intervals = false; nspec = ispec; } @@ -4369,11 +4378,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) { Lisp_Object noescape = conversion == 'S' ? Qnil : Qt; spec->argument = arg = Fprin1_to_string (arg, noescape); + spec->new_string = true; if (STRING_MULTIBYTE (arg) && ! multibyte) { multibyte = true; goto retry; } + new_result = false; } conversion = 's'; } @@ -4387,6 +4398,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) goto retry; } spec->argument = arg = Fchar_to_string (arg); + spec->new_string = true; } if (!EQ (arg, args[n])) @@ -4409,6 +4421,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (conversion == 's') { + if (format == end && format - format_start == 2 + && (!new_result || spec->new_string) + && ! string_intervals (args[0])) + return arg; + /* handle case (precision[n] >= 0) */ ptrdiff_t prec = -1; @@ -4487,6 +4504,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (string_intervals (arg)) spec->intervals = arg_intervals = true; + new_result = true; continue; } } @@ -4754,6 +4772,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) } spec->end = nchars; + new_result = true; continue; } } @@ -4772,9 +4791,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) } convsrc = format_char == '`' ? uLSQM : uRSQM; convbytes = 3; + new_result = true; } else if (format_char == '`' && quoting_style == STRAIGHT_QUOTING_STYLE) - convsrc = "'"; + { + convsrc = "'"; + new_result = true; + } else { /* Copy a single character from format to buf. */ @@ -4798,6 +4821,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) int c = BYTE8_TO_CHAR (format_char); convbytes = CHAR_STRING (c, str); convsrc = (char *) str; + new_result = true; } } @@ -4844,6 +4868,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (bufsize < p - buf) emacs_abort (); + if (! new_result) + return args[0]; + if (maybe_combine_byte) nchars = multibyte_chars_in_text ((unsigned char *) buf, p - buf); Lisp_Object val = make_specified_string (buf, nchars, p - buf, multibyte); diff --git a/src/lisp.h b/src/lisp.h index c503082442..0c3ca3ae06 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3969,6 +3969,7 @@ extern _Noreturn void time_overflow (void); extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, bool); +extern Lisp_Object styled_format (ptrdiff_t, Lisp_Object *, bool, bool); extern void init_editfns (bool); extern void syms_of_editfns (void); diff --git a/src/xdisp.c b/src/xdisp.c index 141275f15a..86164eb9f6 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -10194,7 +10194,7 @@ vadd_to_log (char const *format, va_list ap) for (ptrdiff_t i = 1; i <= nargs; i++) args[i] = va_arg (ap, Lisp_Object); Lisp_Object msg = Qnil; - msg = Fformat_message (nargs, args); + msg = styled_format (nargs, args, true, false); ptrdiff_t len = SBYTES (msg) + 1; USE_SAFE_ALLOCA; @@ -19525,7 +19525,7 @@ DEFUN ("trace-to-stderr", Ftrace_to_stderr, Strace_to_stderr, 1, MANY, "", usage: (trace-to-stderr STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { - Lisp_Object s = Fformat (nargs, args); + Lisp_Object s = styled_format (nargs, args, false, false); fwrite (SDATA (s), 1, SBYTES (s), stderr); return Qnil; } commit a17f30d7cdfa3983f8c97e474015777ec051de35 Author: Mark Oteiza Date: Sun Sep 24 22:28:51 2017 -0400 Add MIME apps spec utilities Facilitates finding associations between MIME types and desktop files that report an association with that type. Combined with mailcap.el's MIME facilities, it should be easy to use desktop files. * lisp/xdg.el (xdg-mime-table): New variable. (xdg-mime-apps-files, xdg-mime-collect-associations, xdg-mime-apps): New functions. * test/data/xdg/mimeapps.list: New file. * test/data/xdg/mimeinfo.cache: New file. * test/lisp/xdg-tests.el (xdg-mime-associations): New test. diff --git a/lisp/xdg.el b/lisp/xdg.el index 76106f4258..4250faaeb4 100644 --- a/lisp/xdg.el +++ b/lisp/xdg.el @@ -34,6 +34,7 @@ ;;; Code: (eval-when-compile + (require 'cl-lib) (require 'subr-x)) @@ -212,6 +213,108 @@ Optional argument GROUP defaults to the string \"Desktop Entry\"." (when (null (string-match-p "[^[:blank:]]" (car res))) (pop res)) (nreverse res))) + +;; MIME apps specification +;; https://standards.freedesktop.org/mime-apps-spec/mime-apps-spec-1.0.1.html + +(defvar xdg-mime-table nil + "Table of MIME type to desktop file associations. +The table is an alist with keys being MIME major types (\"application\", +\"audio\", etc.), and values being hash tables. Each hash table has +MIME subtypes as keys and lists of desktop file absolute filenames.") + +(defun xdg-mime-apps-files () + "Return a list of files containing MIME/Desktop associations. +The list is in order of descending priority: user config, then +admin config, and finally system cached associations." + (let ((xdg-data-dirs (xdg-data-dirs)) + (desktop (getenv "XDG_CURRENT_DESKTOP")) + res) + (when desktop + (setq desktop (format "%s-mimeapps.list" desktop))) + (dolist (name (cons "mimeapps.list" desktop)) + (push (expand-file-name name (xdg-config-home)) res) + (push (expand-file-name (format "applications/%s" name) (xdg-data-home)) + res) + (dolist (dir (xdg-config-dirs)) + (push (expand-file-name name dir) res)) + (dolist (dir xdg-data-dirs) + (push (expand-file-name (format "applications/%s" name) dir) res))) + (dolist (dir xdg-data-dirs) + (push (expand-file-name "applications/mimeinfo.cache" dir) res)) + (nreverse res))) + +(defun xdg-mime-collect-associations (mime files) + "Return a list of desktop file names associated with MIME. +The associations are searched in the list of file names FILES, +which is expected to be ordered by priority as in +`xdg-mime-apps-files'." + (let ((regexp (concat (regexp-quote mime) "=\\([^[:cntrl:]]*\\)$")) + res sec defaults added removed cached) + (with-temp-buffer + (dolist (f (reverse files)) + (when (file-readable-p f) + (insert-file-contents-literally f nil nil nil t) + (goto-char (point-min)) + (let (end) + (while (not (or (eobp) end)) + (if (= (following-char) ?\[) + (progn (setq sec (char-after (1+ (point)))) + (forward-line)) + (if (not (looking-at regexp)) + (forward-line) + (dolist (str (xdg-desktop-strings (match-string 1))) + (cl-pushnew str + (cond ((eq sec ?D) defaults) + ((eq sec ?A) added) + ((eq sec ?R) removed) + ((eq sec ?M) cached)) + :test #'equal)) + (while (and (zerop (forward-line)) + (/= (following-char) ?\[))))))) + ;; Accumulate results into res + (dolist (f cached) + (when (not (member f removed)) (cl-pushnew f res :test #'equal))) + (dolist (f added) + (when (not (member f removed)) (push f res))) + (dolist (f removed) + (setq res (delete f res))) + (dolist (f defaults) + (push f res)) + (setq defaults nil added nil removed nil cached nil)))) + (delete-dups res))) + +(defun xdg-mime-apps (mime) + "Return list of desktop files associated with MIME, otherwise nil. +The list is in order of descending priority, and each element is +an absolute file name of a readable file. +Results are cached in `xdg-mime-table'." + (pcase-let ((`(,type ,subtype) (split-string mime "/")) + (xdg-data-dirs (xdg-data-dirs)) + (caches (xdg-mime-apps-files)) + (files ())) + (let ((mtim1 (get 'xdg-mime-table 'mtime)) + (mtim2 (cl-loop for f in caches when (file-readable-p f) + maximize (float-time (nth 5 (file-attributes f)))))) + ;; If one of the MIME/Desktop cache files has been modified: + (when (or (null mtim1) (time-less-p mtim1 mtim2)) + (setq xdg-mime-table nil))) + (when (null (assoc type xdg-mime-table)) + (push (cons type (make-hash-table :test #'equal)) xdg-mime-table)) + (if (let ((def (make-symbol "def")) + (table (cdr (assoc type xdg-mime-table)))) + (not (eq (setq files (gethash subtype table def)) def))) + files + (and files (setq files nil)) + (let ((dirs (mapcar (lambda (dir) (expand-file-name "applications" dir)) + (cons (xdg-data-home) xdg-data-dirs)))) + ;; Not being particular about desktop IDs + (dolist (f (nreverse (xdg-mime-collect-associations mime caches))) + (push (locate-file f dirs) files)) + (when files + (put 'xdg-mime-table 'mtime (current-time))) + (puthash subtype (delq nil files) (cdr (assoc type xdg-mime-table))))))) + (provide 'xdg) ;;; xdg.el ends here diff --git a/test/data/xdg/mimeapps.list b/test/data/xdg/mimeapps.list new file mode 100644 index 0000000000..27fbd94b16 --- /dev/null +++ b/test/data/xdg/mimeapps.list @@ -0,0 +1,9 @@ +[Default Applications] +x-test/foo=a.desktop + +[Added Associations] +x-test/foo=b.desktop +x-test/baz=a.desktop + +[Removed Associations] +x-test/foo=c.desktop;d.desktop diff --git a/test/data/xdg/mimeinfo.cache b/test/data/xdg/mimeinfo.cache new file mode 100644 index 0000000000..6e54f604fa --- /dev/null +++ b/test/data/xdg/mimeinfo.cache @@ -0,0 +1,4 @@ +[MIME Cache] +x-test/foo=c.desktop;d.desktop +x-test/bar=a.desktop;c.desktop +x-test/baz=b.desktop;d.desktop diff --git a/test/lisp/xdg-tests.el b/test/lisp/xdg-tests.el index b80f5e8552..eaf03ab9a0 100644 --- a/test/lisp/xdg-tests.el +++ b/test/lisp/xdg-tests.el @@ -65,4 +65,16 @@ (should (equal (xdg-desktop-strings " ") nil)) (should (equal (xdg-desktop-strings "a; ;") '("a" " ")))) +(ert-deftest xdg-mime-associations () + "Test reading MIME associations from files." + (let* ((apps (expand-file-name "mimeapps.list" xdg-tests-data-dir)) + (cache (expand-file-name "mimeinfo.cache" xdg-tests-data-dir)) + (fs (list apps cache))) + (should (equal (xdg-mime-collect-associations "x-test/foo" fs) + '("a.desktop" "b.desktop"))) + (should (equal (xdg-mime-collect-associations "x-test/bar" fs) + '("a.desktop" "c.desktop"))) + (should (equal (xdg-mime-collect-associations "x-test/baz" fs) + '("a.desktop" "b.desktop" "d.desktop"))))) + ;;; xdg-tests.el ends here commit 98a37e60142340b9c2b4e6b17c373f4ae6a2d8b4 Author: John Wiegley Date: Tue Sep 26 12:35:52 2017 -0700 lisp/simple.el: Indicate when a list of pairs is meant in a docstring diff --git a/lisp/simple.el b/lisp/simple.el index d21b15d531..469557713d 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -1003,7 +1003,7 @@ Called with one argument METHOD. If METHOD is `delete-only', then delete the region; the return value is undefined. If METHOD is nil, then return the content as a string. If METHOD is `bounds', then return the boundaries of the region -as a pair of (START . END) positions. +as a list of pairs of (START . END) positions. If METHOD is anything else, delete the region and return its content as a string, after filtering it with `filter-buffer-substring', which is called with METHOD as its 3rd argument.") @@ -5473,7 +5473,7 @@ also checks the value of `use-empty-active-region'." (progn (cl-assert (mark)) t))) (defun region-bounds () - "Return the boundaries of the region as a pair of (START . END) positions." + "Return the boundaries of the region as a list of pairs of (START . END) positions." (funcall region-extract-function 'bounds)) (defun region-noncontiguous-p () commit 52a1da03b226b8686856259ac5d9474a8462322a Author: John Wiegley Date: Tue Sep 26 12:35:52 2017 -0700 lisp/simple.el: Indicate when a list of pairs is meant in a docstring diff --git a/lisp/simple.el b/lisp/simple.el index d21b15d531..469557713d 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -1003,7 +1003,7 @@ Called with one argument METHOD. If METHOD is `delete-only', then delete the region; the return value is undefined. If METHOD is nil, then return the content as a string. If METHOD is `bounds', then return the boundaries of the region -as a pair of (START . END) positions. +as a list of pairs of (START . END) positions. If METHOD is anything else, delete the region and return its content as a string, after filtering it with `filter-buffer-substring', which is called with METHOD as its 3rd argument.") @@ -5473,7 +5473,7 @@ also checks the value of `use-empty-active-region'." (progn (cl-assert (mark)) t))) (defun region-bounds () - "Return the boundaries of the region as a pair of (START . END) positions." + "Return the boundaries of the region as a list of pairs of (START . END) positions." (funcall region-extract-function 'bounds)) (defun region-noncontiguous-p () commit 1e5949642a19a21fd9d47f66c66fd4d3bd99e910 Author: Philipp Stephani Date: Tue Sep 26 20:34:27 2017 +0200 ; * src/gtkutil.c (xg_create_frame_widgets): Add FIXME re. X drawing diff --git a/src/gtkutil.c b/src/gtkutil.c index 0203a5d5c1..0da7039919 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1217,7 +1217,10 @@ xg_create_frame_widgets (struct frame *f) with regular X drawing primitives, so from a GTK/GDK point of view, the widget is totally blank. When an expose comes, this will make the widget blank, and then Emacs redraws it. This flickers - a lot, so we turn off double buffering. */ + a lot, so we turn off double buffering. + FIXME: gtk_widget_set_double_buffered is deprecated and might stop + working in the future. We need to migrate away from combining + X and GTK+ drawing to a pure GTK+ build. */ gtk_widget_set_double_buffered (wfixed, FALSE); gtk_window_set_wmclass (GTK_WINDOW (wtop), commit 2fa19cc5510cdbf00c54991a9959be984dd99fbe Author: Philipp Stephani Date: Tue Sep 26 20:28:29 2017 +0200 Revert "Don't attempt to disable double buffering in newer GTK+ versions" This reverts commit c0af83b6ccf2dab9a515dd7f52eb9d4500275ae3. diff --git a/src/gtkutil.c b/src/gtkutil.c index 8ecbc5c91e..b98b0d08e7 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1233,14 +1233,12 @@ xg_create_frame_widgets (struct frame *f) if (FRAME_EXTERNAL_TOOL_BAR (f)) update_frame_tool_bar (f); -#if ! GTK_CHECK_VERSION (3, 14, 0) /* We don't want this widget double buffered, because we draw on it with regular X drawing primitives, so from a GTK/GDK point of view, the widget is totally blank. When an expose comes, this will make the widget blank, and then Emacs redraws it. This flickers a lot, so we turn off double buffering. */ gtk_widget_set_double_buffered (wfixed, FALSE); -#endif #if ! GTK_CHECK_VERSION (3, 22, 0) gtk_window_set_wmclass (GTK_WINDOW (wtop), commit 25ef543a97a80718cc4eb33734d393420a43f41e Author: Devon Sean McCullough Date: Tue Sep 26 10:51:04 2017 -0400 bug#28609: simple.el Correct grammar; also, call a pair a pair. diff --git a/lisp/simple.el b/lisp/simple.el index 4e42fd5241..d21b15d531 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -39,11 +39,11 @@ (defcustom shell-command-dont-erase-buffer nil "If non-nil, output buffer is not erased between shell commands. -Also, a non-nil value set the point in the output buffer -once the command complete. -The value `beg-last-out' set point at the beginning of the output, -`end-last-out' set point at the end of the buffer, `save-point' -restore the buffer position before the command." +Also, a non-nil value sets the point in the output buffer +once the command completes. +The value `beg-last-out' sets point at the beginning of the output, +`end-last-out' sets point at the end of the buffer, `save-point' +restores the buffer position before the command." :type '(choice (const :tag "Erase buffer" nil) (const :tag "Set point to beginning of last output" beg-last-out) @@ -53,9 +53,9 @@ restore the buffer position before the command." :version "26.1") (defvar shell-command-saved-pos nil - "Point position in the output buffer after command complete. -It is an alist (BUFFER . POS), where BUFFER is the output -buffer, and POS is the point position in BUFFER once the command finish. + "Point position in the output buffer after command completes. +It is an alist of (BUFFER . POS), where BUFFER is the output +buffer, and POS is the point position in BUFFER once the command finishes. This variable is used when `shell-command-dont-erase-buffer' is non-nil.") (defcustom idle-update-delay 0.5 @@ -1003,7 +1003,7 @@ Called with one argument METHOD. If METHOD is `delete-only', then delete the region; the return value is undefined. If METHOD is nil, then return the content as a string. If METHOD is `bounds', then return the boundaries of the region -as a list of the form (START . END). +as a pair of (START . END) positions. If METHOD is anything else, delete the region and return its content as a string, after filtering it with `filter-buffer-substring', which is called with METHOD as its 3rd argument.") @@ -5473,7 +5473,7 @@ also checks the value of `use-empty-active-region'." (progn (cl-assert (mark)) t))) (defun region-bounds () - "Return the boundaries of the region as a list of (START . END) positions." + "Return the boundaries of the region as a pair of (START . END) positions." (funcall region-extract-function 'bounds)) (defun region-noncontiguous-p () commit e7c8da4d058233859e74441aff5236a02b039d21 Author: Devon Sean McCullough Date: Tue Sep 26 10:51:04 2017 -0400 bug#28609: simple.el Correct grammar; also, call a pair a pair. diff --git a/lisp/simple.el b/lisp/simple.el index 4e42fd5241..d21b15d531 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -39,11 +39,11 @@ (defcustom shell-command-dont-erase-buffer nil "If non-nil, output buffer is not erased between shell commands. -Also, a non-nil value set the point in the output buffer -once the command complete. -The value `beg-last-out' set point at the beginning of the output, -`end-last-out' set point at the end of the buffer, `save-point' -restore the buffer position before the command." +Also, a non-nil value sets the point in the output buffer +once the command completes. +The value `beg-last-out' sets point at the beginning of the output, +`end-last-out' sets point at the end of the buffer, `save-point' +restores the buffer position before the command." :type '(choice (const :tag "Erase buffer" nil) (const :tag "Set point to beginning of last output" beg-last-out) @@ -53,9 +53,9 @@ restore the buffer position before the command." :version "26.1") (defvar shell-command-saved-pos nil - "Point position in the output buffer after command complete. -It is an alist (BUFFER . POS), where BUFFER is the output -buffer, and POS is the point position in BUFFER once the command finish. + "Point position in the output buffer after command completes. +It is an alist of (BUFFER . POS), where BUFFER is the output +buffer, and POS is the point position in BUFFER once the command finishes. This variable is used when `shell-command-dont-erase-buffer' is non-nil.") (defcustom idle-update-delay 0.5 @@ -1003,7 +1003,7 @@ Called with one argument METHOD. If METHOD is `delete-only', then delete the region; the return value is undefined. If METHOD is nil, then return the content as a string. If METHOD is `bounds', then return the boundaries of the region -as a list of the form (START . END). +as a pair of (START . END) positions. If METHOD is anything else, delete the region and return its content as a string, after filtering it with `filter-buffer-substring', which is called with METHOD as its 3rd argument.") @@ -5473,7 +5473,7 @@ also checks the value of `use-empty-active-region'." (progn (cl-assert (mark)) t))) (defun region-bounds () - "Return the boundaries of the region as a list of (START . END) positions." + "Return the boundaries of the region as a pair of (START . END) positions." (funcall region-extract-function 'bounds)) (defun region-noncontiguous-p () commit 827db6b559100153fd7dcab1ecdabd9233e906ab Author: Dmitry Gutov Date: Tue Sep 26 02:49:00 2017 +0300 Use a separate syntax-ppss cache for narrowed buffers * lisp/emacs-lisp/syntax.el (syntax-ppss-wide): New variable, to contain the data from `syntax-ppss-last' and `syntax-ppss-cache'. (syntax-ppss-cache, syntax-ppss-last): Remove. (syntax-ppss-narrow, syntax-ppss-narrow-start): New variables. (syntax-ppss-flush-cache): Flush both caches. (syntax-ppss--data): Return the appropriate last result and buffer cache for the current restriction. (syntax-ppss, syntax-ppss-debug): Use it (bug#22983). diff --git a/lisp/emacs-lisp/syntax.el b/lisp/emacs-lisp/syntax.el index f613783785..9eb6bde745 100644 --- a/lisp/emacs-lisp/syntax.el +++ b/lisp/emacs-lisp/syntax.el @@ -381,10 +381,26 @@ This function should move the cursor back to some syntactically safe point (where the PPSS is equivalent to nil).") (make-obsolete-variable 'syntax-begin-function nil "25.1") -(defvar-local syntax-ppss-cache nil - "List of (POS . PPSS) pairs, in decreasing POS order.") -(defvar-local syntax-ppss-last nil - "Cache of (LAST-POS . LAST-PPSS).") +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Several caches. +;; +;; Because `syntax-ppss' is equivalent to (parse-partial-sexp +;; (POINT-MIN) x), we need either to empty the cache when we narrow +;; the buffer, which is suboptimal, or we need to use several caches. +;; We use two of them, one for widened buffer, and one for narrowing. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar-local syntax-ppss-wide nil + "Cons of two elements (LAST . CACHE). +Where LAST is a pair (LAST-POS . LAST-PPS) caching the last invocation +and CACHE is a list of (POS . PPSS) pairs, in decreasing POS order. +These are valid when the buffer has no restriction.") + +(defvar-local syntax-ppss-narrow nil + "Same as `syntax-ppss-wide' but for a narrowed buffer.") + +(defvar-local syntax-ppss-narrow-start nil + "Start position of the narrowing for `syntax-ppss-narrow'.") (defalias 'syntax-ppss-after-change-function 'syntax-ppss-flush-cache) (defun syntax-ppss-flush-cache (beg &rest ignored) @@ -392,24 +408,29 @@ point (where the PPSS is equivalent to nil).") ;; Set syntax-propertize to refontify anything past beg. (setq syntax-propertize--done (min beg syntax-propertize--done)) ;; Flush invalid cache entries. - (while (and syntax-ppss-cache (> (caar syntax-ppss-cache) beg)) - (setq syntax-ppss-cache (cdr syntax-ppss-cache))) - ;; Throw away `last' value if made invalid. - (when (< beg (or (car syntax-ppss-last) 0)) - ;; If syntax-begin-function jumped to BEG, then the old state at BEG can - ;; depend on the text after BEG (which is presumably changed). So if - ;; BEG=(car (nth 10 syntax-ppss-last)) don't reuse that data because the - ;; assumed nil state at BEG may not be valid any more. - (if (<= beg (or (syntax-ppss-toplevel-pos (cdr syntax-ppss-last)) - (nth 3 syntax-ppss-last) - 0)) - (setq syntax-ppss-last nil) - (setcar syntax-ppss-last nil))) - ;; Unregister if there's no cache left. Sadly this doesn't work - ;; because `before-change-functions' is temporarily bound to nil here. - ;; (unless syntax-ppss-cache - ;; (remove-hook 'before-change-functions 'syntax-ppss-flush-cache t)) - ) + (dolist (cell (list syntax-ppss-wide syntax-ppss-narrow)) + (pcase cell + (`(,last . ,cache) + (while (and cache (> (caar cache) beg)) + (setq cache (cdr cache))) + ;; Throw away `last' value if made invalid. + (when (< beg (or (car last) 0)) + ;; If syntax-begin-function jumped to BEG, then the old state at BEG can + ;; depend on the text after BEG (which is presumably changed). So if + ;; BEG=(car (nth 10 syntax-ppss-last)) don't reuse that data because the + ;; assumed nil state at BEG may not be valid any more. + (if (<= beg (or (syntax-ppss-toplevel-pos (cdr last)) + (nth 3 last) + 0)) + (setq last nil) + (setcar last nil))) + ;; Unregister if there's no cache left. Sadly this doesn't work + ;; because `before-change-functions' is temporarily bound to nil here. + ;; (unless cache + ;; (remove-hook 'before-change-functions 'syntax-ppss-flush-cache t)) + (setcar cell last) + (setcdr cell cache))) + )) (defvar syntax-ppss-stats [(0 . 0.0) (0 . 0.0) (0 . 0.0) (0 . 0.0) (0 . 0.0) (1 . 2500.0)]) @@ -423,6 +444,17 @@ point (where the PPSS is equivalent to nil).") (defvar-local syntax-ppss-table nil "Syntax-table to use during `syntax-ppss', if any.") +(defun syntax-ppss--data () + (if (eq (point-min) 1) + (progn + (unless syntax-ppss-wide + (setq syntax-ppss-wide (cons nil nil))) + syntax-ppss-wide) + (unless (eq syntax-ppss-narrow-start (point-min)) + (setq syntax-ppss-narrow-start (point-min)) + (setq syntax-ppss-narrow (cons nil nil))) + syntax-ppss-narrow)) + (defun syntax-ppss (&optional pos) "Parse-Partial-Sexp State at POS, defaulting to point. The returned value is the same as that of `parse-partial-sexp' @@ -439,10 +471,13 @@ running the hook." (syntax-propertize pos) ;; (with-syntax-table (or syntax-ppss-table (syntax-table)) - (let ((old-ppss (cdr syntax-ppss-last)) - (old-pos (car syntax-ppss-last)) - (ppss nil) - (pt-min (point-min))) + (let* ((cell (syntax-ppss--data)) + (ppss-last (car cell)) + (ppss-cache (cdr cell)) + (old-ppss (cdr ppss-last)) + (old-pos (car ppss-last)) + (ppss nil) + (pt-min (point-min))) (if (and old-pos (> old-pos pos)) (setq old-pos nil)) ;; Use the OLD-POS if usable and close. Don't update the `last' cache. (condition-case nil @@ -475,7 +510,7 @@ running the hook." ;; The OLD-* data can't be used. Consult the cache. (t (let ((cache-pred nil) - (cache syntax-ppss-cache) + (cache ppss-cache) (pt-min (point-min)) ;; I differentiate between PT-MIN and PT-BEST because ;; I feel like it might be important to ensure that the @@ -491,7 +526,7 @@ running the hook." (if cache (setq pt-min (caar cache) ppss (cdar cache))) ;; Setup the before-change function if necessary. - (unless (or syntax-ppss-cache syntax-ppss-last) + (unless (or ppss-cache ppss-last) (add-hook 'before-change-functions 'syntax-ppss-flush-cache t t)) @@ -541,7 +576,7 @@ running the hook." pt-min (setq pt-min (/ (+ pt-min pos) 2)) nil nil ppss)) (push (cons pt-min ppss) - (if cache-pred (cdr cache-pred) syntax-ppss-cache))) + (if cache-pred (cdr cache-pred) ppss-cache))) ;; Compute the actual return value. (setq ppss (parse-partial-sexp pt-min pos nil nil ppss)) @@ -562,13 +597,15 @@ running the hook." (if (> (- (caar cache-pred) pos) syntax-ppss-max-span) (push pair (cdr cache-pred)) (setcar cache-pred pair)) - (if (or (null syntax-ppss-cache) - (> (- (caar syntax-ppss-cache) pos) + (if (or (null ppss-cache) + (> (- (caar ppss-cache) pos) syntax-ppss-max-span)) - (push pair syntax-ppss-cache) - (setcar syntax-ppss-cache pair))))))))) + (push pair ppss-cache) + (setcar ppss-cache pair))))))))) - (setq syntax-ppss-last (cons pos ppss)) + (setq ppss-last (cons pos ppss)) + (setcar cell ppss-last) + (setcdr cell ppss-cache) ppss) (args-out-of-range ;; If the buffer is more narrowed than when we built the cache, @@ -582,7 +619,7 @@ running the hook." (defun syntax-ppss-debug () (let ((pt nil) (min-diffs nil)) - (dolist (x (append syntax-ppss-cache (list (cons (point-min) nil)))) + (dolist (x (append (cdr (syntax-ppss--data)) (list (cons (point-min) nil)))) (when pt (push (- pt (car x)) min-diffs)) (setq pt (car x))) min-diffs)) commit a2244f417a7cf577172cec927b055f0aca9ef282 Author: Joerg Behrmann Date: Mon Sep 18 16:59:49 2017 +0200 Improve python3-compatibility of fallback completion (Bug#28499) * lisp/progmodes/python.el (python-eldoc-setup-code): Use inspect.getfullargspec instead of inspect.getargspec to avoid a deprecation warning on every usage of eldoc in python-mode. Copyright-paperwork-exempt: yes diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f3513ced4b..365191c56b 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -4271,8 +4271,10 @@ See `python-check-command' for the default." import inspect try: str_type = basestring + argspec_function = inspect.getargspec except NameError: str_type = str + argspec_function = inspect.getfullargspec if isinstance(obj, str_type): obj = eval(obj, globals()) doc = inspect.getdoc(obj) @@ -4285,9 +4287,7 @@ See `python-check-command' for the default." target = obj objtype = 'def' if target: - args = inspect.formatargspec( - *inspect.getargspec(target) - ) + args = inspect.formatargspec(*argspec_function(target)) name = obj.__name__ doc = '{objtype} {name}{args}'.format( objtype=objtype, name=name, args=args commit 79162cb0db1b62eec35f4fec0e6eac8669bc8f37 Author: Noam Postavsky Date: Mon Sep 25 07:15:51 2017 -0400 Fix subr-x-tests when running from elc * test/lisp/emacs-lisp/subr-x-tests.el (subr-x-and-let*-test-group-1): Use `eval' around the `should-error' cases. diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el index 2c6740a96c..0e8871d9a9 100644 --- a/test/lisp/emacs-lisp/subr-x-tests.el +++ b/test/lisp/emacs-lisp/subr-x-tests.el @@ -397,9 +397,14 @@ (should (equal 1 (let ((x 1)) (and-let* (x))))) (should (equal nil (and-let* ((x nil))))) (should (equal 1 (and-let* ((x 1))))) - (should-error (and-let* (nil (x 1))) :type 'setting-constant) + ;; The error doesn't trigger when compiled: the compiler will give + ;; a warning and then drop the erroneous code. Therefore, use + ;; `eval' to avoid compilation. + (should-error (eval '(and-let* (nil (x 1))) lexical-binding) + :type 'setting-constant) (should (equal nil (and-let* ((nil) (x 1))))) - (should-error (and-let* (2 (x 1))) :type 'wrong-type-argument) + (should-error (eval (and-let* (2 (x 1))) lexical-binding) + :type 'wrong-type-argument) (should (equal 1 (and-let* ((2) (x 1))))) (should (equal 2 (and-let* ((x 1) (2))))) (should (equal nil (let ((x nil)) (and-let* (x) x)))) commit 66d35ae49dae8815910198586e277895671bd19b Author: Noam Postavsky Date: Sat Sep 23 10:04:36 2017 -0400 * lisp/eshell/esh-util.el (eshell-condition-case): Add debug declaration. diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el index c204ec869b..8b24ec3c43 100644 --- a/lisp/eshell/esh-util.el +++ b/lisp/eshell/esh-util.el @@ -142,7 +142,7 @@ function `string-to-number'." (defmacro eshell-condition-case (tag form &rest handlers) "If `eshell-handle-errors' is non-nil, this is `condition-case'. Otherwise, evaluates FORM with no error handling." - (declare (indent 2)) + (declare (indent 2) (debug (sexp form &rest form))) (if eshell-handle-errors `(condition-case-unless-debug ,tag ,form commit f5e72b04d930215f6e770e2fe9e02ad6debf03ad Author: Noam Postavsky Date: Wed Aug 30 19:42:47 2017 -0400 Make sh-indentation into an alias for sh-basic-offset (Bug#21751) * lisp/progmodes/sh-script.el (sh-indentation): Redefine as obsolete variable alias for `sh-basic-offset'. (sh-mode, sh-smie--indent-continuation) (sh-smie-rc-rules, sh-basic-indent-line): Replace `sh-indentation' with `sh-basic-offset'. diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index f2027e3734..14598bcafb 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el @@ -593,11 +593,7 @@ sign. See `sh-feature'." (sexp :format "Evaluate: %v")))) :group 'sh-script) - -(defcustom sh-indentation 4 - "The width for further indentation in Shell-Script mode." - :type 'integer - :group 'sh-script) +(define-obsolete-variable-alias 'sh-indentation 'sh-basic-offset "26.1") (put 'sh-indentation 'safe-local-variable 'integerp) (defcustom sh-remember-variable-min 3 @@ -1617,7 +1613,7 @@ with your script for an edit-interpret-debug cycle." (setq-local skeleton-pair-alist '((?` _ ?`))) (setq-local skeleton-pair-filter-function 'sh-quoted-p) (setq-local skeleton-further-elements - '((< '(- (min sh-indentation (current-column)))))) + '((< '(- (min sh-basic-offset (current-column)))))) (setq-local skeleton-filter-function 'sh-feature) (setq-local skeleton-newline-indent-rigidly t) (setq-local defun-prompt-regexp @@ -2012,7 +2008,7 @@ May return nil if the line should not be treated as continued." (forward-line -1) (if (sh-smie--looking-back-at-continuation-p) (current-indentation) - (+ (current-indentation) sh-indentation)))) + (+ (current-indentation) sh-basic-offset)))) (t ;; Just make sure a line-continuation is indented deeper. (save-excursion @@ -2033,13 +2029,13 @@ May return nil if the line should not be treated as continued." ;; check the line before that one. (> ci indent)) (t ;Previous line is the beginning of the continued line. - (setq indent (min (+ ci sh-indentation) max)) + (setq indent (min (+ ci sh-basic-offset) max)) nil))))) indent)))))) (defun sh-smie-sh-rules (kind token) (pcase (cons kind token) - (`(:elem . basic) sh-indentation) + (`(:elem . basic) sh-basic-offset) (`(:after . "case-)") (- (sh-var-value 'sh-indent-for-case-alt) (sh-var-value 'sh-indent-for-case-label))) (`(:before . ,(or `"(" `"{" `"[" "while" "if" "for" "case")) @@ -2248,8 +2244,8 @@ Point should be before the newline." (defun sh-smie-rc-rules (kind token) (pcase (cons kind token) - (`(:elem . basic) sh-indentation) - ;; (`(:after . "case") (or sh-indentation smie-indent-basic)) + (`(:elem . basic) sh-basic-offset) + ;; (`(:after . "case") (or sh-basic-offset smie-indent-basic)) (`(:after . ";") (if (smie-rule-parent-p "case") (smie-rule-parent (sh-var-value 'sh-indent-after-case)))) @@ -2490,7 +2486,7 @@ the value thus obtained, and the result is used instead." (defun sh-basic-indent-line () "Indent a line for Sh mode (shell script mode). -Indent as far as preceding non-empty line, then by steps of `sh-indentation'. +Indent as far as preceding non-empty line, then by steps of `sh-basic-offset'. Lines containing only comments are considered empty." (interactive) (let ((previous (save-excursion @@ -2514,9 +2510,9 @@ Lines containing only comments are considered empty." (delete-region (point) (progn (beginning-of-line) (point))) (if (eolp) - (max previous (* (1+ (/ current sh-indentation)) - sh-indentation)) - (* (1+ (/ current sh-indentation)) sh-indentation)))))) + (max previous (* (1+ (/ current sh-basic-offset)) + sh-basic-offset)) + (* (1+ (/ current sh-basic-offset)) sh-basic-offset)))))) (if (< (current-column) (current-indentation)) (skip-chars-forward " \t")))) commit a58d0c590a777be98e58cd8c92ee1381e07e9b2d Author: Noam Postavsky Date: Wed Aug 30 19:31:48 2017 -0400 Fix loading of smie-config rules (Bug#24848) * lisp/emacs-lisp/smie.el (smie-config--setter): Use `set-default' instead of `setq-default'. (smie-config): Use `custom-initialize-set' instead of `custom-initialize-default' as the :initialize argument. * lisp/progmodes/sh-script.el (sh-learn-buffer-indent): Mention that we call `smie-config-guess' so that the user will have a chance to find the correct docstring to consult. Remove hedging comments regarding use of abnormal hooks. diff --git a/lisp/emacs-lisp/smie.el b/lisp/emacs-lisp/smie.el index 87c4782e21..da1e12b140 100644 --- a/lisp/emacs-lisp/smie.el +++ b/lisp/emacs-lisp/smie.el @@ -1956,7 +1956,7 @@ E.g. provided via a file-local call to `smie-config-local'.") (defvar smie-config--modefuns nil) (defun smie-config--setter (var value) - (setq-default var value) + (set-default var value) (let ((old-modefuns smie-config--modefuns)) (setq smie-config--modefuns nil) (pcase-dolist (`(,mode . ,rules) value) @@ -1982,7 +1982,7 @@ value with which to replace it." ;; FIXME improve value-type. :type '(choice (const nil) (alist :key-type symbol)) - :initialize 'custom-initialize-default + :initialize 'custom-initialize-set :set #'smie-config--setter) (defun smie-config-local (rules) diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index 0bda8bc275..f2027e3734 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el @@ -3594,6 +3594,10 @@ so that `occur-next' and `occur-prev' will work." (defun sh-learn-buffer-indent (&optional arg) "Learn how to indent the buffer the way it currently is. +If `sh-use-smie' is non-nil, call `smie-config-guess'. +Otherwise, run the sh-script specific indent learning command, as +decribed below. + Output in buffer \"*indent*\" shows any lines which have conflicting values of a variable, and the final value of all variables learned. When called interactively, pop to this buffer automatically if @@ -3610,8 +3614,7 @@ to the value of variable `sh-learn-basic-offset'. Abnormal hook `sh-learned-buffer-hook' if non-nil is called when the function completes. The function is abnormal because it is called -with an alist of variables learned. This feature may be changed or -removed in the future. +with an alist of variables learned. This command can often take a long time to run." (interactive "P") @@ -3809,7 +3812,6 @@ This command can often take a long time to run." " has" "s have") (if (zerop num-diffs) "." ":")))))) - ;; Are abnormal hooks considered bad form? (run-hook-with-args 'sh-learned-buffer-hook learned-var-list) (and (called-interactively-p 'any) (or sh-popup-occur-buffer (> num-diffs 0)) commit 3a68dec32730eddfc066b3b9528f4bc63b5fa9f6 Author: Dmitry Gutov Date: Tue Sep 26 02:25:03 2017 +0300 ; Update NEWS for the change in eldoc-message diff --git a/etc/NEWS b/etc/NEWS index 040d265f75..1b5ae658f6 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1353,6 +1353,12 @@ non-nil, but the code returned the list in the increasing order of priority instead. Now the code does what the documentation says it should do. +--- +** 'eldoc-message' only accepts one argument now. Programs that +called it with multiple arguments before should pass them through +'format' first. Even that is discouraged: for ElDoc support, you +should set 'eldoc-documentation-function' instead of calling +'eldoc-message' directly. * Lisp Changes in Emacs 26.1 commit 5a41dd0a1f317b36f86fb4e52db945385250c56e Author: Dmitry Gutov Date: Tue Sep 26 01:44:54 2017 +0300 Reset default-directory inside *xref-grep* buffer * lisp/progmodes/xref.el (xref-collect-matches): Reset default-directory, too. (Bug#28575) diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 623c9c4e07..80cdcb3f18 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -928,12 +928,14 @@ IGNORES is a list of glob patterns." files (expand-file-name dir) ignores)) + (def default-directory) (buf (get-buffer-create " *xref-grep*")) (`(,grep-re ,file-group ,line-group . ,_) (car grep-regexp-alist)) (status nil) (hits nil)) (with-current-buffer buf (erase-buffer) + (setq default-directory def) (setq status (call-process-shell-command command nil t)) (goto-char (point-min)) commit 2d6bd23bef907628d1bd9110c3f689f45bf441a6 Author: Paul Eggert Date: Mon Sep 25 11:21:50 2017 -0700 * etc/NEWS.26: Copy from emacs-26/etc/NEWS. diff --git a/etc/NEWS.26 b/etc/NEWS.26 index a042ce92af..040d265f75 100644 --- a/etc/NEWS.26 +++ b/etc/NEWS.26 @@ -117,6 +117,11 @@ The effect is similar to that of "toolBar" resource on the tool bar. * Changes in Emacs 26.1 ++++ +** Option 'buffer-offer-save' can be set to new value, 'always'. When + set to 'always', the command `save-some-buffers' will always offer + this buffer for saving. + ** Security vulnerability related to Enriched Text mode is removed. +++ @@ -144,8 +149,7 @@ init file: 'save-buffer' process. Previously, saving a buffer that was not visiting a file would always prompt for a file name. Now it only does so if 'write-contents-functions' is nil (or all its functions return -nil). A non-nil buffer-local value for this variable is sufficient -for 'save-some-buffers' to consider the buffer for saving. +nil). --- ** New variable 'executable-prefix-env' for inserting magic signatures. @@ -367,7 +371,7 @@ see the node "Connection Local Variables" in the ELisp manual. puny.el library, so that one can visit Web sites with non-ASCII URLs. +++ -** The new 'timer-list' command lists all active timers in a buffer, +** The new 'list-timers' command lists all active timers in a buffer, where you can cancel them with the 'c' command. +++ @@ -578,7 +582,6 @@ Negative prefix arg flips the direction of selection. Also, defun are selected unless they are separated from the defun by a blank line. ---- ** New command 'replace-buffer-contents'. This command replaces the contents of the accessible portion of the current buffer with the contents of the accessible portion of a @@ -701,6 +704,12 @@ method is an NNTP select method. *** A new command for sorting articles by readedness marks has been added: 'C-c C-s C-m C-m'. ++++ + +*** In message-citation-line-format the %Z format is now the time zone name +instead of the numeric form. The %z format continues to be the +numeric form. The new behavior is compatible with format-time-string. + ** Ibuffer --- @@ -1813,6 +1822,13 @@ can be replicated simply by setting 'comment-auto-fill-only-comments'. ** New pcase pattern 'rx' to match against a rx-style regular expression. For details, see the doc string of 'rx--pcase-macroexpander'. +--- +** New functions to set region from secondary selection and vice versa. +The new functions 'secondary-selection-to-region' and +'secondary-selection-from-region' let you set the beginning and the +end of the region from those of the secondary selection and vise +versa. + * Changes in Emacs 26.1 on Non-Free Operating Systems @@ -1876,6 +1892,12 @@ of frame decorations on macOS 10.9+. --- ** 'process-attributes' on Darwin systems now returns more information. +--- +** Mousewheel and trackpad scrolling on macOS 10.7+ now behaves more +like the macOS default. The new variables 'ns-mwheel-line-height', +'ns-use-mwheel-acceleration' and 'ns-use-mwheel-momentum' can be used +to customize the behavior. + ---------------------------------------------------------------------- This file is part of GNU Emacs. commit abcb2e62dae6aa26308f7ac9efc89247f89cbe65 Merge: 0bd61c212f 49cd561dc6 Author: Paul Eggert Date: Mon Sep 25 11:19:07 2017 -0700 Merge from origin/emacs-26 49cd561dc6 * test/lisp/tramp-tests.el (tramp-test21-file-links): Spec... b719f6b20b Loosen strict parsing requirement for desktop files c7a0c13777 * lisp/xdg.el (xdg-thumb-uri): Fix doc string. dc6b3560e5 Fix documentation of `make-frame' and related variables an... 3d3778d82a Accept new `always' value for option `buffer-offer-save' 638f64c40a Improve new NS scrolling variable names d93301242f Document 'replace-buffer-contents' in the manual. 00e4e3e9d2 Fix undecorated frame resizing issues on NS (bug#28512) 820739bbb5 ; * doc/emacs/display.texi (Display Custom): Fix wording. f2b2201594 ; Spelling and URL fixes 0e143b1fc5 Documentation improvements for 'display-line-numbers' f656ccdb43 ; Fix typo d64da52d57 Fix last change in bat-mode.el 908af46abd Fix restoring in GUI sessions desktop saved in TTY sessions 51cbd85454 Improve syntax highlighting in bat-mode 0273916618 Document the 'list-FOO' convention d24ec58540 Expose viewing conditions in CAM02-UCS metric a81d5a3d3f Revert "Set frame size to actual requested size (bug#18215)" 0bf066d4b2 Add tests for Edebug 68baca3ee1 Catch more messages in ert-with-message-capture 28e0c410c9 ; * lisp/mouse.el (secondary-selection-exist-p): Doc fix. 31e1d9ef2f Support setting region from secondary selection and vice v... 047f02f00f Fix new copy-directory bug with empty dirs fbd15836af * doc/lispref/strings.texi (Formatting Strings): Improve i... f16a8d5dbd Fix 2 testsuite tests for MS-Windows 965cffd89c Rename timer-list to list-timers a5fec62b51 Provide native touchpad scrolling on macOS 7b3d1c6beb Fix MinGW64 build broken by recent MinGW64 import libraries c83d0c5fdf Fix crashes in 'move-point-visually' in minibuffer windows 7f3d5f929d * src/emacs.c (usage_message): Don't mention 'find-file'. 6845282200 Fix a minor inaccuracy in the Emacs manual 74d7bb9498 Fix errors in flyspell-post-command-hook 40fdbb01d0 Work on Tramp's file-truename 1a01423b3c Fix bug with make-directory on MS-Windows root 066efb8666 Fix log-view-diff-common when point is after last entry 3f006b56cd Adapt fileio-tests--symlink-failure to Cygwin ee512e9a82 Ignore buffers whose name begins with a space in save-some... 9e1b5bd92c Improve tramp-interrupt-process robustness 8d4223e61b Minor Tramp doc update 331d0e520f Fix gensym 466df76f7d Cleanup in files-tests.el 6359fe630a Remove old cl-assert calls in 'newline' 059184e645 Avoid crash with C-g C-g in GC 541006c536 Fix format-time-string %Z bug with negative tz 679e05eeb9 message-citation-line-format %Z is now tz name 4e8888d438 Use doc-view or pdf-tools on any window-system 5f28f0db73 Fix bug with min and max and NaNs 37b5e661d2 Fix recently-introduced copy-directory bug 6bbbc38b34 Merge from Gnulib 57249fb297 Fix compatibility problem in Tramp 411bec82c4 Avoid GCC 7 compilation warning in eval.c 34a6774daa ; Partially revert c3445aed5194 commit 0bd61c212fe53fb843a10da9a2da88e110d3785a Merge: 1bb8ac0c02 3003ac0469 Author: Paul Eggert Date: Mon Sep 25 11:16:05 2017 -0700 ; Merge from origin/emacs-26 The following commit was skipped: 3003ac0469 Adapt Tramp version. Do not merge commit 1bb8ac0c02d91c64ae14e37680fc90ff899da2b4 Merge: 00a86a558e 48d39c39e8 Author: Paul Eggert Date: Mon Sep 25 11:16:05 2017 -0700 Merge from origin/emacs-26 48d39c39e8 Search for Syntax section when viewing MDN 9d101376b4 Allow smerge-keep-current to work for empty hunks 13aba24add Call vc-setup-buffer in vc-git-log-{in,out}going 1d599df5e0 Fix last change to textmodes/page-ext.el a726e09a9a * test/src/lcms-tests.el (lcms-cri-cam02-ucs): Skip if lcm... commit 00a86a558ed3cde443b41c0e24934f61dea99236 Merge: 0c06b93c1e 546413e1ac Author: Paul Eggert Date: Mon Sep 25 11:16:05 2017 -0700 ; Merge from origin/emacs-26 The following commit was skipped: 546413e1ac * test/src/lcms-tests.el (lcms-whitepoint): Skip if lcms2 ... commit 0c06b93c1e467debd401eb0b3be4652fde14fa95 Merge: c4e6ff097c 96aaeaaffa Author: Paul Eggert Date: Mon Sep 25 11:16:05 2017 -0700 Merge from origin/emacs-26 96aaeaaffa ; * src/lcms.c: Minor stylistic changes in comments. c3df816585 Fix compilation warning in etags.c 5490ccc5eb Add lisp variable lcms-d65-xyz dee96f4a17 * lisp/emacs-lisp/cl-macs.el (cl-letf): Fix Edebug spec (b... 12e864eb30 Avoid MinGW64 compiler warnings in unexw32.c 625cee5316 Start emacs-26 release branch # Conflicts: # README # configure.ac # msdos/sed2v2.inp # nt/README.W32 commit c4e6ff097c946b46e3a659982c61e25093bde1a7 Author: Paul Eggert Date: Mon Sep 25 08:56:07 2017 -0700 emacs-25 → emacs-26 * admin/gitmerge.el (gitmerge-default-branch): Now emacs-26. diff --git a/admin/gitmerge.el b/admin/gitmerge.el index 0dfd190d75..fa8c9c0f3d 100644 --- a/admin/gitmerge.el +++ b/admin/gitmerge.el @@ -67,7 +67,7 @@ re-?generate\\|bump version\\|from trunk\\|Auto-commit" '((t (:strike-through t))) "Face for skipped commits.") -(defconst gitmerge-default-branch "origin/emacs-25" +(defconst gitmerge-default-branch "origin/emacs-26" "Default for branch that should be merged.") (defconst gitmerge-buffer "*gitmerge*" diff --git a/admin/make-tarball.txt b/admin/make-tarball.txt index 5822f666db..690125ad57 100644 --- a/admin/make-tarball.txt +++ b/admin/make-tarball.txt @@ -5,7 +5,7 @@ Instructions to create pretest or release tarballs. -*- coding: utf-8 -*- Steps to take before starting on the first pretest in any release sequence: -0. The release branch (e.g. emacs-25) should already have been made +0. The release branch (e.g. emacs-26) should already have been made and you should use it for all that follows. Diffs from this branch should be going to the emacs-diffs mailing list. diff --git a/admin/notes/git-workflow b/admin/notes/git-workflow index 2e4bbac70f..cb10638af8 100644 --- a/admin/notes/git-workflow +++ b/admin/notes/git-workflow @@ -19,15 +19,15 @@ Initial setup ============= Then we want to clone the repository. We normally want to have both -the current master and the emacs-25 branch. +the current master and the emacs-26 branch. mkdir ~/emacs cd ~/emacs git clone @git.sv.gnu.org:/srv/git/emacs.git master (cd master; git config push.default current) -./master/admin/git-new-workdir master emacs-25 -cd emacs-25 -git checkout emacs-25 +./master/admin/git-new-workdir master emacs-26 +cd emacs-26 +git checkout emacs-26 You now have both branches conveniently accessible, and you can do "git pull" in them once in a while to keep updated. @@ -57,11 +57,11 @@ you commit your change locally and then send a patch file as a bug report as described in ../../CONTRIBUTE. -Backporting to emacs-25 +Backporting to emacs-26 ======================= If you have applied a fix to the master, but then decide that it should -be applied to the emacs-25 branch, too, then +be applied to the emacs-26 branch, too, then cd ~/emacs/master git log @@ -71,7 +71,7 @@ which will look like commit 958b768a6534ae6e77a8547a56fc31b46b63710b -cd ~/emacs/emacs-25 +cd ~/emacs/emacs-26 git cherry-pick -xe 958b768a6534ae6e77a8547a56fc31b46b63710b and add "Backport:" to the commit string. Then @@ -79,17 +79,17 @@ and add "Backport:" to the commit string. Then git push -Merging emacs-25 to the master +Merging emacs-26 to the master ============================== It is recommended to use the file gitmerge.el in the admin directory -for merging 'emacs-25' into 'master'. It will take care of many +for merging 'emacs-26' into 'master'. It will take care of many things which would otherwise have to be done manually, like ignoring commits that should not land in master, fixing up ChangeLogs and automatically dealing with certain types of conflicts. If you really want to, you can do the merge manually, but then you're on your own. If you still choose to do that, make absolutely sure that you *always* -use the 'merge' command to transport commits from 'emacs-25' to +use the 'merge' command to transport commits from 'emacs-26' to 'master'. *Never* use 'cherry-pick'! If you don't know why, then you shouldn't manually do the merge in the first place; just use gitmerge.el instead. @@ -102,11 +102,11 @@ up-to-date by doing a pull. Then start Emacs with emacs -l admin/gitmerge.el -f gitmerge You'll be asked for the branch to merge, which will default to -'origin/emacs-25', which you should accept. Merging a local tracking +'origin/emacs-26', which you should accept. Merging a local tracking branch is discouraged, since it might not be up-to-date, or worse, contain commits from you which are not yet pushed upstream. -You will now see the list of commits from 'emacs-25' which are not yet +You will now see the list of commits from 'emacs-26' which are not yet merged to 'master'. You might also see commits that are already marked for "skipping", which means that they will be merged with a different merge strategy ('ours'), which will effectively ignore the diff --git a/nt/INSTALL.W64 b/nt/INSTALL.W64 index cb13473573..f603883e83 100644 --- a/nt/INSTALL.W64 +++ b/nt/INSTALL.W64 @@ -112,11 +112,11 @@ C:\emacs\emacs-24.5: ** From the Git repository To download the Git repository, do something like the following -- this will -put the Emacs source into C:\emacs\emacs-25: +put the Emacs source into C:\emacs\emacs-26: mkdir /c/emacs cd /c/emacs - git clone git://git.sv.gnu.org/emacs.git emacs-25 + git clone git://git.sv.gnu.org/emacs.git emacs-26 (We recommend using the command shown on Savannah Emacs project page.) @@ -129,7 +129,7 @@ First we need to switch to the MinGW-w64 environment. Exit the MSYS2 BASH console and run mingw64_shell.bat in the C:\msys64 folder, then cd back to your Emacs source directory, e.g.: - cd /c/emacs/emacs-25 + cd /c/emacs/emacs-26 ** Run autogen @@ -146,7 +146,7 @@ that the example given here is just a simple one - for more information on the options available please see the INSTALL file in this directory. The '--prefix' option specifies a location for the resulting binary files, -which 'make install' will use - in this example we set it to C:\emacs\emacs-25. +which 'make install' will use - in this example we set it to C:\emacs\emacs-26. If a prefix is not specified the files will be put in the standard Unix directories located in your C:\msys64 directory, but this is not recommended. @@ -154,7 +154,7 @@ Note also that we need to disable Imagemagick because Emacs does not yet support it on Windows. PKG_CONFIG_PATH=/mingw64/lib/pkgconfig \ - ./configure --prefix=/c/emacs/emacs-25 --without-imagemagick + ./configure --prefix=/c/emacs/emacs-26 --without-imagemagick ** Run make commit 49cd561dc62ea6b3fbedab7aef0f020733f4cf09 Author: Michael Albinus Date: Mon Sep 25 17:52:24 2017 +0200 * test/lisp/tramp-tests.el (tramp-test21-file-links): Special code for smb. diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index ee6baaab12..35aa811094 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -535,7 +535,7 @@ pass to the OPERATION." ;; Reset the transfer process properties. (tramp-set-connection-property v "process-name" nil) (tramp-set-connection-property v "process-buffer" nil) - (when t1 (delete-directory tmpdir 'recurse)))) + (when t1 (delete-directory tmpdir 'recursive)))) ;; Handle KEEP-DATE argument. (when keep-date @@ -1583,6 +1583,10 @@ If VEC has no cifs capabilities, exchange \"/\" by \"\\\\\"." "Read entries which match DIRECTORY. Either the shares are listed, or the `dir' command is executed. Result is a list of (LOCALNAME MODE SIZE MONTH DAY TIME YEAR)." + ;; If CIFS capabilities are enabled, symlinks are not listed + ;; by `dir'. This is a consequence of + ;; . See also + ;; . (with-parsed-tramp-file-name (file-name-as-directory directory) nil (setq localname (or localname "/")) (with-tramp-file-property v localname "file-entries" diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 88e97092ed..bfdc301780 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -2653,8 +2653,9 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (tmp-name1 (tramp--test-make-temp-name nil quoted)) (tmp-name2 (tramp--test-make-temp-name nil quoted)) (tmp-name3 (tramp--test-make-temp-name 'local quoted)) - (tmp-name4 (tramp--test-make-temp-name nil quoted))) - + (tmp-name4 (tramp--test-make-temp-name nil quoted)) + (tmp-name5 + (expand-file-name (file-name-nondirectory tmp-name1) tmp-name4))) ;; Check `make-symbolic-link'. (unwind-protect (tramp--test-ignore-make-symbolic-link-error @@ -2716,9 +2717,11 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (funcall (if quoted 'tramp-compat-file-name-unquote 'identity) (file-remote-p tmp-name1 'localname)) - (file-symlink-p - (expand-file-name - (file-name-nondirectory tmp-name1) tmp-name4))))) + (file-symlink-p tmp-name5))) + ;; `smbclient' does not show symlinks in directories, so + ;; we cannot delete a non-empty directory. We delete the + ;; file explicitely. + (delete-file tmp-name5)) ;; Cleanup. (ignore-errors @@ -2737,7 +2740,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (should-error (add-name-to-file tmp-name1 tmp-name2) :type 'file-already-exists) - ;; number means interactive case. + ;; A number means interactive case. (cl-letf (((symbol-function 'yes-or-no-p) 'ignore)) (should-error (add-name-to-file tmp-name1 tmp-name2 0) commit b719f6b20ba00c86d860be113d8a842bc384f2df Author: Mark Oteiza Date: Mon Sep 25 08:45:08 2017 -0400 Loosen strict parsing requirement for desktop files There are other desktop-looking files, for instance those having to do with MIME typess, that would benefit from being able to be read by this function. It helps to have some flexibility. * lisp/xdg.el (xdg-desktop-read-file): Remove an error condition. * test/lisp/xdg-tests.el: Remove a test. diff --git a/lisp/xdg.el b/lisp/xdg.el index e962cd21a6..76106f4258 100644 --- a/lisp/xdg.el +++ b/lisp/xdg.el @@ -197,8 +197,6 @@ Optional argument GROUP defaults to the string \"Desktop Entry\"." (unless (looking-at xdg-desktop-group-regexp) (error "Expected group name! Instead saw: %s" (buffer-substring (point) (point-at-eol)))) - (unless (equal (match-string 1) "Desktop Entry") - (error "Wrong first group: %s" (match-string 1))) (when group (while (and (re-search-forward xdg-desktop-group-regexp nil t) (not (equal (match-string 1) group))))) diff --git a/test/lisp/xdg-tests.el b/test/lisp/xdg-tests.el index e3c9a743e4..b80f5e8552 100644 --- a/test/lisp/xdg-tests.el +++ b/test/lisp/xdg-tests.el @@ -40,9 +40,6 @@ (should (equal (gethash "Name" tab1) "Test")) (should (eq 'default (gethash "Exec" tab1 'default))) (should (equal "frobnicate" (gethash "Exec" tab2)))) - (should-error - (xdg-desktop-read-file - (expand-file-name "wrong.desktop" xdg-tests-data-dir))) (should-error (xdg-desktop-read-file (expand-file-name "malformed.desktop" xdg-tests-data-dir))) commit c7a0c137770be2ff5378a6c545fdea2d26e010f0 Author: Mark Oteiza Date: Mon Sep 25 08:44:23 2017 -0400 * lisp/xdg.el (xdg-thumb-uri): Fix doc string. diff --git a/lisp/xdg.el b/lisp/xdg.el index e94fa8ec92..e962cd21a6 100644 --- a/lisp/xdg.el +++ b/lisp/xdg.el @@ -84,7 +84,7 @@ (defun xdg-thumb-uri (filename) "Return the canonical URI for FILENAME. -If FILENAME has absolute path /foo/bar.jpg, its canonical URI is +If FILENAME has absolute file name /foo/bar.jpg, its canonical URI is file:///foo/bar.jpg" (concat "file://" (expand-file-name filename))) commit dc6b3560e56c83b3e3191a3d95d31fe288181742 Author: Martin Rudalics Date: Mon Sep 25 10:09:32 2017 +0200 Fix documentation of `make-frame' and related variables and hooks * lisp/frame.el (before-make-frame-hook) (after-make-frame-functions, frame-inherited-parameters) (make-frame): Fix doc-strings. * doc/lispref/frames.texi (Creating Frames): Fix description of `make-frame' and related variables and hooks. diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 6431bbdedb..f66ecee8e8 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -112,37 +112,39 @@ window of another Emacs frame. @xref{Child Frames}. * Display Feature Testing:: Determining the features of a terminal. @end menu + @node Creating Frames @section Creating Frames @cindex frame creation To create a new frame, call the function @code{make-frame}. -@deffn Command make-frame &optional alist +@deffn Command make-frame &optional parameters This function creates and returns a new frame, displaying the current buffer. -The @var{alist} argument is an alist that specifies frame parameters -for the new frame. @xref{Frame Parameters}. If you specify the -@code{terminal} parameter in @var{alist}, the new frame is created on -that terminal. Otherwise, if you specify the @code{window-system} -frame parameter in @var{alist}, that determines whether the frame -should be displayed on a text terminal or a graphical terminal. -@xref{Window Systems}. If neither is specified, the new frame is -created in the same terminal as the selected frame. - -Any parameters not mentioned in @var{alist} default to the values in -the alist @code{default-frame-alist} (@pxref{Initial Parameters}); +The @var{parameters} argument is an alist that specifies frame +parameters for the new frame. @xref{Frame Parameters}. If you specify +the @code{terminal} parameter in @var{parameters}, the new frame is +created on that terminal. Otherwise, if you specify the +@code{window-system} frame parameter in @var{parameters}, that +determines whether the frame should be displayed on a text terminal or a +graphical terminal. @xref{Window Systems}. If neither is specified, +the new frame is created in the same terminal as the selected frame. + +Any parameters not mentioned in @var{parameters} default to the values +in the alist @code{default-frame-alist} (@pxref{Initial Parameters}); parameters not specified there default from the X resources or its equivalent on your operating system (@pxref{X Resources,, X Resources, -emacs, The GNU Emacs Manual}). After the frame is created, Emacs -applies any parameters listed in @code{frame-inherited-parameters} -(see below) and not present in the argument, taking the values from -the frame that was selected when @code{make-frame} was called. +emacs, The GNU Emacs Manual}). After the frame is created, this +function applies any parameters specified in +@code{frame-inherited-parameters} (see below) it has no assigned yet, +taking the values from the frame that was selected when +@code{make-frame} was called. Note that on multi-monitor displays (@pxref{Multiple Terminals}), the window manager might position the frame differently than specified by -the positional parameters in @var{alist} (@pxref{Position +the positional parameters in @var{parameters} (@pxref{Position Parameters}). For example, some window managers have a policy of displaying the frame on the monitor that contains the largest part of the window (a.k.a.@: the @dfn{dominating} monitor). @@ -158,20 +160,28 @@ A normal hook run by @code{make-frame} before it creates the frame. @end defvar @defvar after-make-frame-functions -An abnormal hook run by @code{make-frame} after it creates the frame. -Each function in @code{after-make-frame-functions} receives one argument, the -frame just created. +An abnormal hook run by @code{make-frame} after it created the frame. +Each function in @code{after-make-frame-functions} receives one +argument, the frame just created. @end defvar +Note that any functions added to these hooks by your initial file are +usually not run for the initial frame, since Emacs reads the initial +file only after creating that frame. However, if the initial frame is +specified to use a separate minibuffer frame (@pxref{Minibuffers and +Frames}), the functions will be run for both, the minibuffer-less and +the minibuffer frame. + @defvar frame-inherited-parameters This variable specifies the list of frame parameters that a newly created frame inherits from the currently selected frame. For each -parameter (a symbol) that is an element in the list and is not present -in the argument to @code{make-frame}, the function sets the value of -that parameter in the created frame to its value in the selected -frame. +parameter (a symbol) that is an element in this list and has not been +assigned earlier when processing @code{make-frame}, the function sets +the value of that parameter in the created frame to its value in the +selected frame. @end defvar + @node Multiple Terminals @section Multiple Terminals @cindex multiple terminals diff --git a/lisp/frame.el b/lisp/frame.el index 5f0e97d5b0..76c1842455 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -604,11 +604,12 @@ new frame." (select-frame (make-frame)))) (defvar before-make-frame-hook nil - "Functions to run before a frame is created.") + "Functions to run before `make-frame' creates a new frame.") (defvar after-make-frame-functions nil - "Functions to run after a frame is created. -The functions are run with one arg, the newly created frame.") + "Functions to run after `make-frame' created a new frame. +The functions are run with one argument, the newly created +frame.") (defvar after-setting-font-hook nil "Functions to run after a frame's font has been changed.") @@ -617,7 +618,7 @@ The functions are run with one arg, the newly created frame.") (define-obsolete-function-alias 'new-frame 'make-frame "22.1") (defvar frame-inherited-parameters '() - "Parameters `make-frame' copies from the `selected-frame' to the new frame.") + "Parameters `make-frame' copies from the selected to the new frame.") (defvar x-display-name) @@ -632,9 +633,6 @@ form (NAME . VALUE), for example: (width . NUMBER) The frame should be NUMBER characters in width. (height . NUMBER) The frame should be NUMBER text lines high. -You cannot specify either `width' or `height', you must specify -neither or both. - (minibuffer . t) The frame should have a minibuffer. (minibuffer . nil) The frame should have no minibuffer. (minibuffer . only) The frame should contain only a minibuffer. @@ -650,10 +648,10 @@ neither or both. In addition, any parameter specified in `default-frame-alist', but not present in PARAMETERS, is applied. -Before creating the frame (via `frame-creation-function-alist'), -this function runs the hook `before-make-frame-hook'. After -creating the frame, it runs the hook `after-make-frame-functions' -with one arg, the newly created frame. +Before creating the frame (via `frame-creation-function'), this +function runs the hook `before-make-frame-hook'. After creating +the frame, it runs the hook `after-make-frame-functions' with one +argument, the newly created frame. If a display parameter is supplied and a window-system is not, guess the window-system from the display. commit 3d3778d82a87139ef50a24146f5bad2a57a82094 Author: Eric Abrahamsen Date: Sun Sep 24 14:01:21 2017 -0700 Accept new `always' value for option `buffer-offer-save' Also revert ee512e9a82 * lisp/files.el (buffer-offer-save): In addition to nil and t, now allows a third symbol value, `always'. A buffer where this option is set to `always' will always be offered for save by `save-some-buffers'. (save-some-buffers): Check the exact value of this buffer-local variable. No longer check the buffer name, or the value of `write-contents-functions'. * doc/lispref/buffers.texi (Killing Buffers): Note change in manual. * doc/lispref/files.texi (Saving Buffers): Remove note about buffer names. * etc/NEWS: Mention in NEWS. diff --git a/doc/lispref/buffers.texi b/doc/lispref/buffers.texi index cf24a730ba..0d02cb3d3e 100644 --- a/doc/lispref/buffers.texi +++ b/doc/lispref/buffers.texi @@ -1089,12 +1089,15 @@ is not cleared by changing major modes. @defopt buffer-offer-save This variable, if non-@code{nil} in a particular buffer, tells -@code{save-buffers-kill-emacs} and @code{save-some-buffers} (if the -second optional argument to that function is @code{t}) to offer to -save that buffer, just as they offer to save file-visiting buffers. -@xref{Definition of save-some-buffers}. The variable -@code{buffer-offer-save} automatically becomes buffer-local when set -for any reason. @xref{Buffer-Local Variables}. +@code{save-buffers-kill-emacs} to offer to save that buffer, just as +it offers to save file-visiting buffers. If @code{save-some-buffers} +is called with the second optional argument set to @code{t}, it will +also offer to save the buffer. Lastly, if this variable is set to the +symbol @code{always}, both @code{save-buffers-kill-emacs} and +@code{save-some-buffers} will always offer to save. @xref{Definition +of save-some-buffers}. The variable @code{buffer-offer-save} +automatically becomes buffer-local when set for any reason. +@xref{Buffer-Local Variables}. @end defopt @defvar buffer-save-without-query diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index b1b858a6b4..f49b02de97 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -368,8 +368,7 @@ With an argument of 0, unconditionally do @emph{not} make any backup file. This command saves some modified file-visiting buffers. Normally it asks the user about each buffer. But if @var{save-silently-p} is non-@code{nil}, it saves all the file-visiting buffers without -querying the user. Additionally, buffers whose name begins with a -space (``internal'' buffers) will not be offered for save. +querying the user. @vindex save-some-buffers-default-predicate The optional @var{pred} argument provides a predicate that controls diff --git a/etc/NEWS b/etc/NEWS index 19a68933c0..040d265f75 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -117,6 +117,11 @@ The effect is similar to that of "toolBar" resource on the tool bar. * Changes in Emacs 26.1 ++++ +** Option 'buffer-offer-save' can be set to new value, 'always'. When + set to 'always', the command `save-some-buffers' will always offer + this buffer for saving. + ** Security vulnerability related to Enriched Text mode is removed. +++ @@ -144,8 +149,7 @@ init file: 'save-buffer' process. Previously, saving a buffer that was not visiting a file would always prompt for a file name. Now it only does so if 'write-contents-functions' is nil (or all its functions return -nil). A non-nil buffer-local value for this variable is sufficient -for 'save-some-buffers' to consider the buffer for saving. +nil). --- ** New variable 'executable-prefix-env' for inserting magic signatures. diff --git a/lisp/files.el b/lisp/files.el index f0a1f2380d..211457ac7d 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -150,8 +150,13 @@ Called with an absolute file name as argument, it returns t to enable backup.") (defcustom buffer-offer-save nil "Non-nil in a buffer means always offer to save buffer on exit. Do so even if the buffer is not visiting a file. -Automatically local in all buffers." - :type 'boolean +Automatically local in all buffers. + +Set to the symbol `always' to offer to save buffer whenever +`save-some-buffers' is called." + :type '(choice (const :tag "Never" nil) + (const :tag "On Emacs exit" t) + (const :tag "Whenever save-some-buffers is called" always)) :group 'backup) (make-variable-buffer-local 'buffer-offer-save) (put 'buffer-offer-save 'permanent-local t) @@ -5188,15 +5193,11 @@ change the additional actions you can take on files." (and (buffer-live-p buffer) (buffer-modified-p buffer) (not (buffer-base-buffer buffer)) - (not (eq (aref (buffer-name buffer) 0) ?\s)) (or (buffer-file-name buffer) - (and pred - (progn - (set-buffer buffer) - (and buffer-offer-save (> (buffer-size) 0)))) - (buffer-local-value - 'write-contents-functions buffer)) + (with-current-buffer buffer + (or (eq buffer-offer-save 'always) + (and pred buffer-offer-save (> (buffer-size) 0))))) (or (not (functionp pred)) (with-current-buffer buffer (funcall pred))) (if arg commit 638f64c40a678c26d78a7d7279e6356e6e92f3fd Author: Alan Third Date: Sun Sep 24 22:35:21 2017 +0100 Improve new NS scrolling variable names * src/nsterm.m (ns-use-system-mwheel-acceleration): Replace with 'ns-use-mwheel-acceleration'. (ns-touchpad-scroll-line-height): Replace with 'ns-mwheel-line-height'. (ns-touchpad-use-momentum): Replace with 'ns-use-mwheel-momentum'. * etc/NEWS: Change variable names. diff --git a/etc/NEWS b/etc/NEWS index fc4531f0e8..19a68933c0 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1890,9 +1890,9 @@ of frame decorations on macOS 10.9+. --- ** Mousewheel and trackpad scrolling on macOS 10.7+ now behaves more -like the macOS default. The new variables -'ns-use-system-mwheel-acceleration', 'ns-touchpad-scroll-line-height' -and 'ns-touchpad-use-momentum' can be used to customize the behavior. +like the macOS default. The new variables 'ns-mwheel-line-height', +'ns-use-mwheel-acceleration' and 'ns-use-mwheel-momentum' can be used +to customize the behavior. ---------------------------------------------------------------------- diff --git a/src/nsterm.m b/src/nsterm.m index fb3ebc963e..f0b6a70dae 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -6520,7 +6520,7 @@ - (void)mouseDown: (NSEvent *)theEvent /* FIXME: At the top or bottom of the buffer we should * ignore momentum-phase events. */ - if (! ns_touchpad_use_momentum + if (! ns_use_mwheel_momentum && [theEvent momentumPhase] != NSEventPhaseNone) return; @@ -6529,8 +6529,8 @@ - (void)mouseDown: (NSEvent *)theEvent static int totalDeltaX, totalDeltaY; int lineHeight; - if (NUMBERP (ns_touchpad_scroll_line_height)) - lineHeight = XINT (ns_touchpad_scroll_line_height); + if (NUMBERP (ns_mwheel_line_height)) + lineHeight = XINT (ns_mwheel_line_height); else { /* FIXME: Use actual line height instead of the default. */ @@ -6571,7 +6571,7 @@ - (void)mouseDown: (NSEvent *)theEvent totalDeltaX = 0; } - if (lines > 1 && ! ns_use_system_mwheel_acceleration) + if (lines > 1 && ! ns_use_mwheel_acceleration) lines = 1; } else @@ -6589,7 +6589,7 @@ - (void)mouseDown: (NSEvent *)theEvent delta = [theEvent scrollingDeltaY]; } - lines = (ns_use_system_mwheel_acceleration) + lines = (ns_use_mwheel_acceleration) ? ceil (fabs (delta)) : 1; scrollUp = delta > 0; @@ -9284,22 +9284,22 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with This variable is ignored on Mac OS X < 10.7 and GNUstep. */); ns_use_srgb_colorspace = YES; - DEFVAR_BOOL ("ns-use-system-mwheel-acceleration", - ns_use_system_mwheel_acceleration, + DEFVAR_BOOL ("ns-use-mwheel-acceleration", + ns_use_mwheel_acceleration, doc: /*Non-nil means use macOS's standard mouse wheel acceleration. This variable is ignored on macOS < 10.7 and GNUstep. Default is t. */); - ns_use_system_mwheel_acceleration = YES; + ns_use_mwheel_acceleration = YES; - DEFVAR_LISP ("ns-touchpad-scroll-line-height", ns_touchpad_scroll_line_height, - doc: /*The number of pixels touchpad scrolling considers a line. + DEFVAR_LISP ("ns-mwheel-line-height", ns_mwheel_line_height, + doc: /*The number of pixels touchpad scrolling considers one line. Nil or a non-number means use the default frame line height. This variable is ignored on macOS < 10.7 and GNUstep. Default is nil. */); - ns_touchpad_scroll_line_height = Qnil; + ns_mwheel_line_height = Qnil; - DEFVAR_BOOL ("ns-touchpad-use-momentum", ns_touchpad_use_momentum, - doc: /*Non-nil means touchpad scrolling uses momentum. + DEFVAR_BOOL ("ns-use-mwheel-momentum", ns_use_mwheel_momentum, + doc: /*Non-nil means mouse wheel scrolling uses momentum. This variable is ignored on macOS < 10.7 and GNUstep. Default is t. */); - ns_touchpad_use_momentum = YES; + ns_use_mwheel_momentum = YES; /* TODO: move to common code */ DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars, commit d93301242f38d3d9aaa55899c07496f0bdecf391 Author: Philipp Stephani Date: Sun Sep 24 19:32:16 2017 +0200 Document 'replace-buffer-contents' in the manual. * doc/lispref/text.texi (Replacing): New node. diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index a7d10797cd..baa3c708e9 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -54,6 +54,8 @@ the character after point. * Registers:: How registers are implemented. Accessing the text or position stored in a register. * Transposition:: Swapping two portions of a buffer. +* Replacing:: Replacing the text of one buffer with the text + of another buffer. * Decompression:: Dealing with compressed data. * Base 64:: Conversion to or from base 64 encoding. * Checksum/Hash:: Computing cryptographic hashes. @@ -4328,6 +4330,28 @@ is non-@code{nil}, @code{transpose-regions} does not do this---it leaves all markers unrelocated. @end defun +@node Replacing +@section Replacing Buffer Text + + You can use the following function to replace the text of one buffer +with the text of another buffer: + +@deffn Command replace-buffer-contents source +This function replaces the accessible portion of the current buffer +with the accessible portion of the buffer @var{source}. @var{source} +may either be a buffer object or the name of a buffer. When +@code{replace-buffer-contents} succeeds, the text of the accessible +portion of the current buffer will be equal to the text of the +accessible portion of the @var{source} buffer. This function attempts +to keep point, markers, text properties, and overlays in the current +buffer intact. One potential case where this behavior is useful is +external code formatting programs: they typically write the +reformatted text into a temporary buffer or file, and using +@code{delete-region} and @code{insert-buffer-substring} would destroy +these properties. However, the latter combination is typically +faster. @xref{Deletion}, and @ref{Insertion}. +@end deffn + @node Decompression @section Dealing With Compressed Data diff --git a/etc/NEWS b/etc/NEWS index 34561acae5..fc4531f0e8 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -578,7 +578,6 @@ Negative prefix arg flips the direction of selection. Also, defun are selected unless they are separated from the defun by a blank line. ---- ** New command 'replace-buffer-contents'. This command replaces the contents of the accessible portion of the current buffer with the contents of the accessible portion of a commit c7474fab180b57174edb3c949422e466100d605c Author: Mark Oteiza Date: Sun Sep 24 08:17:34 2017 -0400 Expand recognized time intervals for MPC seeking Now accepts [+-]H:M:S and subsets. Also accepts some odd variations of it since the regexp is not strict. One unpleasant caveat is that string-to-number simply returns zero on failure instead of signaling an error. At the moment, there are cases where instead of getting a user-error, the seek may simply not go where one expects it. * lisp/mpc.el (mpc-read-seek): New function. (mpc-seek-current): Use it. diff --git a/lisp/mpc.el b/lisp/mpc.el index c23d8ced71..98f4a03183 100644 --- a/lisp/mpc.el +++ b/lisp/mpc.el @@ -2403,10 +2403,38 @@ This is used so that they can be compared with `eq', which is needed for (interactive) (mpc-cmd-pause "0")) +(defun mpc-read-seek (prompt) + "Read a seek time. +Returns a string suitable for MPD \"seekcur\" protocol command." + (let* ((str (read-from-minibuffer prompt nil nil nil nil nil t)) + (seconds "\\(?1:[[:digit:]]+\\(?:\\.[[:digit:]]*\\)?\\)") + (minsec (concat "\\(?2:[[:digit:]]+\\):" seconds "?")) + (hrminsec (concat "\\(?3:[[:digit:]]+\\):\\(?:" minsec "?\\|:\\)")) + time sign) + (setq str (string-trim str)) + (when (memq (string-to-char str) '(?+ ?-)) + (setq sign (string (string-to-char str))) + (setq str (substring str 1))) + (setq time + ;; `string-to-number' returns 0 on failure + (cond + ((string-match (concat "^" hrminsec "$") str) + (+ (* 3600 (string-to-number (match-string 3 str))) + (* 60 (string-to-number (or (match-string 2 str) ""))) + (string-to-number (or (match-string 1 str) "")))) + ((string-match (concat "^" minsec "$") str) + (+ (* 60 (string-to-number (match-string 2 str))) + (string-to-number (match-string 1 str)))) + ((string-match (concat "^" seconds "$") str) + (string-to-number (match-string 1 str))) + (t (user-error "Invalid time")))) + (setq time (number-to-string time)) + (if (null sign) time (concat sign time)))) + (defun mpc-seek-current (pos) "Seek within current track." (interactive - (list (read-string "Position to go ([+-]seconds): "))) + (list (mpc-read-seek "Position to go ([+-][[H:]M:]seconds): "))) (mpc-cmd-seekcur pos)) (defun mpc-toggle-play () commit 108df97bf7d9d93f9fe976ad1e52388920076eba Author: Philipp Stephani Date: Tue Sep 19 10:48:02 2017 +0200 Add configuration for clang-format. This allows developers to auto-format the C code with clang-format. It’s not 100% accurate, but works pretty well for most of the C code. diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..7895ada36d --- /dev/null +++ b/.clang-format @@ -0,0 +1,27 @@ +Language: Cpp +BasedOnStyle: LLVM +AlignEscapedNewlinesLeft: true +AlwaysBreakAfterReturnType: TopLevelDefinitions +BreakBeforeBinaryOperators: All +BreakBeforeBraces: GNU +ColumnLimit: 80 +ContinuationIndentWidth: 2 +ForEachMacros: [FOR_EACH_TAIL, FOR_EACH_TAIL_SAFE] +IncludeCategories: + - Regex: '^$' + Priority: -1 + - Regex: '^<' + Priority: 1 + - Regex: '^"lisp\.h"$' + Priority: 2 + - Regex: '.*' + Priority: 3 +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +PenaltyBreakBeforeFirstCallParameter: 2000 +SpaceAfterCStyleCast: true +SpaceBeforeParens: Always + +# Local Variables: +# mode: yaml +# End: commit 1eb4e5c3c8e70813c8042a38a2b67be74b16500e Author: Philipp Stephani Date: Tue Sep 19 10:29:42 2017 +0200 Make FILENAME argument of 'file-name-base' mandatory * lisp/files.el (file-name-base): Make FILENAME argument mandatory. * lisp/autoinsert.el (auto-insert-alist): * lisp/progmodes/cperl-mode.el (cperl-electric-pod): * lisp/progmodes/idlwave.el (idlwave-parse-definition): * lisp/textmodes/reftex-ref.el (reftex-replace-prefix-escapes): Fix all callers. diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 6be998f0b2..e4a4bfe598 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -2106,7 +2106,7 @@ Note that the @samp{.~3~} in the two last examples is the backup part, not an extension. @end defun -@defun file-name-base &optional filename +@defun file-name-base filename This function is the composition of @code{file-name-sans-extension} and @code{file-name-nondirectory}. For example, @@ -2114,8 +2114,6 @@ and @code{file-name-nondirectory}. For example, (file-name-base "/my/home/foo.c") @result{} "foo" @end example - -The @var{filename} argument defaults to @code{buffer-file-name}. @end defun @node Relative File Names diff --git a/etc/NEWS b/etc/NEWS index 0e62a2bbb4..aacdf79b57 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -48,6 +48,9 @@ sets the XTerm window title. The default is to set the window title. * Incompatible Lisp Changes in Emacs 27.1 +** The FILENAME argument to 'file-name-base' is now mandatory and no +longer defaults to 'buffer-file-name'. + * Lisp Changes in Emacs 27.1 diff --git a/lisp/autoinsert.el b/lisp/autoinsert.el index 2820c8a9af..a43e068a4d 100644 --- a/lisp/autoinsert.el +++ b/lisp/autoinsert.el @@ -141,14 +141,14 @@ If this contains a %s, that will be replaced by the matching rule." " .\\\" You may distribute this file under the terms of the GNU Free .\\\" Documentation License. -.TH " (file-name-base) +.TH " (file-name-base (buffer-file-name)) " " (file-name-extension (buffer-file-name)) " " (format-time-string "%Y-%m-%d ") "\n.SH NAME\n" - (file-name-base) + (file-name-base (buffer-file-name)) " \\- " str "\n.SH SYNOPSIS -.B " (file-name-base) +.B " (file-name-base (buffer-file-name)) "\n" _ " @@ -211,7 +211,7 @@ If this contains a %s, that will be replaced by the matching rule." \(provide '" - (file-name-base) + (file-name-base (buffer-file-name)) ") \;;; " (file-name-nondirectory (buffer-file-name)) " ends here\n") (("\\.texi\\(nfo\\)?\\'" . "Texinfo file skeleton") @@ -219,7 +219,7 @@ If this contains a %s, that will be replaced by the matching rule." "\\input texinfo @c -*-texinfo-*- @c %**start of header @setfilename " - (file-name-base) ".info\n" + (file-name-base (buffer-file-name)) ".info\n" "@settitle " str " @c %**end of header @copying\n" diff --git a/lisp/files.el b/lisp/files.el index c55c8097c1..fe7cb1a8a9 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -4479,8 +4479,8 @@ extension, the value is \"\"." ""))))) (defun file-name-base (&optional filename) - "Return the base name of the FILENAME: no directory, no extension. -FILENAME defaults to `buffer-file-name'." + "Return the base name of the FILENAME: no directory, no extension." + (declare (advertised-calling-convention (filename) "27.1")) (file-name-sans-extension (file-name-nondirectory (or filename (buffer-file-name))))) diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el index abd77bd973..e956637572 100644 --- a/lisp/progmodes/cperl-mode.el +++ b/lisp/progmodes/cperl-mode.el @@ -2314,7 +2314,7 @@ to nil." nil t)))) ; Only one (progn (forward-word-strictly 1) - (setq name (file-name-base) + (setq name (file-name-base (buffer-file-name)) p (point)) (insert " NAME\n\n" name " - \n\n=head1 SYNOPSIS\n\n\n\n" diff --git a/lisp/progmodes/idlwave.el b/lisp/progmodes/idlwave.el index 9231e11890..92a42b1cb9 100644 --- a/lisp/progmodes/idlwave.el +++ b/lisp/progmodes/idlwave.el @@ -5240,7 +5240,7 @@ Can run from `after-save-hook'." class (cond ((not (boundp 'idlwave-scanning-lib)) (list 'buffer (buffer-file-name))) -; ((string= (downcase (file-name-base)) +; ((string= (downcase (file-name-base (buffer-file-name)) ; (downcase name)) ; (list 'lib)) ; (t (cons 'lib (file-name-nondirectory (buffer-file-name)))) diff --git a/lisp/textmodes/reftex-ref.el b/lisp/textmodes/reftex-ref.el index c2c5ca3de0..f9f23201b4 100644 --- a/lisp/textmodes/reftex-ref.el +++ b/lisp/textmodes/reftex-ref.el @@ -314,7 +314,7 @@ also applies `reftex-translate-to-ascii-function' to the string." (save-match-data (cond ((equal letter "f") - (file-name-base)) + (file-name-base (buffer-file-name))) ((equal letter "F") (let ((masterdir (file-name-directory (reftex-TeX-master-file))) (file (file-name-sans-extension (buffer-file-name)))) commit 535db535a9168ae73740f50b57210eb127c37119 Author: Stefan Monnier Date: Sat Sep 23 17:06:23 2017 -0400 * lisp/newcomment.el (comment-search-backward): Obey the docstring (bug#28428) diff --git a/lisp/newcomment.el b/lisp/newcomment.el index 2a0f8a8ae5..2e644c3a99 100644 --- a/lisp/newcomment.el +++ b/lisp/newcomment.el @@ -524,7 +524,7 @@ Ensure that `comment-normalize-vars' has been called before you use this." ;; comment-search-backward is only used to find the comment-column (in ;; comment-set-column) and to find the comment-start string (via ;; comment-beginning) in indent-new-comment-line, it should be harmless. - (if (not (re-search-backward comment-start-skip limit t)) + (if (not (re-search-backward comment-start-skip limit 'move)) (unless noerror (error "No comment")) (beginning-of-line) (let* ((end (match-end 0)) commit 18073beb14f393b4bbcc92890a89dcfcb75fb7ac Merge: 1eef11b7be 00e4e3e9d2 Author: Eli Zaretskii Date: Sat Sep 23 22:12:49 2017 +0300 Merge branch 'emacs-26' of git.savannah.gnu.org:/srv/git/emacs into emacs-26 commit 00e4e3e9d273a193620c3a4bb4914e555cb8e343 Author: Alan Third Date: Sat Sep 23 19:43:58 2017 +0100 Fix undecorated frame resizing issues on NS (bug#28512) * src/nsterm.m (EmacsView::updateFrameSize): Don't wait for the toolbar on undecorated frames. (EmacsView::initFrameFromEmacs): Group window flags correctly. diff --git a/src/nsterm.m b/src/nsterm.m index a41d6be204..fb3ebc963e 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -6824,9 +6824,10 @@ - (void) updateFrameSize: (BOOL) delay if (wait_for_tool_bar) { - /* The toolbar height is always 0 in fullscreen, so don't wait - for it to become available. */ + /* The toolbar height is always 0 in fullscreen and undecorated + frames, so don't wait for it to become available. */ if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0 + && FRAME_UNDECORATED (emacsframe) == false && ! [self isFullscreen]) { NSTRACE_MSG ("Waiting for toolbar"); @@ -7207,9 +7208,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f win = [[EmacsWindow alloc] initWithContentRect: r - styleMask: (FRAME_UNDECORATED (f) - ? FRAME_UNDECORATED_FLAGS - : FRAME_DECORATED_FLAGS + styleMask: ((FRAME_UNDECORATED (f) + ? FRAME_UNDECORATED_FLAGS + : FRAME_DECORATED_FLAGS) #ifdef NS_IMPL_COCOA | NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable commit 1eef11b7be349767f8b25f27e84e7a6adf0844aa Author: Eli Zaretskii Date: Sat Sep 23 17:23:35 2017 +0300 Fix doc string of 'dired-listing-switches' * lisp/dired.el (dired-listing-switches): Fix the quoting example. (Bug#28569) diff --git a/lisp/dired.el b/lisp/dired.el index 782d8ffa51..9e09d349f7 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -61,7 +61,7 @@ May contain all other options that don't contradict `-l'; may contain even `F', `b', `i' and `s'. See also the variable `dired-ls-F-marks-symlinks' concerning the `F' switch. Options that include embedded whitespace must be quoted -like this: \\\"--option=value with spaces\\\"; you can use +like this: \"--option=value with spaces\"; you can use `combine-and-quote-strings' to produce the correct quoting of each option. On systems such as MS-DOS and MS-Windows, which use `ls' emulation in Lisp, commit c0af83b6ccf2dab9a515dd7f52eb9d4500275ae3 Author: Philipp Stephani Date: Sun Aug 27 12:45:52 2017 +0200 Don't attempt to disable double buffering in newer GTK+ versions * src/gtkutil.c (xg_create_frame_widgets): Stop calling deprecated function gtk_widget_set_double_buffered. diff --git a/src/gtkutil.c b/src/gtkutil.c index b98b0d08e7..8ecbc5c91e 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1233,12 +1233,14 @@ xg_create_frame_widgets (struct frame *f) if (FRAME_EXTERNAL_TOOL_BAR (f)) update_frame_tool_bar (f); +#if ! GTK_CHECK_VERSION (3, 14, 0) /* We don't want this widget double buffered, because we draw on it with regular X drawing primitives, so from a GTK/GDK point of view, the widget is totally blank. When an expose comes, this will make the widget blank, and then Emacs redraws it. This flickers a lot, so we turn off double buffering. */ gtk_widget_set_double_buffered (wfixed, FALSE); +#endif #if ! GTK_CHECK_VERSION (3, 22, 0) gtk_window_set_wmclass (GTK_WINDOW (wtop), commit f6818e761eaafe095e07249180dc8f9a329f1473 Author: Philipp Stephani Date: Sun Aug 27 12:31:37 2017 +0200 GTK+: Stop querying for background colors. * src/gtkutil.c (xg_check_special_colors): Don't call deprecated function gtk_style_context_get_background_color in newer versions of GTK+. diff --git a/src/gtkutil.c b/src/gtkutil.c index f3e89c82c6..b98b0d08e7 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -566,6 +566,14 @@ xg_check_special_colors (struct frame *f, if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg)) return success_p; +#if GTK_CHECK_VERSION (3, 16, 0) + if (get_bg) + /* gtk_style_context_get_background_color is deprecated in + GTK+ 3.16. New versions of GTK+ don't use the concept of a + single background color any more, so we can't query for it. */ + return false; +#endif + block_input (); { #ifdef HAVE_GTK3 @@ -577,7 +585,12 @@ xg_check_special_colors (struct frame *f, if (get_fg) gtk_style_context_get_color (gsty, state, &col); else +#if GTK_CHECK_VERSION (3, 16, 0) + /* We can't get here. */ + emacs_abort (); +#else gtk_style_context_get_background_color (gsty, state, &col); +#endif unsigned short r = col.red * 65535, commit f02e76fb8f8a862e43795056d61df5641c8a669b Author: Philipp Stephani Date: Sun Aug 27 13:08:37 2017 +0200 GTK+: stop calling 'gtk_window_set_wmclass' in new versions * src/gtkutil.c (xg_create_frame_widgets): Stop calling deprecated function 'gtk_window_set_wmclass' in GTK+ 3.22. diff --git a/src/gtkutil.c b/src/gtkutil.c index 03c404f86b..f3e89c82c6 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1227,9 +1227,11 @@ xg_create_frame_widgets (struct frame *f) a lot, so we turn off double buffering. */ gtk_widget_set_double_buffered (wfixed, FALSE); +#if ! GTK_CHECK_VERSION (3, 22, 0) gtk_window_set_wmclass (GTK_WINDOW (wtop), SSDATA (Vx_resource_name), SSDATA (Vx_resource_class)); +#endif /* Add callback to do nothing on WM_DELETE_WINDOW. The default in GTK is to destroy the widget. We want Emacs to do that instead. */ commit 7144e5e9f54ee41abe2e8f91ccd5901c86d38c4e Author: Philipp Stephani Date: Sun Aug 27 12:42:56 2017 +0200 GTK+: Use a style provider instead of deprecated function * src/gtkutil.c (xg_set_widget_bg): Use a CSS style provider instead of the deprecated gtk_widget_override_background_color. diff --git a/src/gtkutil.c b/src/gtkutil.c index 1073bd9384..03c404f86b 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1050,16 +1050,23 @@ static void xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel) { #ifdef HAVE_GTK3 - GdkRGBA bg; XColor xbg; xbg.pixel = pixel; if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg)) { - bg.red = (double)xbg.red/65535.0; - bg.green = (double)xbg.green/65535.0; - bg.blue = (double)xbg.blue/65535.0; - bg.alpha = 1.0; - gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg); + const char format[] = "* { background-color: #%02x%02x%02x; }"; + /* The format is always longer than the resulting string. */ + char buffer[sizeof format]; + int n = snprintf(buffer, sizeof buffer, format, + xbg.red >> 8, xbg.green >> 8, xbg.blue >> 8); + eassert (n > 0); + eassert (n < sizeof buffer); + GtkCssProvider *provider = gtk_css_provider_new (); + gtk_css_provider_load_from_data (provider, buffer, -1, NULL); + gtk_style_context_add_provider (gtk_widget_get_style_context(w), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_clear_object (&provider); } #else GdkColor bg; commit 820739bbb572b30b6ce45756c9960e48dca859af Author: Eli Zaretskii Date: Sat Sep 23 10:45:46 2017 +0300 ; * doc/emacs/display.texi (Display Custom): Fix wording. diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index f074e989bc..6afd8366b2 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -1745,7 +1745,7 @@ invisible parts of text), and lines which wrap to consume more than one screen line will be numbered that many times. The displayed numbers are relative, as with @code{relative} value above. This is handy in modes that fold text, such as Outline mode (@pxref{Outline -Mode}), and need to move by exact number of screen lines. +Mode}), and when you need to move by exact number of screen lines. @item anything else Any other non-@code{nil} value is treated as @code{t}. commit f2b2201594b59ff758347644a84cdc8f6b046ec9 Author: Paul Eggert Date: Sat Sep 23 00:34:01 2017 -0700 ; Spelling and URL fixes diff --git a/ChangeLog.2 b/ChangeLog.2 index bd1800b330..e789722a4d 100644 --- a/ChangeLog.2 +++ b/ChangeLog.2 @@ -4808,7 +4808,7 @@ Link from (emacs)Exiting to (lisp)Killing Emacs * doc/emacs/entering.texi (Exiting): Link to the lispref - manual for further customisations (bug#15445). + manual for further customizations (bug#15445). (cherry picked from commit bc5f27aa099cdde02ca66e71501b89300685ab28) @@ -7845,7 +7845,7 @@ 2016-02-20 Lars Ingebrigtsen - Allow customising the article mode cursor behavior + Allow customizing the article mode cursor behavior * doc/misc/gnus.texi (HTML): Mention gnus-article-show-cursor. diff --git a/ChangeLog.3 b/ChangeLog.3 index 9f43511991..9e622cef90 100644 --- a/ChangeLog.3 +++ b/ChangeLog.3 @@ -12949,7 +12949,7 @@ Link from (emacs)Exiting to (lisp)Killing Emacs * doc/emacs/entering.texi (Exiting): Link to the lispref - manual for further customisations (bug#15445). + manual for further customizations (bug#15445). 2016-04-29 Lars Ingebrigtsen @@ -13159,7 +13159,7 @@ Move the diff command to "Operate" in ibuffer * lisp/ibuffer.el (ibuffer-mode-operate-map): Move the diff - command to the "Operate" menu, and remove the customisation + command to the "Operate" menu, and remove the customization entry to make the "View" menu more logical (bug#1150). 2016-04-27 Lars Ingebrigtsen @@ -16589,7 +16589,7 @@ really changed. (save_window_save): Set the pixel_height_before_size_change and pixel_width_before_size_change fields. - (Vwindow_size_change_functions): Move here definiton from xdisp.c. + (Vwindow_size_change_functions): Move here definition from xdisp.c. * src/xdisp.c (prepare_menu_bars, redisplay_internal): Call run_window_size_change_functions. (Vwindow_size_change_functions): Move definition to window.c. @@ -16842,7 +16842,7 @@ 5d17ae7 Improve file-notify-test08-watched-file-in-watched-dir 1cb1268 Fix todo-mode item date editing bugs 1e996cf Fix "[:upper:]" for non-ASCII characters - 896f993 Allow customising the article mode cursor behavior + 896f993 Allow customizing the article mode cursor behavior 24c1c1d Use pop-to-buffer-same-window in woman.el 2a75f64 New filenotify test for bug#22736 c9bccf7 Report critical battery errors diff --git a/etc/NEWS b/etc/NEWS index 280ab64f37..34561acae5 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1893,7 +1893,7 @@ of frame decorations on macOS 10.9+. ** Mousewheel and trackpad scrolling on macOS 10.7+ now behaves more like the macOS default. The new variables 'ns-use-system-mwheel-acceleration', 'ns-touchpad-scroll-line-height' -and 'ns-touchpad-use-momentum' can be used to customise the behavior. +and 'ns-touchpad-use-momentum' can be used to customize the behavior. ---------------------------------------------------------------------- diff --git a/lisp/calendar/cal-tex.el b/lisp/calendar/cal-tex.el index 72db03e5e6..1d295606f2 100644 --- a/lisp/calendar/cal-tex.el +++ b/lisp/calendar/cal-tex.el @@ -266,7 +266,7 @@ specified in ARGS. When ARGS is omitted, by default the option \"12pt,a4paper\" is passed. When ARGS has any other value, then no option is passed to the class. -Insert the \"\\usepacakge{geometry}\" directive when ARGS +Insert the \"\\usepackage{geometry}\" directive when ARGS contains the \"landscape\" string." (set-buffer (generate-new-buffer cal-tex-buffer)) (save-match-data diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el index 5af5262e5d..71d46c1107 100644 --- a/lisp/emacs-lisp/ert-x.el +++ b/lisp/emacs-lisp/ert-x.el @@ -328,7 +328,7 @@ to the real `message'." (funcall func "%s" msg))))) (defun ert--make-print-advice (collector) - "Create around advice for print functions for `ert-collect-messsges'. + "Create around advice for print functions for `ert-collect-messages'. The created advice function will just call the original function unless the output is going to the echo area (when PRINTCHARFUN is t or PRINTCHARFUN is nil and `standard-output' is t). If the diff --git a/lisp/org/ChangeLog.1 b/lisp/org/ChangeLog.1 index 366a3ee9fc..ee50f6fb04 100644 --- a/lisp/org/ChangeLog.1 +++ b/lisp/org/ChangeLog.1 @@ -5015,10 +5015,10 @@ * ox-latex.el (org-latex-listings): Update docstring. * org-pcomplete.el (pcomplete/org-mode/file-option/options): - Apply changes to export back-end definiton. + Apply changes to export back-end definition. * org.el (org-get-export-keywords): Apply changes to export - back-end definiton. + back-end definition. * ox-html.el (org-html--format-toc-headline): Make use of anonymous back-ends. diff --git a/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el b/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el index 0cc7b1e8b4..f52a2b1896 100644 --- a/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el +++ b/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el @@ -17,7 +17,7 @@ ;; 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/'. +;; along with this program. If not, see . ;;; Commentary: diff --git a/test/lisp/emacs-lisp/edebug-tests.el b/test/lisp/emacs-lisp/edebug-tests.el index 037278e772..02f4d1c5ab 100644 --- a/test/lisp/emacs-lisp/edebug-tests.el +++ b/test/lisp/emacs-lisp/edebug-tests.el @@ -17,7 +17,7 @@ ;; 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/'. +;; along with this program. If not, see . ;;; Commentary: @@ -310,7 +310,7 @@ Then clear edebug-tests' saved messages." (setq edebug-tests-messages "")) (defun edebug-tests-locate-def (def-name) - "Search for a definiton of DEF-NAME from the start of the current buffer. + "Search for a definition of DEF-NAME from the start of the current buffer. Place point at the end of DEF-NAME in the buffer." (goto-char (point-min)) (re-search-forward (concat "def\\S-+ edebug-test-code-" def-name))) @@ -584,7 +584,7 @@ test and possibly others should be updated." (ert-deftest edebug-tests-error-trying-to-set-breakpoint-in-uninstrumented-code () - "Edebug refuses to set a breakpoint in uninsented code." + "Edebug refuses to set a breakpoint in uninstrumented code." (edebug-tests-with-normal-env (edebug-tests-setup-@ "fac" '(5) t) (let* ((debug-on-error nil) diff --git a/test/lisp/vc/smerge-mode-tests.el b/test/lisp/vc/smerge-mode-tests.el index 204a4b93ab..10d090632d 100644 --- a/test/lisp/vc/smerge-mode-tests.el +++ b/test/lisp/vc/smerge-mode-tests.el @@ -15,7 +15,7 @@ ;; 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 . +;; along with GNU Emacs. If not, see . ;;; Code: commit 0e143b1fc5d716cbc4509b416a1067b417df2676 Author: Eli Zaretskii Date: Sat Sep 23 10:07:11 2017 +0300 Documentation improvements for 'display-line-numbers' * doc/emacs/display.texi (Display Custom): Document a few more options for display-line-numbers. (Bug#28533) Fix a typo. diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index 2aa79e1161..f074e989bc 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -1756,7 +1756,7 @@ Any other non-@code{nil} value is treated as @code{t}. @vindex display-line-numbers-type A convenient way of turning on display of line numbers is @w{@kbd{M-x display-line-numbers-mode @key{RET}}}. This mode has a globalized -variant, @code{global-display-line0numbers-mode}. The user option +variant, @code{global-display-line-numbers-mode}. The user option @code{display-line-numbers-type} controls which sub-mode of line-number display, described above, will these modes activate. @@ -1778,6 +1778,17 @@ the variable @code{display-line-numbers-widen} to a non-@code{nil} value, line numbers will disregard any narrowing and will start at the first character of the buffer. +@vindex display-line-numbers-width-start +@vindex display-line-numbers-grow-only +@vindex display-line-numbers-width +In selective display mode (@pxref{Selective Display}), and other modes +that hide many lines from display (such as Outline and Org modes), you +may wish to customize the variables +@code{display-line-numbers-width-start} and +@code{display-line-numbers-grow-only}, or set +@code{display-line-numbers-width} to a large enough value, to avoid +occasional miscalculations of space reserved for the line numbers. + @cindex line-number face The line numbers are displayed in a special face @code{line-number}. The current line number is displayed in a different face, commit f656ccdb4384564001ae181c66f2a242bc31a849 Author: Mark Oteiza Date: Fri Sep 22 16:34:31 2017 -0400 ; Fix typo * lisp/emacs-lisp/subr-x.el: Nix extra parenthesis. diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index 077ad22c75..edba6550fa 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -128,7 +128,7 @@ binding value is nil. If all are non-nil, the value of THEN is returned, or the last form in ELSE is returned. Each element of VARLIST is a list (SYMBOL VALUEFORM) which binds -SYMBOL to the value of VALUEFORM). An element can additionally +SYMBOL to the value of VALUEFORM. An element can additionally be of the form (VALUEFORM), which is evaluated and checked for nil; i.e. SYMBOL can be omitted if only the test result is of interest." commit d64da52d57b068da630ba5eb606cae9421de19e9 Author: Eli Zaretskii Date: Fri Sep 22 20:41:10 2017 +0300 Fix last change in bat-mode.el * lisp/progmodes/bat-mode.el (bat-font-lock-keywords): Fix last change. (Bug#28311) diff --git a/lisp/progmodes/bat-mode.el b/lisp/progmodes/bat-mode.el index e06b8e830b..102c318620 100644 --- a/lisp/progmodes/bat-mode.el +++ b/lisp/progmodes/bat-mode.el @@ -84,9 +84,9 @@ . 'bat-label-face) ("\\_<\\(defined\\|set\\)\\_>[ \t]*\\(\\(\\sw\\|\\s_\\)+\\)" (2 font-lock-variable-name-face)) - ("%\\([^% \n]+\\)%?" + ("%\\([^%~ \n]+\\)%?" (1 font-lock-variable-name-face)) - ("!\\([^!% \n]+\\)!?" ; delayed-expansion !variable! + ("!\\([^!%~ \n]+\\)!?" ; delayed-expansion !variable! (1 font-lock-variable-name-face)) ("%%\\(?:~[adfnpstxz]*\\(?:\\$\\(\\(?:\\sw\\|\\s_\\|_\\)+\\):\\)?\\)?\\([]!#$&-:?-[_-{}~]\\)" (1 font-lock-variable-name-face nil t) ; PATH expansion commit 908af46abdb2c19ff3c72543e4fadf8e0ed82d2b Author: Eli Zaretskii Date: Fri Sep 22 17:52:47 2017 +0300 Fix restoring in GUI sessions desktop saved in TTY sessions * lisp/frameset.el (frameset-filter-font-param): New function. (frameset-persistent-filter-alist): Use it for processing the 'font' frame parameter. (Bug#17352) diff --git a/lisp/frameset.el b/lisp/frameset.el index 661f0aee27..593451a4d7 100644 --- a/lisp/frameset.el +++ b/lisp/frameset.el @@ -447,7 +447,7 @@ DO NOT MODIFY. See `frameset-filter-alist' for a full description.") (buffer-predicate . :never) (buried-buffer-list . :never) (delete-before . :never) - (font . frameset-filter-shelve-param) + (font . frameset-filter-font-param) (foreground-color . frameset-filter-sanitize-color) (fullscreen . frameset-filter-shelve-param) (GUI:font . frameset-filter-unshelve-param) @@ -631,6 +631,17 @@ see `frameset-filter-alist'." (setcdr found val) nil)))) +(defun frameset-filter-font-param (current filtered parameters saving + &optional prefix) + "When switching from a tty frame to a GUI frame, remove the FONT param. + +When switching from a GUI frame to a tty frame, behave +as `frameset-filter-shelve-param' does." + (or saving + (if (frameset-switch-to-gui-p parameters) + (frameset-filter-shelve-param current filtered parameters saving + prefix)))) + (defun frameset-filter-iconified (_current _filtered parameters saving) "Remove CURRENT when saving an iconified frame. This is used for positional parameters `left' and `top', which are commit 51cbd85454f6febb635b806dd759c4d054a43552 Author: Eli Zaretskii Date: Fri Sep 22 16:40:59 2017 +0300 Improve syntax highlighting in bat-mode * lisp/progmodes/bat-mode.el (bat-font-lock-keywords): Improve font-locking of environment variables. Suggested by Achim Gratz . (Bug#28311) (Bug#18405) diff --git a/lisp/progmodes/bat-mode.el b/lisp/progmodes/bat-mode.el index f4852fe5b6..e06b8e830b 100644 --- a/lisp/progmodes/bat-mode.el +++ b/lisp/progmodes/bat-mode.el @@ -84,11 +84,11 @@ . 'bat-label-face) ("\\_<\\(defined\\|set\\)\\_>[ \t]*\\(\\(\\sw\\|\\s_\\)+\\)" (2 font-lock-variable-name-face)) - ("%\\(\\(\\sw\\|\\s_\\)+\\)%" + ("%\\([^% \n]+\\)%?" (1 font-lock-variable-name-face)) - ("!\\(\\(\\sw\\|\\s_\\)+\\)!" ; delayed-expansion !variable! + ("!\\([^!% \n]+\\)!?" ; delayed-expansion !variable! (1 font-lock-variable-name-face)) - ("%%\\(?:~[adfnpstxz]*\\(?:\\$\\(\\(?:\\sw\\|\\s_\\)+\\):\\)?\\)?\\([]!#$&-:?-[_-{}~]\\)" + ("%%\\(?:~[adfnpstxz]*\\(?:\\$\\(\\(?:\\sw\\|\\s_\\|_\\)+\\):\\)?\\)?\\([]!#$&-:?-[_-{}~]\\)" (1 font-lock-variable-name-face nil t) ; PATH expansion (2 font-lock-variable-name-face)) ; iteration variable or positional parameter ("[ =][-/]+\\(\\w+\\)" commit 0273916618f33ffd56b861cea187e9df337b8e2d Author: Eli Zaretskii Date: Fri Sep 22 12:41:00 2017 +0300 Document the 'list-FOO' convention * doc/lispref/tips.texi (Coding Conventions): Document the list-FOO convention. diff --git a/doc/lispref/tips.texi b/doc/lispref/tips.texi index bed3bed95b..17fd4a1027 100644 --- a/doc/lispref/tips.texi +++ b/doc/lispref/tips.texi @@ -68,10 +68,13 @@ costs.}. Use two hyphens to separate prefix and name if the symbol is not meant to be used by other packages. Occasionally, for a command name intended for users to use, it is more -convenient if some words come before the package's name prefix. And -constructs that define functions, variables, etc., work better if they -start with @samp{defun} or @samp{defvar}, so put the name prefix later -on in the name. +convenient if some words come before the package's name prefix. For +example, it is our convention to have commands that list objects named +as @samp{list-@var{something}}, e.g., a package called @samp{frob} +could have a command @samp{list-frobs}, when its other global symbols +begin with @samp{frob-}. Also, constructs that define functions, +variables, etc., work better if they start with @samp{defun} or +@samp{defvar}, so put the name prefix later on in the name. This recommendation applies even to names for traditional Lisp primitives that are not primitives in Emacs Lisp---such as commit d24ec5854098841388dfecf2c668e7f48f348af0 Author: Mark Oteiza Date: Thu Sep 21 22:47:24 2017 -0400 Expose viewing conditions in CAM02-UCS metric Also add tests from the colorspacious library. Finally, catch an errant calculation, where degrees were not being converted to radians. * src/lcms.c (deg2rad, default_viewing_conditions): (parse_viewing_conditions): New functions. (lcms-cam02-ucs): Add comments pointing to references used. Expand the docstring and explain viewing conditions. JCh hue is given in degrees and needs to be converted to radians. (lcms-d65-xyz): Remove. No need to duplicate this in Lisp or make the API needlessly impure. * test/src/lcms-tests.el: Reword commentary. (lcms-rgb255->xyz): New function. (lcms-cri-cam02-ucs): Fix let-binding. (lcms-dE-cam02-ucs-silver): New test, assimilated from colorspacious. diff --git a/src/lcms.c b/src/lcms.c index f543a03039..a5e527911e 100644 --- a/src/lcms.c +++ b/src/lcms.c @@ -139,6 +139,26 @@ chroma, and hue, respectively. The parameters each default to 1. */) return make_float (cmsCIE2000DeltaE (&Lab1, &Lab2, Kl, Kc, Kh)); } +static double +deg2rad (double degrees) +{ + return M_PI * degrees / 180.0; +} + +static cmsCIEXYZ illuminant_d65 = { .X = 95.0455, .Y = 100.0, .Z = 108.8753 }; + +static void +default_viewing_conditions (const cmsCIEXYZ *wp, cmsViewingConditions *vc) +{ + vc->whitePoint.X = wp->X; + vc->whitePoint.Y = wp->Y; + vc->whitePoint.Z = wp->Z; + vc->Yb = 20; + vc->La = 100; + vc->surround = AVG_SURROUND; + vc->D_value = 1.0; +} + /* FIXME: code duplication */ static bool @@ -160,11 +180,62 @@ parse_xyz_list (Lisp_Object xyz_list, cmsCIEXYZ *color) return true; } -DEFUN ("lcms-cam02-ucs", Flcms_cam02_ucs, Slcms_cam02_ucs, 2, 3, 0, +static bool +parse_viewing_conditions (Lisp_Object view, const cmsCIEXYZ *wp, + cmsViewingConditions *vc) +{ +#define PARSE_VIEW_CONDITION_FLOAT(field) \ + if (CONSP (view) && NUMBERP (XCAR (view))) \ + { \ + vc->field = XFLOATINT (XCAR (view)); \ + view = XCDR (view); \ + } \ + else \ + return false; +#define PARSE_VIEW_CONDITION_INT(field) \ + if (CONSP (view) && NATNUMP (XCAR (view))) \ + { \ + CHECK_RANGED_INTEGER (XCAR (view), 1, 4); \ + vc->field = XINT (XCAR (view)); \ + view = XCDR (view); \ + } \ + else \ + return false; + + PARSE_VIEW_CONDITION_FLOAT (Yb); + PARSE_VIEW_CONDITION_FLOAT (La); + PARSE_VIEW_CONDITION_INT (surround); + PARSE_VIEW_CONDITION_FLOAT (D_value); + + if (! NILP (view)) + return false; + + vc->whitePoint.X = wp->X; + vc->whitePoint.Y = wp->Y; + vc->whitePoint.Z = wp->Z; + return true; +} + +/* References: + Li, Luo et al. "The CRI-CAM02UCS colour rendering index." COLOR research + and application, 37 No.3, 2012. + Luo et al. "Uniform colour spaces based on CIECAM02 colour appearance + model." COLOR research and application, 31 No.4, 2006. */ + +DEFUN ("lcms-cam02-ucs", Flcms_cam02_ucs, Slcms_cam02_ucs, 2, 4, 0, doc: /* Compute CAM02-UCS metric distance between COLOR1 and COLOR2. -Each color is a list of XYZ coordinates, with Y scaled about unity. -Optional argument is the XYZ white point, which defaults to illuminant D65. */) - (Lisp_Object color1, Lisp_Object color2, Lisp_Object whitepoint) +Each color is a list of XYZ tristimulus values, with Y scaled about unity. +Optional argument WHITEPOINT is the XYZ white point, which defaults to +illuminant D65. +Optional argument VIEW is a list containing the viewing conditions, and +is of the form (YB LA SURROUND DVALUE) where SURROUND corresponds to + 1 AVG_SURROUND + 2 DIM_SURROUND + 3 DARK_SURROUND + 4 CUTSHEET_SURROUND +The default viewing conditions are (20 100 1 1). */) + (Lisp_Object color1, Lisp_Object color2, Lisp_Object whitepoint, + Lisp_Object view) { cmsViewingConditions vc; cmsJCh jch1, jch2; @@ -188,17 +259,13 @@ Optional argument is the XYZ white point, which defaults to illuminant D65. */) if (!(CONSP (color2) && parse_xyz_list (color2, &xyz2))) signal_error ("Invalid color", color2); if (NILP (whitepoint)) - parse_xyz_list (Vlcms_d65_xyz, &xyzw); + xyzw = illuminant_d65; else if (!(CONSP (whitepoint) && parse_xyz_list (whitepoint, &xyzw))) signal_error ("Invalid white point", whitepoint); - - vc.whitePoint.X = xyzw.X; - vc.whitePoint.Y = xyzw.Y; - vc.whitePoint.Z = xyzw.Z; - vc.Yb = 20; - vc.La = 100; - vc.surround = AVG_SURROUND; - vc.D_value = 1.0; + if (NILP (view)) + default_viewing_conditions (&xyzw, &vc); + else if (!(CONSP (view) && parse_viewing_conditions (view, &xyzw, &vc))) + signal_error ("Invalid view conditions", view); h1 = cmsCIECAM02Init (0, &vc); h2 = cmsCIECAM02Init (0, &vc); @@ -227,10 +294,10 @@ Optional argument is the XYZ white point, which defaults to illuminant D65. */) Mp2 = 43.86 * log (1.0 + 0.0228 * (jch2.C * sqrt (sqrt (FL)))); Jp1 = 1.7 * jch1.J / (1.0 + (0.007 * jch1.J)); Jp2 = 1.7 * jch2.J / (1.0 + (0.007 * jch2.J)); - ap1 = Mp1 * cos (jch1.h); - ap2 = Mp2 * cos (jch2.h); - bp1 = Mp1 * sin (jch1.h); - bp2 = Mp2 * sin (jch2.h); + ap1 = Mp1 * cos (deg2rad (jch1.h)); + ap2 = Mp2 * cos (deg2rad (jch2.h)); + bp1 = Mp1 * sin (deg2rad (jch1.h)); + bp2 = Mp2 * sin (deg2rad (jch2.h)); return make_float (sqrt ((Jp2 - Jp1) * (Jp2 - Jp1) + (ap2 - ap1) * (ap2 - ap1) + @@ -291,12 +358,6 @@ DEFUN ("lcms2-available-p", Flcms2_available_p, Slcms2_available_p, 0, 0, 0, void syms_of_lcms2 (void) { - DEFVAR_LISP ("lcms-d65-xyz", Vlcms_d65_xyz, - doc: /* D65 illuminant as a CIE XYZ triple. */); - Vlcms_d65_xyz = list3 (make_float (0.950455), - make_float (1.0), - make_float (1.088753)); - defsubr (&Slcms_cie_de2000); defsubr (&Slcms_cam02_ucs); defsubr (&Slcms2_available_p); diff --git a/test/src/lcms-tests.el b/test/src/lcms-tests.el index 3d0942c8d1..d6d1d16b9a 100644 --- a/test/src/lcms-tests.el +++ b/test/src/lcms-tests.el @@ -21,9 +21,11 @@ ;;; Commentary: -;; Some "exact" values computed using the colorspacious python library -;; written by Nathaniel J. Smith. See -;; https://colorspacious.readthedocs.io/en/v1.1.0/ +;; Some reference values computed using the colorspacious python +;; library, assimilated from its test suite, or adopted from its +;; aggregation of gold values. +;; See https://colorspacious.readthedocs.io/en/v1.1.0/ and +;; https://github.com/njsmith/colorspacious ;; Other references: ;; http://www.babelcolor.com/index_htm_files/A%20review%20of%20RGB%20color%20spaces.pdf @@ -49,6 +51,11 @@ B is considered the exact value." (lcms-approx-p a2 b2 delta) (lcms-approx-p a3 b3 delta)))) +(defun lcms-rgb255->xyz (rgb) + "Return XYZ tristimulus values corresponding to RGB." + (let ((rgb1 (mapcar (lambda (x) (/ x 255.0)) rgb))) + (apply #'color-srgb-to-xyz rgb1))) + (ert-deftest lcms-cri-cam02-ucs () "Test use of `lcms-cam02-ucs'." (skip-unless (featurep 'lcms2)) @@ -56,8 +63,8 @@ B is considered the exact value." (should-error (lcms-cam02-ucs '(0 0 0) 'error)) (should-not (lcms-approx-p - (let ((lcms-d65-xyz '(0.44757 1.0 0.40745))) - (lcms-cam02-ucs '(0.5 0.5 0.5) '(0 0 0))) + (let ((wp '(0.44757 1.0 0.40745))) + (lcms-cam02-ucs '(0.5 0.5 0.5) '(0 0 0) wp)) (lcms-cam02-ucs '(0.5 0.5 0.5) '(0 0 0)))) (should (eql 0.0 (lcms-cam02-ucs '(0.5 0.5 0.5) '(0.5 0.5 0.5)))) (should @@ -87,4 +94,24 @@ B is considered the exact value." (apply #'color-xyz-to-xyy (lcms-temp->white-point 7504)) '(0.29902 0.31485 1.0)))) +(ert-deftest lcms-dE-cam02-ucs-silver () + "Test CRI-CAM02-UCS deltaE metric values from colorspacious." + (skip-unless (featurep 'lcms2)) + (should + (lcms-approx-p + (lcms-cam02-ucs (lcms-rgb255->xyz '(173 52 52)) + (lcms-rgb255->xyz '(59 120 51)) + lcms-colorspacious-d65 + (list 20 (/ 64 float-pi 5) 1 1)) + 44.698469808449964 + 0.03)) + (should + (lcms-approx-p + (lcms-cam02-ucs (lcms-rgb255->xyz '(69 100 52)) + (lcms-rgb255->xyz '(59 120 51)) + lcms-colorspacious-d65 + (list 20 (/ 64 float-pi 5) 1 1)) + 8.503323264883667 + 0.04))) + ;;; lcms-tests.el ends here commit a81d5a3d3fcb76f6b074c2c721b80b1802135d41 Author: Alan Third Date: Thu Sep 21 21:53:30 2017 +0100 Revert "Set frame size to actual requested size (bug#18215)" This reverts commit d31cd79b40dbd5459b16505a4ee4340210499277. See bug#28536. I misunderstood bug#18215. It wasn't a bug. diff --git a/src/nsterm.m b/src/nsterm.m index 776635980e..a41d6be204 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1820,8 +1820,8 @@ -(void)remove if (pixelwise) { - pixelwidth = width; - pixelheight = height; + pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width); + pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height); } else { commit 0bf066d4b25c694cca6b1d24ac0aadc2b9ae05b1 Author: Gemini Lasswell Date: Thu Sep 21 13:36:08 2017 -0700 Add tests for Edebug * tests/lisp/emacs-lisp/edeug-tests.el: New file. * tests/lisp/emacs-lisp/edebug-resources/edebug-test-code.el: New file. diff --git a/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el b/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el new file mode 100644 index 0000000000..0cc7b1e8b4 --- /dev/null +++ b/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el @@ -0,0 +1,130 @@ +;;; edebug-test-code.el --- Sample code for the Edebug test suite + +;; Copyright (C) 2017 Free Software Foundation, Inc. + +;; Author: Gemini Lasswell + +;; This file is part of GNU Emacs. + +;; 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 contains sample code used by edebug-tests.el. +;; Before evaluation, it will be preprocessed by +;; `edebug-tests-setup-code-file' which will remove all tags +;; between !'s and save their positions for use by the tests. + +;;; Code: + +(defun edebug-test-code-fac (n) + !start!(if !step!(< 0 n) + (* n (edebug-test-code-fac (1- n)))!mult! + 1)) + +(defun edebug-test-code-concat (a b flag) + !start!(if flag!flag! + !then-start!(concat a!then-a! b!then-b!)!then-concat! + !else-start!(concat b!else-b! a!else-a!)!else-concat!)!if!) + +(defun edebug-test-code-range (num) + !start!(let ((index 0) + (result nil)) + (while (< index num)!test! + (push index result)!loop! + (cl-incf index))!end-loop! + (nreverse result))) + +(defun edebug-test-code-choices (input) + !start!(cond + ((eq input 0) "zero") + ((eq input 7) 42) + (t !edebug!(edebug)))) + +(defvar edebug-test-code-total nil) + +(defun edebug-test-code-multiply (times value) + !start!(setq edebug-test-code-total 0) + (cl-dotimes (index times) + (setq edebug-test-code-total (+ edebug-test-code-total value))!setq!) + edebug-test-code-total) + +(defun edebug-test-code-format-vector-node (node) + !start!(concat "[" + (apply 'concat (mapcar 'edebug-test-code-format-node node))!apply! + "]")) + +(defun edebug-test-code-format-list-node (node) + !start!(concat "{" + (apply 'concat (mapcar 'edebug-test-code-format-node node))!apply! + "}")) + +(defun edebug-test-code-format-node (node) + !start!(cond + (!vectorp!(vectorp node!vnode!)!vtest! !vbefore!(edebug-test-code-format-vector-node node)) + ((listp node) (edebug-test-code-format-list-node node)) + (t (format "%s" node)))) + +(defvar edebug-test-code-flavor "strawberry") + +(defmacro edebug-test-code-with-flavor (new-flavor &rest body) + (declare (debug (form body)) + (indent 1)) + `(let ((edebug-test-code-flavor ,new-flavor)) + ,@body)) + +(defun edebug-test-code-try-flavors () + (let* (tried) + (push edebug-test-code-flavor tried) + !macro!(edebug-test-code-with-flavor "chocolate" + (push edebug-test-code-flavor tried)) + tried)!end!) + +(unless (featurep 'edebug-tests-nutty)!nutty! + !setq!(setq edebug-test-code-flavor (car (edebug-test-code-try-flavors)))!end-setq!)!end-unless! + +(cl-defgeneric edebug-test-code-emphasize (x)) +(cl-defmethod edebug-test-code-emphasize ((x integer)) + !start!(format "The number is not %s or %s, but %s!" + (1+ x) (1- x) x)) +(cl-defmethod edebug-test-code-emphasize ((x string)) + !start!(format "***%s***" x)) + +(defun edebug-test-code-use-methods () + (list + !number!(edebug-test-code-emphasize 100) + !string!(edebug-test-code-emphasize "yes"))) + +(defun edebug-test-code-make-lambda (n) + (lambda (x) (+ x!x! n))) + +(defun edebug-test-code-use-lambda () + !start!(mapcar (edebug-test-code-make-lambda 10) '(1 2 3))) + +(defun edebug-test-code-circular-read-syntax () + '(#1=a . #1#)) + +(defun edebug-test-code-hash-read-syntax () + !start!(list #("abcd" 1 3 (face italic)) + #x01ff)) + +(defun edebug-test-code-empty-string-list () + !start!(list "")!step!) + +(defun edebug-test-code-current-buffer () + !start!(with-current-buffer (get-buffer-create "*edebug-test-code-buffer*") + !body!(format "current-buffer: %s" (current-buffer)))) + +(provide 'edebug-test-code) +;;; edebug-test-code.el ends here diff --git a/test/lisp/emacs-lisp/edebug-tests.el b/test/lisp/emacs-lisp/edebug-tests.el new file mode 100644 index 0000000000..037278e772 --- /dev/null +++ b/test/lisp/emacs-lisp/edebug-tests.el @@ -0,0 +1,903 @@ +;;; edebug-tests.el --- Edebug test suite -*- lexical-binding:t -*- + +;; Copyright (C) 2017 Free Software Foundation, Inc. + +;; Author: Gemini Lasswell + +;; This file is part of GNU Emacs. + +;; 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: + +;; These tests focus on Edebug's user interface for setting +;; breakpoints, stepping through and tracing code, and evaluating +;; values used by the code. In addition there are some tests of +;; Edebug's reader. There are large parts of Edebug's functionality +;; not covered by these tests, including coverage testing, macro +;; specifications, and the eval list buffer. + +;;; Code: + +(require 'cl-lib) +(require 'ert) +(require 'ert-x) +(require 'edebug) +(require 'kmacro) + +;; Use `eval-and-compile' because this is used by the macro +;; `edebug-tests-deftest'. +(eval-and-compile + (defvar edebug-tests-sample-code-file + (expand-file-name + "edebug-resources/edebug-test-code.el" + (file-name-directory (or (bound-and-true-p byte-compile-current-file) + load-file-name + buffer-file-name))) + "Name of file containing code samples for Edebug tests.")) + +(defvar edebug-tests-temp-file nil + "Name of temp file containing sample code stripped of stop point symbols.") +(defvar edebug-tests-stop-points nil + "An alist of alists mapping function symbol -> stop point name -> marker. +Used by the tests to refer to locations in `edebug-tests-temp-file'.") +(defvar edebug-tests-messages nil + "Messages collected during execution of the current test.") + +(defvar edebug-tests-@-result 'no-result + "Return value of `edebug-tests-func', or no-result if there isn't one yet.") + +(defvar edebug-tests-failure-in-post-command nil + "An error trapped in `edebug-tests-post-command'. +Since `should' failures which happen inside `post-command-hook' will +be trapped by the command loop, this preserves them until we get +back to the top level.") + +(defvar edebug-tests-keymap + (let ((map (make-sparse-keymap))) + (define-key map "@" 'edebug-tests-call-instrumented-func) + (define-key map "C-u" 'universal-argument) + (define-key map "C-p" 'previous-line) + (define-key map "C-n" 'next-line) + (define-key map "C-b" 'backward-char) + (define-key map "C-a" 'move-beginning-of-line) + (define-key map "C-e" 'move-end-of-line) + (define-key map "C-k" 'kill-line) + (define-key map "M-x" 'execute-extended-command) + (define-key map "C-M-x" 'eval-defun) + (define-key map "C-x X b" 'edebug-set-breakpoint) + (define-key map "C-x X w" 'edebug-where) + map) + "Keys used by the keyboard macros in Edebug's tests.") + +;;; Macros for defining tests: + +(defmacro edebug-tests-with-default-config (&rest body) + "Create a consistent environment for an Edebug test BODY to run in." + (declare (debug (body))) + `(cl-letf* ( + ;; These defcustoms are set to their original value. + (edebug-setup-hook nil) + (edebug-all-defs nil) + (edebug-all-forms nil) + (edebug-eval-macro-args nil) + (edebug-save-windows t) + (edebug-save-displayed-buffer-points nil) + (edebug-initial-mode 'step) + (edebug-trace nil) + (edebug-test-coverage nil) + (edebug-print-length 50) + (edebug-print-level 50) + (edebug-print-circle t) + (edebug-unwrap-results nil) + (edebug-on-error t) + (edebug-on-quit t) + (edebug-global-break-condition nil) + (edebug-sit-for-seconds 1) + + ;; sit-on interferes with keyboard macros. + (edebug-sit-on-break nil) + (edebug-continue-kbd-macro t)) + ,@body)) + +(defmacro edebug-tests-with-normal-env (&rest body) + "Set up the environment for an Edebug test BODY, run it, and clean up." + (declare (debug (body))) + `(edebug-tests-with-default-config + (let ((edebug-tests-failure-in-post-command nil) + (edebug-tests-temp-file (make-temp-file "edebug-tests-" nil ".el"))) + (edebug-tests-setup-code-file edebug-tests-temp-file) + (ert-with-message-capture + edebug-tests-messages + (unwind-protect + (with-current-buffer (find-file edebug-tests-temp-file) + (read-only-mode) + (setq lexical-binding t) + (eval-buffer) + ,@body + (when edebug-tests-failure-in-post-command + (signal (car edebug-tests-failure-in-post-command) + (cdr edebug-tests-failure-in-post-command)))) + (unload-feature 'edebug-test-code) + (with-current-buffer (find-file-noselect edebug-tests-temp-file) + (set-buffer-modified-p nil)) + (ignore-errors (kill-buffer (find-file-noselect + edebug-tests-temp-file))) + (ignore-errors (delete-file edebug-tests-temp-file))))))) + +;; The following macro and its support functions implement an extension +;; to keyboard macros to allow interleaving of keyboard macro +;; events with evaluation of Lisp expressions. The Lisp expressions +;; are called from within `post-command-hook', which is a strategy +;; inspired by `kmacro-step-edit-macro'. + +;; Some of the details necessary to get this to work with Edebug are: +;; -- ERT's `should' macros raise errors, and errors within +;; `post-command-hook' are trapped by the command loop. The +;; workaround is to trap and save an error inside the hook +;; function and reraise it after the macro exits. +;; -- `edebug-continue-kbd-macro' must be non-nil. +;; -- Edebug calls `exit-recursive-edit' which turns off keyboard +;; macro execution. Solved with an advice wrapper for +;; `exit-recursive-edit' which preserves the keyboard macro state. + +(defmacro edebug-tests-run-kbd-macro (&rest macro) + "Run a MACRO consisting of both keystrokes and test assertions. +MACRO should be a list, where each item is either a keyboard +macro segment (in string or vector form) or a Lisp expression. +Convert the macro segments into keyboard macros and execute them. +After the execution of the last event of each segment, evaluate +the Lisp expressions following the segment." + (let ((prepared (edebug-tests-prepare-macro macro))) + `(edebug-tests-run-macro ,@prepared))) + +;; Make support functions for edebug-tests-run-kbd-macro +;; available at compile time. +(eval-and-compile + (defun edebug-tests-prepare-macro (macro) + "Prepare a MACRO for execution. +MACRO should be a list containing strings, vectors, and Lisp +forms. Convert the strings and vectors to keyboard macros in +vector representation and concatenate them to make a single +keyboard macro. Also build a list of the same length as the +number of events in the keyboard macro. Each item in that list +will contain the code to evaluate after the corresponding event +in the keyboard macro, either nil or a thunk built from the forms +in the original list. Return a list containing the keyboard +macro as the first item, followed by the list of thunks and/or +nils." + (cl-loop + for item = (pop macro) + while item + for segment = (read-kbd-macro item) + for thunk = (edebug-tests-wrap-thunk + (cl-loop + for form in macro + until (or (stringp form) (vectorp form)) + collect form + do (pop macro))) + vconcat segment into segments + append (edebug-tests-pad-thunk-list (length segment) thunk) + into thunk-list + + finally return (cons segments thunk-list))) + + (defun edebug-tests-wrap-thunk (body) + "If BODY is non-nil, wrap it with a lambda form." + (when body + `(lambda () ,@body))) + + (defun edebug-tests-pad-thunk-list (length thunk) + "Return a list with LENGTH elements with THUNK in the last position. +All other elements will be nil." + (let ((thunk-seg (make-list length nil))) + (setf (car (last thunk-seg)) thunk) + thunk-seg))) + +;;; Support for test execution: + +(defvar edebug-tests-thunks nil + "List containing thunks to run after each command in a keyboard macro.") +(defvar edebug-tests-kbd-macro-index nil + "Index into `edebug-tests-run-unpacked-kbd-macro's current keyboard macro.") + +(defun edebug-tests-run-macro (kbdmac &rest thunks) + "Run a keyboard macro and execute a thunk after each command in it. +KBDMAC should be a vector of events and THUNKS a list of the +same length containing thunks and/or nils. Run the macro, and +after the execution of every command in the macro (which may not +be the same as every keystroke) execute the thunk at the same +index." + (let* ((edebug-tests-thunks thunks) + (edebug-tests-kbd-macro-index 0) + saved-local-map) + (with-current-buffer (find-file-noselect edebug-tests-temp-file) + (setq saved-local-map overriding-local-map) + (setq overriding-local-map edebug-tests-keymap) + (add-hook 'post-command-hook 'edebug-tests-post-command)) + (advice-add 'exit-recursive-edit + :around 'edebug-tests-preserve-keyboard-macro-state) + (unwind-protect + (kmacro-call-macro nil nil nil kbdmac) + (advice-remove 'exit-recursive-edit + 'edebug-tests-preserve-keyboard-macro-state) + (with-current-buffer (find-file-noselect edebug-tests-temp-file) + (setq overriding-local-map saved-local-map) + (remove-hook 'post-command-hook 'edebug-tests-post-command))))) + +(defun edebug-tests-preserve-keyboard-macro-state (orig &rest args) + "Call ORIG with ARGS preserving the value of `executing-kbd-macro'. +Useful to prevent `exit-recursive-edit' from stopping the current +keyboard macro." + (let ((executing-kbd-macro executing-kbd-macro)) + (apply orig args))) + +(defun edebug-tests-post-command () + "Run the thunk from `edebug-tests-thunks' matching the keyboard macro index." + (when (and edebug-tests-kbd-macro-index + (> executing-kbd-macro-index edebug-tests-kbd-macro-index)) + (let ((thunk (nth (1- executing-kbd-macro-index) edebug-tests-thunks))) + (when thunk + (condition-case err + (funcall thunk) + (error + (setq edebug-tests-failure-in-post-command err) + (signal (car err) (cdr err))))) + (setq edebug-tests-kbd-macro-index executing-kbd-macro-index)))) + +(defvar edebug-tests-func nil + "Instrumented function used to launch Edebug.") +(defvar edebug-tests-args nil + "Arguments for `edebug-tests-func'.") + +(defun edebug-tests-setup-@ (def-name args edebug-it) + "Set up the binding for @ in `edebug-tests-keymap'. +Find a definition for DEF-NAME in the current buffer and evaluate it. +Set globals so that `edebug-tests-call-instrumented-func' which +is bound to @ for edebug-tests' keyboard macros will call it with +ARGS. EDEBUG-IT is passed through to `eval-defun'." + (edebug-tests-locate-def def-name) + (eval-defun edebug-it) + (let* ((full-name (concat "edebug-test-code-" def-name)) + (sym (intern-soft full-name))) + (should (and sym (fboundp sym))) + (setq edebug-tests-func sym + edebug-tests-args args) + (setq edebug-tests-@-result 'no-result))) + +(defun edebug-tests-call-instrumented-func () + "Call `edebug-tests-func' with `edebug-tests-args' and save the results." + (interactive) + (let ((result (apply edebug-tests-func edebug-tests-args))) + (should (eq edebug-tests-@-result 'no-result)) + (setq edebug-tests-@-result result))) + +(defun edebug-tests-should-be-at (def-name point-name) + "Require that point be at the location in DEF-NAME named POINT-NAME. +DEF-NAME should be the suffix of a definition in the code samples +file (the part after \"edebug-tests\")." + (let ((stop-point (edebug-tests-get-stop-point def-name point-name))) + (should (eq (current-buffer) (find-file-noselect edebug-tests-temp-file))) + (should (eql (point) stop-point)))) + +(defun edebug-tests-get-stop-point (def-name point-name) + "Return the position in DEF-NAME of the stop point named POINT-NAME. +DEF-NAME should be the suffix of a definition in the code samples +file (the part after \"edebug-tests\")." + (let* ((full-name (concat "edebug-test-code-" def-name))(stop-point + (cdr (assoc point-name + (cdr (assoc full-name edebug-tests-stop-points)))))) + (unless stop-point + (ert-fail (format "%s not found in %s" point-name full-name))) + stop-point)) + +(defun edebug-tests-should-match-result-in-messages (value) + "Require that VALUE (a string) match an Edebug result in *Messages*. +Then clear edebug-tests' saved messages." + (should (string-match-p (concat "Result: " (regexp-quote value) "$") + edebug-tests-messages)) + (setq edebug-tests-messages "")) + +(defun edebug-tests-locate-def (def-name) + "Search for a definiton of DEF-NAME from the start of the current buffer. +Place point at the end of DEF-NAME in the buffer." + (goto-char (point-min)) + (re-search-forward (concat "def\\S-+ edebug-test-code-" def-name))) + +(defconst edebug-tests-start-of-next-def-regexp "^(\\S-*def\\S-+ \\(\\S-+\\)" + "Regexp used to match the start of a definition.") +(defconst edebug-tests-stop-point-regexp "!\\(\\S-+?\\)!" + "Regexp used to match a stop point annotation in the sample code.") + +;;; Set up buffer containing code samples: + +(defmacro edebug-tests-deduplicate (name names-and-numbers) + "Return a unique variation on NAME. +NAME should be a string and NAMES-AND-NUMBERS an alist which can +be used by this macro to retain state. If NAME for example is +\"symbol\" then the first and subsequent uses of this macro will +evaluate to \"symbol\", \"symbol-1\", \"symbol-2\", etc." + (let ((g-name (gensym)) + (g-duplicate (gensym))) + `(let* ((,g-name ,name) + (,g-duplicate (assoc ,g-name ,names-and-numbers))) + (if (null ,g-duplicate) + (progn + (push (cons ,g-name 0) ,names-and-numbers) + ,g-name) + (cl-incf (cdr ,g-duplicate)) + (format "%s-%s" ,g-name (cdr ,g-duplicate)))))) + +(defun edebug-tests-setup-code-file (tmpfile) + "Extract stop points and loadable code from the sample code file. +Write the loadable code to a buffer for TMPFILE, and set +`edebug-tests-stop-points' to a map from defined symbols to stop +point names to positions in the file." + (with-current-buffer (find-file-noselect edebug-tests-sample-code-file) + (let ((marked-up-code (buffer-string))) + (with-temp-file tmpfile + (insert marked-up-code)))) + + (with-current-buffer (find-file-noselect tmpfile) + (let ((stop-points + ;; Delete all the !name! annotations from the code, but remember + ;; their names and where they were in an alist. + (cl-loop + initially (goto-char (point-min)) + while (re-search-forward edebug-tests-stop-point-regexp nil t) + for name = (match-string-no-properties 1) + do (replace-match "") + collect (cons name (point)))) + names-and-numbers) + + ;; Now build an alist mapping definition names to annotation + ;; names and positions. + ;; If duplicate symbols exist in the file, enter them in the + ;; alist as symbol, symbol-1, symbol-2 etc. + (setq edebug-tests-stop-points + (cl-loop + initially (goto-char (point-min)) + while (re-search-forward edebug-tests-start-of-next-def-regexp + nil t) + for name = + (edebug-tests-deduplicate (match-string-no-properties 1) + names-and-numbers) + for end-of-def = + (save-match-data + (save-excursion + (re-search-forward edebug-tests-start-of-next-def-regexp + nil 0) + (point))) + collect (cons name + (cl-loop + while (and stop-points + (< (cdar stop-points) end-of-def)) + collect (pop stop-points)))))))) + +;;; Tests + +(ert-deftest edebug-tests-check-keymap () + "Verify that `edebug-mode-map' is compatible with these tests. +If this test fails, one of two things is true. Either your +customizations modify `edebug-mode-map', in which case starting +Emacs with the -Q flag should fix the problem, or +`edebug-mode-map' has changed in edebug.el, in which case this +test and possibly others should be updated." + ;; The reason verify-keybinding is a macro instead of a function is + ;; that in the event of a failure, it makes the keybinding that + ;; failed show up in ERT's output. + (cl-macrolet ((verify-keybinding (key binding) + `(should (eq (lookup-key edebug-mode-map ,key) + ,binding)))) + (verify-keybinding " " 'edebug-step-mode) + (verify-keybinding "n" 'edebug-next-mode) + (verify-keybinding "g" 'edebug-go-mode) + (verify-keybinding "G" 'edebug-Go-nonstop-mode) + (verify-keybinding "t" 'edebug-trace-mode) + (verify-keybinding "T" 'edebug-Trace-fast-mode) + (verify-keybinding "c" 'edebug-continue-mode) + (verify-keybinding "C" 'edebug-Continue-fast-mode) + (verify-keybinding "f" 'edebug-forward-sexp) + (verify-keybinding "h" 'edebug-goto-here) + (verify-keybinding "I" 'edebug-instrument-callee) + (verify-keybinding "i" 'edebug-step-in) + (verify-keybinding "o" 'edebug-step-out) + (verify-keybinding "q" 'top-level) + (verify-keybinding "Q" 'edebug-top-level-nonstop) + (verify-keybinding "a" 'abort-recursive-edit) + (verify-keybinding "S" 'edebug-stop) + (verify-keybinding "b" 'edebug-set-breakpoint) + (verify-keybinding "u" 'edebug-unset-breakpoint) + (verify-keybinding "B" 'edebug-next-breakpoint) + (verify-keybinding "x" 'edebug-set-conditional-breakpoint) + (verify-keybinding "X" 'edebug-set-global-break-condition) + (verify-keybinding "r" 'edebug-previous-result) + (verify-keybinding "e" 'edebug-eval-expression) + (verify-keybinding "\C-x\C-e" 'edebug-eval-last-sexp) + (verify-keybinding "E" 'edebug-visit-eval-list) + (verify-keybinding "w" 'edebug-where) + (verify-keybinding "v" 'edebug-view-outside) ;; maybe obsolete?? + (verify-keybinding "p" 'edebug-bounce-point) + (verify-keybinding "P" 'edebug-view-outside) ;; same as v + (verify-keybinding "W" 'edebug-toggle-save-windows) + (verify-keybinding "?" 'edebug-help) + (verify-keybinding "d" 'edebug-backtrace) + (verify-keybinding "-" 'negative-argument) + (verify-keybinding "=" 'edebug-temp-display-freq-count))) + +(ert-deftest edebug-tests-stop-point-at-start-of-first-instrumented-function () + "Edebug stops at the beginning of an instrumented function." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "fac" '(0) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "fac" "start") + "SPC" (edebug-tests-should-be-at "fac" "step") + "g" (should (equal edebug-tests-@-result 1))))) + +(ert-deftest edebug-tests-step-showing-evaluation-results () + "Edebug prints expression evaluation results to the echo area." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "concat" '("x" "y" nil) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "concat" "start") + "SPC" (edebug-tests-should-be-at "concat" "flag") + (edebug-tests-should-match-result-in-messages "nil") + "SPC" (edebug-tests-should-be-at "concat" "else-start") + "SPC" (edebug-tests-should-be-at "concat" "else-b") + (edebug-tests-should-match-result-in-messages "\"y\"") + "SPC" (edebug-tests-should-be-at "concat" "else-a") + (edebug-tests-should-match-result-in-messages "\"x\"") + "SPC" (edebug-tests-should-be-at "concat" "else-concat") + (edebug-tests-should-match-result-in-messages "\"yx\"") + "SPC" (edebug-tests-should-be-at "concat" "if") + (edebug-tests-should-match-result-in-messages "\"yx\"") + "SPC" (should (equal edebug-tests-@-result "yx"))))) + +(ert-deftest edebug-tests-set-breakpoint-at-point () + "Edebug can set a breakpoint at point." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "concat" '("x" "y" t) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "concat" "start") + "C-n C-e b C-n" ; Move down, set a breakpoint and move away. + "g" (edebug-tests-should-be-at "concat" "then-concat") + (edebug-tests-should-match-result-in-messages "\"xy\"") + "g" (should (equal edebug-tests-@-result "xy"))))) + +(ert-deftest edebug-tests-set-temporary-breakpoint-at-point () + "Edebug can set a temporary breakpoint at point." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "range" '(3) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "range" "start") + "C-n C-n C-n C-e" ; Move down to the end of a sexp in the loop. + "C-u b" ; Set a temporary breakpoint. + "C-n" ; Move away. + "g" (edebug-tests-should-be-at "range" "loop") + (edebug-tests-should-match-result-in-messages "(0)") + "g" (should (equal edebug-tests-@-result '(0 1 2)))))) + +(ert-deftest edebug-tests-clear-breakpoint () + "Edebug can clear a breakpoint." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "range" '(3) t) + (edebug-tests-run-kbd-macro + "@" + (message "after @") + (edebug-tests-should-be-at "range" "start") + "C-n C-n C-n C-e b C-n" ; Move down, set a breakpoint and move away. + "g" (edebug-tests-should-be-at "range" "loop") + (edebug-tests-should-match-result-in-messages "(0)") + "g" (edebug-tests-should-be-at "range" "loop") + (edebug-tests-should-match-result-in-messages "(1 0)") + "u" ; Unset the breakpoint. + "g" (should (equal edebug-tests-@-result '(0 1 2)))))) + +(ert-deftest edebug-tests-move-point-to-next-breakpoint () + "Edebug can move point to the next breakpoint." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "concat" '("a" "b" nil) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "concat" "start") + "C-n C-e b" ; Move down, set a breakpoint. + "C-n b" ; Set another breakpoint on the next line. + "C-p C-p C-p" ; Move back up. + "B" (edebug-tests-should-be-at "concat" "then-concat") + "B" (edebug-tests-should-be-at "concat" "else-concat") + "G" (should (equal edebug-tests-@-result "ba"))))) + +(ert-deftest edebug-tests-move-point-back-to-stop-point () + "Edebug can move point back to a stop point." + (edebug-tests-with-normal-env + (let ((test-buffer (get-buffer-create "edebug-tests-temp"))) + (edebug-tests-setup-@ "fac" '(4) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "fac" "start") + "C-n w" (edebug-tests-should-be-at "fac" "start") + (pop-to-buffer test-buffer) + "C-x X w" (edebug-tests-should-be-at "fac" "start") + "g" (should (equal edebug-tests-@-result 24))) + (ignore-errors (kill-buffer test-buffer))))) + +(ert-deftest edebug-tests-jump-to-point () + "Edebug can stop at a temporary breakpoint at point." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "range" '(3) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "range" "start") + "C-n C-n C-n C-e" ; Move down to the end of a sexp in the loop. + "h" (edebug-tests-should-be-at "range" "loop") + (edebug-tests-should-match-result-in-messages "(0)") + "g" (should (equal edebug-tests-@-result '(0 1 2)))))) + +(ert-deftest edebug-tests-jump-forward-one-sexp () + "Edebug can run the program for one expression." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "range" '(3) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "range" "start") + "SPC SPC f" (edebug-tests-should-be-at "range" "test") + "g" (should (equal edebug-tests-@-result '(0 1 2)))))) + +(ert-deftest edebug-tests-run-out-of-containing-sexp () + "Edebug can run the program until the end of the containing sexp." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "range" '(3) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "range" "start") + "SPC SPC f" (edebug-tests-should-be-at "range" "test") + "o" (edebug-tests-should-be-at "range" "end-loop") + (edebug-tests-should-match-result-in-messages "nil") + "g" (should (equal edebug-tests-@-result '(0 1 2)))))) + +(ert-deftest edebug-tests-observe-breakpoint-in-source () + "Edebug will stop at a breakpoint embedded in source code." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "choices" '(8) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "choices" "start") + "g" (edebug-tests-should-be-at "choices" "edebug") + "g" (should (equal edebug-tests-@-result nil))))) + +(ert-deftest edebug-tests-set-conditional-breakpoint () + "Edebug can set and observe a conditional breakpoint." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "fac" '(5) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "fac" "start") + ;; Set conditional breakpoint at end of next line. + "C-n C-e x (eql SPC n SPC 3) RET" + "g" (edebug-tests-should-be-at "fac" "mult") + (edebug-tests-should-match-result-in-messages "6 (#o6, #x6, ?\\C-f)") + "g" (should (equal edebug-tests-@-result 120))))) + +(ert-deftest edebug-tests-error-trying-to-set-breakpoint-in-uninstrumented-code + () + "Edebug refuses to set a breakpoint in uninsented code." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "fac" '(5) t) + (let* ((debug-on-error nil) + (edebug-on-error nil) + error-message + (command-error-function (lambda (&rest args) + (setq error-message (cadar args))))) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "fac" "start") + "C-u 10 C-n" ; Move down and out of instrumented function. + "b" (should (string-match-p "Not inside instrumented form" + error-message)) + ;; The error stopped the keyboard macro. Start it again. + (should-not executing-kbd-macro) + (setq executing-kbd-macro t) + "g")))) + +(ert-deftest edebug-tests-set-and-break-on-global-condition () + "Edebug can break when a global condition becomes true." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "multiply" '(5 3) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "multiply" "start") + "X (> SPC edebug-test-code-total SPC 10) RET" + (should edebug-global-break-condition) + "g" (edebug-tests-should-be-at "multiply" "setq") + (should (eql (symbol-value 'edebug-test-code-total) 12)) + "X C-a C-k nil RET" ; Remove suggestion before entering nil. + "g" (should (equal edebug-tests-@-result 15))))) + +(ert-deftest edebug-tests-trace-showing-results-at-stop-points () + "Edebug can trace execution, showing results at stop points." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "concat" '("x" "y" nil) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "concat" "start") + "T" (should (string-match-p + (concat "Result: nil\n.*?" + "Result: \"y\"\n.*?" + "Result: \"x\"\n.*?" + "Result: \"yx\"\n.*?" + "Result: \"yx\"\n") + edebug-tests-messages)) + (should (equal edebug-tests-@-result "yx"))))) + +(ert-deftest edebug-tests-trace-showing-results-at-breakpoints () + "Edebug can trace execution, showing results at breakpoints." + (edebug-tests-with-normal-env + (edebug-tests-locate-def "format-vector-node") + (edebug-tests-run-kbd-macro "C-u C-M-x C-n C-n C-e C-x X b") + (edebug-tests-locate-def "format-list-node") + (edebug-tests-run-kbd-macro "C-u C-M-x C-n C-n C-e C-x X b") + (edebug-tests-setup-@ "format-node" '(([a b] [c d])) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "format-node" "start") + "C" (should (string-match-p + (concat "Result: \"ab\"\n.*?" + "Result: \"cd\"\n.*?" + "Result: \"\\[ab]\\[cd]\"\n") + edebug-tests-messages)) + (should (equal edebug-tests-@-result "{[ab][cd]}"))))) + +(ert-deftest edebug-tests-trace-function-call-and-return () + "Edebug can create a trace of function calls and returns." + (edebug-tests-with-normal-env + (edebug-tests-locate-def "format-vector-node") + (eval-defun t) + (edebug-tests-locate-def "format-list-node") + (eval-defun t) + (edebug-tests-setup-@ "format-node" '((a [b])) t) + (let ((edebug-trace t) + (trace-start (with-current-buffer + (get-buffer-create edebug-trace-buffer) (point-max)))) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "format-node" "start") + "g" (should (equal edebug-tests-@-result "{a[b]}"))) + (with-current-buffer edebug-trace-buffer + (should (string= + "{ edebug-test-code-format-node args: ((a [b])) +:{ edebug-test-code-format-list-node args: ((a [b])) +::{ edebug-test-code-format-node args: (a) +::} edebug-test-code-format-node result: a +::{ edebug-test-code-format-node args: ([b]) +:::{ edebug-test-code-format-vector-node args: ([b]) +::::{ edebug-test-code-format-node args: (b) +::::} edebug-test-code-format-node result: b +:::} edebug-test-code-format-vector-node result: [b] +::} edebug-test-code-format-node result: [b] +:} edebug-test-code-format-list-node result: {a[b]} +} edebug-test-code-format-node result: {a[b]} +" (buffer-substring trace-start (point-max)))))))) + +(ert-deftest edebug-tests-evaluate-expressions () + "Edebug can evaluate an expression in the context outside of itself." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "range" '(2) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "range" "start") + "SPC SPC f" (edebug-tests-should-be-at "range" "test") + (edebug-tests-should-match-result-in-messages "t") + "e (- SPC num SPC index) RET" + ;; Edebug just prints the result without "Result:" + (should (string-match-p + (regexp-quote "2 (#o2, #x2, ?\\C-b)") + edebug-tests-messages)) + "g" (should (equal edebug-tests-@-result '(0 1)))) + + ;; Do it again with lexical-binding turned off. + (setq lexical-binding nil) + (eval-buffer) + (should-not lexical-binding) + (edebug-tests-setup-@ "range" '(2) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "range" "start") + "SPC SPC f" (edebug-tests-should-be-at "range" "test") + (edebug-tests-should-match-result-in-messages "t") + "e (- SPC num SPC index) RET" + ;; Edebug just prints the result without "Result:" + (should (string-match-p + (regexp-quote "2 (#o2, #x2, ?\\C-b)") + edebug-tests-messages)) + "g" (should (equal edebug-tests-@-result '(0 1)))))) + +(ert-deftest edebug-tests-step-into-function () + "Edebug can step into a function." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "format-node" '([b]) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "format-node" "start") + "SPC SPC SPC SPC" + (edebug-tests-should-be-at "format-node" "vbefore") + "i" (edebug-tests-should-be-at "format-vector-node" "start") + "g" (should (equal edebug-tests-@-result "[b]"))))) + +(ert-deftest edebug-tests-error-stepping-into-subr () + "Edebug refuses to step into a C function." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "format-node" '([b]) t) + (let* ((debug-on-error nil) + (edebug-on-error nil) + error-message + (command-error-function (lambda (&rest args) + (setq error-message (cl-cadar args))))) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "format-node" "start") + "SPC" (edebug-tests-should-be-at "format-node" "vectorp") + "i" (should (string-match-p "vectorp is a built-in function" + error-message)) + ;; The error stopped the keyboard macro. Start it again. + (should-not executing-kbd-macro) + (setq executing-kbd-macro t) + "g" (should (equal edebug-tests-@-result "[b]")))))) + +(ert-deftest edebug-tests-step-into-macro-error () + "Edebug gives an error on trying to step into a macro (Bug#26847)." + :expected-result :failed + (ert-fail "Forcing failure because letting this test run aborts the others.") + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "try-flavors" nil t) + (let* ((debug-on-error nil) + (edebug-on-error nil) + (error-message "") + (command-error-function (lambda (&rest args) + (setq error-message (cl-cadar args))))) + (edebug-tests-run-kbd-macro + "@ SPC SPC SPC SPC SPC" + (edebug-tests-should-be-at "try-flavors" "macro") + "i" (should (string-match-p "edebug-test-code-try-flavors is a macro" + error-message)) + ;; The error stopped the keyboard macro. Start it again. + (should-not executing-kbd-macro) + (setq executing-kbd-macro t) + "g" (should (equal edebug-tests-@-result + '("chocolate" "strawberry"))))))) + +(ert-deftest edebug-tests-step-into-generic-method () + "Edebug can step into a generic method (Bug#22294)." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "use-methods" nil t) + (edebug-tests-run-kbd-macro + "@ SPC" (edebug-tests-should-be-at "use-methods" "number") + "i" (edebug-tests-should-be-at "emphasize-1" "start") + "gg" (should (equal edebug-tests-@-result + '("The number is not 101 or 99, but 100!" + "***yes***")))))) + +(ert-deftest edebug-tests-break-in-lambda-out-of-defining-context () + "Edebug observes a breakpoint in a lambda executed out of defining context." + (edebug-tests-with-normal-env + (edebug-tests-locate-def "make-lambda") + (eval-defun t) + (goto-char (edebug-tests-get-stop-point "make-lambda" "x")) + (edebug-set-breakpoint t) + (edebug-tests-setup-@ "use-lambda" nil t) + (edebug-tests-run-kbd-macro + "@g" (edebug-tests-should-be-at "make-lambda" "x") + (edebug-tests-should-match-result-in-messages "1 (#o1, #x1, ?\\C-a)") + "g" (should (equal edebug-tests-@-result '(11 12 13)))))) + +(ert-deftest edebug-tests-respects-initial-mode () + "Edebug can stop first at breakpoint instead of first instrumented function." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "fac" '(4) t) + (goto-char (edebug-tests-get-stop-point "fac" "mult")) + (edebug-set-breakpoint t) + (setq edebug-initial-mode 'go) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "fac" "mult") + (edebug-tests-should-match-result-in-messages "1 (#o1, #x1, ?\\C-a)") + "G" (should (equal edebug-tests-@-result 24))))) + +(ert-deftest edebug-tests-step-through-non-definition () + "Edebug can step through a non-defining form." + (edebug-tests-with-normal-env + (goto-char (edebug-tests-get-stop-point "try-flavors" "end-unless")) + (edebug-tests-run-kbd-macro + "C-u C-M-x" + "SPC SPC" (edebug-tests-should-be-at "try-flavors" "nutty") + (edebug-tests-should-match-result-in-messages "nil") + "SPC" (edebug-tests-should-be-at "try-flavors" "setq") + "f" (edebug-tests-should-be-at "try-flavors" "end-setq") + (edebug-tests-should-match-result-in-messages "\"chocolate\"") + "g"))) + +(ert-deftest edebug-tests-conditional-breakpoints-can-use-lexical-variables () + "Edebug can set a conditional breakpoint using a lexical variable. Bug#12685" + (edebug-tests-with-normal-env + (should lexical-binding) + (edebug-tests-setup-@ "fac" '(5) t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at "fac" "start") + ;; Set conditional breakpoint at end of next line. + "C-n C-e x (eql SPC n SPC 3) RET" + "g" (edebug-tests-should-be-at "fac" "mult") + (edebug-tests-should-match-result-in-messages + "6 (#o6, #x6, ?\\C-f)")))) + +(ert-deftest edebug-tests-writable-buffer-state-is-preserved () + "On Edebug exit writable buffers are still writable (Bug#14144)." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "choices" '(0) t) + (read-only-mode -1) + (edebug-tests-run-kbd-macro + "@g" (should (equal edebug-tests-@-result "zero"))) + (barf-if-buffer-read-only))) + +(ert-deftest edebug-tests-list-containing-empty-string-result-printing () + "Edebug correctly prints a list containing only an empty string (Bug#17934)." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "empty-string-list" nil t) + (edebug-tests-run-kbd-macro + "@ SPC" (edebug-tests-should-be-at + "empty-string-list" "step") + (edebug-tests-should-match-result-in-messages "(\"\")") + "g"))) + +(ert-deftest edebug-tests-evaluation-of-current-buffer-bug-19611 () + "Edebug can evaluate `current-buffer' in correct context. (Bug#19611)." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "current-buffer" nil t) + (edebug-tests-run-kbd-macro + "@" (edebug-tests-should-be-at + "current-buffer" "start") + "SPC SPC SPC" (edebug-tests-should-be-at + "current-buffer" "body") + "e (current-buffer) RET" + ;; Edebug just prints the result without "Result:" + (should (string-match-p + (regexp-quote "*edebug-test-code-buffer*") + edebug-tests-messages)) + "g" (should (equal edebug-tests-@-result + "current-buffer: *edebug-test-code-buffer*"))))) + +(ert-deftest edebug-tests-trivial-backquote () + "Edebug can instrument a trivial backquote expression (Bug#23651)." + (edebug-tests-with-normal-env + (read-only-mode -1) + (delete-region (point-min) (point-max)) + (insert "`1") + (read-only-mode) + (edebug-eval-defun nil) + (should (string-match-p (regexp-quote "1 (#o1, #x1, ?\\C-a)") + edebug-tests-messages)) + (setq edebug-tests-messages "") + + (setq edebug-initial-mode 'go) + ;; In Bug#23651 Edebug would hang reading `1. + (edebug-eval-defun t))) + +(ert-deftest edebug-tests-trivial-comma () + "Edebug can read a trivial comma expression (Bug#23651)." + (edebug-tests-with-normal-env + (read-only-mode -1) + (delete-region (point-min) (point-max)) + (insert ",1") + (read-only-mode) + (should-error (edebug-eval-defun t)))) + +(ert-deftest edebug-tests-circular-read-syntax () + "Edebug can instrument code using circular read object syntax (Bug#23660)." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "circular-read-syntax" nil t) + (edebug-tests-run-kbd-macro + "@" (should (eql (car edebug-tests-@-result) + (cdr edebug-tests-@-result)))))) + +(ert-deftest edebug-tests-hash-read-syntax () + "Edebug can instrument code which uses # read syntax (Bug#25068)." + (edebug-tests-with-normal-env + (edebug-tests-setup-@ "hash-read-syntax" nil t) + (edebug-tests-run-kbd-macro + "@g" (should (equal edebug-tests-@-result + '(#("abcd" 1 3 (face italic)) 511)))))) + +(provide 'edebug-tests) +;;; edebug-tests.el ends here commit 68baca3ee142b42de0bbe4eba84945780fd157d6 Author: Gemini Lasswell Date: Thu Sep 21 13:35:45 2017 -0700 Catch more messages in ert-with-message-capture * lisp/emacs-lisp/ert-x.el (ert-with-message-capture): Capture messages from prin1, princ and print. (ert--make-message-advice): New function. (ert--make-print-advice): New function. diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el index 6d9a7d9211..5af5262e5d 100644 --- a/lisp/emacs-lisp/ert-x.el +++ b/lisp/emacs-lisp/ert-x.el @@ -286,27 +286,60 @@ BUFFER defaults to current buffer. Does not modify BUFFER." (defmacro ert-with-message-capture (var &rest body) - "Execute BODY while collecting anything written with `message' in VAR. + "Execute BODY while collecting messages in VAR. -Capture all messages produced by `message' when it is called from -Lisp, and concatenate them separated by newlines into one string. +Capture messages issued by Lisp code and concatenate them +separated by newlines into one string. This includes messages +written by `message' as well as objects printed by `print', +`prin1' and `princ' to the echo area. Messages issued from C +code using the above mentioned functions will not be captured. This is useful for separating the issuance of messages by the code under test from the behavior of the *Messages* buffer." (declare (debug (symbolp body)) (indent 1)) - (let ((g-advice (gensym))) + (let ((g-message-advice (gensym)) + (g-print-advice (gensym)) + (g-collector (gensym))) `(let* ((,var "") - (,g-advice (lambda (func &rest args) - (if (or (null args) (equal (car args) "")) - (apply func args) - (let ((msg (apply #'format-message args))) - (setq ,var (concat ,var msg "\n")) - (funcall func "%s" msg)))))) - (advice-add 'message :around ,g-advice) + (,g-collector (lambda (msg) (setq ,var (concat ,var msg)))) + (,g-message-advice (ert--make-message-advice ,g-collector)) + (,g-print-advice (ert--make-print-advice ,g-collector))) + (advice-add 'message :around ,g-message-advice) + (advice-add 'prin1 :around ,g-print-advice) + (advice-add 'princ :around ,g-print-advice) + (advice-add 'print :around ,g-print-advice) (unwind-protect (progn ,@body) - (advice-remove 'message ,g-advice))))) + (advice-remove 'print ,g-print-advice) + (advice-remove 'princ ,g-print-advice) + (advice-remove 'prin1 ,g-print-advice) + (advice-remove 'message ,g-message-advice))))) + +(defun ert--make-message-advice (collector) + "Create around advice for `message' for `ert-collect-messages'. +COLLECTOR will be called with the message before it is passed +to the real `message'." + (lambda (func &rest args) + (if (or (null args) (equal (car args) "")) + (apply func args) + (let ((msg (apply #'format-message args))) + (funcall collector (concat msg "\n")) + (funcall func "%s" msg))))) + +(defun ert--make-print-advice (collector) + "Create around advice for print functions for `ert-collect-messsges'. +The created advice function will just call the original function +unless the output is going to the echo area (when PRINTCHARFUN is +t or PRINTCHARFUN is nil and `standard-output' is t). If the +output is destined for the echo area, the advice function will +convert it to a string and pass it to COLLECTOR first." + (lambda (func object &optional printcharfun) + (if (not (eq t (or printcharfun standard-output))) + (funcall func object printcharfun) + (funcall collector (with-output-to-string + (funcall func object))) + (funcall func object printcharfun)))) (provide 'ert-x) commit 28e0c410c972ad8db9bf8a5d32f64921108104d7 Author: Eli Zaretskii Date: Thu Sep 21 11:29:11 2017 +0300 ; * lisp/mouse.el (secondary-selection-exist-p): Doc fix. diff --git a/lisp/mouse.el b/lisp/mouse.el index 4a4fe52c87..169d2632f4 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -1917,7 +1917,7 @@ CLICK position, kill the secondary selection." (gui-set-selection 'SECONDARY str)))) (defun secondary-selection-exist-p () - "Return non-nil if there is the secondary selection in current buffer." + "Return non-nil if the secondary selection exists in the current buffer." (memq mouse-secondary-overlay (overlays-in (point-min) (point-max)))) (defun secondary-selection-to-region () commit 31e1d9ef2f70937cd0f93f67399620201ded300b Author: Tak Kunihiro Date: Thu Sep 21 11:26:00 2017 +0300 Support setting region from secondary selection and vice versa * lisp/mouse.el (secondary-selection-exist-p): New function to allow callers to tell existence of the secondary selection in current buffer. (secondary-selection-to-region): New function to set beginning and end of the region from those of the secondary selection. (secondary-selection-from-region): New function to set beginning and end of the secondary selection from those of the region. (Bug#27530) * etc/NEWS: Mention the new functions. diff --git a/etc/NEWS b/etc/NEWS index a685a9fbe9..280ab64f37 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1819,6 +1819,13 @@ can be replicated simply by setting 'comment-auto-fill-only-comments'. ** New pcase pattern 'rx' to match against a rx-style regular expression. For details, see the doc string of 'rx--pcase-macroexpander'. +--- +** New functions to set region from secondary selection and vice versa. +The new functions 'secondary-selection-to-region' and +'secondary-selection-from-region' let you set the beginning and the +end of the region from those of the secondary selection and vise +versa. + * Changes in Emacs 26.1 on Non-Free Operating Systems diff --git a/lisp/mouse.el b/lisp/mouse.el index 3f448f018a..4a4fe52c87 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -1916,6 +1916,34 @@ CLICK position, kill the secondary selection." (> (length str) 0) (gui-set-selection 'SECONDARY str)))) +(defun secondary-selection-exist-p () + "Return non-nil if there is the secondary selection in current buffer." + (memq mouse-secondary-overlay (overlays-in (point-min) (point-max)))) + +(defun secondary-selection-to-region () + "Set beginning and end of the region to those of the secondary selection. +This puts mark and point at the beginning and the end of the +secondary selection, respectively. This works when the secondary +selection exists and the region does not exist in current buffer; +the secondary selection will be deleted afterward. +If the region is active, or the secondary selection doesn't exist, +this function does nothing." + (when (and (not (region-active-p)) + (secondary-selection-exist-p)) + (let ((beg (overlay-start mouse-secondary-overlay)) + (end (overlay-end mouse-secondary-overlay))) + (push-mark beg t t) + (goto-char end)) + ;; Delete the secondary selection on current buffer. + (delete-overlay mouse-secondary-overlay))) + +(defun secondary-selection-from-region () + "Set beginning and end of the secondary selection to those of the region. +When there is no region, this function does nothing." + (when (region-active-p) ; Create the secondary selection from the region. + (delete-overlay mouse-secondary-overlay) ; Delete the secondary selection even on a different buffer. + (move-overlay mouse-secondary-overlay (region-beginning) (region-end)))) + (defcustom mouse-buffer-menu-maxlen 20 "Number of buffers in one pane (submenu) of the buffer menu. commit 047f02f00f602b9aef63ae8938e12f3f0ab481eb Author: Paul Eggert Date: Wed Sep 20 11:49:12 2017 -0700 Fix new copy-directory bug with empty dirs Problem reported by Afdam Plaice (Bug#28520) and by Eli Zaretskii (Bug#28483#34). This is another bug that I introduced in my recent copy-directory changes. * lisp/files.el (copy-directory): Work with empty subdirectories, too. * test/lisp/files-tests.el (files-tests--copy-directory): Test for this bug. diff --git a/lisp/files.el b/lisp/files.el index 0c30d40c13..f0a1f2380d 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -5564,7 +5564,7 @@ into NEWNAME instead." (filetype (car (file-attributes file)))) (cond ((eq filetype t) ; Directory but not a symlink. - (copy-directory file newname keep-time parents)) + (copy-directory file target keep-time parents t)) ((stringp filetype) ; Symbolic link (make-symbolic-link filetype target t)) ((copy-file file target t keep-time))))) diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el index f2a9a32180..285a884b69 100644 --- a/test/lisp/files-tests.el +++ b/test/lisp/files-tests.el @@ -399,11 +399,16 @@ name (Bug#28412)." (dirname (file-name-as-directory dir)) (source (concat dirname "source")) (dest (concat dirname "dest/new/directory/")) - (file (concat (file-name-as-directory source) "file"))) + (file (concat (file-name-as-directory source) "file")) + (source2 (concat dirname "source2")) + (dest2 (concat dirname "dest/new2"))) (make-directory source) (write-region "" nil file) (copy-directory source dest t t t) (should (file-exists-p (concat dest "file"))) + (make-directory (concat (file-name-as-directory source2) "a") t) + (copy-directory source2 dest2) + (should (file-directory-p (concat (file-name-as-directory dest2) "a"))) (delete-directory dir 'recursive))) (provide 'files-tests) commit 3a09343eabaa751e7d40f0a21af5c63427d9a850 Author: Mark Oteiza Date: Wed Sep 20 11:39:37 2017 -0400 Teach Emacs to set XTerm window titles * lisp/term/xterm.el (terminal-init-xterm): Add initialization. (xterm--init-frame-title, xterm-set-window-title-flag): (xterm-unset-window-title-flag, xterm-set-window-title): New functions. (xterm-window-title-flag): New variable. (xterm-set-window-title): New custom variable. * etc/NEWS: Mention it. diff --git a/etc/NEWS b/etc/NEWS index 371cdf686c..0e62a2bbb4 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -37,6 +37,11 @@ When you add a new item, use the appropriate mark if you are sure it applies, * Changes in Specialized Modes and Packages in Emacs 27.1 +** Enhanced xterm support + +*** New variable 'xterm-set-window-title' controls whether Emacs +sets the XTerm window title. The default is to set the window title. + * New Modes and Packages in Emacs 27.1 diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el index 4f79703833..6a17d382b0 100644 --- a/lisp/term/xterm.el +++ b/lisp/term/xterm.el @@ -68,6 +68,11 @@ string bytes that can be copied is 3/4 of this value." :version "25.1" :type 'integer) +(defcustom xterm-set-window-title t + "Whether Emacs should set window titles to an Emacs frame in an XTerm." + :version "27.1" + :type 'boolean) + (defconst xterm-paste-ending-sequence "\e[201~" "Characters send by the terminal to end a bracketed paste.") @@ -802,6 +807,8 @@ We run the first FUNCTION whose STRING matches the input events." (when (memq 'setSelection xterm-extra-capabilities) (xterm--init-activate-set-selection))) + (when xterm-set-window-title + (xterm--init-frame-title)) ;; Unconditionally enable bracketed paste mode: terminals that don't ;; support it just ignore the sequence. (xterm--init-bracketed-paste-mode) @@ -828,6 +835,34 @@ We run the first FUNCTION whose STRING matches the input events." "Terminal initialization for `gui-set-selection'." (set-terminal-parameter nil 'xterm--set-selection t)) +(defun xterm--init-frame-title () + "Terminal initialization for XTerm frame titles." + (xterm-set-window-title) + (add-hook 'after-make-frame-functions 'xterm-set-window-title-flag) + (add-hook 'window-configuration-change-hook 'xterm-unset-window-title-flag) + (add-hook 'post-command-hook 'xterm-set-window-title) + (add-hook 'minibuffer-exit-hook 'xterm-set-window-title)) + +(defvar xterm-window-title-flag nil + "Whether a new frame has been created, calling for a title update.") + +(defun xterm-set-window-title-flag (_frame) + "Set `xterm-window-title-flag'. +See `xterm--init-frame-title'" + (setq xterm-window-title-flag t)) + +(defun xterm-unset-window-title-flag () + (when xterm-window-title-flag + (setq xterm-window-title-flag nil) + (xterm-set-window-title))) + +(defun xterm-set-window-title (&optional terminal) + "Set the window title of the Xterm TERMINAL. +The title is constructed from `frame-title-format'." + (send-string-to-terminal + (format "\e]2;%s\a" (format-mode-line frame-title-format)) + terminal)) + (defun xterm--selection-char (type) (pcase type ('PRIMARY "p") commit fbd15836af69b1d156f39664f2512f85278fdb08 Author: Eli Zaretskii Date: Wed Sep 20 16:40:20 2017 +0300 * doc/lispref/strings.texi (Formatting Strings): Improve indexing. diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index 23961f99ef..219225d412 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -824,8 +824,9 @@ to the produced string representations of the argument @var{objects}. @end defun @defun format-message string &rest objects -@cindex curved quotes -@cindex curly quotes +@cindex curved quotes, in formatted messages +@cindex curly quotes, in formatted messages +@cindex @code{text-quoting-style}, and formatting messages This function acts like @code{format}, except it also converts any grave accents (@t{`}) and apostrophes (@t{'}) in @var{string} as per the value of @code{text-quoting-style}. commit f16a8d5dbd3bb8a319c951bdde9a6a75dbdb8c17 Author: Eli Zaretskii Date: Wed Sep 20 10:16:11 2017 +0300 Fix 2 testsuite tests for MS-Windows * test/lisp/ibuffer-tests.el (test-buffer-list): Don't try to create files with "*" in their names. * test/src/editfns-tests.el (format-time-string-with-zone): Adapt results to MS-Windows build. Reported by Fabrice Popineau . diff --git a/test/lisp/ibuffer-tests.el b/test/lisp/ibuffer-tests.el index d65acf6071..35605ca28d 100644 --- a/test/lisp/ibuffer-tests.el +++ b/test/lisp/ibuffer-tests.el @@ -456,11 +456,14 @@ (funcall create-non-file-buffer "ibuf-test-8a" :mode #'artist-mode)) (bufB (funcall create-non-file-buffer "*ibuf-test-8b*" :size 32)) - (bufC (funcall create-file-buffer "ibuf-test8c" :suffix "*" - :size 64)) - (bufD (funcall create-file-buffer "*ibuf-test8d" :size 128)) - (bufE (funcall create-file-buffer "*ibuf-test8e" :suffix "*<2>" - :size 16)) + (bufC (or (memq system-type '(ms-dos windows-nt)) + (funcall create-file-buffer "ibuf-test8c" :suffix "*" + :size 64))) + (bufD (or (memq system-type '(ms-dos windows-nt)) + (funcall create-file-buffer "*ibuf-test8d" :size 128))) + (bufE (or (memq system-type '(ms-dos windows-nt)) + (funcall create-file-buffer "*ibuf-test8e" + :suffix "*<2>" :size 16))) (bufF (and (funcall create-non-file-buffer "*ibuf-test8f*") (funcall create-non-file-buffer "*ibuf-test8f*" :size 8)))) @@ -479,22 +482,28 @@ (name . "test.*8b") (size-gt . 31) (not visiting-file))))) - (should (ibuffer-included-in-filters-p - bufC '((and (not (starred-name)) - (visiting-file) - (name . "8c[^*]*\\*") - (size-lt . 65))))) - (should (ibuffer-included-in-filters-p - bufD '((and (not (starred-name)) - (visiting-file) - (name . "\\`\\*.*test8d") - (size-lt . 129) - (size-gt . 127))))) - (should (ibuffer-included-in-filters-p - bufE '((and (starred-name) - (visiting-file) - (name . "8e.*?\\*<[[:digit:]]+>") - (size-gt . 10))))) + ;; MS-DOS and MS-Windows don't allow "*" in file names. + (or (memq system-type '(ms-dos windows-nt)) + (should (ibuffer-included-in-filters-p + bufC '((and (not (starred-name)) + (visiting-file) + (name . "8c[^*]*\\*") + (size-lt . 65)))))) + ;; MS-DOS and MS-Windows don't allow "*" in file names. + (or (memq system-type '(ms-dos windows-nt)) + (should (ibuffer-included-in-filters-p + bufD '((and (not (starred-name)) + (visiting-file) + (name . "\\`\\*.*test8d") + (size-lt . 129) + (size-gt . 127)))))) + ;; MS-DOS and MS-Windows don't allow "*" in file names. + (or (memq system-type '(ms-dos windows-nt)) + (should (ibuffer-included-in-filters-p + bufE '((and (starred-name) + (visiting-file) + (name . "8e.*?\\*<[[:digit:]]+>") + (size-gt . 10)))))) (should (ibuffer-included-in-filters-p bufF '((and (starred-name) (not (visiting-file)) diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el index f910afaf71..70dc9372fa 100644 --- a/test/src/editfns-tests.el +++ b/test/src/editfns-tests.el @@ -169,7 +169,11 @@ ;; Negative UTC offset, as a Lisp integer. (should (string-equal (format-time-string format look -28800) - "1972-06-30 15:59:59.999 -0800 (-08)")) + ;; MS-Windows build replaces unrecognizable TZ values, + ;; such as "-08", with "ZZZ". + (if (eq system-type 'windows-nt) + "1972-06-30 15:59:59.999 -0800 (ZZZ)" + "1972-06-30 15:59:59.999 -0800 (-08)"))) ;; Positive UTC offset that is not an hour multiple, as a string. (should (string-equal (format-time-string format look "IST-5:30") commit 965cffd89cd5727c46a1b0999bef440f8e316742 Author: Mark Oteiza Date: Tue Sep 19 22:21:37 2017 -0400 Rename timer-list to list-timers * doc/emacs/anti.texi (Antinews): * doc/lispref/os.texi (Timers): * etc/NEWS: * lisp/emacs-lisp/timer-list.el: (timer-list-mode): Rename timer-list to list-timers. diff --git a/doc/emacs/anti.texi b/doc/emacs/anti.texi index ffec915cb1..547dbd1b45 100644 --- a/doc/emacs/anti.texi +++ b/doc/emacs/anti.texi @@ -94,7 +94,7 @@ happen. The variables @code{'attempt-stack-overflow-recovery} and @code{attempt-orderly-shutdown-on-fatal-signal} are therefore removed. @item -The @code{timer-list} command was removed, as we decided timers are +The @code{list-timers} command was removed, as we decided timers are not user-level feature, and therefore users should not be allowed to mess with them. Ask an Emacs Lisp guru near you for help if you have a runaway timer in your session. (Of course, as you move back in diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 441fda5d82..af646ce40f 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -1885,8 +1885,8 @@ one of these functions; the arrival of the specified time will not cause anything special to happen. @end defun -@findex timer-list -The @code{timer-list} command lists all the currently active timers. +@findex list-timers +The @code{list-timers} command lists all the currently active timers. There's only one command available in the buffer displayed: @kbd{c} (@code{timer-list-cancel}) that will cancel the timer on the line under point. diff --git a/etc/NEWS b/etc/NEWS index a814c3ec20..a685a9fbe9 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -367,7 +367,7 @@ see the node "Connection Local Variables" in the ELisp manual. puny.el library, so that one can visit Web sites with non-ASCII URLs. +++ -** The new 'timer-list' command lists all active timers in a buffer, +** The new 'list-timers' command lists all active timers in a buffer, where you can cancel them with the 'c' command. +++ diff --git a/lisp/emacs-lisp/timer-list.el b/lisp/emacs-lisp/timer-list.el index 44a315f980..69c6741983 100644 --- a/lisp/emacs-lisp/timer-list.el +++ b/lisp/emacs-lisp/timer-list.el @@ -25,7 +25,7 @@ ;;; Code: ;;;###autoload -(defun timer-list (&optional _ignore-auto _nonconfirm) +(defun list-timers (&optional _ignore-auto _nonconfirm) "List all timers in a buffer." (interactive) (pop-to-buffer-same-window (get-buffer-create "*timer-list*")) @@ -67,7 +67,7 @@ (goto-char (point-min))) ;; This command can be destructive if they don't know what they are ;; doing. Kids, don't try this at home! -;;;###autoload (put 'timer-list 'disabled "Beware: manually canceling timers can ruin your Emacs session.") +;;;###autoload (put 'list-timers 'disabled "Beware: manually canceling timers can ruin your Emacs session.") (defvar timer-list-mode-map (let ((map (make-sparse-keymap))) @@ -84,7 +84,7 @@ (setq bidi-paragraph-direction 'left-to-right) (setq truncate-lines t) (buffer-disable-undo) - (setq-local revert-buffer-function 'timer-list) + (setq-local revert-buffer-function #'list-timers) (setq buffer-read-only t) (setq header-line-format (format "%4s %10s %8s %s" commit a5fec62b519ae8c0a6528366ac8b71cd0c7ac52e Author: Alan Third Date: Fri Sep 8 19:26:47 2017 +0100 Provide native touchpad scrolling on macOS * etc/NEWS: Describe changes. * lisp/term/ns-win.el (mouse-wheel-scroll-amount, mouse-wheel-progressive-speed): Set to smarter values for macOS touchpads. * src/nsterm.m (emacsView::mouseDown): Use precise scrolling deltas to calculate scrolling for touchpads and mouse wheels. (syms_of_nsterm): Add variables 'ns-use-system-mwheel-acceleration', 'ns-touchpad-scroll-line-height' and 'ns-touchpad-use-momentum'. * src/keyboard.c (make_lispy_event): Pass on .arg when relevant. * src/termhooks.h (event_kind): Update comments re. WHEEL_EVENT. * lisp/mwheel.el (mwheel-scroll): Use line count. * lisp/subr.el (event-line-count): New function. diff --git a/etc/NEWS b/etc/NEWS index 5aa57a7776..a814c3ec20 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1882,6 +1882,12 @@ of frame decorations on macOS 10.9+. --- ** 'process-attributes' on Darwin systems now returns more information. +--- +** Mousewheel and trackpad scrolling on macOS 10.7+ now behaves more +like the macOS default. The new variables +'ns-use-system-mwheel-acceleration', 'ns-touchpad-scroll-line-height' +and 'ns-touchpad-use-momentum' can be used to customise the behavior. + ---------------------------------------------------------------------- This file is part of GNU Emacs. diff --git a/lisp/mwheel.el b/lisp/mwheel.el index 2956ba5516..0c0dcb3beb 100644 --- a/lisp/mwheel.el +++ b/lisp/mwheel.el @@ -232,6 +232,7 @@ non-Windows systems." ;; When the double-mouse-N comes in, a mouse-N has been executed already, ;; So by adding things up we get a squaring up (1, 3, 6, 10, 15, ...). (setq amt (* amt (event-click-count event)))) + (when (numberp amt) (setq amt (* amt (event-line-count event)))) (unwind-protect (let ((button (mwheel-event-button event))) (cond ((eq button mouse-wheel-down-event) diff --git a/lisp/subr.el b/lisp/subr.el index 96b1ac19b4..cf15ec287f 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -1270,6 +1270,11 @@ See `event-start' for a description of the value returned." "Return the multi-click count of EVENT, a click or drag event. The return value is a positive integer." (if (and (consp event) (integerp (nth 2 event))) (nth 2 event) 1)) + +(defsubst event-line-count (event) + "Return the line count of EVENT, a mousewheel event. +The return value is a positive integer." + (if (and (consp event) (integerp (nth 3 event))) (nth 3 event) 1)) ;;;; Extracting fields of the positions in an event. diff --git a/lisp/term/ns-win.el b/lisp/term/ns-win.el index 68b659bf75..bc211ea958 100644 --- a/lisp/term/ns-win.el +++ b/lisp/term/ns-win.el @@ -736,6 +736,25 @@ See the documentation of `create-fontset-from-fontset-spec' for the format.") (global-unset-key [horizontal-scroll-bar drag-mouse-1]) +;;;; macOS-like defaults for trackpad and mouse wheel scrolling on +;;;; macOS 10.7+. + +;; FIXME: This doesn't look right. Is there a better way to do this +;; that keeps customize happy? +(let ((appkit-version (progn + (string-match "^appkit-\\([^\s-]*\\)" ns-version-string) + (string-to-number (match-string 1 ns-version-string))))) + ;; Appkit 1138 ~= macOS 10.7. + (when (and (featurep 'cocoa) (>= appkit-version 1138)) + (setq mouse-wheel-scroll-amount '(1 ((shift) . 5) ((control)))) + (put 'mouse-wheel-scroll-amount 'customized-value + (list (custom-quote (symbol-value 'mouse-wheel-scroll-amount)))) + + (setq mouse-wheel-progressive-speed nil) + (put 'mouse-wheel-progressive-speed 'customized-value + (list (custom-quote (symbol-value 'mouse-wheel-progressive-speed)))))) + + ;;;; Color support. ;; Functions for color panel + drag diff --git a/src/keyboard.c b/src/keyboard.c index 4db50be855..e8701b8870 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -5925,7 +5925,10 @@ make_lispy_event (struct input_event *event) ASIZE (wheel_syms)); } - if (event->modifiers & (double_modifier | triple_modifier)) + if (NUMBERP (event->arg)) + return list4 (head, position, make_number (double_click_count), + event->arg); + else if (event->modifiers & (double_modifier | triple_modifier)) return list3 (head, position, make_number (double_click_count)); else return list2 (head, position); diff --git a/src/nsterm.m b/src/nsterm.m index 2751533533..776635980e 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -6498,24 +6498,139 @@ - (void)mouseDown: (NSEvent *)theEvent if ([theEvent type] == NSEventTypeScrollWheel) { - CGFloat delta = [theEvent deltaY]; - /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */ - if (delta == 0) +#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 + if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)]) { - delta = [theEvent deltaX]; - if (delta == 0) +#endif + /* If the input device is a touchpad or similar, use precise + * scrolling deltas. These are measured in pixels, so we + * have to add them up until they exceed one line height, + * then we can send a scroll wheel event. + * + * If the device only has coarse scrolling deltas, like a + * real mousewheel, the deltas represent a ratio of whole + * lines, so round up the number of lines. This means we + * always send one scroll event per click, but can still + * scroll more than one line if the OS tells us to. + */ + bool horizontal; + int lines = 0; + int scrollUp = NO; + + /* FIXME: At the top or bottom of the buffer we should + * ignore momentum-phase events. */ + if (! ns_touchpad_use_momentum + && [theEvent momentumPhase] != NSEventPhaseNone) + return; + + if ([theEvent hasPreciseScrollingDeltas]) { - NSTRACE_MSG ("deltaIsZero"); - return; + static int totalDeltaX, totalDeltaY; + int lineHeight; + + if (NUMBERP (ns_touchpad_scroll_line_height)) + lineHeight = XINT (ns_touchpad_scroll_line_height); + else + { + /* FIXME: Use actual line height instead of the default. */ + lineHeight = default_line_pixel_height + (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe))); + } + + if ([theEvent phase] == NSEventPhaseBegan) + { + totalDeltaX = 0; + totalDeltaY = 0; + } + + totalDeltaX += [theEvent scrollingDeltaX]; + totalDeltaY += [theEvent scrollingDeltaY]; + + /* Calculate the number of lines, if any, to scroll, and + * reset the total delta for the direction we're NOT + * scrolling so that small movements don't add up. */ + if (abs (totalDeltaX) > abs (totalDeltaY) + && abs (totalDeltaX) > lineHeight) + { + horizontal = YES; + scrollUp = totalDeltaX > 0; + + lines = abs (totalDeltaX / lineHeight); + totalDeltaX = totalDeltaX % lineHeight; + totalDeltaY = 0; + } + else if (abs (totalDeltaY) >= abs (totalDeltaX) + && abs (totalDeltaY) > lineHeight) + { + horizontal = NO; + scrollUp = totalDeltaY > 0; + + lines = abs (totalDeltaY / lineHeight); + totalDeltaY = totalDeltaY % lineHeight; + totalDeltaX = 0; + } + + if (lines > 1 && ! ns_use_system_mwheel_acceleration) + lines = 1; } - emacs_event->kind = HORIZ_WHEEL_EVENT; + else + { + CGFloat delta; + + if ([theEvent scrollingDeltaY] == 0) + { + horizontal = YES; + delta = [theEvent scrollingDeltaX]; + } + else + { + horizontal = NO; + delta = [theEvent scrollingDeltaY]; + } + + lines = (ns_use_system_mwheel_acceleration) + ? ceil (fabs (delta)) : 1; + + scrollUp = delta > 0; + } + + if (lines == 0) + return; + + emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT; + emacs_event->arg = (make_number (lines)); + + emacs_event->code = 0; + emacs_event->modifiers = EV_MODIFIERS (theEvent) | + (scrollUp ? up_modifier : down_modifier); +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 } else - emacs_event->kind = WHEEL_EVENT; +#endif +#endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ +#if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070 + { + CGFloat delta = [theEvent deltaY]; + /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */ + if (delta == 0) + { + delta = [theEvent deltaX]; + if (delta == 0) + { + NSTRACE_MSG ("deltaIsZero"); + return; + } + emacs_event->kind = HORIZ_WHEEL_EVENT; + } + else + emacs_event->kind = WHEEL_EVENT; - emacs_event->code = 0; - emacs_event->modifiers = EV_MODIFIERS (theEvent) | - ((delta > 0) ? up_modifier : down_modifier); + emacs_event->code = 0; + emacs_event->modifiers = EV_MODIFIERS (theEvent) | + ((delta > 0) ? up_modifier : down_modifier); + } +#endif } else { @@ -6524,9 +6639,11 @@ - (void)mouseDown: (NSEvent *)theEvent emacs_event->modifiers = EV_MODIFIERS (theEvent) | EV_UDMODIFIERS (theEvent); } + XSETINT (emacs_event->x, lrint (p.x)); XSETINT (emacs_event->y, lrint (p.y)); EV_TRAILER (theEvent); + return; } @@ -9166,6 +9283,23 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with This variable is ignored on Mac OS X < 10.7 and GNUstep. */); ns_use_srgb_colorspace = YES; + DEFVAR_BOOL ("ns-use-system-mwheel-acceleration", + ns_use_system_mwheel_acceleration, + doc: /*Non-nil means use macOS's standard mouse wheel acceleration. +This variable is ignored on macOS < 10.7 and GNUstep. Default is t. */); + ns_use_system_mwheel_acceleration = YES; + + DEFVAR_LISP ("ns-touchpad-scroll-line-height", ns_touchpad_scroll_line_height, + doc: /*The number of pixels touchpad scrolling considers a line. +Nil or a non-number means use the default frame line height. +This variable is ignored on macOS < 10.7 and GNUstep. Default is nil. */); + ns_touchpad_scroll_line_height = Qnil; + + DEFVAR_BOOL ("ns-touchpad-use-momentum", ns_touchpad_use_momentum, + doc: /*Non-nil means touchpad scrolling uses momentum. +This variable is ignored on macOS < 10.7 and GNUstep. Default is t. */); + ns_touchpad_use_momentum = YES; + /* TODO: move to common code */ DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars, doc: /* Which toolkit scroll bars Emacs uses, if any. diff --git a/src/termhooks.h b/src/termhooks.h index 97c128ba4e..b5171bf122 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -116,7 +116,9 @@ enum event_kind .frame_or_window gives the frame the wheel event occurred in. .timestamp gives a timestamp (in - milliseconds) for the event. */ + milliseconds) for the event. + .arg may contain the number of + lines to scroll. */ HORIZ_WHEEL_EVENT, /* A wheel event generated by a second horizontal wheel that is present on some mice. See WHEEL_EVENT. */ commit 7b3d1c6beb54ef6c423a93df88aebfd6fecbe2c2 Author: Eli Zaretskii Date: Tue Sep 19 20:31:02 2017 +0300 Fix MinGW64 build broken by recent MinGW64 import libraries * configure.ac (W32_LIBS): Put -lusp10 before -lgdi32, as latest MinGW64 import libraries require that. (Bug#28493) * src/Makefile.in: Adjust commentary to the new order of w32 libraries. diff --git a/configure.ac b/configure.ac index 6452038d1b..0b0bb5e144 100644 --- a/configure.ac +++ b/configure.ac @@ -2077,15 +2077,15 @@ if test "${HAVE_W32}" = "yes"; then AC_SUBST(comma_space_version) AC_CONFIG_FILES([nt/emacs.rc nt/emacsclient.rc]) if test "${opsys}" = "cygwin"; then - W32_LIBS="$W32_LIBS -lkernel32 -luser32 -lgdi32 -lole32 -lcomdlg32" - W32_LIBS="$W32_LIBS -lusp10 -lcomctl32 -lwinspool" + W32_LIBS="$W32_LIBS -lkernel32 -luser32 -lusp10 -lgdi32" + W32_LIBS="$W32_LIBS -lole32 -lcomdlg32 -lcomctl32 -lwinspool" # Tell the linker that emacs.res is an object (which we compile from # the rc file), not a linker script. W32_RES_LINK="-Wl,emacs.res" else W32_OBJ="$W32_OBJ w32.o w32console.o w32heap.o w32inevt.o w32proc.o" - W32_LIBS="$W32_LIBS -lwinmm -lgdi32 -lcomdlg32" - W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32 -lusp10" + W32_LIBS="$W32_LIBS -lwinmm -lusp10 -lgdi32 -lcomdlg32" + W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32" W32_RES_LINK="\$(EMACSRES)" CLIENTRES="emacsclient.res" CLIENTW="emacsclientw\$(EXEEXT)" diff --git a/src/Makefile.in b/src/Makefile.in index 0e55ad4bb2..9a8c9c85f0 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -280,7 +280,7 @@ GNU_OBJC_CFLAGS=$(patsubst -specs=%-hardened-cc1,,@GNU_OBJC_CFLAGS@) ## w32xfns.o w32select.o image.o w32uniscribe.o if HAVE_W32, else ## empty. W32_OBJ=@W32_OBJ@ -## -lkernel32 -luser32 -lgdi32 -lole32 -lcomdlg32 lusp10 -lcomctl32 +## -lkernel32 -luser32 -lusp10 -lgdi32 -lole32 -lcomdlg32 -lcomctl32 ## --lwinspool if HAVE_W32, else empty. W32_LIBS=@W32_LIBS@ commit c83d0c5fdfd374d5c2e1547d05f02ab3b47a4a5a Author: Eli Zaretskii Date: Tue Sep 19 20:11:42 2017 +0300 Fix crashes in 'move-point-visually' in minibuffer windows * src/xdisp.c (Fmove_point_visually): Fix off-by-one error in comparing against the last valid glyph_row of a window glyph matrix. (Bug#28505) diff --git a/src/xdisp.c b/src/xdisp.c index dc5dbb0576..141275f15a 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -22395,8 +22395,8 @@ Value is the new character position of point. */) row += dir; else row -= dir; - if (row < MATRIX_FIRST_TEXT_ROW (w->current_matrix) - || row > MATRIX_BOTTOM_TEXT_ROW (w->current_matrix, w)) + if (!(MATRIX_FIRST_TEXT_ROW (w->current_matrix) <= row + && row < MATRIX_BOTTOM_TEXT_ROW (w->current_matrix, w))) goto simulate_display; if (dir > 0) commit 7f3d5f929d4e25cd2c0b89a13f4741eb02ce3e64 Author: Eli Zaretskii Date: Tue Sep 19 19:52:50 2017 +0300 * src/emacs.c (usage_message): Don't mention 'find-file'. diff --git a/src/emacs.c b/src/emacs.c index 1ad8af70a7..0fe7d9113b 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -252,7 +252,7 @@ Initialization options:\n\ "\ Action options:\n\ \n\ -FILE visit FILE using find-file\n\ +FILE visit FILE\n\ +LINE go to line LINE in next FILE\n\ +LINE:COLUMN go to line LINE, column COLUMN, in next FILE\n\ --directory, -L DIR prepend DIR to load-path (with :DIR, append DIR)\n\ @@ -260,13 +260,13 @@ FILE visit FILE using find-file\n\ --execute EXPR evaluate Emacs Lisp expression EXPR\n\ ", "\ ---file FILE visit FILE using find-file\n\ ---find-file FILE visit FILE using find-file\n\ +--file FILE visit FILE\n\ +--find-file FILE visit FILE\n\ --funcall, -f FUNC call Emacs Lisp function FUNC with no arguments\n\ --insert FILE insert contents of FILE into current buffer\n\ --kill exit without asking for confirmation\n\ --load, -l FILE load Emacs Lisp FILE using the load function\n\ ---visit FILE visit FILE using find-file\n\ +--visit FILE visit FILE\n\ \n\ ", "\ commit 68452822000fff0e44c40e966fb516fa01d219e7 Author: Eli Zaretskii Date: Tue Sep 19 19:48:27 2017 +0300 Fix a minor inaccuracy in the Emacs manual * doc/emacs/cmdargs.texi (Action Arguments): Don't mention 'find-file', as the implementation has changed. Reported by Everton J. Carpes in http://lists.gnu.org/archive/html/help-gnu-emacs/2017-09/msg00146.html. diff --git a/doc/emacs/cmdargs.texi b/doc/emacs/cmdargs.texi index 819459e0af..618a05d451 100644 --- a/doc/emacs/cmdargs.texi +++ b/doc/emacs/cmdargs.texi @@ -92,7 +92,7 @@ arguments.) @itemx --visit=@var{file} @cindex visiting files, command-line argument @vindex inhibit-startup-buffer-menu -Visit @var{file} using @code{find-file}. @xref{Visiting}. +Visit the specified @var{file}. @xref{Visiting}. When Emacs starts up, it displays the startup buffer in one window, and the buffer visiting @var{file} in another window @@ -111,12 +111,12 @@ Buffer Menu for this, change the variable @item +@var{linenum} @var{file} @opindex +@var{linenum} -Visit @var{file} using @code{find-file}, then go to line number -@var{linenum} in it. +Visit the specified @var{file}, then go to line number @var{linenum} +in it. @item +@var{linenum}:@var{columnnum} @var{file} -Visit @var{file} using @code{find-file}, then go to line number -@var{linenum} and put point at column number @var{columnnum}. +Visit the specified @var{file}, then go to line number @var{linenum} +and put point at column number @var{columnnum}. @item -l @var{file} @opindex -l commit 74d7bb94988055a49ac8f1cbc5af43ac31255581 Author: Eli Zaretskii Date: Tue Sep 19 19:32:09 2017 +0300 Fix errors in flyspell-post-command-hook * lisp/textmodes/ispell.el (ispell-get-decoded-string): Handle the case of a nil Nth element of the language dictionary slot. This avoids errors in 'flyspell-post-command-hook' when switching dictionaries with some spell-checkers. (Bug#28501) diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el index 0c0a51e7df..6a169622f5 100644 --- a/lisp/textmodes/ispell.el +++ b/lisp/textmodes/ispell.el @@ -1492,8 +1492,10 @@ This is passed to the Ispell process using the `-p' switch.") (assoc ispell-current-dictionary ispell-local-dictionary-alist) (assoc ispell-current-dictionary ispell-dictionary-alist) (error "No data for dictionary \"%s\" in `ispell-local-dictionary-alist' or `ispell-dictionary-alist'" - ispell-current-dictionary)))) - (decode-coding-string (nth n slot) (ispell-get-coding-system) t))) + ispell-current-dictionary))) + (str (nth n slot))) + (if (stringp str) + (decode-coding-string str (ispell-get-coding-system) t)))) (defun ispell-get-casechars () (ispell-get-decoded-string 1)) commit 40fdbb01d0017e9e164a24aeb760056778975e65 Author: Michael Albinus Date: Tue Sep 19 18:12:35 2017 +0200 Work on Tramp's file-truename * lisp/net/tramp-sh.el (tramp-perl-file-truename): Check also for symlinks. (tramp-sh-handle-file-truename): Move check for a symlink cycle to the end. Do not blame symlinks which look like a remote file name. * lisp/net/tramp.el (tramp-handle-file-truename): Expand result. diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 5f145d4fae..a744a53ca4 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -613,7 +613,7 @@ use Cwd \"realpath\"; sub myrealpath { my ($file) = @_; - return realpath($file) if -e $file; + return realpath($file) if (-e $file || -l $file); } sub recursive { @@ -1139,12 +1139,7 @@ component is used as the target of the symlink." (tramp-shell-quote-argument localname))) (with-current-buffer (tramp-get-connection-buffer v) (goto-char (point-min)) - (setq result (buffer-substring (point-min) (point-at-eol)))) - (when (and (file-symlink-p filename) - (string-equal result localname)) - (tramp-error - v 'file-error - "Apparent cycle of symbolic links for %s" filename))) + (setq result (buffer-substring (point-min) (point-at-eol))))) ;; Use Perl implementation. ((and (tramp-get-remote-perl v) @@ -1198,16 +1193,6 @@ component is used as the target of the symlink." (setq numchase (1+ numchase)) (when (file-name-absolute-p symlink-target) (setq result nil)) - ;; If the symlink was absolute, we'll get a - ;; string like "/user@host:/some/target"; - ;; extract the "/some/target" part from it. - (when (tramp-tramp-file-p symlink-target) - (unless (tramp-equal-remote filename symlink-target) - (tramp-error - v 'file-error - "Symlink target `%s' on wrong host" - symlink-target)) - (setq symlink-target localname)) (setq steps (append (split-string symlink-target "/" 'omit) steps))) @@ -1226,6 +1211,13 @@ component is used as the target of the symlink." "/")) (when (string= "" result) (setq result "/"))))) + + ;; Detect cycle. + (when (and (file-symlink-p filename) + (string-equal result localname)) + (tramp-error + v 'file-error + "Apparent cycle of symbolic links for %s" filename)) ;; If the resulting localname looks remote, we must quote it ;; for security reasons. (when (or quoted (file-remote-p result)) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index abcd5282d3..3573eeb7d4 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -3169,7 +3169,7 @@ User is always nil." (defun tramp-handle-file-truename (filename) "Like `file-truename' for Tramp files." - (let ((result filename) + (let ((result (expand-file-name filename)) (numchase 0) ;; Don't make the following value larger than ;; necessary. People expect an error message in a @@ -3180,7 +3180,7 @@ User is always nil." symlink-target) (format "%s%s" - (with-parsed-tramp-file-name (expand-file-name result) v1 + (with-parsed-tramp-file-name result v1 (with-tramp-file-property v1 v1-localname "file-truename" (while (and (setq symlink-target (file-symlink-p result)) (< numchase numchase-limit)) @@ -3850,7 +3850,7 @@ Erase echoed commands if exists." (min (+ (point-min) tramp-echo-mark-marker-length) (point-max)))))) ;; No echo to be handled, now we can look for the regexp. - ;; Sometimes, lines are much to long, and we run into a "Stack + ;; Sometimes, lines are much too long, and we run into a "Stack ;; overflow in regexp matcher". For example, //DIRED// lines of ;; directory listings with some thousand files. Therefore, we ;; look from the end. commit b1f83c10df7d1bbb16f4e13d18119ad4aa1a2137 Author: Philipp Stephani Date: Sun Aug 27 12:41:06 2017 +0200 Don't call deprecated GTK function gtk_adjustment_changed * src/gtkutil.c (xg_set_toolkit_scroll_bar_thumb) (xg_set_toolkit_horizontal_scroll_bar_thumb): Remove calls to deprecated function gtk_adjustment_changed. This function has been deprecated since GTK+ 3.18. diff --git a/src/gtkutil.c b/src/gtkutil.c index 0203a5d5c1..1073bd9384 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -4085,8 +4085,10 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar, if (int_gtk_range_get_value (GTK_RANGE (wscroll)) != value) gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value); +#if ! GTK_CHECK_VERSION (3, 18, 0) else if (changed) gtk_adjustment_changed (adj); +#endif xg_ignore_gtk_scrollbar = 0; @@ -4123,7 +4125,9 @@ xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar, gtk_adjustment_configure (adj, (gdouble) value, (gdouble) lower, (gdouble) upper, (gdouble) step_increment, (gdouble) page_increment, (gdouble) pagesize); +#if ! GTK_CHECK_VERSION (3, 18, 0) gtk_adjustment_changed (adj); +#endif unblock_input (); } } commit 1a01423b3c75bf08c255b3bd39f44d91e509a318 Author: Paul Eggert Date: Tue Sep 19 01:47:39 2017 -0700 Fix bug with make-directory on MS-Windows root * lisp/files.el (files--ensure-directory): Treat any error, not just file-already-exists, as an opportunity to check whether DIR is already a directory (Bug#28508). diff --git a/lisp/files.el b/lisp/files.el index ff0ab70633..0c30d40c13 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -5337,7 +5337,7 @@ instance of such commands." "Make directory DIR if it is not already a directory. Return nil." (condition-case err (make-directory-internal dir) - (file-already-exists + (error (unless (file-directory-p dir) (signal (car err) (cdr err)))))) commit eaefbc26d5c6cffbe4a22d3a9f4c7e6209a7b5a7 (refs/remotes/origin/scratch/org-mode-merge) Author: Rasmus Date: Tue Sep 19 09:00:00 2017 +0200 ; Add files missing in ab351d442d7 diff --git a/lisp/org/ob-hledger.el b/lisp/org/ob-hledger.el new file mode 100644 index 0000000000..86276aad81 --- /dev/null +++ b/lisp/org/ob-hledger.el @@ -0,0 +1,70 @@ +;; ob-ledger.el --- Babel Functions for hledger -*- lexical-binding: t; -*- + +;; Copyright (C) 2010-2017 Free Software Foundation, Inc. + +;; Author: Simon Michael +;; Keywords: literate programming, reproducible research, plain text accounting +;; Homepage: http://orgmode.org + +;; 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 . + +;;; Commentary: + +;; Babel support for evaluating hledger entries. +;; +;; Based on ob-ledger.el. +;; If the source block is empty, hledger will use a default journal file, +;; probably ~/.hledger.journal (it may not notice your $LEDGER_FILE env var). +;; So make ~/.hledger.journal a symbolic link to the real file if necessary. + +;;; Code: +(require 'ob) + +(defvar org-babel-default-header-args:hledger + '((:results . "output") (:exports . "results") (:cmdline . "bal")) + "Default arguments to use when evaluating a hledger source block.") + +(defun org-babel-execute:hledger (body params) + "Execute a block of hledger entries with org-babel. +This function is called by `org-babel-execute-src-block'." + (message "executing hledger source code block") + (letrec ( ;(result-params (split-string (or (cdr (assq :results params)) ""))) + (cmdline (cdr (assq :cmdline params))) + (in-file (org-babel-temp-file "hledger-")) + (out-file (org-babel-temp-file "hledger-output-")) + (hledgercmd (concat "hledger" + (if (> (length body) 0) + (concat " -f " (org-babel-process-file-name in-file)) + "") + " " cmdline))) + (with-temp-file in-file (insert body)) +;; TODO This is calling for some refactoring: +;; (concat "hledger" (if ...) " " cmdline) +;; could be built only once and bound to a symbol. + (message "%s" hledgercmd) + (with-output-to-string + (shell-command (concat hledgercmd " > " (org-babel-process-file-name out-file)))) + (with-temp-buffer (insert-file-contents out-file) (buffer-string)))) + +(defun org-babel-prep-session:hledger (_session _params) + (error "hledger does not support sessions")) + +(provide 'ob-hledger) + + + +;;; ob-hledger.el ends here +;; TODO Unit tests are more than welcome, too. diff --git a/lisp/org/ob-scala.el b/lisp/org/ob-scala.el deleted file mode 100644 index d00b97c3db..0000000000 --- a/lisp/org/ob-scala.el +++ /dev/null @@ -1,114 +0,0 @@ -;;; ob-scala.el --- Babel Functions for Scala -*- lexical-binding: t; -*- - -;; Copyright (C) 2012-2017 Free Software Foundation, Inc. - -;; Author: Andrzej Lichnerowicz -;; Keywords: literate programming, reproducible research -;; Homepage: http://orgmode.org - -;; 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 . - -;;; Commentary: -;; Currently only supports the external execution. No session support yet. - -;;; Requirements: -;; - Scala language :: http://www.scala-lang.org/ -;; - Scala major mode :: Can be installed from Scala sources -;; https://github.com/scala/scala-dist/blob/master/tool-support/src/emacs/scala-mode.el - -;;; Code: -(require 'ob) - -(defvar org-babel-tangle-lang-exts) ;; Autoloaded -(add-to-list 'org-babel-tangle-lang-exts '("scala" . "scala")) -(defvar org-babel-default-header-args:scala '()) -(defvar org-babel-scala-command "scala" - "Name of the command to use for executing Scala code.") - -(defun org-babel-execute:scala (body params) - "Execute a block of Scala code with org-babel. This function is -called by `org-babel-execute-src-block'" - (message "executing Scala source code block") - (let* ((processed-params (org-babel-process-params params)) - (session (org-babel-scala-initiate-session (nth 0 processed-params))) - (result-params (nth 2 processed-params)) - (result-type (cdr (assq :result-type params))) - (full-body (org-babel-expand-body:generic - body params)) - (result (org-babel-scala-evaluate - session full-body result-type result-params))) - - (org-babel-reassemble-table - result - (org-babel-pick-name - (cdr (assq :colname-names params)) (cdr (assq :colnames params))) - (org-babel-pick-name - (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) - -(defvar org-babel-scala-wrapper-method - -"var str_result :String = null; - -Console.withOut(new java.io.OutputStream() {def write(b: Int){ -}}) { - str_result = { -%s - }.toString -} - -print(str_result) -") - - -(defun org-babel-scala-evaluate - (session body &optional result-type result-params) - "Evaluate BODY in external Scala process. -If RESULT-TYPE equals `output' then return standard output as a string. -If RESULT-TYPE equals `value' then return the value of the last statement -in BODY as elisp." - (when session (error "Sessions are not (yet) supported for Scala")) - (pcase result-type - (`output - (let ((src-file (org-babel-temp-file "scala-"))) - (with-temp-file src-file (insert body)) - (org-babel-eval - (concat org-babel-scala-command " " src-file) ""))) - (`value - (let* ((src-file (org-babel-temp-file "scala-")) - (wrapper (format org-babel-scala-wrapper-method body))) - (with-temp-file src-file (insert wrapper)) - (let ((raw (org-babel-eval - (concat org-babel-scala-command " " src-file) ""))) - (org-babel-result-cond result-params - raw - (org-babel-script-escape raw))))))) - - -(defun org-babel-prep-session:scala (_session _params) - "Prepare SESSION according to the header arguments specified in PARAMS." - (error "Sessions are not (yet) supported for Scala")) - -(defun org-babel-scala-initiate-session (&optional _session) - "If there is not a current inferior-process-buffer in SESSION -then create. Return the initialized session. Sessions are not -supported in Scala." - nil) - -(provide 'ob-scala) - - - -;;; ob-scala.el ends here diff --git a/lisp/org/ob-vala.el b/lisp/org/ob-vala.el new file mode 100644 index 0000000000..3998e2d4e2 --- /dev/null +++ b/lisp/org/ob-vala.el @@ -0,0 +1,115 @@ +;;; ob-vala.el --- Babel functions for Vala evaluation -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 Free Software Foundation, Inc. + +;; Author: Christian Garbs +;; Keywords: literate programming, reproducible research +;; Homepage: http://orgmode.org + +;;; License: + +;; 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 . + +;;; Commentary: + +;; ob-vala.el provides Babel support for the Vala language +;; (see http://live.gnome.org/Vala for details) + +;;; Requirements: + +;; - Vala compiler binary (valac) +;; - Vala development environment (Vala libraries etc.) +;; +;; vala-mode.el is nice to have for code formatting, but is not needed +;; for ob-vala.el + +;;; Code: + +(require 'ob) + +(declare-function org-trim "org" (s &optional keep-lead)) + +;; File extension. +(add-to-list 'org-babel-tangle-lang-exts '("vala" . "vala")) + +;; Header arguments empty by default. +(defvar org-babel-default-header-args:vala '()) + +(defcustom org-babel-vala-compiler "valac" + "Command used to compile a C source code file into an executable. +May be either a command in the path, like \"valac\" +or an absolute path name, like \"/usr/local/bin/valac\". +Parameters may be used like this: \"valac -v\"" + :group 'org-babel + :version "26.1" + :package-version '(Org . "9.1") + :type 'string) + +;; This is the main function which is called to evaluate a code +;; block. +;; +;; - run Vala compiler and create a binary in a temporary file +;; - compiler/linker flags can be set via :flags header argument +;; - if compilation succeeded, run the binary +;; - commandline parameters to the binary can be set via :cmdline +;; header argument +;; - stdout will be parsed as RESULT (control via :result-params +;; header argument) +;; +;; There is no session support because Vala is a compiled language. +;; +;; This function is heavily based on ob-C.el +(defun org-babel-execute:vala (body params) + "Execute a block of Vala code with Babel. +This function is called by `org-babel-execute-src-block'." + (message "executing Vala source code block") + (let* ((tmp-src-file (org-babel-temp-file + "vala-src-" + ".vala")) + (tmp-bin-file (org-babel-temp-file "vala-bin-" org-babel-exeext)) + (cmdline (cdr (assq :cmdline params))) + (flags (cdr (assq :flags params)))) + (with-temp-file tmp-src-file (insert body)) + (org-babel-eval + (format "%s %s -o %s %s" + org-babel-vala-compiler + (mapconcat #'identity + (if (listp flags) flags (list flags)) " ") + (org-babel-process-file-name tmp-bin-file) + (org-babel-process-file-name tmp-src-file)) "") + (when (file-executable-p tmp-bin-file) + (let ((results + (org-trim + (org-babel-eval + (concat tmp-bin-file (if cmdline (concat " " cmdline) "")) "")))) + (org-babel-reassemble-table + (org-babel-result-cond (cdr (assq :result-params params)) + (org-babel-read results) + (let ((tmp-file (org-babel-temp-file "vala-"))) + (with-temp-file tmp-file (insert results)) + (org-babel-import-elisp-from-file tmp-file))) + (org-babel-pick-name + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) + (org-babel-pick-name + (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))))) + +(defun org-babel-prep-session:vala (_session _params) + "Prepare a session. +This function does nothing as Vala is a compiled language with no +support for sessions." + (error "Vala is a compiled language -- no support for sessions")) + +(provide 'ob-vala) + +;;; ob-vala.el ends here diff --git a/lisp/org/org-duration.el b/lisp/org/org-duration.el new file mode 100644 index 0000000000..3e5f0f56a5 --- /dev/null +++ b/lisp/org/org-duration.el @@ -0,0 +1,446 @@ +;;; org-duration.el --- Library handling durations -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 Free Software Foundation, Inc. + +;; Author: Nicolas Goaziou +;; Keywords: outlines, hypermedia, calendar, wp + +;; 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 . + +;;; Commentary: + +;; This library provides tools to manipulate durations. A duration +;; can have multiple formats: +;; +;; - 3:12 +;; - 1:23:45 +;; - 1y 3d 3h 4min +;; - 3d 13:35 +;; - 2.35h +;; +;; More accurately, it consists of numbers and units, as defined in +;; variable `org-duration-units', separated with white spaces, and +;; a "H:MM" or "H:MM:SS" part. White spaces are tolerated between the +;; number and its relative unit. Variable `org-duration-format' +;; controls durations default representation. +;; +;; The library provides functions allowing to convert a duration to, +;; and from, a number of minutes: `org-duration-to-minutes' and +;; `org-duration-from-minutes'. It also provides two lesser tools: +;; `org-duration-p', and `org-duration-h:mm-only-p'. +;; +;; Users can set the number of minutes per unit, or define new units, +;; in `org-duration-units'. The library also supports canonical +;; duration, i.e., a duration that doesn't depend on user's settings, +;; through optional arguments. + +;;; Code: + +(require 'cl-lib) +(require 'org-macs) +(declare-function org-trim "org-trim" (s &optional keep-lead)) + + +;;; Public variables + +(defconst org-duration-canonical-units + `(("min" . 1) + ("h" . 60) + ("d" . ,(* 60 24))) + "Canonical time duration units. +See `org-duration-units' for details.") + +(defcustom org-duration-units + `(("min" . 1) + ("h" . 60) + ("d" . ,(* 60 24)) + ("w" . ,(* 60 24 7)) + ("m" . ,(* 60 24 30)) + ("y" . ,(* 60 24 365.25))) + "Conversion factor to minutes for a duration. + +Each entry has the form (UNIT . MODIFIER). + +In a duration string, a number followed by UNIT is multiplied by +the specified number of MODIFIER to obtain a duration in minutes. + +For example, the following value + + \\=`((\"min\" . 1) + (\"h\" . 60) + (\"d\" . ,(* 60 8)) + (\"w\" . ,(* 60 8 5)) + (\"m\" . ,(* 60 8 5 4)) + (\"y\" . ,(* 60 8 5 4 10))) + +is meaningful if you work an average of 8 hours per day, 5 days +a week, 4 weeks a month and 10 months a year. + +When setting this variable outside the Customize interface, make +sure to call the following command: + + \\[org-duration-set-regexps]" + :group 'org-agenda + :version "26.1" + :package-version '(Org . "9.1") + :set (lambda (var val) (set-default var val) (org-duration-set-regexps)) + :initialize 'custom-initialize-changed + :type '(choice + (const :tag "H:MM" 'h:mm) + (const :tag "H:MM:SS" 'h:mm:ss) + (alist :key-type (string :tag "Unit") + :value-type (number :tag "Modifier")))) + +(defcustom org-duration-format '(("d" . nil) (special . h:mm)) + "Format definition for a duration. + +The value can be set to, respectively, the symbols `h:mm:ss' or +`h:mm', which means a duration is expressed as, respectively, +a \"H:MM:SS\" or \"H:MM\" string. + +Alternatively, the value can be a list of entries following the +pattern: + + (UNIT . REQUIRED?) + +UNIT is a unit string, as defined in `org-duration-units'. The +time duration is formatted using only the time components that +are specified here. + +Units with a zero value are skipped, unless REQUIRED? is non-nil. +In that case, the unit is always used. + +Eventually, the list can contain one of the following special +entries: + + (special . h:mm) + (special . h:mm:ss) + + Units shorter than an hour are ignored. The hours and + minutes part of the duration is expressed unconditionally + with H:MM, or H:MM:SS, pattern. + + (special . PRECISION) + + A duration is expressed with a single unit, PRECISION being + the number of decimal places to show. The unit chosen is the + first one required or with a non-zero integer part. If there + is no such unit, the smallest one is used. + +For example, + + ((\"d\" . nil) (\"h\" . t) (\"min\" . t)) + +means a duration longer than a day is expressed in days, hours +and minutes, whereas a duration shorter than a day is always +expressed in hours and minutes, even when shorter than an hour. + +On the other hand, the value + + ((\"d\" . nil) (\"min\" . nil)) + +means a duration longer than a day is expressed in days and +minutes, whereas a duration shorter than a day is expressed +entirely in minutes, even when longer than an hour. + +The following format + + ((\"d\" . nil) (special . h:mm)) + +means that any duration longer than a day is expressed with both +a \"d\" unit and a \"H:MM\" part, whereas a duration shorter than +a day is expressed only as a \"H:MM\" string. + +Eventually, + + ((\"d\" . nil) (\"h\" . nil) (special . 2)) + +expresses a duration longer than a day as a decimal number, with +a 2-digits fractional part, of \"d\" unit. A duration shorter +than a day uses \"h\" unit instead." + :group 'org-time + :group 'org-clock + :version "26.1" + :package-version '(Org . "9.1") + :type '(choice + (const :tag "Use H:MM" h:mm) + (const :tag "Use H:MM:SS" h:mm:ss) + (repeat :tag "Use units" + (choice + (cons :tag "Use units" + (string :tag "Unit") + (choice (const :tag "Skip when zero" nil) + (const :tag "Always used" t))) + (cons :tag "Use a single decimal unit" + (const special) + (integer :tag "Number of decimals")) + (cons :tag "Use both units and H:MM" + (const special) + (const h:mm)) + (cons :tag "Use both units and H:MM:SS" + (const special) + (const h:mm:ss)))))) + + +;;; Internal variables and functions + +(defconst org-duration--h:mm-re + "\\`[ \t]*[0-9]+\\(?::[0-9]\\{2\\}\\)\\{1,2\\}[ \t]*\\'" + "Regexp matching a duration expressed with H:MM or H:MM:SS format. +See `org-duration--h:mm:ss-re' to only match the latter. Hours +can use any number of digits.") + +(defconst org-duration--h:mm:ss-re + "\\`[ \t]*[0-9]+\\(?::[0-9]\\{2\\}\\)\\{2\\}[ \t]*\\'" + "Regexp matching a duration expressed H:MM:SS format. +See `org-duration--h:mm-re' to also support H:MM format. Hours +can use any number of digits.") + +(defvar org-duration--unit-re nil + "Regexp matching a duration with an unit. +Allowed units are defined in `org-duration-units'. Match group +1 contains the bare number. Match group 2 contains the unit.") + +(defvar org-duration--full-re nil + "Regexp matching a duration expressed with units. +Allowed units are defined in `org-duration-units'.") + +(defvar org-duration--mixed-re nil + "Regexp matching a duration expressed with units and H:MM or H:MM:SS format. +Allowed units are defined in `org-duration-units'. Match group +1 contains units part. Match group 2 contains H:MM or H:MM:SS +part.") + +(defun org-duration--modifier (unit &optional canonical) + "Return modifier associated to string UNIT. +When optional argument CANONICAL is non-nil, refer to +`org-duration-canonical-units' instead of `org-duration-units'." + (or (cdr (assoc unit (if canonical + org-duration-canonical-units + org-duration-units))) + (error "Unknown unit: %S" unit))) + + +;;; Public functions + +;;;###autoload +(defun org-duration-set-regexps () + "Set duration related regexps." + (interactive) + (setq org-duration--unit-re + (concat "\\([0-9]+\\(?:\\.[0-9]*\\)?\\)[ \t]*" + ;; Since user-defined units in `org-duration-units' + ;; can differ from canonical units in + ;; `org-duration-canonical-units', include both in + ;; regexp. + (regexp-opt (mapcar #'car (append org-duration-canonical-units + org-duration-units)) + t))) + (setq org-duration--full-re + (format "\\`[ \t]*%s\\(?:[ \t]+%s\\)*[ \t]*\\'" + org-duration--unit-re + org-duration--unit-re)) + (setq org-duration--mixed-re + (format "\\`[ \t]*\\(?1:%s\\(?:[ \t]+%s\\)*\\)[ \t]+\ +\\(?2:[0-9]+\\(?::[0-9][0-9]\\)\\{1,2\\}\\)[ \t]*\\'" + org-duration--unit-re + org-duration--unit-re))) + +;;;###autoload +(defun org-duration-p (s) + "Non-nil when string S is a time duration." + (and (stringp s) + (or (string-match-p org-duration--full-re s) + (string-match-p org-duration--mixed-re s) + (string-match-p org-duration--h:mm-re s)))) + +;;;###autoload +(defun org-duration-to-minutes (duration &optional canonical) + "Return number of minutes of DURATION string. + +When optional argument CANONICAL is non-nil, ignore +`org-duration-units' and use standard time units value. + +A bare number is translated into minutes. The empty string is +translated into 0.0. + +Return value as a float. Raise an error if duration format is +not recognized." + (cond + ((equal duration "") 0.0) + ((numberp duration) (float duration)) + ((string-match-p org-duration--h:mm-re duration) + (pcase-let ((`(,hours ,minutes ,seconds) + (mapcar #'string-to-number (split-string duration ":")))) + (+ (/ (or seconds 0) 60.0) minutes (* 60 hours)))) + ((string-match-p org-duration--full-re duration) + (let ((minutes 0) + (s 0)) + (while (string-match org-duration--unit-re duration s) + (setq s (match-end 0)) + (let ((value (string-to-number (match-string 1 duration))) + (unit (match-string 2 duration))) + (cl-incf minutes (* value (org-duration--modifier unit canonical))))) + (float minutes))) + ((string-match org-duration--mixed-re duration) + (let ((units-part (match-string 1 duration)) + (hms-part (match-string 2 duration))) + (+ (org-duration-to-minutes units-part) + (org-duration-to-minutes hms-part)))) + ((string-match-p "\\`[0-9]+\\(\\.[0-9]*\\)?\\'" duration) + (float (string-to-number duration))) + (t (error "Invalid duration format: %S" duration)))) + +;;;###autoload +(defun org-duration-from-minutes (minutes &optional fmt canonical) + "Return duration string for a given number of MINUTES. + +Format duration according to `org-duration-format' or FMT, when +non-nil. + +When optional argument CANONICAL is non-nil, ignore +`org-duration-units' and use standard time units value. + +Raise an error if expected format is unknown." + (pcase (or fmt org-duration-format) + (`h:mm + (let ((minutes (floor minutes))) + (format "%d:%02d" (/ minutes 60) (mod minutes 60)))) + (`h:mm:ss + (let* ((whole-minutes (floor minutes)) + (seconds (floor (* 60 (- minutes whole-minutes))))) + (format "%s:%02d" + (org-duration-from-minutes whole-minutes 'h:mm) + seconds))) + ((pred atom) (error "Invalid duration format specification: %S" fmt)) + ;; Mixed format. Call recursively the function on both parts. + ((and duration-format + (let `(special . ,(and mode (or `h:mm:ss `h:mm))) + (assq 'special duration-format))) + (let* ((truncated-format + ;; Remove "special" mode from duration format in order to + ;; recurse properly. Also remove units smaller or equal + ;; to an hour since H:MM part takes care of it. + (cl-remove-if-not + (lambda (pair) + (pcase pair + (`(,(and unit (pred stringp)) . ,_) + (> (org-duration--modifier unit canonical) 60)) + (_ nil))) + duration-format)) + (min-modifier ;smallest modifier above hour + (and truncated-format + (apply #'min + (mapcar (lambda (p) + (org-duration--modifier (car p) canonical)) + truncated-format))))) + (if (or (null min-modifier) (< minutes min-modifier)) + ;; There is not unit above the hour or the smallest unit + ;; above the hour is too large for the number of minutes we + ;; need to represent. Use H:MM or H:MM:SS syntax. + (org-duration-from-minutes minutes mode canonical) + ;; Represent minutes above hour using provided units and H:MM + ;; or H:MM:SS below. + (let* ((units-part (* min-modifier (/ (floor minutes) min-modifier))) + (minutes-part (- minutes units-part))) + (concat + (org-duration-from-minutes units-part truncated-format canonical) + " " + (org-duration-from-minutes minutes-part mode)))))) + ;; Units format. + (duration-format + (let* ((fractional + (let ((digits (cdr (assq 'special duration-format)))) + (and digits + (or (wholenump digits) + (error "Unknown formatting directive: %S" digits)) + (format "%%.%df" digits)))) + (selected-units + (sort (cl-remove-if + ;; Ignore special format cells. + (lambda (pair) (pcase pair (`(special . ,_) t) (_ nil))) + duration-format) + (lambda (a b) + (> (org-duration--modifier (car a) canonical) + (org-duration--modifier (car b) canonical)))))) + (cond + ;; Fractional duration: use first unit that is either required + ;; or smaller than MINUTES. + (fractional + (let* ((unit (car + (or (cl-find-if + (lambda (pair) + (pcase pair + (`(,u . ,req?) + (or req? + (<= (org-duration--modifier u canonical) + minutes))))) + selected-units) + ;; Fall back to smallest unit. + (org-last selected-units)))) + (modifier (org-duration--modifier unit canonical))) + (concat (format fractional (/ (float minutes) modifier)) unit))) + ;; Otherwise build duration string according to available + ;; units. + ((org-string-nw-p + (org-trim + (mapconcat + (lambda (units) + (pcase-let* ((`(,unit . ,required?) units) + (modifier (org-duration--modifier unit canonical))) + (cond ((<= modifier minutes) + (let ((value (if (integerp modifier) + (/ (floor minutes) modifier) + (floor (/ minutes modifier))))) + (cl-decf minutes (* value modifier)) + (format " %d%s" value unit))) + (required? (concat " 0" unit)) + (t "")))) + selected-units + "")))) + ;; No unit can properly represent MINUTES. Use the smallest + ;; one anyway. + (t + (pcase-let ((`((,unit . ,_)) (last selected-units))) + (concat "0" unit)))))))) + +;;;###autoload +(defun org-duration-h:mm-only-p (times) + "Non-nil when every duration in TIMES has \"H:MM\" or \"H:MM:SS\" format. + +TIMES is a list of duration strings. + +Return nil if any duration is expressed with units, as defined in +`org-duration-units'. Otherwise, if any duration is expressed +with \"H:MM:SS\" format, return `h:mm:ss'. Otherwise, return +`h:mm'." + (let (hms-flag) + (catch :exit + (dolist (time times) + (cond ((string-match-p org-duration--full-re time) + (throw :exit nil)) + ((string-match-p org-duration--mixed-re time) + (throw :exit nil)) + (hms-flag nil) + ((string-match-p org-duration--h:mm:ss-re time) + (setq hms-flag 'h:mm:ss)))) + (or hms-flag 'h:mm)))) + + +;;; Initialization + +(org-duration-set-regexps) + +(provide 'org-duration) +;;; org-duration.el ends here commit 066efb86660542238854a400c3c20b5cb526a3cd Author: Tom Tromey Date: Mon Sep 18 20:02:01 2017 -0600 Fix log-view-diff-common when point is after last entry Bug#28466 * lisp/vc/log-view.el (log-view-diff-common): If point is after last entry, look at the previous revision. diff --git a/lisp/vc/log-view.el b/lisp/vc/log-view.el index 52f56ed990..d6963d0a1b 100644 --- a/lisp/vc/log-view.el +++ b/lisp/vc/log-view.el @@ -608,10 +608,16 @@ considered file(s)." (log-view-diff-common beg end t))) (defun log-view-diff-common (beg end &optional whole-changeset) - (let ((to (log-view-current-tag beg)) - (fr (log-view-current-tag end))) - (when (string-equal fr to) - ;; TO and FR are the same, look at the previous revision. + (let* ((to (log-view-current-tag beg)) + (fr-entry (log-view-current-entry end)) + (fr (cadr fr-entry))) + ;; When TO and FR are the same, or when point is on a line after + ;; the last entry, look at the previous revision. + (when (or (string-equal fr to) + (>= (point) + (save-excursion + (goto-char (car fr-entry)) + (forward-line)))) (setq fr (vc-call-backend log-view-vc-backend 'previous-revision nil fr))) (vc-diff-internal t (list log-view-vc-backend commit 3f006b56cdd9dff313ea88fcedad122968fe1e6b Author: Ken Brown Date: Mon Sep 18 17:22:52 2017 -0400 Adapt fileio-tests--symlink-failure to Cygwin * test/src/fileio-tests.el (fileio-tests--symlink-failure) [CYGWIN]: Skip the case of a symlink target starting with '\'; this is treated specially on Cygwin. diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el index a56fb4474d..01c280d275 100644 --- a/test/src/fileio-tests.el +++ b/test/src/fileio-tests.el @@ -35,6 +35,8 @@ (char 0)) (while (and (not failure) (< char 127)) (setq char (1+ char)) + (when (and (eq system-type 'cygwin) (eq char 92)) + (setq char (1+ char))) (setq failure (try-link (string char) link))) (or failure (try-link "/:" link))) commit 0bddbbc5aae621d5875880ac3e63f68d481df7c3 Author: Sam Steingold Date: Mon Sep 18 16:33:37 2017 -0400 Fix bug#28435: "all" score file is ignored (gnus-score-find-bnews): Fix removing the empty suffix. diff --git a/lisp/gnus/gnus-score.el b/lisp/gnus/gnus-score.el index 11a45dda9a..976ac9f7f3 100644 --- a/lisp/gnus/gnus-score.el +++ b/lisp/gnus/gnus-score.el @@ -2731,8 +2731,10 @@ GROUP using BNews sys file syntax." (insert (car sfiles)) (goto-char (point-min)) ;; First remove the suffix itself. - (when (re-search-forward (concat "." score-regexp) nil t) - (replace-match "" t t) + (when (re-search-forward score-regexp nil t) + (unless (= (match-end 0) (match-beginning 0)) ; non-empty suffix + (replace-match "" t t) + (delete-char -1)) ; remove the "." before the suffix (goto-char (point-min)) (if (looking-at (regexp-quote kill-dir)) ;; If the file name was just "SCORE", `klen' is one character commit ee512e9a825a6dbdf438a432b75b7e18d9a983c7 Author: Eric Abrahamsen Date: Mon Sep 18 13:29:44 2017 -0700 Ignore buffers whose name begins with a space in save-some-buffers * lisp/files.el (save-some-buffers): Consider these buffers "internal", and don't prompt the user to save them. * doc/lispref/files.texi: Document. diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 6be998f0b2..b1b858a6b4 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -332,7 +332,9 @@ in the list @code{find-file-hook}. that is visiting that file---that is, the contents of the file are copied into the buffer and the copy is what you edit. Changes to the buffer do not change the file until you @dfn{save} the buffer, which -means copying the contents of the buffer into the file. +means copying the contents of the buffer into the file. Buffers which +are not visiting a file can still be ``saved'', in a sense, using +functions in the buffer-local @code{write-contents-functions} hook. @deffn Command save-buffer &optional backup-option This function saves the contents of the current buffer in its visited @@ -365,8 +367,9 @@ With an argument of 0, unconditionally do @emph{not} make any backup file. @anchor{Definition of save-some-buffers} This command saves some modified file-visiting buffers. Normally it asks the user about each buffer. But if @var{save-silently-p} is -non-@code{nil}, it saves all the file-visiting buffers without querying -the user. +non-@code{nil}, it saves all the file-visiting buffers without +querying the user. Additionally, buffers whose name begins with a +space (``internal'' buffers) will not be offered for save. @vindex save-some-buffers-default-predicate The optional @var{pred} argument provides a predicate that controls diff --git a/lisp/files.el b/lisp/files.el index 133fed90c3..ff0ab70633 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -5188,6 +5188,7 @@ change the additional actions you can take on files." (and (buffer-live-p buffer) (buffer-modified-p buffer) (not (buffer-base-buffer buffer)) + (not (eq (aref (buffer-name buffer) 0) ?\s)) (or (buffer-file-name buffer) (and pred commit 9dbdc0f00567d0cf2d165ef9704983bfb588146f Author: Sam Steingold Date: Mon Sep 18 12:54:29 2017 -0400 Add define-thing-chars and use it for filename. (define-thing-chars): New defmacro. (filename): Define this thing using `define-thing-chars'. diff --git a/lisp/thingatpt.el b/lisp/thingatpt.el index 13f761e69e..d315040392 100644 --- a/lisp/thingatpt.el +++ b/lisp/thingatpt.el @@ -42,6 +42,9 @@ ;; beginning-op Function to call to skip to the beginning of a "thing". ;; end-op Function to call to skip to the end of a "thing". ;; +;; For simple things, defined as sequences of specific kinds of characters, +;; use macro define-thing-chars. +;; ;; Reliance on existing operators means that many `things' can be accessed ;; without further code: eg. ;; (thing-at-point 'line) @@ -237,21 +240,28 @@ The bounds of THING are determined by `bounds-of-thing-at-point'." (put 'defun 'end-op 'end-of-defun) (put 'defun 'forward-op 'end-of-defun) +;; Things defined by sets of characters + +(defmacro define-thing-chars (thing chars) + "Define THING as a sequence of CHARS. +E.g.: +\(define-thing-chars twitter-screen-name \"[:alnum:]_\")" + `(progn + (put ',thing 'end-op + (lambda () + (re-search-forward (concat "\\=[" ,chars "]*") nil t))) + (put ',thing 'beginning-op + (lambda () + (if (re-search-backward (concat "[^" ,chars "]") nil t) + (forward-char) + (goto-char (point-min))))))) + ;; Filenames (defvar thing-at-point-file-name-chars "-~/[:alnum:]_.${}#%,:" "Characters allowable in filenames.") -(put 'filename 'end-op - (lambda () - (re-search-forward (concat "\\=[" thing-at-point-file-name-chars "]*") - nil t))) -(put 'filename 'beginning-op - (lambda () - (if (re-search-backward (concat "[^" thing-at-point-file-name-chars "]") - nil t) - (forward-char) - (goto-char (point-min))))) +(define-thing-chars filename thing-at-point-file-name-chars) ;; URIs commit 0925a20e0a48bc5ff8e9bad6ca4aa0a4c91fdc3c Author: Philipp Stephani Date: Mon Sep 18 18:00:45 2017 +0200 Revert "Implement native JSON support using Jansson" This reverts commit cb99cf5a99680af7dc2c49fdf5b840d1ff4dd928. diff --git a/configure.ac b/configure.ac index c9ce5ee120..35b7e69daf 100644 --- a/configure.ac +++ b/configure.ac @@ -348,7 +348,6 @@ OPTION_DEFAULT_ON([libsystemd],[don't compile with libsystemd support]) OPTION_DEFAULT_OFF([cairo],[compile with Cairo drawing (experimental)]) OPTION_DEFAULT_ON([xml2],[don't compile with XML parsing support]) OPTION_DEFAULT_ON([imagemagick],[don't compile with ImageMagick image support]) -OPTION_DEFAULT_ON([json], [don't compile with native JSON support]) OPTION_DEFAULT_ON([xft],[don't use XFT for anti aliased fonts]) OPTION_DEFAULT_ON([libotf],[don't use libotf for OpenType font support]) @@ -2857,22 +2856,6 @@ fi AC_SUBST(LIBSYSTEMD_LIBS) AC_SUBST(LIBSYSTEMD_CFLAGS) -HAVE_JSON=no -JSON_OBJ= - -if test "${with_json}" = yes; then - EMACS_CHECK_MODULES([JSON], [jansson >= 2.5], - [HAVE_JSON=yes], [HAVE_JSON=no]) - if test "${HAVE_JSON}" = yes; then - AC_DEFINE(HAVE_JSON, 1, [Define if using Jansson.]) - JSON_OBJ=json.o - fi -fi - -AC_SUBST(JSON_LIBS) -AC_SUBST(JSON_CFLAGS) -AC_SUBST(JSON_OBJ) - NOTIFY_OBJ= NOTIFY_SUMMARY=no @@ -5385,7 +5368,7 @@ emacs_config_features= for opt in XAW3D XPM JPEG TIFF GIF PNG RSVG CAIRO IMAGEMAGICK SOUND GPM DBUS \ GCONF GSETTINGS NOTIFY ACL LIBSELINUX GNUTLS LIBXML2 FREETYPE M17N_FLT \ LIBOTF XFT ZLIB TOOLKIT_SCROLL_BARS X_TOOLKIT OLDXMENU X11 NS MODULES \ - XWIDGETS LIBSYSTEMD JSON CANNOT_DUMP LCMS2; do + XWIDGETS LIBSYSTEMD CANNOT_DUMP LCMS2; do case $opt in CANNOT_DUMP) eval val=\${$opt} ;; @@ -5435,7 +5418,6 @@ AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D Does Emacs use -lotf? ${HAVE_LIBOTF} Does Emacs use -lxft? ${HAVE_XFT} Does Emacs use -lsystemd? ${HAVE_LIBSYSTEMD} - Does Emacs use -ljanssoon? ${HAVE_JSON} Does Emacs directly use zlib? ${HAVE_ZLIB} Does Emacs have dynamic modules support? ${HAVE_MODULES} Does Emacs use toolkit scroll bars? ${USE_TOOLKIT_SCROLL_BARS} diff --git a/src/Makefile.in b/src/Makefile.in index 4d33682629..0e55ad4bb2 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -312,10 +312,6 @@ LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@ LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@ LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@ -JSON_LIBS = @JSON_LIBS@ -JSON_CFLAGS = @JSON_CFLAGS@ -JSON_OBJ = @JSON_OBJ@ - INTERVALS_H = dispextern.h intervals.h composite.h GETLOADAVG_LIBS = @GETLOADAVG_LIBS@ @@ -367,7 +363,7 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \ $(WEBKIT_CFLAGS) \ $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ - $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) \ + $(LIBSYSTEMD_CFLAGS) \ $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \ $(WERROR_CFLAGS) ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS) @@ -401,7 +397,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ thread.o systhread.o \ $(if $(HYBRID_MALLOC),sheap.o) \ $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \ - $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ) + $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) obj = $(base_obj) $(NS_OBJC_OBJ) ## Object files used on some machine or other. @@ -497,8 +493,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \ $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LIBLCMS2) \ - $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \ - $(JSON_LIBS) + $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) ## FORCE it so that admin/unidata can decide whether these files ## are up-to-date. Although since charprop depends on bootstrap-emacs, diff --git a/src/emacs.c b/src/emacs.c index eb5f1128f6..1ad8af70a7 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1610,10 +1610,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem syms_of_threads (); syms_of_profiler (); -#ifdef HAVE_JSON - syms_of_json (); -#endif - keys_of_casefiddle (); keys_of_cmds (); keys_of_buffer (); diff --git a/src/json.c b/src/json.c deleted file mode 100644 index 85abf87e21..0000000000 --- a/src/json.c +++ /dev/null @@ -1,469 +0,0 @@ -/* JSON parsing and serialization. - -Copyright (C) 2017 Free Software Foundation, Inc. - -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 . */ - -#include - -#include -#include - -#include - -#include "lisp.h" -#include "buffer.h" - -static _Noreturn void -json_out_of_memory (void) -{ - xsignal0 (Qjson_out_of_memory); -} - -static _Noreturn void -json_parse_error (const json_error_t *error) -{ - xsignal (Qjson_parse_error, - list5 (build_string (error->text), build_string (error->source), - make_natnum (error->line), make_natnum (error->column), - make_natnum (error->position))); -} - -static void -json_release_object (void *object) -{ - json_decref (object); -} - -static void -check_string_without_embedded_nulls (Lisp_Object object) -{ - CHECK_STRING (object); - CHECK_TYPE (memchr (SDATA (object), '\0', SBYTES (object)) == NULL, - Qstring_without_embedded_nulls_p, object); -} - -static json_t * -json_check (json_t *object) -{ - if (object == NULL) - json_out_of_memory (); - return object; -} - -/* This returns Lisp_Object so we can use unbind_to. The return value - is always nil. */ - -static Lisp_Object -lisp_to_json (Lisp_Object lisp, json_t **json) -{ - if (NILP (lisp)) - { - *json =json_check (json_null ()); - return Qnil; - } - else if (EQ (lisp, QCjson_false)) - { - *json = json_check (json_false ()); - return Qnil; - } - else if (EQ (lisp, Qt)) - { - *json = json_check (json_true ()); - return Qnil; - } - else if (INTEGERP (lisp)) - { - CHECK_TYPE_RANGED_INTEGER (json_int_t, lisp); - *json = json_check (json_integer (XINT (lisp))); - return Qnil; - } - else if (FLOATP (lisp)) - { - *json = json_check (json_real (XFLOAT_DATA (lisp))); - return Qnil; - } - else if (STRINGP (lisp)) - { - ptrdiff_t size = SBYTES (lisp); - eassert (size >= 0); - if (size > SIZE_MAX) - xsignal1 (Qoverflow_error, build_pure_c_string ("string is too long")); - *json = json_check (json_stringn (SSDATA (lisp), size)); - return Qnil; - } - else if (VECTORP (lisp)) - { - if (++lisp_eval_depth > max_lisp_eval_depth) - xsignal0 (Qjson_object_too_deep); - ptrdiff_t size = ASIZE (lisp); - eassert (size >= 0); - if (size > SIZE_MAX) - xsignal1 (Qoverflow_error, build_pure_c_string ("vector is too long")); - *json = json_check (json_array ()); - ptrdiff_t count = SPECPDL_INDEX (); - record_unwind_protect_ptr (json_release_object, json); - for (ptrdiff_t i = 0; i < size; ++i) - { - json_t *element; - lisp_to_json (AREF (lisp, i), &element); - int status = json_array_append_new (*json, element); - if (status == -1) - json_out_of_memory (); - eassert (status == 0); - } - eassert (json_array_size (*json) == size); - clear_unwind_protect (count); - --lisp_eval_depth; - return unbind_to (count, Qnil); - } - else if (HASH_TABLE_P (lisp)) - { - if (++lisp_eval_depth > max_lisp_eval_depth) - xsignal0 (Qjson_object_too_deep); - struct Lisp_Hash_Table *h = XHASH_TABLE (lisp); - *json = json_check (json_object ()); - ptrdiff_t count = SPECPDL_INDEX (); - record_unwind_protect_ptr (json_release_object, *json); - for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (h); ++i) - if (!NILP (HASH_HASH (h, i))) - { - Lisp_Object key = HASH_KEY (h, i); - /* We can’t specify the length, so the string must be - null-terminated. */ - check_string_without_embedded_nulls (key); - json_t *value; - lisp_to_json (HASH_VALUE (h, i), &value); - int status = json_object_set_new (*json, SSDATA (key), value); - if (status == -1) - json_out_of_memory (); - eassert (status == 0); - } - clear_unwind_protect (count); - --lisp_eval_depth; - return unbind_to (count, Qnil); - } - wrong_type_argument (Qjson_value_p, lisp); -} - -DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, 1, NULL, - doc: /* Return the JSON representation of OBJECT as a string. -OBJECT must be a vector or hashtable, and its elements can recursively -contain nil, t, `:json-false', numbers, strings, or other vectors and -hashtables. nil, t, and `:json-false' will be converted to JSON null, -true, and false values, respectively. Vectors will be converted to -JSON arrays, and hashtables to JSON objects. Hashtable keys must be -strings without embedded null characters and must be unique within -each object. */) - (Lisp_Object object) -{ - ptrdiff_t count = SPECPDL_INDEX (); - - json_t *json; - lisp_to_json (object, &json); - record_unwind_protect_ptr (json_release_object, json); - - char *string = json_dumps (json, JSON_COMPACT); - if (string == NULL) - json_out_of_memory (); - record_unwind_protect_ptr (free, string); - - return unbind_to (count, build_string (string)); -} - -struct json_buffer_and_size -{ - const char *buffer; - size_t size; -}; - -static Lisp_Object -json_insert (Lisp_Object data) -{ - const struct json_buffer_and_size *buffer_and_size = XSAVE_POINTER (data, 0); - if (FIXNUM_OVERFLOW_P (buffer_and_size->size)) - xsignal1 (Qoverflow_error, build_pure_c_string ("buffer too large")); - Lisp_Object string - = make_string (buffer_and_size->buffer, buffer_and_size->size); - insert_from_string (string, 0, 0, SCHARS (string), SBYTES (string), false); - return Qnil; -} - -struct json_insert_data -{ - /* nil if json_insert succeeded, otherwise a cons - (ERROR-SYMBOL . ERROR-DATA). */ - Lisp_Object error; -}; - -static int -json_insert_callback (const char *buffer, size_t size, void *data) -{ - /* This function may not exit nonlocally. */ - struct json_insert_data *d = data; - struct json_buffer_and_size buffer_and_size - = {.buffer = buffer, .size = size}; - d->error - = internal_condition_case_1 (json_insert, make_save_ptr (&buffer_and_size), - Qt, Fidentity); - return 0; -} - -DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 1, NULL, - doc: /* Insert the JSON representation of OBJECT before point. -This is the same as (insert (json-serialize OBJECT)), but potentially -faster. See the function `json-serialize' for allowed values of -OBJECT. */) - (Lisp_Object object) -{ - ptrdiff_t count = SPECPDL_INDEX (); - - json_t *json; - lisp_to_json (object, &json); - record_unwind_protect_ptr (json_release_object, json); - - struct json_insert_data data; - int status - = json_dump_callback (json, json_insert_callback, &data, JSON_COMPACT); - if (status == -1) - json_out_of_memory (); - eassert (status == 0); - - if (!NILP (data.error)) - xsignal (XCAR (data.error), XCDR (data.error)); - - return unbind_to (count, Qnil); -} - -static Lisp_Object -json_to_lisp (json_t *json) -{ - switch (json_typeof (json)) - { - case JSON_NULL: - return Qnil; - case JSON_FALSE: - return QCjson_false; - case JSON_TRUE: - return Qt; - case JSON_INTEGER: - { - json_int_t value = json_integer_value (json); - if (FIXNUM_OVERFLOW_P (value)) - xsignal1 (Qoverflow_error, - build_pure_c_string ("JSON integer is too large")); - return make_number (value); - } - case JSON_REAL: - return make_float (json_real_value (json)); - case JSON_STRING: - { - size_t size = json_string_length (json); - if (FIXNUM_OVERFLOW_P (size)) - xsignal1 (Qoverflow_error, - build_pure_c_string ("JSON string is too long")); - return make_string (json_string_value (json), size); - } - case JSON_ARRAY: - { - if (++lisp_eval_depth > max_lisp_eval_depth) - xsignal0 (Qjson_object_too_deep); - size_t size = json_array_size (json); - if (FIXNUM_OVERFLOW_P (size)) - xsignal1 (Qoverflow_error, - build_pure_c_string ("JSON array is too long")); - Lisp_Object result = Fmake_vector (make_natnum (size), Qunbound); - for (ptrdiff_t i = 0; i < size; ++i) - ASET (result, i, - json_to_lisp (json_array_get (json, i))); - --lisp_eval_depth; - return result; - } - case JSON_OBJECT: - { - if (++lisp_eval_depth > max_lisp_eval_depth) - xsignal0 (Qjson_object_too_deep); - size_t size = json_object_size (json); - if (FIXNUM_OVERFLOW_P (size)) - xsignal1 (Qoverflow_error, - build_pure_c_string ("JSON object has too many elements")); - Lisp_Object result = CALLN (Fmake_hash_table, QCtest, Qequal, - QCsize, make_natnum (size)); - struct Lisp_Hash_Table *h = XHASH_TABLE (result); - const char *key_str; - json_t *value; - json_object_foreach (json, key_str, value) - { - Lisp_Object key = build_string (key_str); - EMACS_UINT hash; - ptrdiff_t i = hash_lookup (h, key, &hash); - eassert (i < 0); - hash_put (h, key, json_to_lisp (value), hash); - } - --lisp_eval_depth; - return result; - } - } - /* Can’t get here. */ - emacs_abort (); -} - -DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, 1, NULL, - doc: /* Parse the JSON STRING into a Lisp object. -This is essentially the reverse operation of `json-serialize', which -see. The returned object will be a vector or hashtable. Its elements -will be nil, t, `:json-false', numbers, strings, or further vectors -and hashtables. If there are duplicate keys in an object, all but the -last one are ignored. If STRING doesn't contain a valid JSON object, -an error of type `json-parse-error' is signaled. */) - (Lisp_Object string) -{ - ptrdiff_t count = SPECPDL_INDEX (); - check_string_without_embedded_nulls (string); - - json_error_t error; - json_t *object = json_loads (SSDATA (string), 0, &error); - if (object == NULL) - json_parse_error (&error); - - /* Avoid leaking the object in case of further errors. */ - if (object != NULL) - record_unwind_protect_ptr (json_release_object, object); - - return unbind_to (count, json_to_lisp (object)); -} - -struct json_read_buffer_data -{ - ptrdiff_t point; -}; - -static size_t -json_read_buffer_callback (void *buffer, size_t buflen, void *data) -{ - struct json_read_buffer_data *d = data; - - /* First, parse from point to the gap or the end of the accessible - portion, whatever is closer. */ - ptrdiff_t point = d->point; - ptrdiff_t end; - { - bool overflow = INT_ADD_WRAPV (BUFFER_CEILING_OF (point), 1, &end); - eassert (!overflow); - } - size_t count; - { - bool overflow = INT_SUBTRACT_WRAPV (end, point, &count); - eassert (!overflow); - } - if (buflen < count) - count = buflen; - memcpy (buffer, BYTE_POS_ADDR (point), count); - { - bool overflow = INT_ADD_WRAPV (d->point, count, &d->point); - eassert (!overflow); - } - return count; -} - -DEFUN ("json-parse-buffer", Fjson_parse_buffer, Sjson_parse_buffer, - 0, 0, NULL, - doc: /* Read JSON object from current buffer starting at point. -This is similar to `json-parse-string', which see. Move point after -the end of the object if parsing was successful. On error, point is -not moved. */) - (void) -{ - ptrdiff_t count = SPECPDL_INDEX (); - - ptrdiff_t point = PT_BYTE; - struct json_read_buffer_data data = {.point = point}; - json_error_t error; - json_t *object = json_load_callback (json_read_buffer_callback, &data, - JSON_DISABLE_EOF_CHECK, &error); - - if (object == NULL) - json_parse_error (&error); - - /* Avoid leaking the object in case of further errors. */ - record_unwind_protect_ptr (json_release_object, object); - - /* Convert and then move point only if everything succeeded. */ - Lisp_Object lisp = json_to_lisp (object); - - { - /* Adjust point by how much we just read. Do this here because - tokener->char_offset becomes incorrect below. */ - bool overflow = INT_ADD_WRAPV (point, error.position, &point); - eassert (!overflow); - eassert (point <= ZV_BYTE); - SET_PT_BOTH (BYTE_TO_CHAR (point), point); - } - - return unbind_to (count, lisp); -} - -/* Simplified version of ‘define-error’ that works with pure - objects. */ - -static void -define_error (Lisp_Object name, const char *message, Lisp_Object parent) -{ - eassert (SYMBOLP (name)); - eassert (SYMBOLP (parent)); - Lisp_Object parent_conditions = Fget (parent, Qerror_conditions); - eassert (CONSP (parent_conditions)); - eassert (!NILP (Fmemq (parent, parent_conditions))); - eassert (NILP (Fmemq (name, parent_conditions))); - Fput (name, Qerror_conditions, pure_cons (name, parent_conditions)); - Fput (name, Qerror_message, build_pure_c_string (message)); -} - -void -syms_of_json (void) -{ - DEFSYM (QCjson_false, ":json-false"); - - DEFSYM (Qstring_without_embedded_nulls_p, "string-without-embedded-nulls-p"); - DEFSYM (Qjson_value_p, "json-value-p"); - - DEFSYM (Qjson_error, "json-error"); - DEFSYM (Qjson_out_of_memory, "json-out-of-memory"); - DEFSYM (Qjson_parse_error, "json-parse-error"); - DEFSYM (Qjson_object_too_deep, "json-object-too-deep"); - define_error (Qjson_error, "generic JSON error", Qerror); - define_error (Qjson_out_of_memory, "no free memory for creating JSON object", - Qjson_error); - define_error (Qjson_parse_error, "could not parse JSON stream", - Qjson_error); - define_error (Qjson_object_too_deep, "object cyclic or too deep", - Qjson_error); - - DEFSYM (Qpure, "pure"); - DEFSYM (Qside_effect_free, "side-effect-free"); - - DEFSYM (Qjson_serialize, "json-serialize"); - DEFSYM (Qjson_parse_string, "json-parse-string"); - Fput (Qjson_serialize, Qpure, Qt); - Fput (Qjson_serialize, Qside_effect_free, Qt); - Fput (Qjson_parse_string, Qpure, Qt); - Fput (Qjson_parse_string, Qside_effect_free, Qt); - - defsubr (&Sjson_serialize); - defsubr (&Sjson_insert); - defsubr (&Sjson_parse_string); - defsubr (&Sjson_parse_buffer); -} diff --git a/src/lisp.h b/src/lisp.h index 8d485098ac..c503082442 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3440,11 +3440,6 @@ extern int x_bitmap_mask (struct frame *, ptrdiff_t); extern void reset_image_types (void); extern void syms_of_image (void); -#ifdef HAVE_JSON -/* Defined in json.c. */ -extern void syms_of_json (void); -#endif - /* Defined in insdel.c. */ extern void move_gap_both (ptrdiff_t, ptrdiff_t); extern _Noreturn void buffer_overflow (void); diff --git a/test/src/json-tests.el b/test/src/json-tests.el deleted file mode 100644 index 1d8f9a490b..0000000000 --- a/test/src/json-tests.el +++ /dev/null @@ -1,61 +0,0 @@ -;;; json-tests.el --- unit tests for json.c -*- lexical-binding: t; -*- - -;; Copyright (C) 2017 Free Software Foundation, Inc. - -;; 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 . - -;;; Commentary: - -;; Unit tests for src/json.c. - -;;; Code: - -(require 'cl-lib) -(require 'map) - -(ert-deftest json-serialize/roundtrip () - (let ((lisp [nil :json-false t 0 123 -456 3.75 "foo"]) - (json "[null,false,true,0,123,-456,3.75,\"foo\"]")) - (should (equal (json-serialize lisp) json)) - (with-temp-buffer - (json-insert lisp) - (should (equal (buffer-string) json)) - (should (eobp))) - (should (equal (json-parse-string json) lisp)) - (with-temp-buffer - (insert json) - (goto-char 1) - (should (equal (json-parse-buffer) lisp)) - (should (eobp))))) - -(ert-deftest json-serialize/object () - (let ((table (make-hash-table :test #'equal))) - (puthash "abc" [1 2 t] table) - (puthash "def" nil table) - (should (equal (json-serialize table) - "{\"abc\":[1,2,true],\"def\":null}")))) - -(ert-deftest json-parse-string/object () - (let ((actual - (json-parse-string - "{ \"abc\" : [1, 2, true], \"def\" : null, \"abc\" : [9, false] }\n"))) - (should (hash-table-p actual)) - (should (equal (hash-table-count actual) 2)) - (should (equal (cl-sort (map-pairs actual) #'string< :key #'car) - '(("abc" . [9 :json-false]) ("def")))))) - -(provide 'json-tests) -;;; json-tests.el ends here commit 9e1b5bd92ce26291c71ddb33a6291225e6ec1152 Author: Michael Albinus Date: Mon Sep 18 18:00:27 2017 +0200 Improve tramp-interrupt-process robustness * lisp/net/tramp.el (tramp-interrupt-process): Wait, until the process has disappeared. diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 07c06808bb..abcd5282d3 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -4556,6 +4556,12 @@ Only works for Bourne-like shells." 'tramp-send-command (tramp-get-connection-property proc "vector" nil) (format "kill -2 %d" pid)) + ;; Wait, until the process has disappeared. + (with-timeout + (1 (tramp-error proc 'error "Process %s did not interrupt" proc)) + (while (process-live-p proc) + ;; We cannot run `tramp-accept-process-output', it blocks timers. + (accept-process-output proc 0.1))) ;; Report success. proc)))) commit 8d4223e61b5e4661ececb6ab84c665fe761d0438 Author: Michael Albinus Date: Mon Sep 18 18:00:07 2017 +0200 Minor Tramp doc update * doc/misc/tramp.texi (Frequently Asked Questions): Mention `vc-handled-backends'. diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 7e8ce75f2d..6478479c38 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -3043,6 +3043,14 @@ Disable version control to avoid delays: @end group @end lisp +If this is too radical, because you want to use version control +remotely, trim @code{vc-handled-backends} to just those you care +about, for example: + +@lisp +(setq vc-handled-backends '(SVN Git)) +@end lisp + Disable excessive traces. Set @code{tramp-verbose} to 3 or lower, default being 3. Increase trace levels temporarily when hunting for bugs. commit cb99cf5a99680af7dc2c49fdf5b840d1ff4dd928 Author: Philipp Stephani Date: Mon Sep 18 10:51:39 2017 +0200 Implement native JSON support using Jansson * configure.ac: New option --with-json. * src/json.c (Fjson_serialize, Fjson_insert, Fjson_parse_string) (Fjson_parse_buffer): New defuns. (json_out_of_memory, json_parse_error, json_release_object) (check_string_without_embedded_nulls, json_check, lisp_to_json) (json_insert, json_insert_callback, json_to_lisp) (json_read_buffer_callback, Fjson_parse_buffer, define_error): New helper function. (syms_of_json): New file. * src/lisp.h: Declaration for syms_of_json. * src/emacs.c (main): Enable JSON functions. * src/Makefile.in (JSON_LIBS, JSON_CFLAGS, JSON_OBJ, EMACS_CFLAGS) (base_obj, LIBES): Compile json.c if --with-json is enabled. * test/src/json-tests.el (json-serialize/roundtrip) (json-serialize/object, json-parse-string/object): New unit tests. diff --git a/configure.ac b/configure.ac index 35b7e69daf..c9ce5ee120 100644 --- a/configure.ac +++ b/configure.ac @@ -348,6 +348,7 @@ OPTION_DEFAULT_ON([libsystemd],[don't compile with libsystemd support]) OPTION_DEFAULT_OFF([cairo],[compile with Cairo drawing (experimental)]) OPTION_DEFAULT_ON([xml2],[don't compile with XML parsing support]) OPTION_DEFAULT_ON([imagemagick],[don't compile with ImageMagick image support]) +OPTION_DEFAULT_ON([json], [don't compile with native JSON support]) OPTION_DEFAULT_ON([xft],[don't use XFT for anti aliased fonts]) OPTION_DEFAULT_ON([libotf],[don't use libotf for OpenType font support]) @@ -2856,6 +2857,22 @@ fi AC_SUBST(LIBSYSTEMD_LIBS) AC_SUBST(LIBSYSTEMD_CFLAGS) +HAVE_JSON=no +JSON_OBJ= + +if test "${with_json}" = yes; then + EMACS_CHECK_MODULES([JSON], [jansson >= 2.5], + [HAVE_JSON=yes], [HAVE_JSON=no]) + if test "${HAVE_JSON}" = yes; then + AC_DEFINE(HAVE_JSON, 1, [Define if using Jansson.]) + JSON_OBJ=json.o + fi +fi + +AC_SUBST(JSON_LIBS) +AC_SUBST(JSON_CFLAGS) +AC_SUBST(JSON_OBJ) + NOTIFY_OBJ= NOTIFY_SUMMARY=no @@ -5368,7 +5385,7 @@ emacs_config_features= for opt in XAW3D XPM JPEG TIFF GIF PNG RSVG CAIRO IMAGEMAGICK SOUND GPM DBUS \ GCONF GSETTINGS NOTIFY ACL LIBSELINUX GNUTLS LIBXML2 FREETYPE M17N_FLT \ LIBOTF XFT ZLIB TOOLKIT_SCROLL_BARS X_TOOLKIT OLDXMENU X11 NS MODULES \ - XWIDGETS LIBSYSTEMD CANNOT_DUMP LCMS2; do + XWIDGETS LIBSYSTEMD JSON CANNOT_DUMP LCMS2; do case $opt in CANNOT_DUMP) eval val=\${$opt} ;; @@ -5418,6 +5435,7 @@ AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D Does Emacs use -lotf? ${HAVE_LIBOTF} Does Emacs use -lxft? ${HAVE_XFT} Does Emacs use -lsystemd? ${HAVE_LIBSYSTEMD} + Does Emacs use -ljanssoon? ${HAVE_JSON} Does Emacs directly use zlib? ${HAVE_ZLIB} Does Emacs have dynamic modules support? ${HAVE_MODULES} Does Emacs use toolkit scroll bars? ${USE_TOOLKIT_SCROLL_BARS} diff --git a/src/Makefile.in b/src/Makefile.in index 0e55ad4bb2..4d33682629 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -312,6 +312,10 @@ LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@ LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@ LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@ +JSON_LIBS = @JSON_LIBS@ +JSON_CFLAGS = @JSON_CFLAGS@ +JSON_OBJ = @JSON_OBJ@ + INTERVALS_H = dispextern.h intervals.h composite.h GETLOADAVG_LIBS = @GETLOADAVG_LIBS@ @@ -363,7 +367,7 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \ $(WEBKIT_CFLAGS) \ $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ - $(LIBSYSTEMD_CFLAGS) \ + $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) \ $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \ $(WERROR_CFLAGS) ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS) @@ -397,7 +401,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ thread.o systhread.o \ $(if $(HYBRID_MALLOC),sheap.o) \ $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \ - $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) + $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ) obj = $(base_obj) $(NS_OBJC_OBJ) ## Object files used on some machine or other. @@ -493,7 +497,8 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \ $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LIBLCMS2) \ - $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) + $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \ + $(JSON_LIBS) ## FORCE it so that admin/unidata can decide whether these files ## are up-to-date. Although since charprop depends on bootstrap-emacs, diff --git a/src/emacs.c b/src/emacs.c index 1ad8af70a7..eb5f1128f6 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1610,6 +1610,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem syms_of_threads (); syms_of_profiler (); +#ifdef HAVE_JSON + syms_of_json (); +#endif + keys_of_casefiddle (); keys_of_cmds (); keys_of_buffer (); diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000000..85abf87e21 --- /dev/null +++ b/src/json.c @@ -0,0 +1,469 @@ +/* JSON parsing and serialization. + +Copyright (C) 2017 Free Software Foundation, Inc. + +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 . */ + +#include + +#include +#include + +#include + +#include "lisp.h" +#include "buffer.h" + +static _Noreturn void +json_out_of_memory (void) +{ + xsignal0 (Qjson_out_of_memory); +} + +static _Noreturn void +json_parse_error (const json_error_t *error) +{ + xsignal (Qjson_parse_error, + list5 (build_string (error->text), build_string (error->source), + make_natnum (error->line), make_natnum (error->column), + make_natnum (error->position))); +} + +static void +json_release_object (void *object) +{ + json_decref (object); +} + +static void +check_string_without_embedded_nulls (Lisp_Object object) +{ + CHECK_STRING (object); + CHECK_TYPE (memchr (SDATA (object), '\0', SBYTES (object)) == NULL, + Qstring_without_embedded_nulls_p, object); +} + +static json_t * +json_check (json_t *object) +{ + if (object == NULL) + json_out_of_memory (); + return object; +} + +/* This returns Lisp_Object so we can use unbind_to. The return value + is always nil. */ + +static Lisp_Object +lisp_to_json (Lisp_Object lisp, json_t **json) +{ + if (NILP (lisp)) + { + *json =json_check (json_null ()); + return Qnil; + } + else if (EQ (lisp, QCjson_false)) + { + *json = json_check (json_false ()); + return Qnil; + } + else if (EQ (lisp, Qt)) + { + *json = json_check (json_true ()); + return Qnil; + } + else if (INTEGERP (lisp)) + { + CHECK_TYPE_RANGED_INTEGER (json_int_t, lisp); + *json = json_check (json_integer (XINT (lisp))); + return Qnil; + } + else if (FLOATP (lisp)) + { + *json = json_check (json_real (XFLOAT_DATA (lisp))); + return Qnil; + } + else if (STRINGP (lisp)) + { + ptrdiff_t size = SBYTES (lisp); + eassert (size >= 0); + if (size > SIZE_MAX) + xsignal1 (Qoverflow_error, build_pure_c_string ("string is too long")); + *json = json_check (json_stringn (SSDATA (lisp), size)); + return Qnil; + } + else if (VECTORP (lisp)) + { + if (++lisp_eval_depth > max_lisp_eval_depth) + xsignal0 (Qjson_object_too_deep); + ptrdiff_t size = ASIZE (lisp); + eassert (size >= 0); + if (size > SIZE_MAX) + xsignal1 (Qoverflow_error, build_pure_c_string ("vector is too long")); + *json = json_check (json_array ()); + ptrdiff_t count = SPECPDL_INDEX (); + record_unwind_protect_ptr (json_release_object, json); + for (ptrdiff_t i = 0; i < size; ++i) + { + json_t *element; + lisp_to_json (AREF (lisp, i), &element); + int status = json_array_append_new (*json, element); + if (status == -1) + json_out_of_memory (); + eassert (status == 0); + } + eassert (json_array_size (*json) == size); + clear_unwind_protect (count); + --lisp_eval_depth; + return unbind_to (count, Qnil); + } + else if (HASH_TABLE_P (lisp)) + { + if (++lisp_eval_depth > max_lisp_eval_depth) + xsignal0 (Qjson_object_too_deep); + struct Lisp_Hash_Table *h = XHASH_TABLE (lisp); + *json = json_check (json_object ()); + ptrdiff_t count = SPECPDL_INDEX (); + record_unwind_protect_ptr (json_release_object, *json); + for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (h); ++i) + if (!NILP (HASH_HASH (h, i))) + { + Lisp_Object key = HASH_KEY (h, i); + /* We can’t specify the length, so the string must be + null-terminated. */ + check_string_without_embedded_nulls (key); + json_t *value; + lisp_to_json (HASH_VALUE (h, i), &value); + int status = json_object_set_new (*json, SSDATA (key), value); + if (status == -1) + json_out_of_memory (); + eassert (status == 0); + } + clear_unwind_protect (count); + --lisp_eval_depth; + return unbind_to (count, Qnil); + } + wrong_type_argument (Qjson_value_p, lisp); +} + +DEFUN ("json-serialize", Fjson_serialize, Sjson_serialize, 1, 1, NULL, + doc: /* Return the JSON representation of OBJECT as a string. +OBJECT must be a vector or hashtable, and its elements can recursively +contain nil, t, `:json-false', numbers, strings, or other vectors and +hashtables. nil, t, and `:json-false' will be converted to JSON null, +true, and false values, respectively. Vectors will be converted to +JSON arrays, and hashtables to JSON objects. Hashtable keys must be +strings without embedded null characters and must be unique within +each object. */) + (Lisp_Object object) +{ + ptrdiff_t count = SPECPDL_INDEX (); + + json_t *json; + lisp_to_json (object, &json); + record_unwind_protect_ptr (json_release_object, json); + + char *string = json_dumps (json, JSON_COMPACT); + if (string == NULL) + json_out_of_memory (); + record_unwind_protect_ptr (free, string); + + return unbind_to (count, build_string (string)); +} + +struct json_buffer_and_size +{ + const char *buffer; + size_t size; +}; + +static Lisp_Object +json_insert (Lisp_Object data) +{ + const struct json_buffer_and_size *buffer_and_size = XSAVE_POINTER (data, 0); + if (FIXNUM_OVERFLOW_P (buffer_and_size->size)) + xsignal1 (Qoverflow_error, build_pure_c_string ("buffer too large")); + Lisp_Object string + = make_string (buffer_and_size->buffer, buffer_and_size->size); + insert_from_string (string, 0, 0, SCHARS (string), SBYTES (string), false); + return Qnil; +} + +struct json_insert_data +{ + /* nil if json_insert succeeded, otherwise a cons + (ERROR-SYMBOL . ERROR-DATA). */ + Lisp_Object error; +}; + +static int +json_insert_callback (const char *buffer, size_t size, void *data) +{ + /* This function may not exit nonlocally. */ + struct json_insert_data *d = data; + struct json_buffer_and_size buffer_and_size + = {.buffer = buffer, .size = size}; + d->error + = internal_condition_case_1 (json_insert, make_save_ptr (&buffer_and_size), + Qt, Fidentity); + return 0; +} + +DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, 1, NULL, + doc: /* Insert the JSON representation of OBJECT before point. +This is the same as (insert (json-serialize OBJECT)), but potentially +faster. See the function `json-serialize' for allowed values of +OBJECT. */) + (Lisp_Object object) +{ + ptrdiff_t count = SPECPDL_INDEX (); + + json_t *json; + lisp_to_json (object, &json); + record_unwind_protect_ptr (json_release_object, json); + + struct json_insert_data data; + int status + = json_dump_callback (json, json_insert_callback, &data, JSON_COMPACT); + if (status == -1) + json_out_of_memory (); + eassert (status == 0); + + if (!NILP (data.error)) + xsignal (XCAR (data.error), XCDR (data.error)); + + return unbind_to (count, Qnil); +} + +static Lisp_Object +json_to_lisp (json_t *json) +{ + switch (json_typeof (json)) + { + case JSON_NULL: + return Qnil; + case JSON_FALSE: + return QCjson_false; + case JSON_TRUE: + return Qt; + case JSON_INTEGER: + { + json_int_t value = json_integer_value (json); + if (FIXNUM_OVERFLOW_P (value)) + xsignal1 (Qoverflow_error, + build_pure_c_string ("JSON integer is too large")); + return make_number (value); + } + case JSON_REAL: + return make_float (json_real_value (json)); + case JSON_STRING: + { + size_t size = json_string_length (json); + if (FIXNUM_OVERFLOW_P (size)) + xsignal1 (Qoverflow_error, + build_pure_c_string ("JSON string is too long")); + return make_string (json_string_value (json), size); + } + case JSON_ARRAY: + { + if (++lisp_eval_depth > max_lisp_eval_depth) + xsignal0 (Qjson_object_too_deep); + size_t size = json_array_size (json); + if (FIXNUM_OVERFLOW_P (size)) + xsignal1 (Qoverflow_error, + build_pure_c_string ("JSON array is too long")); + Lisp_Object result = Fmake_vector (make_natnum (size), Qunbound); + for (ptrdiff_t i = 0; i < size; ++i) + ASET (result, i, + json_to_lisp (json_array_get (json, i))); + --lisp_eval_depth; + return result; + } + case JSON_OBJECT: + { + if (++lisp_eval_depth > max_lisp_eval_depth) + xsignal0 (Qjson_object_too_deep); + size_t size = json_object_size (json); + if (FIXNUM_OVERFLOW_P (size)) + xsignal1 (Qoverflow_error, + build_pure_c_string ("JSON object has too many elements")); + Lisp_Object result = CALLN (Fmake_hash_table, QCtest, Qequal, + QCsize, make_natnum (size)); + struct Lisp_Hash_Table *h = XHASH_TABLE (result); + const char *key_str; + json_t *value; + json_object_foreach (json, key_str, value) + { + Lisp_Object key = build_string (key_str); + EMACS_UINT hash; + ptrdiff_t i = hash_lookup (h, key, &hash); + eassert (i < 0); + hash_put (h, key, json_to_lisp (value), hash); + } + --lisp_eval_depth; + return result; + } + } + /* Can’t get here. */ + emacs_abort (); +} + +DEFUN ("json-parse-string", Fjson_parse_string, Sjson_parse_string, 1, 1, NULL, + doc: /* Parse the JSON STRING into a Lisp object. +This is essentially the reverse operation of `json-serialize', which +see. The returned object will be a vector or hashtable. Its elements +will be nil, t, `:json-false', numbers, strings, or further vectors +and hashtables. If there are duplicate keys in an object, all but the +last one are ignored. If STRING doesn't contain a valid JSON object, +an error of type `json-parse-error' is signaled. */) + (Lisp_Object string) +{ + ptrdiff_t count = SPECPDL_INDEX (); + check_string_without_embedded_nulls (string); + + json_error_t error; + json_t *object = json_loads (SSDATA (string), 0, &error); + if (object == NULL) + json_parse_error (&error); + + /* Avoid leaking the object in case of further errors. */ + if (object != NULL) + record_unwind_protect_ptr (json_release_object, object); + + return unbind_to (count, json_to_lisp (object)); +} + +struct json_read_buffer_data +{ + ptrdiff_t point; +}; + +static size_t +json_read_buffer_callback (void *buffer, size_t buflen, void *data) +{ + struct json_read_buffer_data *d = data; + + /* First, parse from point to the gap or the end of the accessible + portion, whatever is closer. */ + ptrdiff_t point = d->point; + ptrdiff_t end; + { + bool overflow = INT_ADD_WRAPV (BUFFER_CEILING_OF (point), 1, &end); + eassert (!overflow); + } + size_t count; + { + bool overflow = INT_SUBTRACT_WRAPV (end, point, &count); + eassert (!overflow); + } + if (buflen < count) + count = buflen; + memcpy (buffer, BYTE_POS_ADDR (point), count); + { + bool overflow = INT_ADD_WRAPV (d->point, count, &d->point); + eassert (!overflow); + } + return count; +} + +DEFUN ("json-parse-buffer", Fjson_parse_buffer, Sjson_parse_buffer, + 0, 0, NULL, + doc: /* Read JSON object from current buffer starting at point. +This is similar to `json-parse-string', which see. Move point after +the end of the object if parsing was successful. On error, point is +not moved. */) + (void) +{ + ptrdiff_t count = SPECPDL_INDEX (); + + ptrdiff_t point = PT_BYTE; + struct json_read_buffer_data data = {.point = point}; + json_error_t error; + json_t *object = json_load_callback (json_read_buffer_callback, &data, + JSON_DISABLE_EOF_CHECK, &error); + + if (object == NULL) + json_parse_error (&error); + + /* Avoid leaking the object in case of further errors. */ + record_unwind_protect_ptr (json_release_object, object); + + /* Convert and then move point only if everything succeeded. */ + Lisp_Object lisp = json_to_lisp (object); + + { + /* Adjust point by how much we just read. Do this here because + tokener->char_offset becomes incorrect below. */ + bool overflow = INT_ADD_WRAPV (point, error.position, &point); + eassert (!overflow); + eassert (point <= ZV_BYTE); + SET_PT_BOTH (BYTE_TO_CHAR (point), point); + } + + return unbind_to (count, lisp); +} + +/* Simplified version of ‘define-error’ that works with pure + objects. */ + +static void +define_error (Lisp_Object name, const char *message, Lisp_Object parent) +{ + eassert (SYMBOLP (name)); + eassert (SYMBOLP (parent)); + Lisp_Object parent_conditions = Fget (parent, Qerror_conditions); + eassert (CONSP (parent_conditions)); + eassert (!NILP (Fmemq (parent, parent_conditions))); + eassert (NILP (Fmemq (name, parent_conditions))); + Fput (name, Qerror_conditions, pure_cons (name, parent_conditions)); + Fput (name, Qerror_message, build_pure_c_string (message)); +} + +void +syms_of_json (void) +{ + DEFSYM (QCjson_false, ":json-false"); + + DEFSYM (Qstring_without_embedded_nulls_p, "string-without-embedded-nulls-p"); + DEFSYM (Qjson_value_p, "json-value-p"); + + DEFSYM (Qjson_error, "json-error"); + DEFSYM (Qjson_out_of_memory, "json-out-of-memory"); + DEFSYM (Qjson_parse_error, "json-parse-error"); + DEFSYM (Qjson_object_too_deep, "json-object-too-deep"); + define_error (Qjson_error, "generic JSON error", Qerror); + define_error (Qjson_out_of_memory, "no free memory for creating JSON object", + Qjson_error); + define_error (Qjson_parse_error, "could not parse JSON stream", + Qjson_error); + define_error (Qjson_object_too_deep, "object cyclic or too deep", + Qjson_error); + + DEFSYM (Qpure, "pure"); + DEFSYM (Qside_effect_free, "side-effect-free"); + + DEFSYM (Qjson_serialize, "json-serialize"); + DEFSYM (Qjson_parse_string, "json-parse-string"); + Fput (Qjson_serialize, Qpure, Qt); + Fput (Qjson_serialize, Qside_effect_free, Qt); + Fput (Qjson_parse_string, Qpure, Qt); + Fput (Qjson_parse_string, Qside_effect_free, Qt); + + defsubr (&Sjson_serialize); + defsubr (&Sjson_insert); + defsubr (&Sjson_parse_string); + defsubr (&Sjson_parse_buffer); +} diff --git a/src/lisp.h b/src/lisp.h index c503082442..8d485098ac 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3440,6 +3440,11 @@ extern int x_bitmap_mask (struct frame *, ptrdiff_t); extern void reset_image_types (void); extern void syms_of_image (void); +#ifdef HAVE_JSON +/* Defined in json.c. */ +extern void syms_of_json (void); +#endif + /* Defined in insdel.c. */ extern void move_gap_both (ptrdiff_t, ptrdiff_t); extern _Noreturn void buffer_overflow (void); diff --git a/test/src/json-tests.el b/test/src/json-tests.el new file mode 100644 index 0000000000..1d8f9a490b --- /dev/null +++ b/test/src/json-tests.el @@ -0,0 +1,61 @@ +;;; json-tests.el --- unit tests for json.c -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 Free Software Foundation, Inc. + +;; 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 . + +;;; Commentary: + +;; Unit tests for src/json.c. + +;;; Code: + +(require 'cl-lib) +(require 'map) + +(ert-deftest json-serialize/roundtrip () + (let ((lisp [nil :json-false t 0 123 -456 3.75 "foo"]) + (json "[null,false,true,0,123,-456,3.75,\"foo\"]")) + (should (equal (json-serialize lisp) json)) + (with-temp-buffer + (json-insert lisp) + (should (equal (buffer-string) json)) + (should (eobp))) + (should (equal (json-parse-string json) lisp)) + (with-temp-buffer + (insert json) + (goto-char 1) + (should (equal (json-parse-buffer) lisp)) + (should (eobp))))) + +(ert-deftest json-serialize/object () + (let ((table (make-hash-table :test #'equal))) + (puthash "abc" [1 2 t] table) + (puthash "def" nil table) + (should (equal (json-serialize table) + "{\"abc\":[1,2,true],\"def\":null}")))) + +(ert-deftest json-parse-string/object () + (let ((actual + (json-parse-string + "{ \"abc\" : [1, 2, true], \"def\" : null, \"abc\" : [9, false] }\n"))) + (should (hash-table-p actual)) + (should (equal (hash-table-count actual) 2)) + (should (equal (cl-sort (map-pairs actual) #'string< :key #'car) + '(("abc" . [9 :json-false]) ("def")))))) + +(provide 'json-tests) +;;; json-tests.el ends here commit 331d0e520ff5a3599cc9958108a6b6b8cb277ce3 Author: Mark Oteiza Date: Mon Sep 18 09:00:45 2017 -0400 Fix gensym * lisp/subr.el (gensym): Actually implement the default prefix. * test/lisp/subr-tests.el (subr-tests--gensym): New test. diff --git a/lisp/subr.el b/lisp/subr.el index 79ae1f4830..96b1ac19b4 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -289,7 +289,7 @@ The name is made by appending `gensym-counter' to PREFIX. PREFIX is a string, and defaults to \"g\"." (let ((num (prog1 gensym-counter (setq gensym-counter (1+ gensym-counter))))) - (make-symbol (format "%s%d" prefix num)))) + (make-symbol (format "%s%d" (or prefix "g") num)))) (defun ignore (&rest _ignore) "Do nothing and return nil. diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index ac9e2df603..a68688eba7 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -300,6 +300,12 @@ cf. Bug#25477." (setq res (read-passwd "pass: " 'confirm (mapconcat #'string default ""))) (should (string= default res))))) +(ert-deftest subr-tests--gensym () + "Test `gensym' behavior." + (should (equal (symbol-name (let ((gensym-counter 0)) (gensym))) + "g0")) + (should (eq (string-to-char (symbol-name (gensym))) ?g)) + (should (eq (string-to-char (symbol-name (gensym "X"))) ?X))) (provide 'subr-tests) ;;; subr-tests.el ends here commit ab351d442d7bb4d17cbb43638aaed1775d8c0344 Author: Rasmus Date: Mon Sep 18 12:01:12 2017 +0200 Update Org to v9.1.1 Please see etc/ORG-NEWS for major changes. diff --git a/doc/misc/org.texi b/doc/misc/org.texi index ca57501f3d..37f2ba551a 100644 --- a/doc/misc/org.texi +++ b/doc/misc/org.texi @@ -4,8 +4,8 @@ @settitle The Org Manual @include docstyle.texi -@set VERSION 9.0.10 -@set DATE 2017-08-27 +@set VERSION 9.1.1 +@set DATE 2017-09-17 @c Version and Contact Info @set MAINTAINERSITE @uref{http://orgmode.org,maintainers web page} @@ -527,7 +527,6 @@ The built-in agenda views * Weekly/daily agenda:: The calendar page with current tasks * Global TODO list:: All unfinished action items * Matching tags and properties:: Structured information with fine-tuned search -* Timeline:: Time-sorted view for single file * Search view:: Find entries by searching for text * Stuck projects:: Find projects you need to review @@ -1311,8 +1310,8 @@ Show the current subtree in an indirect buffer@footnote{The indirect buffer buffer, but will be narrowed to the current tree. Editing the indirect buffer will also change the original buffer, but without affecting visibility in that buffer.}. With a numeric prefix argument N, go up to level N and -then take that tree. If N is negative then go up that many levels. With a -@kbd{C-u} prefix, do not remove the previously used indirect buffer. +then take that tree. If N is negative then go up that many levels. With +a @kbd{C-u} prefix, do not remove the previously used indirect buffer. @orgcmd{C-c C-x v,org-copy-visible} Copy the @i{visible} text in the region into the kill ring. @end table @@ -1422,9 +1421,9 @@ See also the option @code{org-goto-interface}. @cindex subtrees, cut and paste @table @asis -@orgcmd{M-@key{RET},org-insert-heading} +@orgcmd{M-@key{RET},org-meta-return} @vindex org-M-RET-may-split-line -Insert a new heading/item with the same level as the one at point. +Insert a new heading, item or row. If the command is used at the @emph{beginning} of a line, and if there is a heading or a plain list item (@pxref{Plain lists}) at point, the new @@ -2108,16 +2107,14 @@ create the above table, you would only type fields. Even faster would be to type @code{|Name|Phone|Age} followed by @kbd{C-c @key{RET}}. -@vindex org-enable-table-editor @vindex org-table-auto-blank-field -When typing text into a field, Org treats @key{DEL}, -@key{Backspace}, and all character keys in a special way, so that -inserting and deleting avoids shifting other fields. Also, when -typing @emph{immediately after the cursor was moved into a new field -with @kbd{@key{TAB}}, @kbd{S-@key{TAB}} or @kbd{@key{RET}}}, the -field is automatically made blank. If this behavior is too -unpredictable for you, configure the options -@code{org-enable-table-editor} and @code{org-table-auto-blank-field}. +When typing text into a field, Org treats @key{DEL}, @key{Backspace}, and all +character keys in a special way, so that inserting and deleting avoids +shifting other fields. Also, when typing @emph{immediately after the cursor +was moved into a new field with @kbd{@key{TAB}}, @kbd{S-@key{TAB}} or +@kbd{@key{RET}}}, the field is automatically made blank. If this behavior is +too unpredictable for you, configure the option +@code{org-table-auto-blank-field}. @table @kbd @tsubheading{Creation and conversion} @@ -2672,7 +2669,7 @@ calculation precision is greater. Degree and radian angle modes of Calc. @item @code{F}, @code{S} Fraction and symbolic modes of Calc. -@item @code{T}, @code{t} +@item @code{T}, @code{t}, @code{U} Duration computations in Calc or Lisp, @pxref{Durations and time values}. @item @code{E} If and how to consider empty fields. Without @samp{E} empty fields in range @@ -2789,26 +2786,31 @@ Compute the sum of columns 1 to 4, like Calc's @code{vsum($1..$4)}. @cindex Time, computing @vindex org-table-duration-custom-format -If you want to compute time values use the @code{T} flag, either in Calc -formulas or Elisp formulas: +If you want to compute time values use the @code{T}, @code{t}, or @code{U} +flag, either in Calc formulas or Elisp formulas: @example @group | Task 1 | Task 2 | Total | |---------+----------+----------| | 2:12 | 1:47 | 03:59:00 | + | 2:12 | 1:47 | 03:59 | | 3:02:20 | -2:07:00 | 0.92 | - #+TBLFM: @@2$3=$1+$2;T::@@3$3=$1+$2;t + #+TBLFM: @@2$3=$1+$2;T::@@3$3=$1+$2;U::@@4$3=$1+$2;t @end group @end example Input duration values must be of the form @code{HH:MM[:SS]}, where seconds are optional. With the @code{T} flag, computed durations will be displayed -as @code{HH:MM:SS} (see the first formula above). With the @code{t} flag, -computed durations will be displayed according to the value of the option -@code{org-table-duration-custom-format}, which defaults to @code{'hours} and -will display the result as a fraction of hours (see the second formula in the -example above). +as @code{HH:MM:SS} (see the first formula above). With the @code{U} flag, +seconds will be omitted so that the result will be only @code{HH:MM} (see +second formula above). Zero-padding of the hours field will depend upon the +value of the variable @code{org-table-duration-hour-zero-padding}. + +With the @code{t} flag, computed durations will be displayed according to the +value of the option @code{org-table-duration-custom-format}, which defaults +to @code{'hours} and will display the result as a fraction of hours (see the +third formula in the example above). Negative duration values can be manipulated as well, and integers will be considered as seconds in addition and subtraction. @@ -4034,8 +4036,8 @@ states}), you will be prompted for a TODO keyword through the fast selection interface; this is the default behavior when @code{org-use-fast-todo-selection} is non-@code{nil}. -The same rotation can also be done ``remotely'' from the timeline and agenda -buffers with the @kbd{t} command key (@pxref{Agenda commands}). +The same rotation can also be done ``remotely'' from agenda buffers with the +@kbd{t} command key (@pxref{Agenda commands}). @orgkey{C-u C-c C-t} When TODO keywords have no selection keys, select a specific keyword using @@ -4151,19 +4153,19 @@ be set up like this: @end lisp In this case, different keywords do not indicate a sequence, but rather -different types. So the normal work flow would be to assign a task to a -person, and later to mark it DONE@. Org mode supports this style by adapting -the workings of the command @kbd{C-c C-t}@footnote{This is also true for the -@kbd{t} command in the timeline and agenda buffers.}. When used several -times in succession, it will still cycle through all names, in order to first -select the right type for a task. But when you return to the item after some -time and execute @kbd{C-c C-t} again, it will switch from any name directly -to DONE@. Use prefix arguments or completion to quickly select a specific -name. You can also review the items of a specific TODO type in a sparse tree -by using a numeric prefix to @kbd{C-c / t}. For example, to see all things -Lucy has to do, you would use @kbd{C-3 C-c / t}. To collect Lucy's items -from all agenda files into a single buffer, you would use the numeric prefix -argument as well when creating the global TODO list: @kbd{C-3 C-c a t}. +different types. So the normal work flow would be to assign a task to +a person, and later to mark it DONE@. Org mode supports this style by +adapting the workings of the command @kbd{C-c C-t}@footnote{This is also true +for the @kbd{t} command in the agenda buffers.}. When used several times in +succession, it will still cycle through all names, in order to first select +the right type for a task. But when you return to the item after some time +and execute @kbd{C-c C-t} again, it will switch from any name directly to +DONE@. Use prefix arguments or completion to quickly select a specific name. +You can also review the items of a specific TODO type in a sparse tree by +using a numeric prefix to @kbd{C-c / t}. For example, to see all things Lucy +has to do, you would use @kbd{C-3 C-c / t}. To collect Lucy's items from all +agenda files into a single buffer, you would use the numeric prefix argument +as well when creating the global TODO list: @kbd{C-3 C-c a t}. @node Multiple sets in one file @subsection Multiple keyword sets in one file @@ -4435,11 +4437,6 @@ lognotedone}.} You will then be prompted for a note, and that note will be stored below the entry with a @samp{Closing Note} heading. -In the timeline (@pxref{Timeline}) and in the agenda -(@pxref{Weekly/daily agenda}), you can then use the @kbd{l} key to -display the TODO items with a @samp{CLOSED} timestamp on each day, -giving you an overview of what has been done. - @node Tracking TODO state changes @subsection Tracking TODO state changes @cindex drawer, for state change recording @@ -4654,8 +4651,8 @@ items. Set the priority of the current headline (@command{org-priority}). The command prompts for a priority character @samp{A}, @samp{B} or @samp{C}. When you press @key{SPC} instead, the priority cookie is removed from the -headline. The priorities can also be changed ``remotely'' from the timeline -and agenda buffer with the @kbd{,} command (@pxref{Agenda commands}). +headline. The priorities can also be changed ``remotely'' from the agenda +buffer with the @kbd{,} command (@pxref{Agenda commands}). @c @orgcmdkkcc{S-@key{up},S-@key{down},org-priority-up,org-priority-down} @vindex org-priority-start-cycle-with-default @@ -5901,10 +5898,10 @@ agenda (@pxref{Weekly/daily agenda}). We distinguish: @item Plain timestamp; Event; Appointment @cindex timestamp @cindex appointment -A simple timestamp just assigns a date/time to an item. This is just -like writing down an appointment or event in a paper agenda. In the -timeline and agenda displays, the headline of an entry associated with a -plain timestamp will be shown exactly on that date. +A simple timestamp just assigns a date/time to an item. This is just like +writing down an appointment or event in a paper agenda. In the agenda +display, the headline of an entry associated with a plain timestamp will be +shown exactly on that date. @example * Meet Peter at the movies @@ -6584,9 +6581,8 @@ buffer (see variable @code{org-remove-highlights-with-change}) or press @kbd{C-c C-c}. @end table -The @kbd{l} key may be used in the timeline (@pxref{Timeline}) and in -the agenda (@pxref{Weekly/daily agenda}) to show which tasks have been -worked on or closed during a day. +The @kbd{l} key may be used the agenda (@pxref{Weekly/daily agenda}) to show +which tasks have been worked on or closed during a day. @strong{Important:} note that both @code{org-clock-out} and @code{org-clock-in-last} can have a global key binding and will not @@ -6649,6 +6645,7 @@ be selected: tree @r{the surrounding level 1 tree} agenda @r{all agenda files} ("file"..) @r{scan these files} + function @r{the list of files returned by a function of no argument} file-with-archives @r{current file and its archives} agenda-with-archives @r{all agenda files, including archives} :block @r{The time block to consider. This block is specified either} @@ -7083,7 +7080,7 @@ would look like: (setq org-capture-templates '(("t" "Todo" entry (file+headline "~/org/gtd.org" "Tasks") "* TODO %?\n %i\n %a") - ("j" "Journal" entry (file+datetree "~/org/journal.org") + ("j" "Journal" entry (file+olp+datetree "~/org/journal.org") "* %?\nEntered on %U\n %i\n %a"))) @end group @end smalllisp @@ -7191,21 +7188,19 @@ For non-unique headings, the full path is safer. @item (file+regexp "path/to/file" "regexp to find location") Use a regular expression to position the cursor. -@item (file+datetree "path/to/file") -Will create a heading in a date tree for today's date@footnote{Datetree -headlines for years accept tags, so if you use both @code{* 2013 :noexport:} -and @code{* 2013} in your file, the capture will refile the note to the first -one matched.}. - -@item (file+datetree+prompt "path/to/file") -Will create a heading in a date tree, but will prompt for the date. - -@item (file+weektree "path/to/file") -Will create a heading in a week tree for today's date. Week trees are sorted -by week and not by month unlike datetrees. - -@item (file+weektree+prompt "path/to/file") -Will create a heading in a week tree, but will prompt for the date. +@item (file+olp+datetree "path/to/file" [ "Level 1 heading" ....]) +This target@footnote{Org used to offer four different targets for date/week +tree capture. Now, Org automatically translates these to use +@code{file+olp+datetree}, applying the @code{:time-prompt} and +@code{:tree-type} properties. Please rewrite your date/week-tree targets +using @code{file+olp+datetree} since the older targets are now deprecated.} +will create a heading in a date tree@footnote{A date tree is an outline +structure with years on the highest level, months or ISO-weeks as sublevels +and then dates on the lowest level. Tags are allowed in the tree structure.} +for today's date. If the optional outline path is given, the tree will be +built under the node it is pointing to, instead of at top level. Check out +the @code{:time-prompt} and @code{:tree-type} properties below for additional +options. @item (file+function "path/to/file" function-finding-location) A function to find the right location in the file. @@ -7257,6 +7252,16 @@ with the capture. Note that @code{:clock-keep} has precedence over @code{:clock-resume}. When setting both to @code{t}, the current clock will run and the previous one will not be resumed. +@item :time-prompt +Prompt for a date/time to be used for date/week trees and when filling the +template. Without this property, capture uses the current date and time. +Even if this property has not been set, you can force the same behavior by +calling @code{org-capture} with a @kbd{C-1} prefix argument. + +@item :tree-type +When `week', make a week tree instead of the month tree, i.e. place the +headings for each day under a heading with the current iso week. + @item :unnarrowed Do not narrow the target buffer, simply show the full buffer. Default is to narrow it so that you only see the new material. @@ -7428,6 +7433,9 @@ Note that hard links are not supported on all systems. Attach a file using the copy/move/link method. Note that hard links are not supported on all systems. +@orgcmdtkc{u,C-c C-a u,org-attach-url} +Attach a file from URL + @orgcmdtkc{n,C-c C-a n,org-attach-new} Create a new attachment as an Emacs buffer. @@ -7920,7 +7928,7 @@ important for a particular date, this information must be collected, sorted and displayed in an organized way. Org can select items based on various criteria and display them -in a separate buffer. Seven different view types are provided: +in a separate buffer. Six different view types are provided: @itemize @bullet @item @@ -7933,9 +7941,6 @@ action items, a @emph{match view}, showings headlines based on the tags, properties, and TODO state associated with them, @item -a @emph{timeline view} that shows all events in a single Org file, -in time-sorted view, -@item a @emph{text search view} that shows all entries from multiple files that contain specified keywords, @item @@ -8075,8 +8080,6 @@ Create a list of all TODO items (@pxref{Global TODO list}). @item m @r{/} M Create a list of headlines matching a TAGS expression (@pxref{Matching tags and properties}). -@item L -Create the timeline view for the current buffer (@pxref{Timeline}). @item s Create a list of entries selected by a boolean expression of keywords and/or regular expressions that must or must not occur in the entry. @@ -8130,7 +8133,6 @@ In this section we describe the built-in views. * Weekly/daily agenda:: The calendar page with current tasks * Global TODO list:: All unfinished action items * Matching tags and properties:: Structured information with fine-tuned search -* Timeline:: Time-sorted view for single file * Search view:: Find entries by searching for text * Stuck projects:: Find projects you need to review @end menu @@ -8528,26 +8530,6 @@ Select @samp{:work:}-tagged TODO lines that are either @samp{WAITING} or @samp{NEXT}. @end table -@node Timeline -@subsection Timeline for a single file -@cindex timeline, single file -@cindex time-sorted view - -The timeline summarizes all time-stamped items from a single Org mode -file in a @emph{time-sorted view}. The main purpose of this command is -to give an overview over events in a project. - -@table @kbd -@orgcmd{C-c a L,org-timeline} -Show a time-sorted view of the Org file, with all time-stamped items. -When called with a @kbd{C-u} prefix, all unfinished TODO entries -(scheduled or not) are also listed under the current date. -@end table - -@noindent -The commands available in the timeline buffer are listed in -@ref{Agenda commands}. - @node Search view @subsection Search view @cindex search view @@ -9068,7 +9050,7 @@ prefix arguments @kbd{C-u C-u}, show only logging information, nothing else. @c @orgcmdkskc{v [,[,org-agenda-manipulate-query-add} Include inactive timestamps into the current view. Only for weekly/daily -agenda and timeline views. +agenda. @c @orgcmd{v a,org-agenda-archives-mode} @xorgcmd{v A,org-agenda-archives-mode 'files} @@ -9708,8 +9690,9 @@ See the docstring of the variable for more information. If you are away from your computer, it can be very useful to have a printed version of some agenda views to carry around. Org mode can export custom -agenda views as plain text, HTML@footnote{You need to install Hrvoje Niksic's -@file{htmlize.el}.}, Postscript, PDF@footnote{To create PDF output, the +agenda views as plain text, HTML@footnote{You need to install +@file{htmlize.el} from @uref{https://github.com/hniksic/emacs-htmlize,Hrvoje +Niksic's repository.}}, Postscript, PDF@footnote{To create PDF output, the ghostscript @file{ps2pdf} utility must be installed on the system. Selecting a PDF file will also create the postscript file.}, and iCalendar files. If you want to do this only occasionally, use the command @@ -9771,13 +9754,13 @@ or absolute. @end lisp The extension of the file name determines the type of export. If it is -@file{.html}, Org mode will use the @file{htmlize.el} package to convert -the buffer to HTML and save it to this file name. If the extension is -@file{.ps}, @code{ps-print-buffer-with-faces} is used to produce -Postscript output. If the extension is @file{.ics}, iCalendar export is -run export over all files that were used to construct the agenda, and -limit the export to entries listed in the agenda. Any other -extension produces a plain ASCII file. +@file{.html}, Org mode will try to use the @file{htmlize.el} package to +convert the buffer to HTML and save it to this file name. If the extension +is @file{.ps}, @code{ps-print-buffer-with-faces} is used to produce +Postscript output. If the extension is @file{.ics}, iCalendar export is run +export over all files that were used to construct the agenda, and limit the +export to entries listed in the agenda. Any other extension produces a plain +ASCII file. The export files are @emph{not} created when you use one of those commands interactively because this might use too much overhead. @@ -10085,7 +10068,7 @@ If the example is source code from a programming language, or any other text that can be marked up by font-lock in Emacs, you can ask for the example to look like the fontified Emacs buffer@footnote{This works automatically for the HTML back-end (it requires version 1.34 of the @file{htmlize.el} package, -which is distributed with Org). Fontified code chunks in @LaTeX{} can be +which you need to install). Fontified code chunks in @LaTeX{} can be achieved using either the @url{https://www.ctan.org/tex-archive/macros/latex/contrib/listings/?lang=en, listings,} or the @@ -10424,7 +10407,7 @@ major @LaTeX{} mode like AUC@TeX{} in order to speed-up insertion of environments and math templates. Inside Org mode, you can make use of some of the features of CD@LaTeX{} mode. You need to install @file{cdlatex.el} and @file{texmathp.el} (the latter comes also with -AUC@TeX{}) from @url{http://www.astro.uva.nl/~dominik/Tools/cdlatex}. +AUC@TeX{}) from @url{https://staff.fnwi.uva.nl/c.dominik/Tools/cdlatex}. Don't use CD@LaTeX{} mode itself under Org mode, but use the light version @code{org-cdlatex-mode} that comes as part of Org mode. Turn it on for the current buffer with @kbd{M-x org-cdlatex-mode RET}, or for all @@ -10624,14 +10607,14 @@ override options set at a more general level. @cindex #+SETUPFILE In-buffer settings may appear anywhere in the file, either directly or -indirectly through a file included using @samp{#+SETUPFILE: filename} syntax. -Option keyword sets tailored to a particular back-end can be inserted from -the export dispatcher (@pxref{The export dispatcher}) using the @code{Insert -template} command by pressing @key{#}. To insert keywords individually, -a good way to make sure the keyword is correct is to type @code{#+} and then -to use @kbd{M-@key{TAB}}@footnote{Many desktops intercept @kbd{M-TAB} to -switch windows. Use @kbd{C-M-i} or @kbd{@key{ESC} @key{TAB}} instead.} for -completion. +indirectly through a file included using @samp{#+SETUPFILE: filename or URL} +syntax. Option keyword sets tailored to a particular back-end can be +inserted from the export dispatcher (@pxref{The export dispatcher}) using the +@code{Insert template} command by pressing @key{#}. To insert keywords +individually, a good way to make sure the keyword is correct is to type +@code{#+} and then to use @kbd{M-@key{TAB}}@footnote{Many desktops intercept +@kbd{M-TAB} to switch windows. Use @kbd{C-M-i} or @kbd{@key{ESC} @key{TAB}} +instead.} for completion. The export keywords available for every back-end, and their equivalent global variables, include: @@ -10690,6 +10673,12 @@ code blocks contained in them. @cindex #+TITLE @cindex document title Org displays this title. For long titles, use multiple @code{#+TITLE} lines. + +@item EXPORT_FILE_NAME +@cindex #+EXPORT_FILE_NAME +The name of the output file to be generated. Otherwise, Org generates the +file name based on the buffer name and the extension based on the back-end +format. @end table The @code{#+OPTIONS} keyword is a compact form. To configure multiple @@ -10862,9 +10851,10 @@ Toggle inclusion of tables (@code{org-export-with-tables}). When exporting sub-trees, special node properties in them can override the above keywords. They are special because they have an @samp{EXPORT_} prefix. -For example, @samp{DATE} and @samp{OPTIONS} keywords become, respectively, -@samp{EXPORT_DATE} and @samp{EXPORT_OPTIONS}. Except for @samp{SETUPFILE}, -all other keywords listed above have an @samp{EXPORT_} equivalent. +For example, @samp{DATE} and @samp{EXPORT_FILE_NAME} keywords become, +respectively, @samp{EXPORT_DATE} and @samp{EXPORT_FILE_NAME}. Except for +@samp{SETUPFILE}, all other keywords listed above have an @samp{EXPORT_} +equivalent. @cindex #+BIND @vindex org-export-allow-bind-keywords @@ -10873,11 +10863,6 @@ can become buffer-local during export by using the BIND keyword. Its syntax is @samp{#+BIND: variable value}. This is particularly useful for in-buffer settings that cannot be changed using keywords. -@cindex property, EXPORT_FILE_NAME -Normally Org generates the file name based on the buffer name and the -extension based on the back-end format. For sub-trees, Org can export to a -file name as specified in the @code{EXPORT_FILE_NAME} property. - @node Table of contents @section Table of contents @cindex table of contents @@ -11014,8 +10999,9 @@ Visit the include file at point. @cindex macro replacement, during export @cindex #+MACRO -Macros replace text snippets during export. This is a macro definition in -Org: +@vindex org-export-global-macros +Macros replace text snippets during export. Macros are defined globally in +@code{org-export-global-macros}, or document-wise with the following syntax: @example #+MACRO: name replacement text $1, $2 are arguments @@ -11074,6 +11060,19 @@ This macro refers to the filename of the exported file. This macro returns the value of property @var{PROPERTY-NAME} in the current entry. If @var{SEARCH-OPTION} (@pxref{Search options}) refers to a remote entry, that will be used instead. + +@item @{@{@{n@}@}@} +@itemx @{@{@{n(@var{NAME})@}@}@} +@itemx @{@{@{n(@var{NAME},@var{ACTION})@}@}@} +@cindex n, macro +@cindex counter, macro +This macro implements custom counters by returning the number of times the +macro has been expanded so far while exporting the buffer. You can create +more than one counter using different @var{NAME} values. If @var{ACTION} is +@code{-}, previous value of the counter is held, i.e. the specified counter +is not incremented. If the value is a number, the specified counter is set +to that value. If it is any other non-empty string, the specified counter is +reset to 1. You may leave @var{NAME} empty to reset the default counter. @end table The surrounding brackets can be made invisible by setting @@ -13939,21 +13938,45 @@ This paragraph is preceded by... @node Plain lists in Texinfo export @subsection Plain lists in Texinfo export @cindex #+ATTR_TEXINFO, in plain lists +@cindex Two-column tables, in Texinfo export + +@cindex :table-type attribute, in Texinfo export The Texinfo export back-end by default converts description lists in the Org file using the default command @code{@@table}, which results in a table with two columns. To change this behavior, specify @code{:table-type} with -@code{@@ftable} or @code{@@vtable} attributes. For more information, +@code{ftable} or @code{vtable} attributes. For more information, @inforef{Two-column Tables,,texinfo}. -@vindex org-texinfo-def-table-markup +@vindex org-texinfo-table-default-markup +@cindex :indic attribute, in Texinfo export The Texinfo export back-end by default also applies a text highlight based on -the defaults stored in @code{org-texinfo-def-table-markup}. To override the -default highlight command, specify another one with the @code{:indic} -attribute as shown in this example: +the defaults stored in @code{org-texinfo-table-default-markup}. To override +the default highlight command, specify another one with the @code{:indic} +attribute. + +@cindex Multiple entries in two-column tables, in Texinfo export +@cindex :sep attribute, in Texinfo export +Org syntax is limited to one entry per list item. Nevertheless, the Texinfo +export back-end can split that entry according to any text provided through +the @code{:sep} attribute. Each part then becomes a new entry in the first +column of the table. + +The following example illustrates all the attributes above: @example -#+ATTR_TEXINFO: :indic @@asis -- foo :: This is the text for /foo/, with no highlighting. +#+ATTR_TEXINFO: :table-type vtable :sep , :indic asis +- foo, bar :: This is the common text for variables foo and bar. +@end example + +@noindent +becomes + +@example +@@vtable @@asis +@@item foo +@@itemx bar +This is the common text for variables foo and bar. +@@end table @end example @node Tables in Texinfo export @@ -14011,8 +14034,9 @@ A somewhat obsessive function. @node A Texinfo example @subsection A Texinfo example -Here is a more detailed example Org file. @inforef{GNU Sample -Texts,,texinfo} for an equivalent example using Texinfo code. +Here is a more detailed example Org file. @xref{GNU Sample +Texts,,,texinfo,GNU Texinfo Manual} for an equivalent example using Texinfo +code. @example #+TITLE: GNU Sample @{@{@{version@}@}@} @@ -14140,9 +14164,10 @@ and write it to @code{org-icalendar-combined-agenda-file} file name. @cindex property, SUMMARY @cindex property, DESCRIPTION @cindex property, LOCATION -The iCalendar export back-end includes SUMMARY, DESCRIPTION and LOCATION -properties from the Org entries when exporting. To force the back-end to -inherit the LOCATION property, configure the +@cindex property, TIMEZONE +The iCalendar export back-end includes SUMMARY, DESCRIPTION, LOCATION and +TIMEZONE properties from the Org entries when exporting. To force the +back-end to inherit the LOCATION and TIMEZONE properties, configure the @code{org-use-property-inheritance} variable. When Org entries do not have SUMMARY, DESCRIPTION and LOCATION properties, @@ -14151,6 +14176,12 @@ derives the description from the body of the Org item. The @code{org-icalendar-include-body} variable limits the maximum number of characters of the content are turned into its description. +The TIMEZONE property can be used to specify a per-entry time zone, and will +be applied to any entry with timestamp information. Time zones should be +specified as per the IANA time zone database format, e.g.@: ``Asia/Almaty''. +Alternately, the property value can be ``UTC'', to force UTC time for this +entry only. + Exporting to iCalendar format depends in large part on the capabilities of the destination application. Some are more lenient than others. Consult the Org mode FAQ for advice on specific applications. @@ -14772,7 +14803,7 @@ however, override everything. @item @code{:texinfo-active-timestamp-format} @tab @code{org-texinfo-active-timestamp-format} @item @code{:texinfo-classes} @tab @code{org-texinfo-classes} @item @code{:texinfo-class} @tab @code{org-texinfo-default-class} -@item @code{:texinfo-def-table-markup} @tab @code{org-texinfo-def-table-markup} +@item @code{:texinfo-table-default-markup} @tab @code{org-texinfo-table-default-markup} @item @code{:texinfo-diary-timestamp-format} @tab @code{org-texinfo-diary-timestamp-format} @item @code{:texinfo-filename} @tab @code{org-texinfo-filename} @item @code{:texinfo-format-drawer-function} @tab @code{org-texinfo-format-drawer-function} @@ -14834,15 +14865,30 @@ becomes @file{sitemap.html}). @item @code{:sitemap-title} @tab Title of sitemap page. Defaults to name of file. +@item @code{:sitemap-format-entry} +@tab With this option one can tell how a site-map entry is formatted in the +site-map. It is a function called with three arguments: the file or +directory name relative to base directory of the project, the site-map style +and the current project. It is expected to return a string. Default value +turns file names into links and use document titles as descriptions. For +specific formatting needs, one can use @code{org-publish-find-date}, +@code{org-publish-find-title} and @code{org-publish-find-property}, to +retrieve additional information about published documents. + @item @code{:sitemap-function} -@tab Plug-in function to use for generation of the sitemap. -Defaults to @code{org-publish-org-sitemap}, which generates a plain list -of links to all files in the project. +@tab Plug-in function to use for generation of the sitemap. It is called +with two arguments: the title of the site-map and a representation of the +files and directories involved in the project as a radio list (@pxref{Radio +lists}). The latter can further be transformed using +@code{org-list-to-generic}, @code{org-list-to-subtree} and alike. Default +value generates a plain list of links to all files in the project. @item @code{:sitemap-sort-folders} @tab Where folders should appear in the sitemap. Set this to @code{first} -(default) or @code{last} to display folders first or last, -respectively. Any other value will mix files and folders. +(default) or @code{last} to display folders first or last, respectively. +When set to @code{ignore}, folders are ignored altogether. Any other value +will mix files and folders. This variable has no effect when site-map style +is @code{tree}. @item @code{:sitemap-sort-files} @tab How the files are sorted in the site map. Set this to @@ -14855,24 +14901,11 @@ a file is retrieved with @code{org-publish-find-date}. @item @code{:sitemap-ignore-case} @tab Should sorting be case-sensitive? Default @code{nil}. -@item @code{:sitemap-file-entry-format} -@tab With this option one can tell how a sitemap's entry is formatted in the -sitemap. This is a format string with some escape sequences: @code{%t} stands -for the title of the file, @code{%a} stands for the author of the file and -@code{%d} stands for the date of the file. The date is retrieved with the -@code{org-publish-find-date} function and formatted with -@code{org-publish-sitemap-date-format}. Default @code{%t}. - @item @code{:sitemap-date-format} @tab Format string for the @code{format-time-string} function that tells how a sitemap entry's date is to be formatted. This property bypasses @code{org-publish-sitemap-date-format} which defaults to @code{%Y-%m-%d}. -@item @code{:sitemap-sans-extension} -@tab When non-@code{nil}, remove filenames' extensions from the generated sitemap. -Useful to have cool URIs (see @uref{http://www.w3.org/Provider/Style/URI}). -Defaults to @code{nil}. - @end multitable @node Generating an index @@ -15300,9 +15333,12 @@ Org exports both the code block and the results. Org does not export the code block nor the results. @end table -@vindex org-export-babel-evaluate -To stop Org from evaluating code blocks during export, set -@code{org-export-babel-evaluate} variable to @code{nil}. +@vindex org-export-use-babel +To stop Org from evaluating code blocks to speed exports, use the header +argument @code{:eval never-export} (@pxref{eval}). To stop Org from +evaluating code blocks for greater security, set the +@code{org-export-use-babel} variable to @code{nil}, but understand that +header arguments will have no effect. Turning off evaluation comes in handy when batch processing. For example, markup languages for wikis, which have a high risk of untrusted code. @@ -15312,12 +15348,6 @@ during export, to allow evaluation of just the header arguments but not any code evaluation in the source block, set @code{:eval never-export} (@pxref{eval}). -To evaluate just the inline code blocks, set @code{org-export-babel-evaluate} -to @code{inline-only}. Isolating the option to allow inline evaluations -separate from @samp{src} code block evaluations during exports is not for -security but for avoiding any delays due to recalculations, such as calls to -a remote database. - Org never evaluates code blocks in commented sub-trees when exporting (@pxref{Comment lines}). On the other hand, Org does evaluate code blocks in sub-trees excluded from export (@pxref{Export settings}). @@ -15471,10 +15501,10 @@ For more examples of header arguments for @code{#+CALL:} lines, @cindex code block, library The ``Library of Babel'' is a collection of code blocks. Like a function -library, these code blocks can be called from other Org files. This -collection is in a repository file in Org mode format in the @samp{doc} -directory of Org mode installation. For remote code block evaluation syntax, -@pxref{Evaluating code blocks}. +library, these code blocks can be called from other Org files. A collection +of useful code blocks is available on +@uref{http://orgmode.org/worg/library-of-babel.html,Worg}. For remote code +block evaluation syntax, @pxref{Evaluating code blocks}. @kindex C-c C-v i For any user to add code to the library, first save the code in regular @@ -15511,6 +15541,7 @@ Org supports the following languages for the @samp{src} code blocks: @item Scheme @tab scheme @tab GNU Screen @tab screen @item Sed @tab sed @tab shell @tab sh @item SQL @tab sql @tab SQLite @tab sqlite +@item Vala @tab vala @end multitable Additional documentation for some languages are at @@ -17201,12 +17232,9 @@ The sample script shows batch processing of multiple files using emacs -Q --batch --eval " (progn (require 'ob-tangle) - (mapc (lambda (file) - (save-current-buffer - (find-file file) - (org-babel-tangle) - (kill-buffer))) - command-line-args-left)) + (dolist (file command-line-args-left) + (with-current-buffer (find-file-noselect file) + (org-babel-tangle)))) " "$@@" @end example @@ -17316,6 +17344,7 @@ Org comes with these pre-defined easy templates: @item @kbd{q} @tab @code{#+BEGIN_QUOTE ... #+END_QUOTE} @item @kbd{v} @tab @code{#+BEGIN_VERSE ... #+END_VERSE} @item @kbd{c} @tab @code{#+BEGIN_CENTER ... #+END_CENTER} +@item @kbd{C} @tab @code{#+BEGIN_COMMENT ... #+END_COMMENT} @item @kbd{l} @tab @code{#+BEGIN_EXPORT latex ... #+END_EXPORT} @item @kbd{L} @tab @code{#+LATEX:} @item @kbd{h} @tab @code{#+BEGIN_EXPORT html ... #+END_EXPORT} @@ -17477,14 +17506,16 @@ have a lower ASCII number than the lowest priority. This line sets a default inheritance value for entries in the current buffer, most useful for specifying the allowed values of a property. @cindex #+SETUPFILE -@item #+SETUPFILE: file -The setup file is for additional in-buffer settings. Org loads this file and -parses it for any settings in it only when Org opens the main file. @kbd{C-c -C-c} on the settings line will also parse and load. Org also parses and -loads the file during normal exporting process. Org parses the contents of -this file as if it was included in the buffer. It can be another Org file. -To visit the file, @kbd{C-c '} while the cursor is on the line with the file -name. +@item #+SETUPFILE: file or URL +The setup file or a URL pointing to such file is for additional in-buffer +settings. Org loads this file and parses it for any settings in it only when +Org opens the main file. If URL is specified, the contents are downloaded +and stored in a temporary file cache. @kbd{C-c C-c} on the settings line +will parse and load the file, and also reset the temporary file cache. Org +also parses and loads the document during normal exporting process. Org +parses the contents of this document as if it was included in the buffer. It +can be another Org file. To visit the file (not a URL), @kbd{C-c '} while +the cursor is on the line with the file name. @item #+STARTUP: @cindex #+STARTUP Startup options Org uses when first visiting a file. @@ -17725,7 +17756,9 @@ If any highlights shown in the buffer from the creation of a sparse tree, or from clock display, remove such highlights. @item If the cursor is in one of the special @code{#+KEYWORD} lines, scan the -buffer for these lines and update the information. +buffer for these lines and update the information. Also reset the Org file +cache used to temporary store the contents of URLs used as values for +keywords like @code{#+SETUPFILE}. @item If the cursor is inside a table, realign the table. The table realigns even if automatic table editor is turned off. @@ -17941,7 +17974,7 @@ Org can use names for constants in formulas in tables. Org can also use calculation suffixes for units, such as @samp{M} for @samp{Mega}. For a standard collection of such constants, install the @file{constants} package. Install version 2.0 of this package, available at -@url{http://www.astro.uva.nl/~dominik/Tools}. Org checks if the function +@url{https://staff.fnwi.uva.nl/c.dominik/Tools/}. Org checks if the function @code{constants-get} has been autoloaded. Installation instructions are in the file, @file{constants.el}. @item @file{cdlatex.el} by Carsten Dominik diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index bb1a4008a7..d1e476267b 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -8,6 +8,499 @@ See the end of the file for license conditions. Please send Org bug reports to mailto:emacs-orgmode@gnu.org. +* Version 9.1 + +** Incompatible changes + +*** Variables relative to clocksum duration are obsolete + +~org-time-clocksum-format~, ~org-time-clocksum-use-fractional~ and +~org-time-clocksum-fractional-format~ are obsolete. If you changed +them, consider modifying ~org-duration-format~ instead. + +Variable ~org-time-clocksum-use-effort-durations~ is also obsolete. +Consider setting ~org-duration-units~ instead. + +*** ~org-at-timestamp-p~ optional argument accepts different values + +See docstrings for the allowed values. For backward compatibility, +~(org-at-timestamp-p t)~ is still supported, but should be updated +accordingly. + +*** ~org-capture-templates~ no longer accepts S-expressions as file names + +Since functions are allowed there, a straightforward way to migrate +is to turn, e.g., + +: (file (sexp)) + +into + +: (file (lambda () (sexp))) + +*** Deleted contributed packages + +=org-ebib.el, =org-bullets.el= and =org-mime.el= have been deleted +from the contrib/ directory. + +You can now find them here : + +- https://github.com/joostkremers/ebib +- https://github.com/sabof/org-bullets +- https://github.com/org-mime/org-mime + +*** Change ~org-texinfo-classes~ value +The value cannot support functions to create sectionning commands +anymore. Also, the sectionning commands should include commands for +appendices. See the docstring for more information. +*** Removal of ~:sitemap-sans-extension~ + +The publishing property is no longer recognized, as a consequence of +changes to site-map generation. + +You can get the same functionality by setting ~:sitemap-format-entry~ +to the following + +#+BEGIN_SRC elisp +(lambda (entry style project) + (cond ((not (directory-name-p entry)) + (format "[[file:%s][%s]]" + (file-name-sans-extension entry) + (org-publish-find-title entry project))) + ((eq style 'tree) (file-name-nondirectory (directory-file-name entry))) + (t entry))) +#+END_SRC + +*** Change signature for ~:sitemap-function~ + +~:sitemap-function~ now expects to be called with two arguments. See +~org-publish-project-alist~ for details. + +*** Change signature for some properties in ~org-list-to-generic~ + +~:istart~, ~:icount~, ~:iend~ and ~:isep~ now expect the type of the +list as their first argument. + +*** Change signature for ~org-get-repeater~ +The optional argument is now a string to extract the repeater from. +See docstring for details. + +*** Change signature for ~org-time-string-to-time~ +See docstring for changes. + +*** Change order of items in ~org-agenda-time-grid~ +~org-agenda-time-grid~ gained an extra item to allow users to customize +the string displayed after times in the agenda. See docstring for +details. + +*** ~tags-todo~ custom searches now include DONE keywords + +Use "/!" markup when filtering TODO keywords to get only not-done TODO +keywords. + +*** ~org-split-string~ returns ~("")~ when called on an empty string +It used to return nil. +*** Removal of =ob-scala.el= + +See [[https://github.com/ensime/emacs-scala-mode/issues/114][this github issue]]. + +You can use =ob-scala.el= as packaged in scala-mode, available from the +MELPA repository. + +** New features +*** iCalendar export uses inheritance for TIMEZONE and LOCATION properties +Both these properties can be inherited during iCalendar export, +depending on the value of ~org-use-property-inheritance~. +*** iCalendar export respects a TIMEZONE property +Set the TIMEZONE property on an entry to specify a time zone for that +entry only during iCalendar export. The property value should be +specified as in "Europe/London". +*** ~org-attach~ can move directory contents +When setting a new directory for an entry, org-attach offers to move +files over from the old directory. Using a prefix arg will reset the +directory to old, ID based one. +*** New Org duration library +This new library implements tools to read and print time durations in +various formats (e.g., "H:MM", or "1d 2h 3min"...). + +See ~org-duration-to-minutes~ and ~org-duration-from-minutes~ +docstrings. + +*** Agenda +**** New variable : ~org-agenda-show-future-repeats~ +**** New variable : ~org-agenda-prefer-last-repeat~ +**** New variable : ~org-deadline-past-days~ +See docstring for details. +**** Binding C-c C-x < for ~org-agenda-set-restriction-lock-from-agenda~ +**** New auto-align default setting for =org-agenda-tags-column= + +=org-agenda-tags-column= can now be set to =auto=, which will +automatically align tags to the right edge of the window. This is now +the default setting. + +*** New value for ~org-publish-sitemap-sort-folders~ + +The new ~ignore~ value effectively allows toggling inclusion of +directories in published site-maps. + +*** Babel + +**** Scheme: support for tables +**** Scheme: new variable: ~org-babel-scheme-null-to~ + +This new custom option allows to use a empty list or null symbol to +format the table output, initially assigned to ~hlines~. + +**** Scheme: new header ~:prologue~ + +A new block code header has been created for Org Babel that enables +developers to prepend code to the scheme block being processed. + +Multiple ~:prologue~ headers can be added each of them using a string +with the content to be added. + +The scheme blocks are prepared by surronding the code in the block +with a let form. The content of the ~:prologue~ headers are prepended +before this let form. + +**** Support for hledger accounting reports added +**** Clojure: new setting ~org-babel-clojure-sync-nrepl-timeout~ + +Creation of a new setting to specify the Cider timeout. By setting +the =org-babel-clojure-sync-nrepl-timeout= setting option. The value +is in seconds and if set to =nil= then no timeout will occur. +**** Clojure: new header ~:show-process~ + +A new block code header has been created for Org Babel that enables +developers to output the process of an ongoing process into a new +window/buffer. + +You can tell Org Babel to output the process of a running code block. + +To show that output you only have to specify the =:show-process= +option in the code block's header like this: + +#+begin_example +,#+BEGIN_SRC clojure :results output :show-process t + (dotimes [n 10] + (println n ".") + (Thread/sleep 500)) +,#+END_SRC +#+end_example + +If =:show-process= is specified that way, then when you will run the +code using =C-c C-c= a new window will open in Emacs. Everything that +is output by the REPL will immediately be added to that new window. + +When the processing of the code is finished, then the window and its +buffer will be closed and the results will be reported in the +=#+RESULTS= section. + +Note that the =:results= parameter's behavior is *not* changed. If +=silent= is specified, then no result will be displayed. If =output= +is specified then all the output from the window will appears in the +results section. If =value= is specified, then only the last returned +value of the code will be displayed in the results section. + +**** Maxima: new headers ~:prologue~ and ~:epilogue~ +Babel options ~:prologue~ and ~:epilogue~ have been implemented for +Maxima src blocks which prepend and append, respectively, the given +code strings. This can be useful for specifying formatting settings +which would add clutter to exported code. For instance, you can use +this ~:prologue "fpprintprec: 2; linel: 50;"~ for presenting Maxima +results in a beamer presentation. +**** PlantUML: add support for header arguments + +[[http://plantuml.com/][Plantuml]] source blocks now support the [[http://orgmode.org/manual/prologue.html#prologue][~:prologue~]], [[http://orgmode.org/manual/epilogue.html#epilogue][~:epilogue~]] and +[[http://orgmode.org/manual/var.html#var][~:var~]] header arguments. + +**** SQL: new engine added ~sqsh~ + +A new engine was added to support ~sqsh~ command line utility for use +against Microsoft SQL Server or Sybase SQL server. + +More information on ~sqsh~ can be found here: [[https://sourceforge.net/projects/sqsh/][sourceforge/sqsh]] + +To use ~sqsh~ in an *sql* =SRC_BLK= set the =:engine= like this: + +#+begin_example +,#+BEGIN_SRC sql :engine sqsh :dbhost my_host :dbuser master :dbpassword pass :database support +Select * From Users +Where clue > 0 +,#+END_SRC +#+end_example + +**** SQL: new engine added =vertica= + +A new engine was added to support vsql command line utility for use +against HP Vertica. + +More information on =vsql= can be found here: [[https://my.vertica.com/docs/7.2.x/HTML/index.htm#Authoring/ConnectingToHPVertica/vsql/UsingVsql.htm][my.vertica.com]] + +To use =vertica= in an sql =SRC_BLK= set the =:engine= like this: + +#+BEGIN_EXAMPLE + ,#+BEGIN_SRC sql :engine vertica :dbhost my_host :dbuser dbadmin :dbpassword pw :database vmart + SELECT * FROM nodes; + ,#+END_SRC +#+END_EXAMPLE +**** C++: New header ~:namespaces~ + +The new ~:namespaces~ export option can be used to specify namespaces +to be used within a C++ org source block. Its usage is similar to +~:includes~, in that it can accept multiple, space-separated +namespaces to use. This header is equivalent to adding ~using +namespace ;~ in the source block. Here is a "Hello World" in C++ +using ~:namespaces~: + +#+begin_example + ,#+BEGIN_SRC C++ :results output :namespaces std :includes + cout << "Hello World" << endl; + ,#+END_SRC +#+end_example + +**** Support for Vala language + +[[https://wiki.gnome.org/Projects/Vala][Vala]] language blocks support two special header arguments: + +- ~:flags~ passes arguments to the compiler +- ~:cmdline~ passes commandline arguments to the generated executable + +Support for [[http://orgmode.org/manual/var.html#var][~:var~]] does not exist yet, also there is no [[http://orgmode.org/manual/session.html#session][~:session~]] +support because Vala is a compiled language. + +The Vala compiler binary can be changed via the ~defcustom~ +~org-babel-vala-compiler~. + +*** New ~function~ scope argument for the Clock Table +Added a nullary function that returns a list of files as a possible +argument for the scope of the clock table. +*** Export +**** Implement vernacular table of contents in Markdown exporter +Global table of contents are generated using vanilla Markdown syntax +instead of HTML. Also #+TOC keyword, including local table of +contents, are now supported. +**** Add Slovanian translations +**** Implement ~org-export-insert-image-links~ +This new function is meant to be used in back-ends supporting images +as descriptions of links, a.k.a. image links. See its docstring for +details. +**** New macro : ~{{{n}}}~ +This macro creates and increment multiple counters in a document. See +manual for details. +**** Add global macros through ~org-export-global-macros~ +With this variable, one can define macros available for all documents. +**** New keyword ~#+EXPORT_FILE_NAME~ +Similarly to ~:EXPORT_FILE_NAME:~ property, this keyword allows the +user to specify the name of the output file upon exporting the +document. This also has an effect on publishing. +**** Horizontal rules are no longer ignored in LaTeX table math mode +**** Use ~compilation-mode~ for compilation output +**** Plain lists accept a new ~:separator~ attribute in Texinfo + +The new ~:separator~ attribute splits a tag from a description list +item into multiple parts. This allows to have two-column tables with +multiple entries in the first column. See manual for more details. + +**** ~latex-environment~ elements support ~caption~ keywords for LaTeX export +*** ~org-edit-special~ can edit LaTeX environments + +Using ~C-c '~ on a LaTeX environment opens a sub-editing buffer. By +default, major mode in that buffer is ~latex-mode~, but it can be +changed by configuring ~org-src-lang-modes~. + +*** ~org-list-to-generic~ includes a new property: ~:ifmt~ + +~:ifmt~ is a function to be called on the body of each item. See +~org-list-to-generic~ documentation for details. + +*** New variable : ~org-bibtex-headline-format-function~ +This allow to use a different title than entry title. + +*** ~org-attach~ supports attaching files from URLs + +Using ~C-c C-a u~ prompts for a URL pointing to a file to be attached +to the document. + +*** New option for ~org-refile-use-outline-path~ +~org-refile-use-outline-path~ now supports the setting ~buffer-name~, +which causes refile targets to be prefixed with the buffer’s +name. This is particularly useful when used in conjunction with +~uniquify.el~. + +*** ~org-file-contents~ now allows the FILE argument to be a URL. +This allows ~#+SETUPFILE:~ to accept a URL instead of a local file +path. The URL contents are auto-downloaded and saved to a temporary +cache ~org--file-cache~. A new optional argument ~NOCACHE~ is added +to ~org-file-contents~. + +*** ~org-mode-restart~ now resets the newly added ~org--file-cache~. +Using ~C-c C-c~ on any keyword (like ~#+SETUPFILE~) will reset the +that file cache. + +*** New option : ~org-table-duration-hour-zero-padding~ +This variable allow computed durations in tables to be zero-padded. + +*** New mode switch for table formulas : =U= +This mode omits seconds in durations. + +** Removed functions + +*** Org Timeline + +This feature has been removed. Use a custom agenda view, possibly +narrowed to current buffer to achieve a similar functionality. + +*** ~org-agenda-skip-entry-when-regexp-matches~ is obsolete + +Use ~org-agenda-skip-if~ instead. + +*** ~org-agenda-skip-subtree-when-regexp-matches~ is obsolete + +Use ~org-agenda-skip-if~ instead. + +*** ~org-agenda-skip-entry-when-regexp-matches-in-subtree~ is obsolete + +Use ~org-agenda-skip-if~ instead. + +*** ~org-minutes-to-clocksum-string~ is obsolete + +Use ~org-duration-from-minutes~ instead. + +*** ~org-hh:mm-string-to-minutes~ is obsolete + +Use ~org-duration-to-minutes~ instead. + +*** ~org-duration-string-to-minutes~ is obsolete + +Use ~org-duration-to-minutes~ instead. + +*** ~org-gnus-nnimap-cached-article-number~ is removed. + +This function relied on ~nnimap-group-overview-filename~, which was +removed from Gnus circa September 2010. + +** Removed options + +*** ~org-agenda-repeating-timestamp-show-all~ is removed. + +For an equivalent to a ~nil~ value, set +~org-agenda-show-future-repeats~ to nil and +~org-agenda-prefer-last-repeat~ to ~t~. + +*** ~org-gnus-nnimap-query-article-no-from-file~ is removed. + +This variable has no effect, as it was relying on a function that was +removed from Gnus circa September 2010. + +*** ~org-usenet-links-prefer-google~ is obsolete. + +Use ~org-gnus-prefer-web-links~ instead. + +*** ~org-publish-sitemap-file-entry-format~ is deprecated + +One can provide new ~:sitemap-format-entry~ property for a function +equivalent to the removed format string. + +*** ~org-enable-table-editor~ is removed. + +Setting it to a ~nil~ value broke some other features (e.g., speed +keys). + +*** ~org-export-use-babel~ cannot be set to ~inline-only~ + +The variable is now a boolean. + +*** ~org-texinfo-def-table-markup~ is obsolete + +Use ~org-texinfo-table-default-markup~ instead. + +** New functions + +*** ~org-publish-find-property~ + +This function can be used as a tool to format entries in a site-map, +in addition to ~org-publish-find-title~ and ~org-publish-find-date~. + +*** ~org-list-to-org~ + +It is the reciprocal of ~org-list-to-lisp~, which see. + +*** ~org-agenda-set-restriction-lock-from-agenda~ + +Call ~org-agenda-set-restriction-lock~ from the agenda. + +** Miscellaneous + +*** The Library of Babel now on Worg + +The library-of-babel.org used to be accessible from the =doc/= +directory, distributed with Org’s core. It is now accessible +from the Worg community-driven documentation [[http://orgmode.org/worg/library-of-babel.html][here]]. + +If you want to contribute to it, please see [[http://orgmode.org/worg/org-contribute.html][how to contribute]]. + +*** Allow multiple columns view + +Columns view is not limited to a single buffer anymore. +*** Org Attach obeys ~dired-dwim-target~ + +When a Dired buffer is opened next to the Org document being edited, +the prompt for file to attach can start in the Dired buffer's +directory if `dired-dwim-target' in non-nil. + +*** ~org-fill-paragraph~ can now fill a whole region +*** More specific anniversary descriptions + +Anniversary descriptions (used in the agenda view, for instance) +include the point in time, when the anniversary appears. This is, +in its most general form, just the date of the anniversary. Or +more specific terms, like "today", "tomorrow" or "in n days" are +used to describe the time span. + +This feature allows to automatically change the description of an +anniversary, depending on if it occurs in the next few days or +far away in the future. + +*** Computed dates in tables appear as inactive time stamps + +*** Save point before opening a file with an unknown search option + +When following a file link with a search option (e.g., =::#custom-id=) +that doesn't exist in the target file, save positon before raising an +error. As a consequence, it is possible to jump back to the original +document with ~org-mark-ring-goto~ (default binding =C-c &=). + +*** ~org-get-heading~ accepts two more optional arguments + +See docstring for details. + +*** New option ~org-babel-uppercase-example-markers~ + +This variable is a ~defcustom~ and replaces the variable +~org-babel-capitalize-example-region-markers~, which is a ~defvar~ and +is now obselete. +*** =INCLUDE= keywords in commented trees are now ignored. +*** Default value for ~org-texinfo-text-markup-alist~ changed. + +Now ~=...=~ markup uses ~@samp{}~ instead of ~@verb{}~. You can use +~@verb{}~ again by customizing the variable. +*** Texinfo exports example blocks as ~@example~ +*** Texinfo exports inline src blocks as ~@code{}~ +*** Texinfo default table markup is ~@asis~ +It used to be ~@samp~ but ~@asis~ is neutral and, therefore, more +suitable as a default value. +*** Texinfo default process includes ~--no-split~ option +*** New entities : ~\dollar~ and ~\USD~ +*** ~org-parse-time-string~ accepts a new optional argument +=ZONE= specifies the current time zone. +*** ~org-time-string-to-seconds~ now accepts an optional =ZONE= argument +*** Support for date style URLs in =org-protocol://open-source= + URLs like =https://cool-blog.com/2017/05/20/cool-post/= are + covered by rewrite rules. + +*** Add (C) =COMMENT= support to ~org-structure-template-alist~ + * Version 9.0 ** Incompatible changes diff --git a/etc/refcards/orgcard.tex b/etc/refcards/orgcard.tex index 71d1ef5f90..0880841a94 100644 --- a/etc/refcards/orgcard.tex +++ b/etc/refcards/orgcard.tex @@ -1,5 +1,5 @@ % Reference Card for Org Mode -\def\orgversionnumber{9.0.10} +\def\orgversionnumber{9.1.1} \def\versionyear{2017} % latest update \input emacsver.tex diff --git a/lisp/org/ob-C.el b/lisp/org/ob-C.el index 86047eeecc..78528a882b 100644 --- a/lisp/org/ob-C.el +++ b/lisp/org/ob-C.el @@ -46,6 +46,19 @@ (defvar org-babel-default-header-args:C '()) +(defconst org-babel-header-args:C '((includes . :any) + (defines . :any) + (main . :any) + (flags . :any) + (cmdline . :any) + (libs . :any)) + "C/C++-specific header arguments.") + +(defconst org-babel-header-args:C++ + (append '((namespaces . :any)) + org-babel-header-args:C) + "C++-specific header arguments.") + (defcustom org-babel-C-compiler "gcc" "Command used to compile a C source code file into an executable. May be either a command in the path, like gcc @@ -196,15 +209,18 @@ its header arguments." (colnames (cdr (assq :colname-names params))) (main-p (not (string= (cdr (assq :main params)) "no"))) (includes (org-babel-read - (or (cdr (assq :includes params)) - (org-entry-get nil "includes" t)) + (cdr (assq :includes params)) nil)) (defines (org-babel-read - (or (cdr (assq :defines params)) - (org-entry-get nil "defines" t)) - nil))) + (cdr (assq :defines params)) + nil)) + (namespaces (org-babel-read + (cdr (assq :namespaces params)) + nil))) (when (stringp includes) (setq includes (split-string includes))) + (when (stringp namespaces) + (setq namespaces (split-string namespaces))) (when (stringp defines) (let ((y nil) (result (list t))) @@ -224,6 +240,11 @@ its header arguments." (mapconcat (lambda (inc) (format "#define %s" inc)) (if (listp defines) defines (list defines)) "\n") + ;; namespaces + (mapconcat + (lambda (inc) (format "using namespace %s;" inc)) + namespaces + "\n") ;; variables (mapconcat 'org-babel-C-var-to-C vars "\n") ;; table sizes diff --git a/lisp/org/ob-R.el b/lisp/org/ob-R.el index ded825b1d0..6781fb30a3 100644 --- a/lisp/org/ob-R.el +++ b/lisp/org/ob-R.el @@ -159,10 +159,10 @@ This function is called by `org-babel-execute-src-block'." (result-type (cdr (assq :result-type params))) (session (org-babel-R-initiate-session (cdr (assq :session params)) params)) - (colnames-p (cdr (assq :colnames params))) - (rownames-p (cdr (assq :rownames params))) (graphics-file (and (member "graphics" (assq :result-params params)) (org-babel-graphical-output-file params))) + (colnames-p (unless graphics-file (cdr (assq :colnames params)))) + (rownames-p (unless graphics-file (cdr (assq :rownames params)))) (full-body (let ((inside (list (org-babel-expand-body:R body params graphics-file)))) diff --git a/lisp/org/ob-clojure.el b/lisp/org/ob-clojure.el index b99035b4cc..b49bfe5889 100644 --- a/lisp/org/ob-clojure.el +++ b/lisp/org/ob-clojure.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. -;; Author: Joel Boehland, Eric Schulte, Oleh Krehel +;; Author: Joel Boehland, Eric Schulte, Oleh Krehel, Frederick Giasson ;; ;; Keywords: literate programming, reproducible research ;; Homepage: http://orgmode.org @@ -43,19 +43,34 @@ (require 'ob) (declare-function cider-current-connection "ext:cider-client" (&optional type)) -(declare-function cider-current-session "ext:cider-client" ()) +(declare-function cider-current-ns "ext:cider-client" ()) +(declare-function nrepl--merge "ext:nrepl-client" (dict1 dict2)) (declare-function nrepl-dict-get "ext:nrepl-client" (dict key)) +(declare-function nrepl-dict-put "ext:nrepl-client" (dict key value)) +(declare-function nrepl-request:eval "ext:nrepl-client" + (input callback connection &optional session ns line column additional-params)) (declare-function nrepl-sync-request:eval "ext:nrepl-client" (input connection session &optional ns)) (declare-function org-trim "org" (s &optional keep-lead)) (declare-function slime-eval "ext:slime" (sexp &optional package)) +(defvar nrepl-sync-request-timeout) + (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("clojure" . "clj")) (defvar org-babel-default-header-args:clojure '()) (defvar org-babel-header-args:clojure '((package . :any))) +(defcustom org-babel-clojure-sync-nrepl-timeout 10 + "Timeout value, in seconds, of a Clojure sync call. +If the value is nil, timeout is disabled." + :group 'org-babel + :type 'integer + :version "26.1" + :package-version '(Org . "9.1") + :safe #'wholenump) + (defcustom org-babel-clojure-backend (cond ((featurep 'cider) 'cider) (t 'slime)) @@ -84,21 +99,86 @@ body))) (defun org-babel-execute:clojure (body params) - "Execute a block of Clojure code with Babel." + "Execute a block of Clojure code with Babel. +The underlying process performed by the code block can be output +using the :show-process parameter." (let ((expanded (org-babel-expand-body:clojure body params)) - result) + (response (list 'dict)) + result) (cl-case org-babel-clojure-backend (cider (require 'cider) - (let ((result-params (cdr (assq :result-params params)))) - (setq result - (nrepl-dict-get - (nrepl-sync-request:eval - expanded (cider-current-connection) (cider-current-session)) - (if (or (member "output" result-params) - (member "pp" result-params)) - "out" - "value"))))) + (let ((result-params (cdr (assq :result-params params))) + (show (cdr (assq :show-process params)))) + (if (member show '(nil "no")) + ;; Run code without showing the process. + (progn + (setq response + (let ((nrepl-sync-request-timeout + org-babel-clojure-sync-nrepl-timeout)) + (nrepl-sync-request:eval expanded + (cider-current-connection) + (cider-current-ns)))) + (setq result + (concat + (nrepl-dict-get response + (if (or (member "output" result-params) + (member "pp" result-params)) + "out" + "value")) + (nrepl-dict-get response "ex") + (nrepl-dict-get response "root-ex") + (nrepl-dict-get response "err")))) + ;; Show the process in an output buffer/window. + (let ((process-buffer (switch-to-buffer-other-window + "*Clojure Show Process Sub Buffer*")) + status) + ;; Run the Clojure code in nREPL. + (nrepl-request:eval + expanded + (lambda (resp) + (when (member "out" resp) + ;; Print the output of the nREPL in the output buffer. + (princ (nrepl-dict-get resp "out") process-buffer)) + (when (member "ex" resp) + ;; In case there is an exception, then add it to the + ;; output buffer as well. + (princ (nrepl-dict-get resp "ex") process-buffer) + (princ (nrepl-dict-get resp "root-ex") process-buffer)) + (when (member "err" resp) + ;; In case there is an error, then add it to the + ;; output buffer as well. + (princ (nrepl-dict-get resp "err") process-buffer)) + (nrepl--merge response resp) + ;; Update the status of the nREPL output session. + (setq status (nrepl-dict-get response "status"))) + (cider-current-connection) + (cider-current-ns)) + + ;; Wait until the nREPL code finished to be processed. + (while (not (member "done" status)) + (nrepl-dict-put response "status" (remove "need-input" status)) + (accept-process-output nil 0.01) + (redisplay)) + + ;; Delete the show buffer & window when the processing is + ;; finalized. + (mapc #'delete-window + (get-buffer-window-list process-buffer nil t)) + (kill-buffer process-buffer) + + ;; Put the output or the value in the result section of + ;; the code block. + (setq result + (concat + (nrepl-dict-get response + (if (or (member "output" result-params) + (member "pp" result-params)) + "out" + "value")) + (nrepl-dict-get response "ex") + (nrepl-dict-get response "root-ex") + (nrepl-dict-get response "err"))))))) (slime (require 'slime) (with-temp-buffer diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el index e18716823d..c7c0384545 100644 --- a/lisp/org/ob-core.el +++ b/lisp/org/ob-core.el @@ -82,7 +82,6 @@ (declare-function org-reverse-string "org" (string)) (declare-function org-set-outline-overlay-data "org" (data)) (declare-function org-show-context "org" (&optional key)) -(declare-function org-split-string "org" (string &optional separators)) (declare-function org-src-coderef-format "org-src" (element)) (declare-function org-src-coderef-regexp "org-src" (fmt &optional label)) (declare-function org-table-align "org-table" ()) @@ -179,6 +178,14 @@ This string must include a \"%s\" which will be replaced by the results." :package-version '(Org . "9.0") :safe #'booleanp) +(defcustom org-babel-uppercase-example-markers nil + "When non-nil, begin/end example markers will be inserted in upper case." + :group 'org-babel + :type 'boolean + :version "26.1" + :package-version '(Org . "9.1") + :safe #'booleanp) + (defun org-babel-noweb-wrap (&optional regexp) (concat org-babel-noweb-wrap-start (or regexp "\\([^ \t\n].+?[^ \t]\\|[^ \t\n]\\)") @@ -234,11 +241,9 @@ should be asked whether to allow evaluation." (query (or (equal eval "query") (and export (equal eval "query-export")) (if (functionp org-confirm-babel-evaluate) - (save-excursion - (goto-char (nth 5 info)) - (funcall org-confirm-babel-evaluate - ;; language, code block body - (nth 0 info) (nth 1 info))) + (funcall org-confirm-babel-evaluate + ;; Language, code block body. + (nth 0 info) (nth 1 info)) org-confirm-babel-evaluate)))) (cond (noeval nil) @@ -2348,7 +2353,7 @@ INFO may provide the values of these header arguments (in the ((assq :wrap (nth 2 info)) (let ((name (or (cdr (assq :wrap (nth 2 info))) "RESULTS"))) (funcall wrap (concat "#+BEGIN_" name) - (concat "#+END_" (car (org-split-string name))) + (concat "#+END_" (car (split-string name))) nil nil (concat "{{{results(@@" name ":") "@@)}}}"))) ((member "html" result-params) (funcall wrap "#+BEGIN_EXPORT html" "#+END_EXPORT" nil nil @@ -2483,15 +2488,12 @@ file's directory then expand relative links." result) (if description (concat "[" description "]") "")))) -(defvar org-babel-capitalize-example-region-markers nil - "Make true to capitalize begin/end example markers inserted by code blocks.") - (defun org-babel-examplify-region (beg end &optional results-switches inline) "Comment out region using the inline `==' or `: ' org example quote." (interactive "*r") (let ((maybe-cap (lambda (str) - (if org-babel-capitalize-example-region-markers (upcase str) str)))) + (if org-babel-uppercase-example-markers (upcase str) str)))) (if inline (save-excursion (goto-char beg) diff --git a/lisp/org/ob-exp.el b/lisp/org/ob-exp.el index dc9c53aade..9606d3e474 100644 --- a/lisp/org/ob-exp.el +++ b/lisp/org/ob-exp.el @@ -38,19 +38,18 @@ (defvar org-src-preserve-indentation) -(defcustom org-export-babel-evaluate t - "Switch controlling code evaluation during export. +(defcustom org-export-use-babel t + "Switch controlling code evaluation and header processing during export. When set to nil no code will be evaluated as part of the export -process and no header arguments will be obeyed. When set to -`inline-only', only inline code blocks will be executed. Users -who wish to avoid evaluating code on export should use the header -argument `:eval never-export'." +process and no header arguments will be obeyed. Users who wish +to avoid evaluating code on export should use the header argument +`:eval never-export'." :group 'org-babel :version "24.1" :type '(choice (const :tag "Never" nil) - (const :tag "Only inline code" inline-only) - (const :tag "Always" t))) -(put 'org-export-babel-evaluate 'safe-local-variable #'null) + (const :tag "Always" t)) + :safe #'null) + (defmacro org-babel-exp--at-source (&rest body) "Evaluate BODY at the source of the Babel block at point. @@ -128,12 +127,10 @@ this template." (defun org-babel-exp-process-buffer () "Execute all Babel blocks in current buffer." (interactive) - (when org-export-babel-evaluate + (when org-export-use-babel (save-window-excursion (let ((case-fold-search t) - (regexp (if (eq org-export-babel-evaluate 'inline-only) - "\\(call\\|src\\)_" - "\\(call\\|src\\)_\\|^[ \t]*#\\+\\(BEGIN_SRC\\|CALL:\\)")) + (regexp "\\(call\\|src\\)_\\|^[ \t]*#\\+\\(BEGIN_SRC\\|CALL:\\)") ;; Get a pristine copy of current buffer so Babel ;; references are properly resolved and source block ;; context is preserved. diff --git a/lisp/org/ob-gnuplot.el b/lisp/org/ob-gnuplot.el index f35374758f..763386270d 100644 --- a/lisp/org/ob-gnuplot.el +++ b/lisp/org/ob-gnuplot.el @@ -40,7 +40,7 @@ ;;; Code: (require 'ob) -(declare-function org-time-string-to-time "org" (s &optional buffer pos)) +(declare-function org-time-string-to-time "org" (s &optional zone)) (declare-function org-combine-plists "org" (&rest plists)) (declare-function orgtbl-to-generic "org-table" (table params)) (declare-function gnuplot-mode "ext:gnuplot-mode" ()) diff --git a/lisp/org/ob-lilypond.el b/lisp/org/ob-lilypond.el index 3320a7e55b..0cc85685e9 100644 --- a/lisp/org/ob-lilypond.el +++ b/lisp/org/ob-lilypond.el @@ -89,7 +89,7 @@ you can leave the string empty on this case." (string :tag "Lilypond ") (string :tag "PDF Viewer ") (string :tag "MIDI Player")) - :version "24.3" + :version "24.4" :package-version '(Org . "8.2.7") :set (lambda (_symbol value) diff --git a/lisp/org/ob-lua.el b/lisp/org/ob-lua.el index 4fd7a32382..fc9d9f2f0e 100644 --- a/lisp/org/ob-lua.el +++ b/lisp/org/ob-lua.el @@ -49,7 +49,7 @@ (defcustom org-babel-lua-command "lua" "Name of the command for executing Lua code." - :version "24.5" + :version "26.1" :package-version '(Org . "8.3") :group 'org-babel :type 'string) @@ -58,21 +58,21 @@ "Preferred lua mode for use in running lua interactively. This will typically be 'lua-mode." :group 'org-babel - :version "24.5" + :version "26.1" :package-version '(Org . "8.3") :type 'symbol) (defcustom org-babel-lua-hline-to "None" "Replace hlines in incoming tables with this when translating to lua." :group 'org-babel - :version "24.5" + :version "26.1" :package-version '(Org . "8.3") :type 'string) (defcustom org-babel-lua-None-to 'hline "Replace 'None' in lua tables with this before returning." :group 'org-babel - :version "24.5" + :version "26.1" :package-version '(Org . "8.3") :type 'symbol) diff --git a/lisp/org/ob-maxima.el b/lisp/org/ob-maxima.el index b2680aa7b6..224b360503 100644 --- a/lisp/org/ob-maxima.el +++ b/lisp/org/ob-maxima.el @@ -48,9 +48,13 @@ (defun org-babel-maxima-expand (body params) "Expand a block of Maxima code according to its header arguments." - (let ((vars (org-babel--get-vars params))) + (let ((vars (org-babel--get-vars params)) + (epilogue (cdr (assq :epilogue params))) + (prologue (cdr (assq :prologue params)))) (mapconcat 'identity (list + ;; Any code from the specified prologue at the start. + prologue ;; graphic output (let ((graphic-file (ignore-errors (org-babel-graphical-output-file params)))) (if graphic-file @@ -62,6 +66,8 @@ (mapconcat 'org-babel-maxima-var-to-maxima vars "\n") ;; body body + ;; Any code from the specified epilogue at the end. + epilogue "gnuplot_close ()$") "\n"))) diff --git a/lisp/org/ob-plantuml.el b/lisp/org/ob-plantuml.el index 20dc25f648..8093100eda 100644 --- a/lisp/org/ob-plantuml.el +++ b/lisp/org/ob-plantuml.el @@ -46,6 +46,31 @@ :version "24.1" :type 'string) +(defun org-babel-variable-assignments:plantuml (params) + "Return a list of PlantUML statements assigning the block's variables. +PARAMS is a property list of source block parameters, which may +contain multiple entries for the key `:var'. `:var' entries in PARAMS +are expected to be scalar variables." + (mapcar + (lambda (pair) + (format "!define %s %s" + (car pair) + (replace-regexp-in-string "\"" "" (cdr pair)))) + (org-babel--get-vars params))) + +(defun org-babel-plantuml-make-body (body params) + "Return PlantUML input string. +BODY is the content of the source block and PARAMS is a property list +of source block parameters. This function relies on the +`org-babel-expand-body:generic' function to extract `:var' entries +from PARAMS and on the `org-babel-variable-assignments:plantuml' +function to convert variables to PlantUML assignments." + (concat + "@startuml\n" + (org-babel-expand-body:generic + body params (org-babel-variable-assignments:plantuml params)) + "\n@enduml")) + (defun org-babel-execute:plantuml (body params) "Execute a block of plantuml code with org-babel. This function is called by `org-babel-execute-src-block'." @@ -54,6 +79,7 @@ This function is called by `org-babel-execute-src-block'." (cmdline (cdr (assq :cmdline params))) (in-file (org-babel-temp-file "plantuml-")) (java (or (cdr (assq :java params)) "")) + (full-body (org-babel-plantuml-make-body body params)) (cmd (if (string= "" org-plantuml-jar-path) (error "`org-plantuml-jar-path' is not set") (concat "java " java " -jar " @@ -85,7 +111,7 @@ This function is called by `org-babel-execute-src-block'." (org-babel-process-file-name out-file))))) (unless (file-exists-p org-plantuml-jar-path) (error "Could not find plantuml.jar at %s" org-plantuml-jar-path)) - (with-temp-file in-file (insert (concat "@startuml\n" body "\n@enduml"))) + (with-temp-file in-file (insert full-body)) (message "%s" cmd) (org-babel-eval cmd "") nil)) ;; signal that output has already been written to file diff --git a/lisp/org/ob-scheme.el b/lisp/org/ob-scheme.el index 2782853220..f67080adfd 100644 --- a/lisp/org/ob-scheme.el +++ b/lisp/org/ob-scheme.el @@ -44,37 +44,51 @@ (defvar geiser-impl--implementation) ; Defined in geiser-impl.el (defvar geiser-default-implementation) ; Defined in geiser-impl.el (defvar geiser-active-implementations) ; Defined in geiser-impl.el +(defvar geiser-debug-show-debug-p) ; Defined in geiser-debug.el +(defvar geiser-debug-jump-to-debug-p) ; Defined in geiser-debug.el +(defvar geiser-repl-use-other-window) ; Defined in geiser-repl.el +(defvar geiser-repl-window-allow-split) ; Defined in geiser-repl.el (declare-function run-geiser "ext:geiser-repl" (impl)) (declare-function geiser-mode "ext:geiser-mode" ()) (declare-function geiser-eval-region "ext:geiser-mode" (start end &optional and-go raw nomsg)) (declare-function geiser-repl-exit "ext:geiser-repl" (&optional arg)) +(declare-function geiser-eval--retort-output "ext:geiser-eval" (ret)) +(declare-function geiser-eval--retort-result-str "ext:geiser-eval" (ret prefix)) + +(defcustom org-babel-scheme-null-to 'hline + "Replace `null' and empty lists in scheme tables with this before returning." + :group 'org-babel + :version "26.1" + :package-version '(Org . "9.1") + :type 'symbol) (defvar org-babel-default-header-args:scheme '() "Default header arguments for scheme code blocks.") (defun org-babel-expand-body:scheme (body params) "Expand BODY according to PARAMS, return the expanded body." - (let ((vars (org-babel--get-vars params))) - (if (> (length vars) 0) - (concat "(let (" - (mapconcat - (lambda (var) (format "%S" (print `(,(car var) ',(cdr var))))) - vars "\n ") - ")\n" body ")") - body))) - - -(defvar org-babel-scheme-repl-map (make-hash-table :test 'equal) + (let ((vars (org-babel--get-vars params)) + (prepends (cdr (assq :prologue params)))) + (concat (and prepends (concat prepends "\n")) + (if (null vars) body + (format "(let (%s)\n%s\n)" + (mapconcat + (lambda (var) + (format "%S" (print `(,(car var) ',(cdr var))))) + vars + "\n ") + body))))) + + +(defvar org-babel-scheme-repl-map (make-hash-table :test #'equal) "Map of scheme sessions to session names.") (defun org-babel-scheme-cleanse-repl-map () "Remove dead buffers from the REPL map." (maphash - (lambda (x y) - (when (not (buffer-name y)) - (remhash x org-babel-scheme-repl-map))) + (lambda (x y) (unless (buffer-name y) (remhash x org-babel-scheme-repl-map))) org-babel-scheme-repl-map)) (defun org-babel-scheme-get-session-buffer (session-name) @@ -112,12 +126,9 @@ If the session is unnamed (nil), generate a name. If the session is `none', use nil for the session name, and org-babel-scheme-execute-with-geiser will use a temporary session." - (let ((result - (cond ((not name) - (concat buffer " " (symbol-name impl) " REPL")) - ((string= name "none") nil) - (name)))) - result)) + (cond ((not name) (concat buffer " " (symbol-name impl) " REPL")) + ((string= name "none") nil) + (name))) (defmacro org-babel-scheme-capture-current-message (&rest body) "Capture current message in both interactive and noninteractive mode" @@ -145,37 +156,46 @@ is true; otherwise returns the last value." (with-temp-buffer (insert (format ";; -*- geiser-scheme-implementation: %s -*-" impl)) (newline) - (insert (if output - (format "(with-output-to-string (lambda () %s))" code) - code)) + (insert code) (geiser-mode) - (let ((repl-buffer (save-current-buffer - (org-babel-scheme-get-repl impl repl)))) - (when (not (eq impl (org-babel-scheme-get-buffer-impl - (current-buffer)))) - (message "Implementation mismatch: %s (%s) %s (%s)" impl (symbolp impl) - (org-babel-scheme-get-buffer-impl (current-buffer)) - (symbolp (org-babel-scheme-get-buffer-impl - (current-buffer))))) - (setq geiser-repl--repl repl-buffer) - (setq geiser-impl--implementation nil) - (setq result (org-babel-scheme-capture-current-message - (geiser-eval-region (point-min) (point-max)))) - (setq result - (if (and (stringp result) (equal (substring result 0 3) "=> ")) - (replace-regexp-in-string "^=> " "" result) - "\"An error occurred.\"")) - (when (not repl) - (save-current-buffer (set-buffer repl-buffer) - (geiser-repl-exit)) - (set-process-query-on-exit-flag (get-buffer-process repl-buffer) nil) - (kill-buffer repl-buffer)) - (setq result (if (or (string= result "#") - (string= result "#")) - nil - result)))) + (let ((geiser-repl-window-allow-split nil) + (geiser-repl-use-other-window nil)) + (let ((repl-buffer (save-current-buffer + (org-babel-scheme-get-repl impl repl)))) + (when (not (eq impl (org-babel-scheme-get-buffer-impl + (current-buffer)))) + (message "Implementation mismatch: %s (%s) %s (%s)" impl (symbolp impl) + (org-babel-scheme-get-buffer-impl (current-buffer)) + (symbolp (org-babel-scheme-get-buffer-impl + (current-buffer))))) + (setq geiser-repl--repl repl-buffer) + (setq geiser-impl--implementation nil) + (let ((geiser-debug-jump-to-debug-p nil) + (geiser-debug-show-debug-p nil)) + (let ((ret (geiser-eval-region (point-min) (point-max)))) + (setq result (if output + (geiser-eval--retort-output ret) + (geiser-eval--retort-result-str ret ""))))) + (when (not repl) + (save-current-buffer (set-buffer repl-buffer) + (geiser-repl-exit)) + (set-process-query-on-exit-flag (get-buffer-process repl-buffer) nil) + (kill-buffer repl-buffer))))) result)) +(defun org-babel-scheme--table-or-string (results) + "Convert RESULTS into an appropriate elisp value. +If the results look like a list or tuple, then convert them into an +Emacs-lisp table, otherwise return the results as a string." + (let ((res (org-babel-script-escape results))) + (cond ((listp res) + (mapcar (lambda (el) + (if (or (null el) (eq el 'null)) + org-babel-scheme-null-to + el)) + res)) + (t res)))) + (defun org-babel-execute:scheme (body params) "Execute a block of Scheme code with org-babel. This function is called by `org-babel-execute-src-block'" @@ -184,24 +204,28 @@ This function is called by `org-babel-execute-src-block'" "^ ?\\*\\([^*]+\\)\\*" "\\1" (buffer-name source-buffer)))) (save-excursion - (org-babel-reassemble-table - (let* ((result-type (cdr (assq :result-type params))) - (impl (or (when (cdr (assq :scheme params)) - (intern (cdr (assq :scheme params)))) - geiser-default-implementation - (car geiser-active-implementations))) - (session (org-babel-scheme-make-session-name - source-buffer-name (cdr (assq :session params)) impl)) - (full-body (org-babel-expand-body:scheme body params))) - (org-babel-scheme-execute-with-geiser - full-body ; code - (string= result-type "output") ; output? - impl ; implementation - (and (not (string= session "none")) session))) ; session - (org-babel-pick-name (cdr (assq :colname-names params)) - (cdr (assq :colnames params))) - (org-babel-pick-name (cdr (assq :rowname-names params)) - (cdr (assq :rownames params))))))) + (let* ((result-type (cdr (assq :result-type params))) + (impl (or (when (cdr (assq :scheme params)) + (intern (cdr (assq :scheme params)))) + geiser-default-implementation + (car geiser-active-implementations))) + (session (org-babel-scheme-make-session-name + source-buffer-name (cdr (assq :session params)) impl)) + (full-body (org-babel-expand-body:scheme body params)) + (result + (org-babel-scheme-execute-with-geiser + full-body ; code + (string= result-type "output") ; output? + impl ; implementation + (and (not (string= session "none")) session)))) ; session + (let ((table + (org-babel-reassemble-table + result + (org-babel-pick-name (cdr (assq :colname-names params)) + (cdr (assq :colnames params))) + (org-babel-pick-name (cdr (assq :rowname-names params)) + (cdr (assq :rownames params)))))) + (org-babel-scheme--table-or-string table)))))) (provide 'ob-scheme) diff --git a/lisp/org/ob-sql.el b/lisp/org/ob-sql.el index 7c3ee120d7..9250825d4e 100644 --- a/lisp/org/ob-sql.el +++ b/lisp/org/ob-sql.el @@ -43,15 +43,25 @@ ;; - colnames (default, nil, means "yes") ;; - result-params ;; - out-file +;; ;; The following are used but not really implemented for SQL: ;; - colname-names ;; - rownames ;; - rowname-names ;; +;; Engines supported: +;; - mysql +;; - dbi +;; - mssql +;; - sqsh +;; - postgresql +;; - oracle +;; - vertica +;; ;; TODO: ;; ;; - support for sessions -;; - support for more engines (currently only supports mysql) +;; - support for more engines ;; - what's a reasonable way to drop table data into SQL? ;; @@ -116,6 +126,28 @@ SQL Server on Windows and Linux platform." (when database (format "-d \"%s\"" database)))) " ")) +(defun org-babel-sql-dbstring-sqsh (host user password database) + "Make sqsh commmand line args for database connection. +\"sqsh\" is one method to access Sybase or MS SQL via Linux platform" + (mapconcat #'identity + (delq nil + (list (when host (format "-S \"%s\"" host)) + (when user (format "-U \"%s\"" user)) + (when password (format "-P \"%s\"" password)) + (when database (format "-D \"%s\"" database)))) + " ")) + +(defun org-babel-sql-dbstring-vertica (host port user password database) + "Make Vertica command line args for database connection. Pass nil to omit that arg." + (mapconcat #'identity + (delq nil + (list (when host (format "-h %s" host)) + (when port (format "-p %d" port)) + (when user (format "-U %s" user)) + (when password (format "-w %s" (shell-quote-argument password) )) + (when database (format "-d %s" database)))) + " ")) + (defun org-babel-sql-convert-standard-filename (file) "Convert FILE to OS standard file name. If in Cygwin environment, uses Cygwin specific function to @@ -179,6 +211,20 @@ footer=off -F \"\t\" %s -f %s -o %s %s" (org-babel-process-file-name in-file) (org-babel-process-file-name out-file) (or cmdline ""))) + (`sqsh (format "sqsh %s %s -i %s -o %s -m csv" + (or cmdline "") + (org-babel-sql-dbstring-sqsh + dbhost dbuser dbpassword database) + (org-babel-sql-convert-standard-filename + (org-babel-process-file-name in-file)) + (org-babel-sql-convert-standard-filename + (org-babel-process-file-name out-file)))) + (`vertica (format "vsql %s -f %s -o %s %s" + (org-babel-sql-dbstring-vertica + dbhost dbport dbuser dbpassword database) + (org-babel-process-file-name in-file) + (org-babel-process-file-name out-file) + (or cmdline ""))) (`oracle (format "sqlplus -s %s < %s > %s" (org-babel-sql-dbstring-oracle @@ -203,18 +249,21 @@ SET MARKUP HTML OFF SPOOL OFF SET COLSEP '|' ") - (`mssql "SET NOCOUNT ON + ((or `mssql `sqsh) "SET NOCOUNT ON ") + (`vertica "\\a\n") (_ "")) - (org-babel-expand-body:sql body params))) + (org-babel-expand-body:sql body params) + ;; "sqsh" requires "go" inserted at EOF. + (if (string= engine "sqsh") "\ngo" ""))) (org-babel-eval command "") (org-babel-result-cond result-params (with-temp-buffer (progn (insert-file-contents-literally out-file) (buffer-string))) (with-temp-buffer (cond - ((memq (intern engine) '(dbi mysql postgresql)) + ((memq (intern engine) '(dbi mysql postgresql sqsh vertica)) ;; Add header row delimiter after column-names header in first line (cond (colnames-p @@ -239,7 +288,7 @@ SET COLSEP '|' (goto-char (point-max)) (forward-char -1)) (write-file out-file)))) - (org-table-import out-file '(16)) + (org-table-import out-file (if (string= engine "sqsh") '(4) '(16))) (org-babel-reassemble-table (mapcar (lambda (x) (if (string= (car x) header-delim) diff --git a/lisp/org/ob-sqlite.el b/lisp/org/ob-sqlite.el index 50e8ac1ab9..38058274a9 100644 --- a/lisp/org/ob-sqlite.el +++ b/lisp/org/ob-sqlite.el @@ -123,10 +123,7 @@ This function is called by `org-babel-execute-src-block'." (if (listp val) (let ((data-file (org-babel-temp-file "sqlite-data-"))) (with-temp-file data-file - (insert (orgtbl-to-csv - val '(:fmt (lambda (el) (if (stringp el) - el - (format "%S" el))))))) + (insert (orgtbl-to-csv val nil))) data-file) (if (stringp val) val (format "%S" val)))) body))) diff --git a/lisp/org/ob-tangle.el b/lisp/org/ob-tangle.el index ed09ff563a..adc6806766 100644 --- a/lisp/org/ob-tangle.el +++ b/lisp/org/ob-tangle.el @@ -29,13 +29,13 @@ (require 'cl-lib) (require 'org-src) +(require 'org-macs) (declare-function make-directory "files" (dir &optional parents)) (declare-function org-at-heading-p "org" (&optional ignored)) (declare-function org-babel-update-block-body "ob-core" (new-body)) (declare-function org-back-to-heading "org" (&optional invisible-ok)) (declare-function org-before-first-heading-p "org" ()) -(declare-function org-edit-special "org" (&optional arg)) (declare-function org-element-at-point "org-element" ()) (declare-function org-element-type "org-element" (element)) (declare-function org-fill-template "org" (template alist)) @@ -45,7 +45,6 @@ (declare-function org-open-link-from-string "org" (s &optional arg reference-buffer)) (declare-function org-remove-indentation "org" (code &optional n)) (declare-function org-store-link "org" (arg)) -(declare-function org-string-nw-p "org-macs" (s)) (declare-function org-trim "org" (s &optional keep-lead)) (declare-function outline-previous-heading "outline" ()) (declare-function org-id-find "org-id" (id &optional markerp)) diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el index a1ff76b36d..cf7a4dbf38 100644 --- a/lisp/org/org-agenda.el +++ b/lisp/org/org-agenda.el @@ -277,10 +277,7 @@ list are are :deadline List deadline due on that date. When the date is today, also list any deadlines past due, or due within - `org-deadline-warning-days'. `:deadline' must appear before - `:scheduled' if the setting of - `org-agenda-skip-scheduled-if-deadline-is-shown' is to have - any effect. + `org-deadline-warning-days'. :deadline* Same as above, but only include the deadline if it has an hour specification as [h]h:mm. @@ -327,12 +324,14 @@ the daily/weekly agenda, see `org-agenda-skip-function'.") (string)) (list :tag "Number of days in agenda" (const org-agenda-span) - (choice (const :tag "Day" day) - (const :tag "Week" week) - (const :tag "Fortnight" fortnight) - (const :tag "Month" month) - (const :tag "Year" year) - (integer :tag "Custom"))) + (list + (const :format "" quote) + (choice (const :tag "Day" day) + (const :tag "Week" week) + (const :tag "Fortnight" fortnight) + (const :tag "Month" month) + (const :tag "Year" year) + (integer :tag "Custom")))) (list :tag "Fixed starting date" (const org-agenda-start-day) (string :value "2007-11-01")) @@ -975,18 +974,6 @@ will only be dimmed." (const :tag "Dim to a gray face" t) (const :tag "Make invisible" invisible))) -(defcustom org-timeline-show-empty-dates 3 - "Non-nil means `org-timeline' also shows dates without an entry. -When nil, only the days which actually have entries are shown. -When t, all days between the first and the last date are shown. -When an integer, show also empty dates, but if there is a gap of more than -N days, just insert a special line indicating the size of the gap." - :group 'org-agenda-skip - :type '(choice - (const :tag "None" nil) - (const :tag "All" t) - (integer :tag "at most"))) - (defgroup org-agenda-startup nil "Options concerning initial settings in the Agenda in Org Mode." :tag "Org Agenda Startup" @@ -1081,7 +1068,7 @@ have been removed when this is called, as will any matches for regular expressions listed in `org-agenda-entry-text-exclude-regexps'.") (defvar org-agenda-include-inactive-timestamps nil - "Non-nil means include inactive time stamps in agenda and timeline. + "Non-nil means include inactive time stamps in agenda. Dynamically scoped.") (defgroup org-agenda-windows nil @@ -1155,17 +1142,17 @@ When nil, only the days which actually have entries are shown." (defcustom org-agenda-format-date 'org-agenda-format-date-aligned "Format string for displaying dates in the agenda. -Used by the daily/weekly agenda and by the timeline. This should be -a format string understood by `format-time-string', or a function returning -the formatted date as a string. The function must take a single argument, -a calendar-style date list like (month day year)." +Used by the daily/weekly agenda. This should be a format string +understood by `format-time-string', or a function returning the +formatted date as a string. The function must take a single +argument, a calendar-style date list like (month day year)." :group 'org-agenda-daily/weekly :type '(choice (string :tag "Format string") (function :tag "Function"))) (defun org-agenda-format-date-aligned (date) - "Format a DATE string for display in the daily/weekly agenda, or timeline. + "Format a DATE string for display in the daily/weekly agenda. This function makes sure that dates are aligned for easy reading." (require 'cal-iso) (let* ((dayname (calendar-day-name date)) @@ -1225,8 +1212,7 @@ For example, 9:30am would become 09:30 rather than 9:30." (defcustom org-agenda-weekend-days '(6 0) "Which days are weekend? -These days get the special face `org-agenda-date-weekend' in the agenda -and timeline buffers." +These days get the special face `org-agenda-date-weekend' in the agenda." :group 'org-agenda-daily/weekly :type '(set :greedy t (const :tag "Monday" 1) @@ -1260,17 +1246,43 @@ Custom commands can set this variable in the options section." :version "24.1" :type 'boolean) -(defcustom org-agenda-repeating-timestamp-show-all t - "Non-nil means show all occurrences of a repeating stamp in the agenda. -When set to a list of strings, only show occurrences of repeating -stamps for these TODO keywords. When nil, only one occurrence is -shown, either today or the nearest into the future." +(defcustom org-agenda-show-future-repeats t + "Non-nil shows repeated entries in the future part of the agenda. +When set to the symbol `next' only the first future repeat is shown." + :group 'org-agenda-daily/weekly + :type '(choice + (const :tag "Show all repeated entries" t) + (const :tag "Show next repeated entry" next) + (const :tag "Do not show repeated entries" nil)) + :version "26.1" + :package-version '(Org . "9.1") + :safe #'symbolp) + +(defcustom org-agenda-prefer-last-repeat nil + "Non-nil sets date for repeated entries to their last repeat. + +When nil, display SCHEDULED and DEADLINE dates at their base +date, and in today's agenda, as a reminder. Display plain +time-stamps, on the other hand, at every repeat date in the past +in addition to the base date. + +When non-nil, show a repeated entry at its latest repeat date, +possibly being today even if it wasn't marked as done. This +setting is useful if you do not always mark repeated entries as +done and, yet, consider that reaching repeat date starts the task +anew. + +When set to a list of strings, prefer last repeats only for +entries with these TODO keywords." :group 'org-agenda-daily/weekly :type '(choice - (const :tag "Show repeating stamps" t) - (repeat :tag "Show repeating stamps for these TODO keywords" - (string :tag "TODO Keyword")) - (const :tag "Don't show repeating stamps" nil))) + (const :tag "Prefer last repeat" t) + (const :tag "Prefer base date" nil) + (repeat :tag "Prefer last repeat for entries with these TODO keywords" + (string :tag "TODO keyword"))) + :version "26.1" + :package-version '(Org . "9.1") + :safe (lambda (x) (or (booleanp x) (consp x)))) (defcustom org-scheduled-past-days 10000 "Number of days to continue listing scheduled items not marked DONE. @@ -1278,7 +1290,19 @@ When an item is scheduled on a date, it shows up in the agenda on this day and will be listed until it is marked done or for the number of days given here." :group 'org-agenda-daily/weekly - :type 'integer) + :type 'integer + :safe 'integerp) + +(defcustom org-deadline-past-days 10000 + "Number of days to warn about missed deadlines. +When an item has deadline on a date, it shows up in the agenda on +this day and will appear as a reminder until it is marked DONE or +for the number of days given here." + :group 'org-agenda-daily/weekly + :type 'integer + :version "26.1" + :package-version '(Org . "9.1") + :safe 'integerp) (defcustom org-agenda-log-mode-items '(closed clock) "List of items that should be shown in agenda log mode. @@ -1421,7 +1445,7 @@ E.g. when this is set to 1, the search view will only show headlines of level 1. When set to 0, the default value, don't limit agenda view by outline level." :group 'org-agenda-search-view - :version "24.4" + :version "26.1" :package-version '(Org . "8.3") :type 'integer) @@ -1453,11 +1477,12 @@ the variable `org-agenda-time-grid'." (defcustom org-agenda-time-grid '((daily today require-timed) - "----------------" - (800 1000 1200 1400 1600 1800 2000)) + (800 1000 1200 1400 1600 1800 2000) + "......" + "----------------") "The settings for time grid for agenda display. -This is a list of three items. The first item is again a list. It contains +This is a list of four items. The first item is again a list. It contains symbols specifying conditions when the grid should be displayed: daily if the agenda shows a single day @@ -1466,10 +1491,14 @@ symbols specifying conditions when the grid should be displayed: require-timed show grid only if at least one item has a time specification remove-match skip grid times already present in an entry -The second item is a string which will be placed behind the grid time. +The second item is a list of integers, indicating the times that +should have a grid line. -The third item is a list of integers, indicating the times that should have -a grid line." +The third item is a string which will be placed right after the +times that have a grid line. + +The fourth item is a string placed after the grid times. This +will align with agenda items" :group 'org-agenda-time-grid :type '(list @@ -1481,8 +1510,9 @@ a grid line." require-timed) (const :tag "Skip grid times already present in an entry" remove-match)) - (string :tag "Grid String") - (repeat :tag "Grid Times" (integer :tag "Time")))) + (repeat :tag "Grid Times" (integer :tag "Time")) + (string :tag "Grid String (after agenda times)") + (string :tag "Grid String (aligns with agenda items)"))) (defcustom org-agenda-show-current-time-in-grid t "Non-nil means show the current time in the time grid." @@ -1610,13 +1640,12 @@ When nil, such items are sorted as 0 minutes effort." (defcustom org-agenda-prefix-format '((agenda . " %i %-12:c%?-12t% s") - (timeline . " % s") (todo . " %i %-12:c") (tags . " %i %-12:c") (search . " %i %-12:c")) "Format specifications for the prefix of items in the agenda views. An alist with five entries, each for the different agenda types. The -keys of the sublists are `agenda', `timeline', `todo', `search' and `tags'. +keys of the sublists are `agenda', `todo', `search' and `tags'. The values are format strings. This format works similar to a printf format, with the following meaning: @@ -1669,11 +1698,12 @@ Custom commands can set this variable in the options section." (string :tag "General format") (list :greedy t :tag "View dependent" (cons (const agenda) (string :tag "Format")) - (cons (const timeline) (string :tag "Format")) (cons (const todo) (string :tag "Format")) (cons (const tags) (string :tag "Format")) (cons (const search) (string :tag "Format")))) - :group 'org-agenda-line-format) + :group 'org-agenda-line-format + :version "26.1" + :package-version '(Org . "9.1")) (defvar org-prefix-format-compiled nil "The compiled prefix format and associated variables. @@ -1795,7 +1825,7 @@ given agenda type. This can be set to a list of agenda types in which the agenda must display the inherited tags. Available types are `todo', -`agenda', `search' and `timeline'. +`agenda' and `search'. When set to nil, never show inherited tags in agenda lines." :group 'org-agenda-line-format @@ -1807,7 +1837,7 @@ When set to nil, never show inherited tags in agenda lines." (repeat :tag "Show inherited tags only in selected agenda types" (symbol :tag "Agenda type")))) -(defcustom org-agenda-use-tag-inheritance '(todo search timeline agenda) +(defcustom org-agenda-use-tag-inheritance '(todo search agenda) "List of agenda view types where to use tag inheritance. In tags/tags-todo/tags-tree agenda views, tag inheritance is @@ -1816,7 +1846,7 @@ controlled by `org-use-tag-inheritance'. In other agenda types, agenda entries. Still, you may want the agenda to be aware of the inherited tags anyway, e.g. for later tag filtering. -Allowed value are `todo', `search', `timeline' and `agenda'. +Allowed value are `todo', `search' and `agenda'. This variable has no effect if `org-agenda-show-inherited-tags' is set to `always'. In that case, the agenda is aware of those @@ -1825,7 +1855,8 @@ tags. The default value sets tags in every agenda type. Setting this option to nil will speed up non-tags agenda view a lot." :group 'org-agenda - :version "24.3" + :version "26.1" + :package-version '(Org . "9.1") :type '(choice (const :tag "Use tag inheritance in all agenda types" t) (repeat :tag "Use tag inheritance in selected agenda types" @@ -1854,13 +1885,21 @@ When this is the symbol `prefix', only remove tags when (defvaralias 'org-agenda-remove-tags-when-in-prefix 'org-agenda-remove-tags) -(defcustom org-agenda-tags-column -80 +(defcustom org-agenda-tags-column 'auto "Shift tags in agenda items to this column. -If this number is positive, it specifies the column. If it is negative, -it means that the tags should be flushright to that column. For example, --80 works well for a normal 80 character screen." +If set to `auto', tags will be automatically aligned to the right +edge of the window. + +If set to a positive number, tags will be left-aligned to that +column. If set to a negative number, tags will be right-aligned +to that column. For example, -80 works well for a normal 80 +character screen." :group 'org-agenda-line-format - :type 'integer) + :type '(choice + (const :tag "Automatically align to right edge of window" auto) + (integer :tag "Specific column" -80)) + :package-version '(Org . "9.1") + :version "26.1") (defvaralias 'org-agenda-align-tags-to-column 'org-agenda-tags-column) @@ -2259,7 +2298,7 @@ The following commands are available: (org-defkey org-agenda-mode-map "!" 'org-agenda-toggle-deadlines) (org-defkey org-agenda-mode-map "G" 'org-agenda-toggle-time-grid) (org-defkey org-agenda-mode-map "r" 'org-agenda-redo) -(org-defkey org-agenda-mode-map "g" (lambda () (interactive) (org-agenda-redo t))) +(org-defkey org-agenda-mode-map "g" 'org-agenda-redo-all) (org-defkey org-agenda-mode-map "e" 'org-agenda-set-effort) (org-defkey org-agenda-mode-map "\C-c\C-xe" 'org-agenda-set-effort) (org-defkey org-agenda-mode-map "\C-c\C-x\C-e" @@ -2310,6 +2349,7 @@ The following commands are available: (org-defkey org-agenda-mode-map "b" 'org-agenda-earlier) (org-defkey org-agenda-mode-map "\C-c\C-x\C-c" 'org-agenda-columns) (org-defkey org-agenda-mode-map "\C-c\C-x>" 'org-agenda-remove-restriction-lock) +(org-defkey org-agenda-mode-map "\C-c\C-x<" 'org-agenda-set-restriction-lock-from-agenda) (org-defkey org-agenda-mode-map "[" 'org-agenda-manipulate-query-add) (org-defkey org-agenda-mode-map "]" 'org-agenda-manipulate-query-subtract) @@ -2323,6 +2363,7 @@ The following commands are available: (org-defkey org-agenda-mode-map "<" 'org-agenda-filter-by-category) (org-defkey org-agenda-mode-map "^" 'org-agenda-filter-by-top-headline) (org-defkey org-agenda-mode-map ";" 'org-timer-set-timer) +(org-defkey org-agenda-mode-map "\C-c\C-x_" 'org-timer-stop) (define-key org-agenda-mode-map "?" 'org-agenda-show-the-flagging-note) (org-defkey org-agenda-mode-map "\C-c\C-x\C-mg" 'org-mobile-pull) (org-defkey org-agenda-mode-map "\C-c\C-x\C-mp" 'org-mobile-push) @@ -2340,7 +2381,7 @@ The following commands are available: ("Agenda Files") "--" ("Agenda Dates" - ["Goto Today" org-agenda-goto-today (org-agenda-check-type nil 'agenda 'timeline)] + ["Goto Today" org-agenda-goto-today (org-agenda-check-type nil 'agenda)] ["Next Dates" org-agenda-later (org-agenda-check-type nil 'agenda)] ["Previous Dates" org-agenda-earlier (org-agenda-check-type nil 'agenda)] ["Jump to date" org-agenda-goto-date (org-agenda-check-type nil 'agenda)]) @@ -2386,7 +2427,7 @@ The following commands are available: "--" ["Show Logbook entries" org-agenda-log-mode :style toggle :selected org-agenda-show-log - :active (org-agenda-check-type nil 'agenda 'timeline) + :active (org-agenda-check-type nil 'agenda) :keys "v l (or just l)"] ["Include archived trees" org-agenda-archives-mode :style toggle :selected org-agenda-archives-mode :active t @@ -2443,13 +2484,13 @@ The following commands are available: ["Schedule" org-agenda-schedule t] ["Set Deadline" org-agenda-deadline t] "--" - ["Change Date +1 day" org-agenda-date-later (org-agenda-check-type nil 'agenda 'timeline)] - ["Change Date -1 day" org-agenda-date-earlier (org-agenda-check-type nil 'agenda 'timeline)] - ["Change Time +1 hour" org-agenda-do-date-later :active (org-agenda-check-type nil 'agenda 'timeline) :keys "C-u S-right"] - ["Change Time -1 hour" org-agenda-do-date-earlier :active (org-agenda-check-type nil 'agenda 'timeline) :keys "C-u S-left"] - ["Change Time + min" org-agenda-date-later :active (org-agenda-check-type nil 'agenda 'timeline) :keys "C-u C-u S-right"] - ["Change Time - min" org-agenda-date-earlier :active (org-agenda-check-type nil 'agenda 'timeline) :keys "C-u C-u S-left"] - ["Change Date to ..." org-agenda-date-prompt (org-agenda-check-type nil 'agenda 'timeline)]) + ["Change Date +1 day" org-agenda-date-later (org-agenda-check-type nil 'agenda)] + ["Change Date -1 day" org-agenda-date-earlier (org-agenda-check-type nil 'agenda)] + ["Change Time +1 hour" org-agenda-do-date-later :active (org-agenda-check-type nil 'agenda) :keys "C-u S-right"] + ["Change Time -1 hour" org-agenda-do-date-earlier :active (org-agenda-check-type nil 'agenda) :keys "C-u S-left"] + ["Change Time + min" org-agenda-date-later :active (org-agenda-check-type nil 'agenda) :keys "C-u C-u S-right"] + ["Change Time - min" org-agenda-date-earlier :active (org-agenda-check-type nil 'agenda) :keys "C-u C-u S-left"] + ["Change Date to ..." org-agenda-date-prompt (org-agenda-check-type nil 'agenda)]) ("Clock and Effort" ["Clock in" org-agenda-clock-in t] ["Clock out" org-agenda-clock-out t] @@ -2465,12 +2506,12 @@ The following commands are available: ["Decrease Priority" org-agenda-priority-down t] ["Show Priority" org-show-priority t]) ("Calendar/Diary" - ["New Diary Entry" org-agenda-diary-entry (org-agenda-check-type nil 'agenda 'timeline)] - ["Goto Calendar" org-agenda-goto-calendar (org-agenda-check-type nil 'agenda 'timeline)] - ["Phases of the Moon" org-agenda-phases-of-moon (org-agenda-check-type nil 'agenda 'timeline)] - ["Sunrise/Sunset" org-agenda-sunrise-sunset (org-agenda-check-type nil 'agenda 'timeline)] - ["Holidays" org-agenda-holidays (org-agenda-check-type nil 'agenda 'timeline)] - ["Convert" org-agenda-convert-date (org-agenda-check-type nil 'agenda 'timeline)] + ["New Diary Entry" org-agenda-diary-entry (org-agenda-check-type nil 'agenda)] + ["Goto Calendar" org-agenda-goto-calendar (org-agenda-check-type nil 'agenda)] + ["Phases of the Moon" org-agenda-phases-of-moon (org-agenda-check-type nil 'agenda)] + ["Sunrise/Sunset" org-agenda-sunrise-sunset (org-agenda-check-type nil 'agenda)] + ["Holidays" org-agenda-holidays (org-agenda-check-type nil 'agenda)] + ["Convert" org-agenda-convert-date (org-agenda-check-type nil 'agenda)] "--" ["Create iCalendar File" org-icalendar-combine-agenda-files t]) "--" @@ -2606,8 +2647,7 @@ type." (const agenda) (const todo) (const tags) - (const search) - (const timeline)) + (const search)) (integer :tag "Max number of entries"))))) (defcustom org-agenda-max-todos nil @@ -2625,8 +2665,7 @@ type." (const agenda) (const todo) (const tags) - (const search) - (const timeline)) + (const search)) (integer :tag "Max number of TODOs"))))) (defcustom org-agenda-max-tags nil @@ -2644,8 +2683,7 @@ type." (const agenda) (const todo) (const tags) - (const search) - (const timeline)) + (const search)) (integer :tag "Max number of tagged entries"))))) (defcustom org-agenda-max-effort nil @@ -2663,8 +2701,7 @@ to limit entries to in this type." (const agenda) (const todo) (const tags) - (const search) - (const timeline)) + (const search)) (integer :tag "Max number of minutes"))))) (defvar org-agenda-keep-restricted-file-list nil) @@ -2683,7 +2720,6 @@ T Call `org-todo-list' to display the global todo list, select only m Call `org-tags-view' to display headlines with tags matching a condition (the user is prompted for the condition). M Like `m', but select only TODO entries, no ordinary headlines. -L Create a timeline for the current buffer. e Export views to associated files. s Search entries for keywords. S Search entries for keywords, only with TODO keywords. @@ -2846,12 +2882,6 @@ Pressing `<' twice means to restrict to the current subtree or region (copy-sequence note)) nil 'face 'org-warning))))))) t t)) - ((equal org-keys "L") - (unless (derived-mode-p 'org-mode) - (user-error "This is not an Org file")) - (unless restriction - (put 'org-agenda-files 'org-restrict (list bfn)) - (org-call-with-arg 'org-timeline arg))) ((equal org-keys "#") (call-interactively 'org-agenda-list-stuck-projects)) ((equal org-keys "/") (call-interactively 'org-occur-in-agenda-files)) ((equal org-keys "!") (customize-variable 'org-stuck-projects)) @@ -2901,15 +2931,15 @@ Agenda views are separated by `org-agenda-block-separator'." (erase-buffer) (insert (eval-when-compile (let ((header - "Press key for an agenda command: < Buffer, subtree/region restriction --------------------------------- > Remove restriction -a Agenda for current week or day e Export agenda views -t List of all TODO entries T Entries with special TODO kwd -m Match a TAGS/PROP/TODO query M Like m, but only TODO entries -s Search for keywords S Like s, but only TODO entries -L Timeline for current buffer # List stuck projects (!=configure) -/ Multi-occur C Configure custom agenda commands -? Find :FLAGGED: entries * Toggle sticky agenda views + "Press key for an agenda command: +-------------------------------- < Buffer, subtree/region restriction +a Agenda for current week or day > Remove restriction +t List of all TODO entries e Export agenda views +m Match a TAGS/PROP/TODO query T Entries with special TODO kwd +s Search for keywords M Like m, but only TODO entries +/ Multi-occur S Like s, but only TODO entries +? Find :FLAGGED: entries C Configure custom agenda commands +* Toggle sticky agenda views # List stuck projects (!=configure) ") (start 0)) (while (string-match @@ -3344,6 +3374,7 @@ the agenda to write." (save-window-excursion (let ((bs (copy-sequence (buffer-string))) (extension (file-name-extension file)) + (default-directory (file-name-directory file)) beg content) (with-temp-buffer (rename-buffer org-agenda-write-buffer-name t) @@ -3374,7 +3405,8 @@ the agenda to write." (kill-buffer (current-buffer)) (message "Org file written to %s" file))) ((member extension '("html" "htm")) - (require 'htmlize) + (or (require 'htmlize nil t) + (error "Please install htmlize from https://github.com/hniksic/emacs-htmlize")) (set-buffer (htmlize-buffer (current-buffer))) (when org-agenda-export-html-style ;; replace } section in this way, without -referring to an external file. +For longer style definitions, either use several @code{#+HTML_HEAD} and +@code{#+HTML_HEAD_EXTRA} lines, or use @code{} blocks +around them. Both of these approaches can avoid referring to an external +file. -In order to add styles to a subtree, use the @code{:HTML_CONTAINER_CLASS:} +In order to add styles to a sub-tree, use the @code{:HTML_CONTAINER_CLASS:} property to assign a class to the tree. In order to specify CSS styles for a particular headline, you can use the id specified in a @code{:CUSTOM_ID:} property. +Never change the @code{org-html-style-default} constant. Instead use other +simpler ways of customizing as described above. + + @c FIXME: More about header and footer styles @c FIXME: Talk about links and targets. -@node JavaScript support, , CSS support, HTML export +@node JavaScript support @subsection JavaScript supported display of web pages @cindex Rose, Sebastian Sebastian Rose has written a JavaScript program especially designed to enhance the web viewing experience of HTML files created with Org. This -program allows you to view large files in two different ways. The first one -is an @emph{Info}-like mode where each section is displayed separately and +program enhances large files in two different ways of viewing. One is an +@emph{Info}-like mode where each section is displayed separately and navigation can be done with the @kbd{n} and @kbd{p} keys (and some other keys as well, press @kbd{?} for an overview of the available keys). The second -view type is a @emph{folding} view much like Org provides inside Emacs. The -script is available at @url{http://orgmode.org/org-info.js} and you can find -the documentation for it at @url{http://orgmode.org/worg/code/org-info-js/}. -We host the script at our site, but if you use it a lot, you might not want -to be dependent on @url{http://orgmode.org} and prefer to install a local -copy on your own web server. +one has a @emph{folding} view, much like Org provides inside Emacs. The +script is available at @url{http://orgmode.org/org-info.js} and the +documentation at @url{http://orgmode.org/worg/code/org-info-js/}. The script +is hosted on @url{http://orgmode.org}, but for reliability, prefer installing +it on your own web server. -All it then takes to use this program is adding a single line to the Org -file: +To use this program, just add this line to the Org file: @cindex #+INFOJS_OPT @example @@ -11459,12 +11865,12 @@ file: @end example @noindent -If this line is found, the HTML header will automatically contain the code -needed to invoke the script. Using the line above, you can set the following -viewing options: +The HTML header now has the code needed to automatically invoke the script. +For setting options, use the syntax from the above line for options described +below: @example -path: @r{The path to the script. The default is to grab the script from} +path: @r{The path to the script. The default grabs the script from} @r{@url{http://orgmode.org/org-info.js}, but you might want to have} @r{a local copy and use a path like @samp{../scripts/org-info.js}.} view: @r{Initial view when the website is first shown. Possible values are:} @@ -11494,105 +11900,204 @@ buttons: @r{Should view-toggle buttons be everywhere? When @code{nil} (the} @vindex org-html-infojs-options @vindex org-html-use-infojs You can choose default values for these options by customizing the variable -@code{org-html-infojs-options}. If you always want to apply the script to your -pages, configure the variable @code{org-html-use-infojs}. +@code{org-html-infojs-options}. If you want the script to always apply to +your pages, configure the variable @code{org-html-use-infojs}. -@node @LaTeX{} and PDF export, Markdown export, HTML export, Exporting -@section @LaTeX{} and PDF export +@node @LaTeX{} export +@section @LaTeX{} export @cindex @LaTeX{} export @cindex PDF export -@LaTeX{} export can produce an arbitrarily complex LaTeX document of any -standard or custom document class. With further processing@footnote{The -default @LaTeX{} output is designed for processing with @code{pdftex} or -@LaTeX{}. It includes packages that are not compatible with @code{xetex} and -possibly @code{luatex}. The @LaTeX{} exporter can be configured to support -alternative TeX engines, see the options -@code{org-latex-default-packages-alist} and @code{org-latex-packages-alist}.}, -which the @LaTeX{} exporter is able to control, this back-end is able to -produce PDF output. Because the @LaTeX{} exporter can be configured to use -the @code{hyperref} package, the default setup produces fully-linked PDF -output. - -As in @LaTeX{}, blank lines are meaningful for this back-end: a paragraph -will not be started if two contiguous syntactical elements are not separated -by an empty line. - -This back-end also offers enhanced support for footnotes. Thus, it handles -nested footnotes, footnotes in tables and footnotes in a list item's -description. +The @LaTeX{} export back-end can handle complex documents, incorporate +standard or custom @LaTeX{} document classes, generate documents using +alternate @LaTeX{} engines, and produce fully linked PDF files with indexes, +bibliographies, and tables of contents, destined for interactive online +viewing or high-quality print publication. + +While the details are covered in-depth in this section, here are some quick +references to variables for the impatient: for engines, see +@code{org-latex-compiler}; for build sequences, see +@code{org-latex-pdf-process}; for packages, see +@code{org-latex-default-packages-alist} and @code{org-latex-packages-alist}. + +An important note about the @LaTeX{} export back-end: it is sensitive to +blank lines in the Org document. That's because @LaTeX{} itself depends on +blank lines to tell apart syntactical elements, such as paragraphs. @menu -* @LaTeX{} export commands:: How to export to LaTeX and PDF -* Header and sectioning:: Setting up the export file structure -* Quoting @LaTeX{} code:: Incorporating literal @LaTeX{} code -* @LaTeX{} specific attributes:: Controlling @LaTeX{} output +* @LaTeX{} export commands:: For producing @LaTeX{} and PDF documents. +* @LaTeX{} specific export settings:: Unique to this @LaTeX{} back-end. +* @LaTeX{} header and sectioning:: For file structure. +* Quoting @LaTeX{} code:: Directly in the Org document. +* Tables in @LaTeX{} export:: Attributes specific to tables. +* Images in @LaTeX{} export:: Attributes specific to images. +* Plain lists in @LaTeX{} export:: Attributes specific to lists. +* Source blocks in @LaTeX{} export:: Attributes specific to source code blocks. +* Example blocks in @LaTeX{} export:: Attributes specific to example blocks. +* Special blocks in @LaTeX{} export:: Attributes specific to special blocks. +* Horizontal rules in @LaTeX{} export:: Attributes specific to horizontal rules. @end menu -@node @LaTeX{} export commands, Header and sectioning, @LaTeX{} and PDF export, @LaTeX{} and PDF export +@node @LaTeX{} export commands @subsection @LaTeX{} export commands @table @kbd @orgcmd{C-c C-e l l,org-latex-export-to-latex} -Export as a @LaTeX{} file. For an Org file @file{myfile.org}, the @LaTeX{} -file will be @file{myfile.tex}. The file will be overwritten without -warning. +Export as @LaTeX{} file with a @file{.tex} extension. For @file{myfile.org}, +Org exports to @file{myfile.tex}, overwriting without warning. @kbd{C-c C-e +l l} Exports to @LaTeX{} file. + @orgcmd{C-c C-e l L,org-latex-export-as-latex} Export to a temporary buffer. Do not create a file. @orgcmd{C-c C-e l p,org-latex-export-to-pdf} -Export as @LaTeX{} and then process to PDF. +Export as @LaTeX{} file and convert it to PDF file. @item C-c C-e l o -Export as @LaTeX{} and then process to PDF, then open the resulting PDF file. +Export as @LaTeX{} file and convert it to PDF, then open the PDF using the default viewer. +@end table + +@vindex org-latex-compiler +@vindex org-latex-bibtex-compiler +@vindex org-latex-default-packages-alist +The @LaTeX{} export back-end can use any of these @LaTeX{} engines: +@samp{pdflatex}, @samp{xelatex}, and @samp{lualatex}. These engines compile +@LaTeX{} files with different compilers, packages, and output options. The +@LaTeX{} export back-end finds the compiler version to use from +@code{org-latex-compiler} variable or the @code{#+LATEX_COMPILER} keyword in +the Org file. See the docstring for the +@code{org-latex-default-packages-alist} for loading packages with certain +compilers. Also see @code{org-latex-bibtex-compiler} to set the bibliography +compiler@footnote{This does not allow setting different bibliography +compilers for different files. However, ``smart'' @LaTeX{} compilation +systems, such as @samp{latexmk}, can select the correct bibliography +compiler.}. + +@node @LaTeX{} specific export settings +@subsection @LaTeX{} specific export settings + +The @LaTeX{} export back-end has several additional keywords for customizing +@LaTeX{} output. Setting these keywords works similar to the general options +(@pxref{Export settings}). + +@table @samp +@item DESCRIPTION +@cindex #+DESCRIPTION (@LaTeX{}) +The document's description. The description along with author name, +keywords, and related file metadata are inserted in the output file by the +@samp{hyperref} package. See @code{org-latex-hyperref-template} for +customizing metadata items. See @code{org-latex-title-command} for +typesetting description into the document's front matter. Use multiple +@code{#+DESCRIPTION} lines for long descriptions. + +@item LATEX_CLASS +@cindex #+LATEX_CLASS +@vindex org-latex-default-class +@vindex org-latex-classes +This is @LaTeX{} document class, such as @code{article}, @code{report}, +@code{book}, and so on, which contain predefined preamble and headline level +mapping that the @LaTeX{} export back-end needs. The back-end reads the +default class name from the @code{org-latex-default-class} variable. Org has +@code{article} as the default class. A valid default class must be an +element of @code{org-latex-classes}. + +@item LATEX_CLASS_OPTIONS +@cindex #+LATEX_CLASS_OPTIONS +Options the @LaTeX{} export back-end uses when calling the @LaTeX{} document +class. + +@item LATEX_COMPILER +@cindex #+LATEX_COMPILER +@vindex org-latex-compiler +The compiler, such as @samp{pdflatex}, @samp{xelatex}, @samp{lualatex}, for +producing the PDF (@code{org-latex-compiler}). + +@item LATEX_HEADER +@cindex #+LATEX_HEADER +@vindex org-latex-classes +Arbitrary lines to add to the document's preamble, before the @samp{hyperref} +settings. See @code{org-latex-classes} for adjusting the structure and order +of the @LaTeX{} headers. + +@item LATEX_HEADER_EXTRA +@cindex #+LATEX_HEADER_EXTRA +@vindex org-latex-classes +Arbitrary lines to add to the document's preamble, before the @samp{hyperref} +settings. See @code{org-latex-classes} for adjusting the structure and order +of the @LaTeX{} headers. + +@item KEYWORDS +@cindex #+KEYWORDS (@LaTeX{}) +The keywords for the document. The description along with author name, +keywords, and related file metadata are inserted in the output file by the +@samp{hyperref} package. See @code{org-latex-hyperref-template} for +customizing metadata items. See @code{org-latex-title-command} for +typesetting description into the document's front matter. Use multiple +@code{#+KEYWORDS} lines if necessary. + +@item SUBTITLE +@cindex #+SUBTITLE (@LaTeX{}) +@vindex org-latex-subtitle-separate +@vindex org-latex-subtitle-format +The document's subtitle. It is typeset as per +@code{org-latex-subtitle-format}. If @code{org-latex-subtitle-separate} is +non-@code{nil}, it is typed as part of the @samp{\title}-macro. See +@code{org-latex-hyperref-template} for customizing metadata items. See +@code{org-latex-title-command} for typesetting description into the +document's front matter. @end table -@node Header and sectioning, Quoting @LaTeX{} code, @LaTeX{} export commands, @LaTeX{} and PDF export -@subsection Header and sectioning structure +The following sections have further details. + +@node @LaTeX{} header and sectioning +@subsection @LaTeX{} header and sectioning structure @cindex @LaTeX{} class @cindex @LaTeX{} sectioning structure @cindex @LaTeX{} header @cindex header, for @LaTeX{} files @cindex sectioning structure, for @LaTeX{} export -By default, the first three outline levels become headlines, defining a -general document structure. Additional levels are exported as @code{itemize} -or @code{enumerate} lists. The transition can also occur at a different -level (@pxref{Export settings}). +The @LaTeX{} export back-end converts the first three of Org's outline levels +into @LaTeX{} headlines. The remaining Org levels are exported as +@code{itemize} or @code{enumerate} lists. To change this globally for the +cut-off point between levels and lists, (@pxref{Export settings}). -By default, the @LaTeX{} output uses the class @code{article}. +By default, the @LaTeX{} export back-end uses the @code{article} class. @vindex org-latex-default-class @vindex org-latex-classes @vindex org-latex-default-packages-alist @vindex org-latex-packages-alist -You can change this globally by setting a different value for -@code{org-latex-default-class} or locally by adding an option like -@code{#+LATEX_CLASS: myclass} in your file, or with -a @code{EXPORT_LATEX_CLASS} property that applies when exporting a region -containing only this (sub)tree. The class must be listed in -@code{org-latex-classes}. This variable defines a header template for each -class@footnote{Into which the values of -@code{org-latex-default-packages-alist} and @code{org-latex-packages-alist} -are spliced.}, and allows you to define the sectioning structure for each -class. You can also define your own classes there. +To change the default class globally, edit @code{org-latex-default-class}. +To change the default class locally in an Org file, add option lines +@code{#+LATEX_CLASS: myclass}. To change the default class for just a part +of the Org file, set a sub-tree property, @code{EXPORT_LATEX_CLASS}. The +class name entered here must be valid member of @code{org-latex-classes}. +This variable defines a header template for each class into which the +exporter splices the values of @code{org-latex-default-packages-alist} and +@code{org-latex-packages-alist}. Use the same three variables to define +custom sectioning or custom classes. @cindex #+LATEX_CLASS @cindex #+LATEX_CLASS_OPTIONS @cindex property, EXPORT_LATEX_CLASS @cindex property, EXPORT_LATEX_CLASS_OPTIONS -The @code{LATEX_CLASS_OPTIONS} keyword or @code{EXPORT_LATEX_CLASS_OPTIONS} -property can specify the options for the @code{\documentclass} macro. These -options have to be provided, as expected by @LaTeX{}, within square brackets. +The @LaTeX{} export back-end sends the @code{LATEX_CLASS_OPTIONS} keyword and +@code{EXPORT_LATEX_CLASS_OPTIONS} property as options to the @LaTeX{} +@code{\documentclass} macro. The options and the syntax for specifying them, +including enclosing them in square brackets, follow @LaTeX{} conventions. + +@example +#+LATEX_CLASS_OPTIONS: [a4paper,11pt,twoside,twocolumn] +@end example @cindex #+LATEX_HEADER @cindex #+LATEX_HEADER_EXTRA -You can also use the @code{LATEX_HEADER} and -@code{LATEX_HEADER_EXTRA}@footnote{Unlike @code{LATEX_HEADER}, contents -from @code{LATEX_HEADER_EXTRA} keywords will not be loaded when previewing -@LaTeX{} snippets (@pxref{Previewing @LaTeX{} fragments}).} keywords in order -to add lines to the header. See the docstring of @code{org-latex-classes} for -more information. +The @LaTeX{} export back-end appends values from @code{LATEX_HEADER} and +@code{LATEX_HEADER_EXTRA} keywords to the @LaTeX{} header. The docstring for +@code{org-latex-classes} explains in more detail. Also note that @LaTeX{} +export back-end does not append @code{LATEX_HEADER_EXTRA} to the header when +previewing @LaTeX{} snippets (@pxref{Previewing @LaTeX{} fragments}). -An example is shown below. +A sample Org file with the above headers: @example #+LATEX_CLASS: article @@ -11601,103 +12106,117 @@ An example is shown below. * Headline 1 some text +* Headline 2 + some more text @end example -@node Quoting @LaTeX{} code, @LaTeX{} specific attributes, Header and sectioning, @LaTeX{} and PDF export +@node Quoting @LaTeX{} code @subsection Quoting @LaTeX{} code -Embedded @LaTeX{} as described in @ref{Embedded @LaTeX{}}, will be correctly -inserted into the @LaTeX{} file. Furthermore, you can add special code that -should only be present in @LaTeX{} export with the following constructs: +The @LaTeX{} export back-end can insert any arbitrary @LaTeX{} code, +@pxref{Embedded @LaTeX{}}. There are three ways to embed such code in the +Org file and they all use different quoting syntax. -@cindex #+LATEX -@cindex #+BEGIN_LATEX +Inserting in-line quoted with @ symbols: +@cindex inline, in @LaTeX{} export @example -Code within @@@@latex:some code@@@@ a paragraph. - -#+LATEX: Literal @LaTeX{} code for export - -#+BEGIN_LATEX -All lines between these markers are exported literally -#+END_LATEX +Code embedded in-line @@@@latex:any arbitrary LaTeX code@@@@ in a paragraph. @end example -@node @LaTeX{} specific attributes, , Quoting @LaTeX{} code, @LaTeX{} and PDF export -@subsection @LaTeX{} specific attributes -@cindex #+ATTR_LATEX +Inserting as one or more keyword lines in the Org file: +@cindex #+LATEX +@example +#+LATEX: any arbitrary LaTeX code +@end example -@LaTeX{} understands attributes specified in an @code{ATTR_LATEX} line. They -affect tables, images, plain lists, special blocks and source blocks. +Inserting as an export block in the Org file, where the back-end exports any +code between begin and end markers: +@cindex #+BEGIN_EXPORT latex +@example +#+BEGIN_EXPORT latex +any arbitrary LaTeX code +#+END_EXPORT +@end example -@subsubheading Tables in @LaTeX{} export +@node Tables in @LaTeX{} export +@subsection Tables in @LaTeX{} export @cindex tables, in @LaTeX{} export +@cindex #+ATTR_LATEX, in tables -For @LaTeX{} export of a table, you can specify a label and a caption -(@pxref{Images and tables}). You can also use attributes to control table -layout and contents. Valid @LaTeX{} attributes include: +The @LaTeX{} export back-end can pass several @LaTeX{} attributes for table +contents and layout. Besides specifying label and caption (@pxref{Images and +tables}), the other valid @LaTeX{} attributes include: @table @code @item :mode @vindex org-latex-default-table-mode -Nature of table's contents. It can be set to @code{table}, @code{math}, -@code{inline-math} or @code{verbatim}. In particular, when in @code{math} or -@code{inline-math} mode, every cell is exported as-is, horizontal rules are -ignored and the table will be wrapped in a math environment. Also, -contiguous tables sharing the same math mode will be wrapped within the same -environment. Default mode is determined in -@code{org-latex-default-table-mode}. +The @LaTeX{} export back-end wraps the table differently depending on the +mode for accurate rendering of math symbols. Mode is either @code{table}, +@code{math}, @code{inline-math} or @code{verbatim}. For @code{math} or +@code{inline-math} mode, @LaTeX{} export back-end wraps the table in a math +environment, but every cell in it is exported as-is. The @LaTeX{} export +back-end determines the default mode from +@code{org-latex-default-table-mode}. For , The @LaTeX{} export back-end +merges contiguous tables in the same mode into a single environment. @item :environment @vindex org-latex-default-table-environment -Environment used for the table. It can be set to any @LaTeX{} table -environment, like @code{tabularx}@footnote{Requires adding the -@code{tabularx} package to @code{org-latex-packages-alist}.}, -@code{longtable}, @code{array}, @code{tabu}@footnote{Requires adding the -@code{tabu} package to @code{org-latex-packages-alist}.}, -@code{bmatrix}@enddots{} It defaults to -@code{org-latex-default-table-environment} value. +Set the default @LaTeX{} table environment for the @LaTeX{} export back-end +to use when exporting Org tables. Common @LaTeX{} table environments are +provided by these packages: @code{tabularx}, @code{longtable}, @code{array}, +@code{tabu}, and @code{bmatrix}. For packages, such as @code{tabularx} and +@code{tabu}, or any newer replacements, include them in the +@code{org-latex-packages-alist} variable so the @LaTeX{} export back-end can +insert the appropriate load package headers in the converted @LaTeX{} file. +Look in the docstring for the @code{org-latex-packages-alist} variable for +configuring these packages for @LaTeX{} snippet previews, if any. @item :caption -@code{#+CAPTION} keyword is the simplest way to set a caption for a table -(@pxref{Images and tables}). If you need more advanced commands for that -task, you can use @code{:caption} attribute instead. Its value should be raw -@LaTeX{} code. It has precedence over @code{#+CAPTION}. +Use @code{#+CAPTION} keyword to set a simple caption for a table +(@pxref{Images and tables}). For custom captions, use @code{:caption} +attribute, which accepts raw @LaTeX{} code. @code{:caption} value overrides +@code{#+CAPTION} value. @item :float @itemx :placement -Float environment for the table. Possible values are @code{sidewaystable}, -@code{multicolumn}, @code{t} and @code{nil}. When unspecified, a table with -a caption will have a @code{table} environment. Moreover, @code{:placement} -attribute can specify the positioning of the float. +The table environments by default are not floats in @LaTeX{}. To make them +floating objects use @code{:float} with one of the following options: +@code{sideways}, @code{multicolumn}, @code{t}, and @code{nil}. Note that +@code{sidewaystable} has been deprecated since Org 8.3. @LaTeX{} floats can +also have additional layout @code{:placement} attributes. These are the +usual @code{[h t b p ! H]} permissions specified in square brackets. Note +that for @code{:float sideways} tables, the @LaTeX{} export back-end ignores +@code{:placement} attributes. @item :align @itemx :font @itemx :width -Set, respectively, the alignment string of the table, its font size and its -width. They only apply on regular tables. +The @LaTeX{} export back-end uses these attributes for regular tables to set +their alignments, fonts, and widths. @item :spread -Boolean specific to the @code{tabu} and @code{longtabu} environments, and -only takes effect when used in conjunction with the @code{:width} attribute. -When @code{:spread} is non-@code{nil}, the table will be spread or shrunk by the -value of @code{:width}. +When @code{:spread} is non-@code{nil}, the @LaTeX{} export back-end spreads +or shrinks the table by the @code{:width} for @code{tabu} and @code{longtabu} +environments. @code{:spread} has no effect if @code{:width} is not set. @item :booktabs @itemx :center @itemx :rmlines @vindex org-latex-tables-booktabs @vindex org-latex-tables-centered -They toggle, respectively, @code{booktabs} usage (assuming the package is -properly loaded), table centering and removal of every horizontal rule but -the first one (in a "table.el" table only). In particular, -@code{org-latex-tables-booktabs} (respectively @code{org-latex-tables-centered}) -activates the first (respectively second) attribute globally. +All three commands are toggles. @code{:booktabs} brings in modern +typesetting enhancements to regular tables. The @code{booktabs} package has +to be loaded through @code{org-latex-packages-alist}. @code{:center} is for +centering the table. @code{:rmlines} removes all but the very first +horizontal line made of ASCII characters from "table.el" tables only. @item :math-prefix @itemx :math-suffix @itemx :math-arguments -A string that will be inserted, respectively, before the table within the -math environment, after the table within the math environment, and between -the macro name and the contents of the table. The @code{:math-arguments} -attribute is used for matrix macros that require more than one argument -(e.g., @code{qbordermatrix}). +The @LaTeX{} export back-end inserts @code{:math-prefix} string value in a +math environment before the table. The @LaTeX{} export back-end inserts +@code{:math-suffix} string value in a math environment after the table. The +@LaTeX{} export back-end inserts @code{:math-arguments} string value between +the macro name and the table's contents. @code{:math-arguments} comes in use +for matrix macros that require more than one argument, such as +@code{qbordermatrix}. @end table -Thus, attributes can be used in a wide array of situations, like writing -a table that will span over multiple pages, or a matrix product: +@LaTeX{} table attributes help formatting tables for a wide range of +situations, such as matrix product or spanning multiple pages: @example #+ATTR_LATEX: :environment longtable :align l|lp@{3cm@}r|l @@ -11712,8 +12231,8 @@ a table that will span over multiple pages, or a matrix product: | 3 | 4 | @end example -In the example below, @LaTeX{} command -@code{\bicaption@{HeadingA@}@{HeadingB@}} will set the caption. +Set the caption with the @LaTeX{} command +@code{\bicaption@{HeadingA@}@{HeadingB@}}: @example #+ATTR_LATEX: :caption \bicaption@{HeadingA@}@{HeadingB@} @@ -11722,128 +12241,203 @@ In the example below, @LaTeX{} command @end example -@subsubheading Images in @LaTeX{} export +@node Images in @LaTeX{} export +@subsection Images in @LaTeX{} export @cindex images, inline in @LaTeX{} @cindex inlining images in @LaTeX{} +@cindex #+ATTR_LATEX, in images -Images that are linked to without a description part in the link, like -@samp{[[file:img.jpg]]} or @samp{[[./img.jpg]]} will be inserted into the PDF -output file resulting from @LaTeX{} processing. Org will use an -@code{\includegraphics} macro to insert the image@footnote{In the case of -TikZ (@url{http://sourceforge.net/projects/pgf/}) images, it will become an -@code{\input} macro wrapped within a @code{tikzpicture} environment.}. +The @LaTeX{} export back-end processes image links in Org files that do not +have descriptions, such as these links @samp{[[file:img.jpg]]} or +@samp{[[./img.jpg]]}, as direct image insertions in the final PDF output. In +the PDF, they are no longer links but actual images embedded on the page. +The @LaTeX{} export back-end uses @code{\includegraphics} macro to insert the +image. But for TikZ@footnote{@url{http://sourceforge.net/projects/pgf/}} +images, the back-end uses an @code{\input} macro wrapped within +a @code{tikzpicture} environment. -You can specify specify image width or height with, respectively, -@code{:width} and @code{:height} attributes. It is also possible to add any -other option with the @code{:options} attribute, as shown in the following -example: +For specifying image @code{:width}, @code{:height}, and other +@code{:options}, use this syntax: @example #+ATTR_LATEX: :width 5cm :options angle=90 [[./img/sed-hr4049.pdf]] @end example -If you need a specific command for the caption, use @code{:caption} -attribute. It will override standard @code{#+CAPTION} value, if any. +For custom commands for captions, use the @code{:caption} attribute. It will +override the default @code{#+CAPTION} value: @example #+ATTR_LATEX: :caption \bicaption@{HeadingA@}@{HeadingB@} [[./img/sed-hr4049.pdf]] @end example -If you have specified a caption as described in @ref{Images and tables}, the -picture will be wrapped into a @code{figure} environment and thus become -a floating element. You can also ask Org to export an image as a float -without specifying caption by setting the @code{:float} attribute. You may -also set it to: +When captions follow the method as described in @ref{Images and tables}, the +@LaTeX{} export back-end wraps the picture in a floating @code{figure} +environment. To float an image without specifying a caption, set the +@code{:float} attribute to one of the following: @itemize @minus @item -@code{t}: if you want to use the standard @samp{figure} environment. It is -used by default if you provide a caption to the image. +@code{t}: for a standard @samp{figure} environment; used by default whenever +an image has a caption. @item -@code{multicolumn}: if you wish to include an image which spans multiple -columns in a page. This will export the image wrapped in a @code{figure*} -environment. +@code{multicolumn}: to span the image across multiple columns of a page; the +back-end wraps the image in a @code{figure*} environment. +@item +@code{wrap}: for text to flow around the image on the right; the figure +occupies the left half of the page. @item -@code{wrap}: if you would like to let text flow around the image. It will -make the figure occupy the left half of the page. +@code{sideways}: for a new page with the image sideways, rotated ninety +degrees, in a @code{sidewaysfigure} environment; overrides @code{:placement} +setting. @item -@code{nil}: if you need to avoid any floating environment, even when -a caption is provided. +@code{nil}: to avoid a @code{:float} even if using a caption. @end itemize @noindent -To modify the placement option of any floating environment, set the -@code{placement} attribute. +Use the @code{placement} attribute to modify a floating environment's placement. @example -#+ATTR_LATEX: :float wrap :width 0.38\textwidth :placement @{r@}@{0.4\textwidth@} -[[./img/hst.png]] +#+ATTR_LATEX: :float wrap :width 0.38\textwidth :placement +@{r@}@{0.4\textwidth@} [[./img/hst.png]] @end example -If the @code{:comment-include} attribute is set to a non-@code{nil} value, -the @LaTeX{} @code{\includegraphics} macro will be commented out. +@vindex org-latex-images-centered +@cindex center image (@LaTeX{} export) +@cindex image, centering (@LaTeX{} export) + +The @LaTeX{} export back-end centers all images by default. Setting +@code{:center} attribute to @code{nil} disables centering. To disable +centering globally, set @code{org-latex-images-centered} to @code{t}. + +Set the @code{:comment-include} attribute to non-@code{nil} value for the +@LaTeX{} export back-end to comment out the @code{\includegraphics} macro. -@subsubheading Plain lists in @LaTeX{} export +@node Plain lists in @LaTeX{} export +@subsection Plain lists in @LaTeX{} export @cindex plain lists, in @LaTeX{} export +@cindex #+ATTR_LATEX, in plain lists + +The @LaTeX{} export back-end accepts the @code{:environment} and +@code{:options} attributes for plain lists. Both attributes work together +for customizing lists, as shown in the examples: + +@example +#+LATEX_HEADER: \usepackage[inline]@{enumitem@} +Some ways to say "Hello": +#+ATTR_LATEX: :environment itemize* +#+ATTR_LATEX: :options [label=@{@}, itemjoin=@{,@}, itemjoin*=@{, and@}] +- Hola +- Bonjour +- Guten Tag. +@end example -Plain lists accept two optional attributes: @code{:environment} and -@code{:options}. The first one allows the use of a non-standard environment -(e.g., @samp{inparaenum}). The second one specifies additional arguments for -that environment. +Since @LaTeX{} supports only four levels of nesting for lists, use an +external package, such as @samp{enumitem} in @LaTeX{}, for levels deeper than +four: @example -#+ATTR_LATEX: :environment compactitem :options [$\circ$] -- you need ``paralist'' package to reproduce this example. +#+LATEX_HEADER: \usepackage@{enumitem@} +#+LATEX_HEADER: \renewlist@{itemize@}@{itemize@}@{9@} +#+LATEX_HEADER: \setlist[itemize]@{label=$\circ$@} +- One + - Two + - Three + - Four + - Five @end example -@subsubheading Source blocks in @LaTeX{} export +@node Source blocks in @LaTeX{} export +@subsection Source blocks in @LaTeX{} export @cindex source blocks, in @LaTeX{} export +@cindex #+ATTR_LATEX, in source blocks + +The @LaTeX{} export back-end can make source code blocks into floating +objects through the attributes @code{:float} and @code{:options}. For +@code{:float}: -In addition to syntax defined in @ref{Literal examples}, names and captions -(@pxref{Images and tables}), source blocks also accept a @code{:float} -attribute. You may set it to: @itemize @minus @item -@code{t}: if you want to make the source block a float. It is the default -value when a caption is provided. +@code{t}: makes a source block float; by default floats any source block with +a caption. @item -@code{multicolumn}: if you wish to include a source block which spans multiple -columns in a page. +@code{multicolumn}: spans the source block across multiple columns of a page. @item -@code{nil}: if you need to avoid any floating environment, even when a caption -is provided. It is useful for source code that may not fit in a single page. +@code{nil}: avoids a @code{:float} even if using a caption; useful for +source code blocks that may not fit on a page. @end itemize @example #+ATTR_LATEX: :float nil #+BEGIN_SRC emacs-lisp -Code that may not fit in a single page. +Lisp code that may not fit in a single page. +#+END_SRC +@end example + +@vindex org-latex-listings-options +@vindex org-latex-minted-options +The @LaTeX{} export back-end passes string values in @code{:options} to +@LaTeX{} packages for customization of that specific source block. In the +example below, the @code{:options} are set for Minted. Minted is a source +code highlighting @LaTeX{}package with many configurable options. + +@example +#+ATTR_LATEX: :options commentstyle=\bfseries +#+BEGIN_SRC emacs-lisp + (defun Fib (n) + (if (< n 2) n (+ (Fib (- n 1)) (Fib (- n 2))))) #+END_SRC @end example -@subsubheading Special blocks in @LaTeX{} export +To apply similar configuration options for all source blocks in a file, use +the @code{org-latex-listings-options} and @code{org-latex-minted-options} +variables. + +@node Example blocks in @LaTeX{} export +@subsection Example blocks in @LaTeX{} export +@cindex example blocks, in @LaTeX{} export +@cindex verbatim blocks, in @LaTeX{} export +@cindex #+ATTR_LATEX, in example blocks + +The @LaTeX{} export back-end wraps the contents of example blocks in a +@samp{verbatim} environment. To change this behavior to use another +environment globally, specify an appropriate export filter (@pxref{Advanced +configuration}). To change this behavior to use another environment for each +block, use the @code{:environment} parameter to specify a custom environment. + +@example +#+ATTR_LATEX: :environment myverbatim +#+BEGIN_EXAMPLE +This sentence is false. +#+END_EXAMPLE +@end example + +@node Special blocks in @LaTeX{} export +@subsection Special blocks in @LaTeX{} export @cindex special blocks, in @LaTeX{} export @cindex abstract, in @LaTeX{} export @cindex proof, in @LaTeX{} export +@cindex #+ATTR_LATEX, in special blocks + -In @LaTeX{} back-end, special blocks become environments of the same name. -Value of @code{:options} attribute will be appended as-is to that -environment's opening string. For example: +For other special blocks in the Org file, the @LaTeX{} export back-end makes +a special environment of the same name. The back-end also takes +@code{:options}, if any, and appends as-is to that environment's opening +string. For example: @example -#+BEGIN_ABSTRACT +#+BEGIN_abstract We demonstrate how to solve the Syracuse problem. -#+END_ABSTRACT +#+END_abstract #+ATTR_LATEX: :options [Proof of important theorem] -#+BEGIN_PROOF +#+BEGIN_proof ... Therefore, any even number greater than 2 is the sum of two primes. -#+END_PROOF +#+END_proof @end example @noindent -becomes +exports to @example \begin@{abstract@} @@ -11862,43 +12456,43 @@ example: @example #+ATTR_LATEX: :caption \MyCaption@{HeadingA@} -#+BEGIN_PROOF +#+BEGIN_proof ... -#+END_PROOF +#+END_proof @end example -@subsubheading Horizontal rules +@node Horizontal rules in @LaTeX{} export +@subsection Horizontal rules in @LaTeX{} export @cindex horizontal rules, in @LaTeX{} export +@cindex #+ATTR_LATEX, in horizontal rules -Width and thickness of a given horizontal rule can be controlled with, -respectively, @code{:width} and @code{:thickness} attributes: +The @LaTeX{} export back-end converts horizontal rules by the specified +@code{:width} and @code{:thickness} attributes. For example: @example #+ATTR_LATEX: :width .6\textwidth :thickness 0.8pt ----- @end example -@node Markdown export, OpenDocument Text export, @LaTeX{} and PDF export, Exporting +@node Markdown export @section Markdown export @cindex Markdown export -@code{md} export back-end generates Markdown syntax@footnote{Vanilla flavor, -as defined at @url{http://daringfireball.net/projects/markdown/}.} for an Org -mode buffer. +The Markdown export back-end, @code{md}, converts an Org file to a Markdown +format, as defined at @url{http://daringfireball.net/projects/markdown/}. -It is built over HTML back-end: any construct not supported by Markdown -syntax (e.g., tables) will be controlled and translated by @code{html} -back-end (@pxref{HTML export}). +Since @code{md} is built on top of the HTML back-end, any Org constructs not +supported by Markdown, such as tables, the underlying @code{html} back-end +(@pxref{HTML export}) converts them. @subheading Markdown export commands @table @kbd @orgcmd{C-c C-e m m,org-md-export-to-markdown} -Export as a text file written in Markdown syntax. For an Org file, -@file{myfile.org}, the resulting file will be @file{myfile.md}. The file -will be overwritten without warning. +Export to a text file with Markdown syntax. For @file{myfile.org}, Org +exports to @file{myfile.md}, overwritten without warning. @orgcmd{C-c C-e m M,org-md-export-as-markdown} -Export to a temporary buffer. Do not create a file. +Export to a temporary buffer. Does not create a file. @item C-c C-e m o Export as a text file with Markdown syntax, then open it. @end table @@ -11906,54 +12500,52 @@ Export as a text file with Markdown syntax, then open it. @subheading Header and sectioning structure @vindex org-md-headline-style -Markdown export can generate both @code{atx} and @code{setext} types for -headlines, according to @code{org-md-headline-style}. The former introduces -a hard limit of two levels, whereas the latter pushes it to six. Headlines -below that limit are exported as lists. You can also set a soft limit before -that one (@pxref{Export settings}). +Based on @code{org-md-headline-style}, markdown export can generate headlines +of both @code{atx} and @code{setext} types. @code{atx} limits headline +levels to two. @code{setext} limits headline levels to six. Beyond these +limits, the export back-end converts headlines to lists. To set a limit to a +level before the absolute limit (@pxref{Export settings}). @c begin opendocument -@node OpenDocument Text export, Org export, Markdown export, Exporting +@node OpenDocument Text export @section OpenDocument Text export @cindex ODT @cindex OpenDocument @cindex export, OpenDocument @cindex LibreOffice -Org mode@footnote{Versions 7.8 or later} supports export to OpenDocument Text -(ODT) format. Documents created by this exporter use the -@cite{OpenDocument-v1.2 +The ODT export back-end handles creating of OpenDocument Text (ODT) format +files. The format complies with @cite{OpenDocument-v1.2 specification}@footnote{@url{http://docs.oasis-open.org/office/v1.2/OpenDocument-v1.2.html, Open Document Format for Office Applications (OpenDocument) Version 1.2}} and -are compatible with LibreOffice 3.4. +is compatible with LibreOffice 3.4. @menu -* Pre-requisites for ODT export:: What packages ODT exporter relies on -* ODT export commands:: How to invoke ODT export -* Extending ODT export:: How to produce @samp{doc}, @samp{pdf} files -* Applying custom styles:: How to apply custom styles to the output -* Links in ODT export:: How links will be interpreted and formatted -* Tables in ODT export:: How Tables are exported -* Images in ODT export:: How to insert images -* Math formatting in ODT export:: How @LaTeX{} fragments are formatted -* Labels and captions in ODT export:: How captions are rendered -* Literal examples in ODT export:: How source and example blocks are formatted -* Advanced topics in ODT export:: Read this if you are a power user +* Pre-requisites for ODT export:: Required packages. +* ODT export commands:: Invoking export. +* ODT specific export settings:: Configuration options. +* Extending ODT export:: Producing @file{.doc}, @file{.pdf} files. +* Applying custom styles:: Styling the output. +* Links in ODT export:: Handling and formatting links. +* Tables in ODT export:: Org table conversions. +* Images in ODT export:: Inserting images. +* Math formatting in ODT export:: Formatting @LaTeX{} fragments. +* Labels and captions in ODT export:: Rendering objects. +* Literal examples in ODT export:: For source code and example blocks. +* Advanced topics in ODT export:: For power users. @end menu -@node Pre-requisites for ODT export, ODT export commands, OpenDocument Text export, OpenDocument Text export +@node Pre-requisites for ODT export @subsection Pre-requisites for ODT export @cindex zip -The ODT exporter relies on the @file{zip} program to create the final -output. Check the availability of this program before proceeding further. +The ODT export back-end relies on the @file{zip} program to create the final +compressed ODT output. Check if @file{zip} is locally available and +executable. Without @file{zip}, export cannot finish. -@node ODT export commands, Extending ODT export, Pre-requisites for ODT export, OpenDocument Text export +@node ODT export commands @subsection ODT export commands - -@subsubheading Exporting to ODT @anchor{x-export-to-odt} - @cindex region, active @cindex active region @cindex transient-mark-mode @@ -11964,94 +12556,121 @@ output. Check the availability of this program before proceeding further. Export as OpenDocument Text file. @vindex org-odt-preferred-output-format -If @code{org-odt-preferred-output-format} is specified, automatically convert -the exported file to that format. @xref{x-export-to-other-formats, , -Automatically exporting to other formats}. - -For an Org file @file{myfile.org}, the ODT file will be -@file{myfile.odt}. The file will be overwritten without warning. If there -is an active region,@footnote{This requires @code{transient-mark-mode} to be -turned on} only the region will be exported. If the selected region is a -single tree,@footnote{To select the current subtree, use @kbd{C-c @@}} the -tree head will become the document title. If the tree head entry has, or -inherits, an @code{EXPORT_FILE_NAME} property, that name will be used for the -export. +If @code{org-odt-preferred-output-format} is specified, the ODT export +back-end automatically converts the exported file to that format. +@xref{x-export-to-other-formats, , Automatically exporting to other formats}. + +For @file{myfile.org}, Org exports to @file{myfile.odt}, overwriting without +warning. The ODT export back-end exports a region only if a region was +active. Note for exporting active regions, the @code{transient-mark-mode} +has to be turned on. + +If the selected region is a single tree, the ODT export back-end makes the +tree head the document title. Incidentally, @kbd{C-c @@} selects the current +sub-tree. If the tree head entry has, or inherits, an +@code{EXPORT_FILE_NAME} property, the ODT export back-end uses that for file +name. @kbd{C-c C-e o O} -Export as an OpenDocument Text file and open the resulting file. +Export to an OpenDocument Text file format and open it. @vindex org-odt-preferred-output-format -If @code{org-odt-preferred-output-format} is specified, open the converted +When @code{org-odt-preferred-output-format} is specified, open the converted file instead. @xref{x-export-to-other-formats, , Automatically exporting to other formats}. @end table -@node Extending ODT export, Applying custom styles, ODT export commands, OpenDocument Text export +@node ODT specific export settings +@subsection ODT specific export settings +The ODT export back-end has several additional keywords for customizing ODT +output. Setting these keywords works similar to the general options +(@pxref{Export settings}). + +@table @samp +@item DESCRIPTION +@cindex #+DESCRIPTION (ODT) +This is the document's description, which the ODT export back-end inserts as +document metadata. For long descriptions, use multiple @code{#+DESCRIPTION} +lines. + +@item KEYWORDS +@cindex #+KEYWORDS (ODT) +The keywords for the document. The ODT export back-end inserts the +description along with author name, keywords, and related file metadata as +metadata in the output file. Use multiple @code{#+KEYWORDS} lines if +necessary. + +@item ODT_STYLES_FILE +@cindex ODT_STYLES_FILE +@vindex org-odt-styles-file +The ODT export back-end uses the @code{org-odt-styles-file} by default. See +@ref{Applying custom styles} for details. + +@item SUBTITLE +@cindex SUBTITLE (ODT) +The document subtitle. +@end table + +@node Extending ODT export @subsection Extending ODT export -The ODT exporter can interface with a variety of document -converters and supports popular converters out of the box. As a result, you -can use it to export to formats like @samp{doc} or convert a document from -one format (say @samp{csv}) to another format (say @samp{ods} or @samp{xls}). +The ODT export back-end can produce documents in other formats besides ODT +using a specialized ODT converter process. Its common interface works with +popular converters to produce formats such as @samp{doc}, or convert a +document from one format, say @samp{csv}, to another format, say @samp{xls}. @cindex @file{unoconv} @cindex LibreOffice -If you have a working installation of LibreOffice, a document converter is -pre-configured for you and you can use it right away. If you would like to -use @file{unoconv} as your preferred converter, customize the variable -@code{org-odt-convert-process} to point to @code{unoconv}. You can -also use your own favorite converter or tweak the default settings of the -@file{LibreOffice} and @samp{unoconv} converters. @xref{Configuring a -document converter}. - -@subsubsection Automatically exporting to other formats + +Customize @code{org-odt-convert-process} variable to point to @code{unoconv}, +which is the ODT's preferred converter. Working installations of LibreOffice +would already have @code{unoconv} installed. Alternatively, other converters +may be substituted here. @xref{Configuring a document converter}. + +@subsubheading Automatically exporting to other formats @anchor{x-export-to-other-formats} @vindex org-odt-preferred-output-format -Very often, you will find yourself exporting to ODT format, only to -immediately save the exported document to other formats like @samp{doc}, -@samp{docx}, @samp{rtf}, @samp{pdf} etc. In such cases, you can specify your -preferred output format by customizing the variable -@code{org-odt-preferred-output-format}. This way, the export commands -(@pxref{x-export-to-odt,,Exporting to ODT}) can be extended to export to a -format that is of immediate interest to you. - -@subsubsection Converting between document formats +If ODT format is just an intermediate step to get to other formats, such as +@samp{doc}, @samp{docx}, @samp{rtf}, or @samp{pdf}, etc., then extend the ODT +export back-end to directly produce that format. Specify the final format in +the @code{org-odt-preferred-output-format} variable. This is one way to +extend (@pxref{x-export-to-odt,,Exporting to ODT}). + +@subsubheading Converting between document formats @anchor{x-convert-to-other-formats} -There are many document converters in the wild which support conversion to -and from various file formats, including, but not limited to the -ODT format. LibreOffice converter, mentioned above, is one such -converter. Once a converter is configured, you can interact with it using -the following command. +The Org export back-end is made to be inter-operable with a wide range of text +document format converters. Newer generation converters, such as LibreOffice +and Pandoc, can handle hundreds of formats at once. Org provides a +consistent interaction with whatever converter is installed. Here are some +generic commands: @vindex org-odt-convert @table @kbd @item M-x org-odt-convert RET Convert an existing document from one format to another. With a prefix -argument, also open the newly produced file. +argument, opens the newly produced file. @end table -@node Applying custom styles, Links in ODT export, Extending ODT export, OpenDocument Text export +@node Applying custom styles @subsection Applying custom styles @cindex styles, custom @cindex template, custom -The ODT exporter ships with a set of OpenDocument styles -(@pxref{Working with OpenDocument style files}) that ensure a well-formatted -output. These factory styles, however, may not cater to your specific -tastes. To customize the output, you can either modify the above styles -files directly, or generate the required styles using an application like -LibreOffice. The latter method is suitable for expert and non-expert -users alike, and is described here. +The ODT export back-end comes with many OpenDocument styles (@pxref{Working +with OpenDocument style files}). To expand or further customize these +built-in style sheets, either edit the style sheets directly or generate them +using an application such as LibreOffice. The example here shows creating a +style using LibreOffice. -@subsubsection Applying custom styles: the easy way +@subsubheading Applying custom styles: the easy way @enumerate @item -Create a sample @file{example.org} file with the below settings and export it -to ODT format. +Create a sample @file{example.org} file with settings as shown below, and +export it to ODT format. @example #+OPTIONS: H:10 num:t @@ -12059,9 +12678,9 @@ to ODT format. @item Open the above @file{example.odt} using LibreOffice. Use the @file{Stylist} -to locate the target styles---these typically have the @samp{Org} prefix---and -modify those to your taste. Save the modified file either as an -OpenDocument Text (@file{.odt}) or OpenDocument Template (@file{.ott}) file. +to locate the target styles, which typically have the @samp{Org} prefix. +Open one, modify, and save as either OpenDocument Text (@file{.odt}) or +OpenDocument Template (@file{.ott}) file. @item @cindex #+ODT_STYLES_FILE @@ -12070,8 +12689,8 @@ Customize the variable @code{org-odt-styles-file} and point it to the newly created file. For additional configuration options @pxref{x-overriding-factory-styles,,Overriding factory styles}. -If you would like to choose a style on a per-file basis, you can use the -@code{#+ODT_STYLES_FILE} option. A typical setting will look like +To apply and ODT style to a particular file, use the @code{#+ODT_STYLES_FILE} +option as shown in the example below: @example #+ODT_STYLES_FILE: "/path/to/example.ott" @@ -12085,51 +12704,48 @@ or @end enumerate -@subsubsection Using third-party styles and templates +@subsubheading Using third-party styles and templates -You can use third-party styles and templates for customizing your output. -This will produce the desired output only if the template provides all -style names that the @samp{ODT} exporter relies on. Unless this condition is -met, the output is going to be less than satisfactory. So it is highly -recommended that you only work with templates that are directly derived from -the factory settings. +The ODT export back-end relies on many templates and style names. Using +third-party styles and templates can lead to mismatches. Templates derived +from built in ODT templates and styles seem to have fewer problems. -@node Links in ODT export, Tables in ODT export, Applying custom styles, OpenDocument Text export +@node Links in ODT export @subsection Links in ODT export @cindex links, in ODT export -ODT exporter creates native cross-references for internal links. It creates -Internet-style links for all other links. +ODT export back-end creates native cross-references for internal links and +Internet-style links for all other link types. -A link with no description and destined to a regular (un-itemized) outline +A link with no description and pointing to a regular---un-itemized---outline heading is replaced with a cross-reference and section number of the heading. A @samp{\ref@{label@}}-style reference to an image, table etc.@: is replaced with a cross-reference and sequence number of the labeled entity. @xref{Labels and captions in ODT export}. -@node Tables in ODT export, Images in ODT export, Links in ODT export, OpenDocument Text export +@node Tables in ODT export @subsection Tables in ODT export @cindex tables, in ODT export -Export of native Org mode tables (@pxref{Tables}) and simple @file{table.el} -tables is supported. However, export of complex @file{table.el} tables---tables -that have column or row spans---is not supported. Such tables are -stripped from the exported document. +The ODT export back-end handles native Org mode tables (@pxref{Tables}) and +simple @file{table.el} tables. Complex @file{table.el} tables having column +or row spans are not supported. Such tables are stripped from the exported +document. + +By default, the ODT export back-end exports a table with top and bottom +frames and with ruled lines separating row and column groups (@pxref{Column +groups}). All tables are typeset to occupy the same width. The ODT export +back-end honors any table alignments and relative widths for columns +(@pxref{Column width and alignment}). -By default, a table is exported with top and bottom frames and with rules -separating row and column groups (@pxref{Column groups}). Furthermore, all -tables are typeset to occupy the same width. If the table specifies -alignment and relative width for its columns (@pxref{Column width and -alignment}) then these are honored on export.@footnote{The column widths are -interpreted as weighted ratios with the default weight being 1} +Note that the ODT export back-end interprets column widths as weighted +ratios, the default weight being 1. @cindex #+ATTR_ODT -You can control the width of the table by specifying @code{:rel-width} -property using an @code{#+ATTR_ODT} line. -For example, consider the following table which makes use of all the rules -mentioned above. +Specifying @code{:rel-width} property on an @code{#+ATTR_ODT} line controls +the width of the table. For example: @example #+ATTR_ODT: :rel-width 50 @@ -12144,25 +12760,25 @@ mentioned above. | Sum | 16 | 123 | 2560 | 2699 | @end example -On export, the table will occupy 50% of text area. The columns will be sized -(roughly) in the ratio of 13:5:5:5:6. The first column will be left-aligned -and rest of the columns will be right-aligned. There will be vertical rules -after separating the header and last columns from other columns. There will -be horizontal rules separating the header and last rows from other rows. +On export, the above table takes 50% of text width area. The exporter sizes +the columns in the ratio: 13:5:5:5:6. The first column is left-aligned and +rest of the columns, right-aligned. Vertical rules separate the header and +the last column. Horizontal rules separate the header and the last row. -If you are not satisfied with the above formatting options, you can create -custom table styles and associate them with a table using the -@code{#+ATTR_ODT} line. @xref{Customizing tables in ODT export}. +For even more customization, create custom table styles and associate them +with a table using the @code{#+ATTR_ODT} line. @xref{Customizing tables in +ODT export}. -@node Images in ODT export, Math formatting in ODT export, Tables in ODT export, OpenDocument Text export +@node Images in ODT export @subsection Images in ODT export @cindex images, embedding in ODT @cindex embedding images in ODT @subsubheading Embedding images -You can embed images within the exported document by providing a link to the -desired image file with no link description. For example, to embed -@samp{img.png} do either of the following: +The ODT export back-end processes image links in Org files that do not have +descriptions, such as these links @samp{[[file:img.jpg]]} or +@samp{[[./img.jpg]]}, as direct image insertions in the final output. Either +of these examples works: @example [[file:img.png]] @@ -12173,10 +12789,9 @@ desired image file with no link description. For example, to embed @end example @subsubheading Embedding clickable images -You can create clickable images by providing a link whose description is a -link to an image file. For example, to embed a image -@file{org-mode-unicorn.png} which when clicked jumps to -@uref{http://Orgmode.org} website, do the following +For clickable images, provide a link whose description is another link to an +image file. For example, to embed a image @file{org-mode-unicorn.png} which +when clicked jumps to @uref{http://Orgmode.org} website, do the following @example [[http://orgmode.org][./org-mode-unicorn.png]] @@ -12185,25 +12800,22 @@ link to an image file. For example, to embed a image @subsubheading Sizing and scaling of embedded images @cindex #+ATTR_ODT -You can control the size and scale of the embedded images using the -@code{#+ATTR_ODT} attribute. +Control the size and scale of the embedded images with the @code{#+ATTR_ODT} +attribute. @cindex identify, ImageMagick @vindex org-odt-pixels-per-inch -The exporter specifies the desired size of the image in the final document in -units of centimeters. In order to scale the embedded images, the exporter -queries for pixel dimensions of the images using one of a) ImageMagick's -@file{identify} program or b) Emacs @code{create-image} and @code{image-size} -APIs@footnote{Use of @file{ImageMagick} is only desirable. However, if you -routinely produce documents that have large images or you export your Org -files that has images using a Emacs batch script, then the use of -@file{ImageMagick} is mandatory.}. The pixel dimensions are subsequently -converted in to units of centimeters using -@code{org-odt-pixels-per-inch}. The default value of this variable is -set to @code{display-pixels-per-inch}. You can tweak this variable to -achieve the best results. - -The examples below illustrate the various possibilities. +The ODT export back-end starts with establishing the size of the image in the +final document. The dimensions of this size is measured in centimeters. The +back-end then queries the image file for its dimensions measured in pixels. +For this measurement, the back-end relies on ImageMagick's @file{identify} +program or Emacs @code{create-image} and @code{image-size} API. ImageMagick +is the preferred choice for large file sizes or frequent batch operations. +The back-end then converts the pixel dimensions using +@code{org-odt-pixels-per-inch} into the familiar 72 dpi or 96 dpi. The +default value for this is in @code{display-pixels-per-inch}, which can be +tweaked for better results based on the capabilities of the output device. +Here are some common image scaling operations: @table @asis @item Explicitly size the image @@ -12244,38 +12856,37 @@ height:width ratio, do the following @subsubheading Anchoring of images @cindex #+ATTR_ODT -You can control the manner in which an image is anchored by setting the -@code{:anchor} property of it's @code{#+ATTR_ODT} line. You can specify one -of the following three values for the @code{:anchor} property: -@samp{"as-char"}, @samp{"paragraph"} and @samp{"page"}. +The ODT export back-end can anchor images to @samp{"as-char"}, +@samp{"paragraph"}, or @samp{"page"}. Set the preferred anchor using the +@code{:anchor} property of the @code{#+ATTR_ODT} line. -To create an image that is anchored to a page, do the following: +To create an image that is anchored to a page: @example #+ATTR_ODT: :anchor "page" [[./img.png]] @end example -@node Math formatting in ODT export, Labels and captions in ODT export, Images in ODT export, OpenDocument Text export +@node Math formatting in ODT export @subsection Math formatting in ODT export -The ODT exporter has special support for handling math. +The ODT export back-end has special support built-in for handling math. @menu -* Working with @LaTeX{} math snippets:: How to embed @LaTeX{} math fragments -* Working with MathML or OpenDocument formula files:: How to embed equations in native format +* Working with @LaTeX{} math snippets:: Embedding in @LaTeX{} format. +* Working with MathML or OpenDocument formula files:: Embedding in native format. @end menu -@node Working with @LaTeX{} math snippets, Working with MathML or OpenDocument formula files, Math formatting in ODT export, Math formatting in ODT export -@subsubsection Working with @LaTeX{} math snippets +@node Working with @LaTeX{} math snippets +@subsubheading Working with @LaTeX{} math snippets -@LaTeX{} math snippets (@pxref{@LaTeX{} fragments}) can be embedded in the ODT +@LaTeX{} math snippets (@pxref{@LaTeX{} fragments}) can be embedded in an ODT document in one of the following ways: @cindex MathML @enumerate @item MathML -This option is activated on a per-file basis with +Add this line to the Org file. This option is activated on a per-file basis. @example #+OPTIONS: LaTeX:t @@ -12289,13 +12900,13 @@ the exported document. @vindex org-latex-to-mathml-convert-command @vindex org-latex-to-mathml-jar-file -You can specify the @LaTeX{}-to-MathML converter by customizing the variables +To specify the @LaTeX{}-to-MathML converter, customize the variables @code{org-latex-to-mathml-convert-command} and @code{org-latex-to-mathml-jar-file}. -If you prefer to use @file{MathToWeb}@footnote{See -@uref{http://www.mathtoweb.com/cgi-bin/mathtoweb_home.pl, MathToWeb}} as your -converter, you can configure the above variables as shown below. +To use MathToWeb@footnote{See +@uref{http://www.mathtoweb.com/cgi-bin/mathtoweb_home.pl, MathToWeb}.} as the +preferred converter, configure the above variables as @lisp (setq org-latex-to-mathml-convert-command @@ -12303,9 +12914,14 @@ converter, you can configure the above variables as shown below. org-latex-to-mathml-jar-file "/path/to/mathtoweb.jar") @end lisp +To use @LaTeX{}ML@footnote{See @uref{http://dlmf.nist.gov/LaTeXML/}.} use +@lisp +(setq org-latex-to-mathml-convert-command + "latexmlmath \"%i\" --presentationmathml=%o") +@end lisp -You can use the following commands to quickly verify the reliability of -the @LaTeX{}-to-MathML converter. +To quickly verify the reliability of the @LaTeX{}-to-MathML converter, use +the following commands: @table @kbd @item M-x org-odt-export-as-odf RET @@ -12317,34 +12933,39 @@ and open the formula file with the system-registered application. @end table @cindex dvipng +@cindex dvisvgm @cindex imagemagick @item PNG images -This option is activated on a per-file basis with +Add this line to the Org file. This option is activated on a per-file basis. @example #+OPTIONS: tex:dvipng @end example +@example +#+OPTIONS: tex:dvisvgm +@end example + or: @example #+OPTIONS: tex:imagemagick @end example -With this option, @LaTeX{} fragments are processed into PNG images and the -resulting images are embedded in the exported document. This method requires -that the @file{dvipng} program or @file{imagemagick} suite be available on -your system. +Under this option, @LaTeX{} fragments are processed into PNG or SVG images +and the resulting images are embedded in the exported document. This method +requires @file{dvipng} program, @file{dvisvgm} or @file{imagemagick} +programs. @end enumerate -@node Working with MathML or OpenDocument formula files, , Working with @LaTeX{} math snippets, Math formatting in ODT export -@subsubsection Working with MathML or OpenDocument formula files +@node Working with MathML or OpenDocument formula files +@subsubheading Working with MathML or OpenDocument formula files -For various reasons, you may find embedding @LaTeX{} math snippets in an -ODT document less than reliable. In that case, you can embed a -math equation by linking to its MathML (@file{.mml}) source or its -OpenDocument formula (@file{.odf}) file as shown below: +When embedding @LaTeX{} math snippets in ODT documents is not reliable, there +is one more option to try. Embed an equation by linking to its MathML +(@file{.mml}) source or its OpenDocument formula (@file{.odf}) file as shown +below: @example [[./equation.mml]] @@ -12356,19 +12977,14 @@ or [[./equation.odf]] @end example -@node Labels and captions in ODT export, Literal examples in ODT export, Math formatting in ODT export, OpenDocument Text export +@node Labels and captions in ODT export @subsection Labels and captions in ODT export -You can label and caption various category of objects---an inline image, a -table, a @LaTeX{} fragment or a Math formula---using @code{#+LABEL} and -@code{#+CAPTION} lines. @xref{Images and tables}. ODT exporter enumerates -each labeled or captioned object of a given category separately. As a -result, each such object is assigned a sequence number based on order of it's -appearance in the Org file. - -In the exported document, a user-provided caption is augmented with the -category and sequence number. Consider the following inline image in an Org -file. +ODT format handles labeling and captioning of objects based on their +types. Inline images, tables, @LaTeX{} fragments, and Math formulas are +numbered and captioned separately. Each object also gets a unique sequence +number based on its order of first appearance in the Org file. Each category +has its own sequence. A caption is just a label applied to these objects. @example #+CAPTION: Bell curve @@ -12376,94 +12992,86 @@ file. [[./img/a.png]] @end example -It could be rendered as shown below in the exported document. +When rendered, it may show as follows in the exported document: @example Figure 2: Bell curve @end example @vindex org-odt-category-map-alist -You can modify the category component of the caption by customizing the -option @code{org-odt-category-map-alist}. For example, to tag all embedded -images with the string @samp{Illustration} (instead of the default -@samp{Figure}) use the following setting: +To modify the category component of the caption, customize the option +@code{org-odt-category-map-alist}. For example, to tag embedded images with +the string @samp{Illustration} instead of the default string @samp{Figure}, +use the following setting: @lisp (setq org-odt-category-map-alist - (("__Figure__" "Illustration" "value" "Figure" org-odt--enumerable-image-p))) + '(("__Figure__" "Illustration" "value" "Figure" org-odt--enumerable-image-p))) @end lisp -With this, previous image will be captioned as below in the exported -document. +With the above modification, the previous example changes to: @example Illustration 2: Bell curve @end example -@node Literal examples in ODT export, Advanced topics in ODT export, Labels and captions in ODT export, OpenDocument Text export +@node Literal examples in ODT export @subsection Literal examples in ODT export -Export of literal examples (@pxref{Literal examples}) with full fontification -is supported. Internally, the exporter relies on @file{htmlfontify.el} to -generate all style definitions needed for a fancy listing.@footnote{Your -@file{htmlfontify.el} library must at least be at Emacs 24.1 levels for -fontification to be turned on.} The auto-generated styles have @samp{OrgSrc} -as prefix and inherit their color from the faces used by Emacs -@code{font-lock} library for the source language. +The ODT export back-end supports literal examples (@pxref{Literal examples}) +with full fontification. Internally, the ODT export back-end relies on +@file{htmlfontify.el} to generate the style definitions needed for fancy +listings. The auto-generated styles get @samp{OrgSrc} prefix and inherit +colors from the faces used by Emacs @code{font-lock} library for that source +language. @vindex org-odt-fontify-srcblocks -If you prefer to use your own custom styles for fontification, you can do -so by customizing the option -@code{org-odt-create-custom-styles-for-srcblocks}. +For custom fontification styles, customize the +@code{org-odt-create-custom-styles-for-srcblocks} option. @vindex org-odt-create-custom-styles-for-srcblocks -You can turn off fontification of literal examples by customizing the -option @code{org-odt-fontify-srcblocks}. +To turn off fontification of literal examples, customize the +@code{org-odt-fontify-srcblocks} option. -@node Advanced topics in ODT export, , Literal examples in ODT export, OpenDocument Text export +@node Advanced topics in ODT export @subsection Advanced topics in ODT export -If you rely heavily on ODT export, you may want to exploit the full -set of features that the exporter offers. This section describes features -that would be of interest to power users. +The ODT export back-end has extensive features useful for power users and +frequent uses of ODT formats. @menu -* Configuring a document converter:: How to register a document converter -* Working with OpenDocument style files:: Explore the internals -* Creating one-off styles:: How to produce custom highlighting etc -* Customizing tables in ODT export:: How to define and use Table templates -* Validating OpenDocument XML:: How to debug corrupt OpenDocument files +* Configuring a document converter:: Registering a document converter. +* Working with OpenDocument style files:: Exploring internals. +* Creating one-off styles:: Customizing styles, highlighting. +* Customizing tables in ODT export:: Defining table templates. +* Validating OpenDocument XML:: Debugging corrupted OpenDocument files. @end menu -@node Configuring a document converter, Working with OpenDocument style files, Advanced topics in ODT export, Advanced topics in ODT export -@subsubsection Configuring a document converter +@node Configuring a document converter +@subsubheading Configuring a document converter @cindex convert @cindex doc, docx, rtf @cindex converter -The ODT exporter can work with popular converters with little or no -extra configuration from your side. @xref{Extending ODT export}. -If you are using a converter that is not supported by default or if you would -like to tweak the default converter settings, proceed as below. +The ODT export back-end works with popular converters with little or no extra +configuration. @xref{Extending ODT export}. The following is for unsupported +converters or tweaking existing defaults. @enumerate @item Register the converter @vindex org-odt-convert-processes -Name your converter and add it to the list of known converters by -customizing the option @code{org-odt-convert-processes}. Also specify how -the converter can be invoked via command-line to effect the conversion. +Add the name of the converter to the @code{org-odt-convert-processes} +variable. Note that it also requires how the converter is invoked on the +command line. See the variable's docstring for details. @item Configure its capabilities @vindex org-odt-convert-capabilities -@anchor{x-odt-converter-capabilities} Specify the set of formats the -converter can handle by customizing the variable -@code{org-odt-convert-capabilities}. Use the default value for this -variable as a guide for configuring your converter. As suggested by the -default setting, you can specify the full set of formats supported by the -converter and not limit yourself to specifying formats that are related to -just the OpenDocument Text format. +@anchor{x-odt-converter-capabilities} Specify which formats the converter can +handle by customizing the variable @code{org-odt-convert-capabilities}. Use +the entry for the default values in this variable for configuring the new +converter. Also see its docstring for details. @item Choose the converter @@ -12472,18 +13080,17 @@ Select the newly added converter as the preferred one by customizing the option @code{org-odt-convert-process}. @end enumerate -@node Working with OpenDocument style files, Creating one-off styles, Configuring a document converter, Advanced topics in ODT export -@subsubsection Working with OpenDocument style files +@node Working with OpenDocument style files +@subsubheading Working with OpenDocument style files @cindex styles, custom @cindex template, custom -This section explores the internals of the ODT exporter and the -means by which it produces styled documents. Read this section if you are -interested in exploring the automatic and custom OpenDocument styles used by -the exporter. +This section explores the internals of the ODT exporter; the means by which +it produces styled documents; the use of automatic and custom OpenDocument +styles. @anchor{x-factory-styles} -@subsubheading Factory styles +@subsubheading a) Factory styles The ODT exporter relies on two files for generating its output. These files are bundled with the distribution under the directory pointed to @@ -12524,25 +13131,25 @@ the exporter. @item It contains @samp{}@dots{}@samp{} -elements that control how various entities---tables, images, equations, -etc.---are numbered. +elements that control numbering of tables, images, equations, and similar +entities. @end enumerate @end itemize @anchor{x-overriding-factory-styles} -@subsubheading Overriding factory styles -The following two variables control the location from which the ODT -exporter picks up the custom styles and content template files. You can -customize these variables to override the factory styles used by the -exporter. +@subsubheading b) Overriding factory styles +The following two variables control the location from where the ODT exporter +picks up the custom styles and content template files. Customize these +variables to override the factory styles used by the exporter. @itemize @anchor{x-org-odt-styles-file} @item @code{org-odt-styles-file} -Use this variable to specify the @file{styles.xml} that will be used in the -final output. You can specify one of the following values: +The ODT export back-end uses the file pointed to by this variable, such as +@file{styles.xml}, for the final output. It can take one of the following +values: @enumerate @item A @file{styles.xml} file @@ -12576,28 +13183,26 @@ Use this variable to specify the blank @file{content.xml} that will be used in the final output. @end itemize -@node Creating one-off styles, Customizing tables in ODT export, Working with OpenDocument style files, Advanced topics in ODT export -@subsubsection Creating one-off styles +@node Creating one-off styles +@subsubheading Creating one-off styles -There are times when you would want one-off formatting in the exported -document. You can achieve this by embedding raw OpenDocument XML in the Org -file. The use of this feature is better illustrated with couple of examples. +The ODT export back-end can read embedded raw OpenDocument XML from the Org +file. Such direct formatting are useful for one-off instances. @enumerate @item Embedding ODT tags as part of regular text -You can inline OpenDocument syntax by enclosing it within -@samp{@@@@odt:...@@@@} markup. For example, to highlight a region of text do -the following: +Enclose OpenDocument syntax in @samp{@@@@odt:...@@@@} for inline markup. For +example, to highlight a region of text do the following: @example -@@@@odt:This is a highlighted -text@@@@. But this is a regular text. +@@@@odt:This is highlighted +text@@@@. But this is regular text. @end example -@strong{Hint:} To see the above example in action, edit your -@file{styles.xml} (@pxref{x-orgodtstyles-xml,,Factory styles}) and add a -custom @samp{Highlight} style as shown below. +@strong{Hint:} To see the above example in action, edit the @file{styles.xml} +(@pxref{x-orgodtstyles-xml,,Factory styles}) and add a custom +@samp{Highlight} style as shown below: @example @@ -12607,8 +13212,8 @@ custom @samp{Highlight} style as shown below. @item Embedding a one-line OpenDocument XML -You can add a simple OpenDocument one-liner using the @code{#+ODT:} -directive. For example, to force a page break do the following: +The ODT export back-end can read one-liner options with @code{#+ODT:} +in the Org file. For example, to force a page break: @example #+ODT: @@ -12627,41 +13232,40 @@ custom @samp{PageBreak} style as shown below. @item Embedding a block of OpenDocument XML -You can add a large block of OpenDocument XML using the -@code{#+BEGIN_ODT}@dots{}@code{#+END_ODT} construct. +The ODT export back-end can also read ODT export blocks for OpenDocument XML. +Such blocks use the @code{#+BEGIN_EXPORT odt}@dots{}@code{#+END_EXPORT} +constructs. For example, to create a one-off paragraph that uses bold text, do the following: @example -#+BEGIN_ODT +#+BEGIN_EXPORT odt This paragraph is specially formatted and uses bold text. -#+END_ODT +#+END_EXPORT @end example @end enumerate -@node Customizing tables in ODT export, Validating OpenDocument XML, Creating one-off styles, Advanced topics in ODT export -@subsubsection Customizing tables in ODT export +@node Customizing tables in ODT export +@subsubheading Customizing tables in ODT export @cindex tables, in ODT export @cindex #+ATTR_ODT -You can override the default formatting of the table by specifying a custom -table style with the @code{#+ATTR_ODT} line. For a discussion on default -formatting of tables @pxref{Tables in ODT export}. +Override the default table format by specifying a custom table style with the +@code{#+ATTR_ODT} line. For a discussion on default formatting of tables +@pxref{Tables in ODT export}. This feature closely mimics the way table templates are defined in the OpenDocument-v1.2 specification.@footnote{@url{http://docs.oasis-open.org/office/v1.2/OpenDocument-v1.2.html, OpenDocument-v1.2 Specification}} -@subsubheading Custom table styles: an illustration - @vindex org-odt-table-styles -To have a quick preview of this feature, install the below setting and -export the table that follows: +For quick preview of this feature, install the settings below and export the +table that follows: @lisp (setq org-odt-table-styles @@ -12675,22 +13279,20 @@ export the table that follows: @end lisp @example -#+ATTR_ODT: :style "TableWithHeaderRowAndColumn" +#+ATTR_ODT: :style TableWithHeaderRowAndColumn | Name | Phone | Age | | Peter | 1234 | 17 | | Anna | 4321 | 25 | @end example -In the above example, you used a template named @samp{Custom} and installed -two table styles with the names @samp{TableWithHeaderRowAndColumn} and -@samp{TableWithFirstRowandLastRow}. (@strong{Important:} The OpenDocument -styles needed for producing the above template have been pre-defined for -you. These styles are available under the section marked @samp{Custom -Table Template} in @file{OrgOdtContentTemplate.xml} -(@pxref{x-orgodtcontenttemplate-xml,,Factory styles}). If you need -additional templates you have to define these styles yourselves. +The example above used @samp{Custom} template and installed two table styles +@samp{TableWithHeaderRowAndColumn} and @samp{TableWithFirstRowandLastRow}. +@strong{Important:} The OpenDocument styles needed for producing the above +template were pre-defined. They are available in the section marked +@samp{Custom Table Template} in @file{OrgOdtContentTemplate.xml} +(@pxref{x-orgodtcontenttemplate-xml,,Factory styles}. For adding new +templates, define new styles here. -@subsubheading Custom table styles: the nitty-gritty To use this feature proceed as follows: @enumerate @@ -12698,8 +13300,8 @@ To use this feature proceed as follows: Create a table template@footnote{See the @code{} element of the OpenDocument-v1.2 specification} -A table template is nothing but a set of @samp{table-cell} and -@samp{paragraph} styles for each of the following table cell categories: +A table template is set of @samp{table-cell} and @samp{paragraph} styles for +each of the following table cell categories: @itemize @minus @item Body @@ -12809,25 +13411,22 @@ the @code{ATTR_ODT} line as shown below. @end example @end enumerate -@node Validating OpenDocument XML, , Customizing tables in ODT export, Advanced topics in ODT export -@subsubsection Validating OpenDocument XML - -Occasionally, you will discover that the document created by the -ODT exporter cannot be opened by your favorite application. One of -the common reasons for this is that the @file{.odt} file is corrupt. In such -cases, you may want to validate the document against the OpenDocument RELAX -NG Compact Syntax (RNC) schema. +@node Validating OpenDocument XML +@subsubheading Validating OpenDocument XML -For de-compressing the @file{.odt} file@footnote{@file{.odt} files are -nothing but @samp{zip} archives}: @inforef{File Archives,,emacs}. For -general help with validation (and schema-sensitive editing) of XML files: +Sometimes ODT format files may not open due to @file{.odt} file corruption. +To verify if the @file{.odt} file is corrupt, validate it against the +OpenDocument RELAX NG Compact Syntax---RNC---schema. But first the +@file{.odt} files have to be decompressed using @samp{zip}. Note that +@file{.odt} files are @samp{zip} archives: @inforef{File Archives,,emacs}. +The contents of @file{.odt} files are in @file{.xml}. For general help with +validation---and schema-sensitive editing---of XML files: @inforef{Introduction,,nxml-mode}. @vindex org-odt-schema-dir -If you have ready access to OpenDocument @file{.rnc} files and the needed -schema-locating rules in a single folder, you can customize the variable -@code{org-odt-schema-dir} to point to that directory. The ODT exporter -will take care of updating the @code{rng-schema-locating-files} for you. +Customize @code{org-odt-schema-dir} to point to a directory with OpenDocument +@file{.rnc} files and the needed schema-locating rules. The ODT export +back-end takes care of updating the @code{rng-schema-locating-files}. @c end opendocument @@ -12836,102 +13435,146 @@ will take care of updating the @code{rng-schema-locating-files} for you. @cindex Org export @code{org} export back-end creates a normalized version of the Org document -in current buffer. In particular, it evaluates Babel code (@pxref{Evaluating -code blocks}) and removes other back-ends specific contents. +in current buffer. The exporter evaluates Babel code (@pxref{Evaluating code +blocks}) and removes content specific to other back-ends. @subheading Org export commands @table @kbd @orgcmd{C-c C-e O o,org-org-export-to-org} -Export as an Org document. For an Org file, @file{myfile.org}, the resulting -file will be @file{myfile.org.org}. The file will be overwritten without -warning. +Export as an Org file with a @file{.org} extension. For @file{myfile.org}, +Org exports to @file{myfile.org.org}, overwriting without warning. + @orgcmd{C-c C-e O O,org-org-export-as-org} -Export to a temporary buffer. Do not create a file. +Export to a temporary buffer. Does not create a file. @item C-c C-e O v Export to an Org file, then open it. @end table -@node Texinfo export, iCalendar export, Org export, Exporting +@node Texinfo export @section Texinfo export @cindex Texinfo export -@samp{texinfo} export back-end generates Texinfo code and can compile it into -an Info file. +The @samp{texinfo} export back-end generates documents with Texinfo code that +can compile to Info format. @menu -* Texinfo export commands:: How to invoke Texinfo export -* Document preamble:: File header, title and copyright page -* Headings and sectioning structure:: Building document structure -* Indices:: Creating indices -* Quoting Texinfo code:: Incorporating literal Texinfo code -* Texinfo specific attributes:: Controlling Texinfo output -* An example:: +* Texinfo export commands:: Invoking commands. +* Texinfo specific export settings:: Setting the environment. +* Texinfo file header:: Generating the header. +* Texinfo title and copyright page:: Creating preamble pages. +* Info directory file:: Installing a manual in Info file hierarchy. +* Headings and sectioning structure:: Building document structure. +* Indices:: Creating indices. +* Quoting Texinfo code:: Incorporating literal Texinfo code. +* Plain lists in Texinfo export:: List attributes. +* Tables in Texinfo export:: Table attributes. +* Images in Texinfo export:: Image attributes. +* Special blocks in Texinfo export:: Special block attributes. +* A Texinfo example:: Processing Org to Texinfo. @end menu -@node Texinfo export commands, Document preamble, Texinfo export, Texinfo export +@node Texinfo export commands @subsection Texinfo export commands @vindex org-texinfo-info-process @table @kbd @orgcmd{C-c C-e i t,org-texinfo-export-to-texinfo} -Export as a Texinfo file. For an Org file, @file{myfile.org}, the resulting -file will be @file{myfile.texi}. The file will be overwritten without -warning. +Export as a Texinfo file with @file{.texi} extension. For @file{myfile.org}, +Org exports to @file{myfile.texi}, overwriting without warning. @orgcmd{C-c C-e i i,org-texinfo-export-to-info} -Export to Texinfo and then process to an Info file@footnote{By setting -@code{org-texinfo-info-process}, it is possible to generate other formats, -including DocBook.}. +Export to Texinfo format first and then process it to make an Info file. To +generate other formats, such as DocBook, customize the +@code{org-texinfo-info-process} variable. @end table -@node Document preamble, Headings and sectioning structure, Texinfo export commands, Texinfo export -@subsection Document preamble +@node Texinfo specific export settings +@subsection Texinfo specific export settings +The Texinfo export back-end has several additional keywords for customizing +Texinfo output. Setting these keywords works similar to the general options +(@pxref{Export settings}). + +@table @samp + +@item SUBTITLE +@cindex #+SUBTITLE (Texinfo) +The document subtitle. + +@item SUBAUTHOR +@cindex #+SUBAUTHOR +The document subauthor. + +@item TEXINFO_FILENAME +@cindex #+TEXINFO_FILENAME +The Texinfo filename. + +@item TEXINFO_CLASS +@cindex #+TEXINFO_CLASS +@vindex org-texinfo-default-class +The default document class (@code{org-texinfo-default-class}), which must be +a member of @code{org-texinfo-classes}. + +@item TEXINFO_HEADER +@cindex #+TEXINFO_HEADER +Arbitrary lines inserted at the end of the header. + +@item TEXINFO_POST_HEADER +@cindex #+TEXINFO_POST_HEADER +Arbitrary lines inserted after the end of the header. -When processing a document, @samp{texinfo} back-end generates a minimal file -header along with a title page, a copyright page, and a menu. You control -the latter through the structure of the document (@pxref{Headings and -sectioning structure}). Various keywords allow you to tweak the other parts. -It is also possible to give directions to install the document in the -@samp{Top} node. +@item TEXINFO_DIR_CATEGORY +@cindex #+TEXINFO_DIR_CATEGORY +The directory category of the document. + +@item TEXINFO_DIR_TITLE +@cindex #+TEXINFO_DIR_TITLE +The directory title of the document. + +@item TEXINFO_DIR_DESC +@cindex #+TEXINFO_DIR_DESC +The directory description of the document. + +@item TEXINFO_PRINTED_TITLE +@cindex #+TEXINFO_PRINTED_TITLE +The printed title of the document. +@end table -@subsubheading File header +@node Texinfo file header +@subsection Texinfo file header @cindex #+TEXINFO_FILENAME -Upon creating the header of a Texinfo file, the back-end guesses a name for -the Info file to be compiled. This may not be a sensible choice, e.g., if -you want to produce the final document in a different directory. Specify an -alternate path with @code{#+TEXINFO_FILENAME} keyword to override the default -destination. +After creating the header for a Texinfo file, the Texinfo back-end +automatically generates a name and destination path for the Info file. To +override this default with a more sensible path and name, specify the +@code{#+TEXINFO_FILENAME} keyword. @vindex org-texinfo-coding-system @vindex org-texinfo-classes @cindex #+TEXINFO_HEADER @cindex #+TEXINFO_CLASS -Along with the output file name, the header contains information about the -language (@pxref{Export settings}) and current encoding used@footnote{See -@code{org-texinfo-coding-system} for more information.}. Insert -a @code{#+TEXINFO_HEADER} keyword for each additional command needed, e.g., +Along with the output's file name, the Texinfo header also contains language +details (@pxref{Export settings}) and encoding system as set in the +@code{org-texinfo-coding-system} variable. Insert @code{#+TEXINFO_HEADER} +keywords for each additional command in the header, for example: @@code@{@@synindex@}. -If you happen to regularly install the same set of commands, it may be easier -to define your own class in @code{org-texinfo-classes}, which see. Set -@code{#+TEXINFO_CLASS} keyword accordingly in your document to activate it. +Instead of repeatedly installing the same set of commands, define a class in +@code{org-texinfo-classes} once, and then activate it in the document by +setting the @code{#+TEXINFO_CLASS} keyword to that class. -@subsubheading Title and copyright page +@node Texinfo title and copyright page +@subsection Texinfo title and copyright page @cindex #+TEXINFO_PRINTED_TITLE -@cindex #+SUBTITLE -The default template includes a title page for hard copy output. The title -and author displayed on this page are extracted from, respectively, -@code{#+TITLE} and @code{#+AUTHOR} keywords (@pxref{Export settings}). It is -also possible to print a different, more specific, title with -@code{#+TEXINFO_PRINTED_TITLE} keyword, and add subtitles with -@code{#+SUBTITLE} keyword. Both expect raw Texinfo code in their value. +The default template for hard copy output has a title page with +@code{#+TITLE} and @code{#+AUTHOR} (@pxref{Export settings}). To replace the +regular @code{#+TITLE} with something different for the printed version, use +the @code{#+TEXINFO_PRINTED_TITLE} and @code{#+SUBTITLE} keywords. Both +expect raw Texinfo code for setting their values. @cindex #+SUBAUTHOR -Likewise, information brought by @code{#+AUTHOR} may not be enough. You can -include other authors with several @code{#+SUBAUTHOR} keywords. Values are -also expected to be written in Texinfo code. +If one @code{#+AUTHOR} is not sufficient, add multiple @code{#+SUBAUTHOR} +keywords. They have to be set in raw Texinfo code. @example #+AUTHOR: Jane Smith @@ -12940,10 +13583,10 @@ also expected to be written in Texinfo code. @end example @cindex property, COPYING -Copying material is defined in a dedicated headline with a non-nil -@code{:COPYING:} property. The contents are inserted within -a @code{@@copying} command at the beginning of the document whereas the -heading itself does not appear in the structure of the document. +Copying material is defined in a dedicated headline with a non-@code{nil} +@code{:COPYING:} property. The back-end inserts the contents within a +@code{@@copying} command at the beginning of the document. The heading +itself does not appear in the structure of the document. Copyright information is printed on the back of the title page. @@ -12955,20 +13598,28 @@ Copyright information is printed on the back of the title page. This is a short example of a complete Texinfo file, version 1.0. - Copyright \copy 2017 Free Software Foundation, Inc. + Copyright \copy 2016 Free Software Foundation, Inc. @end example -@subsubheading The Top node +@node Info directory file +@subsection Info directory file +@cindex @samp{dir} file, in Texinfo export +@cindex Texinfo export, @samp{dir} file +@cindex Info directory file, in Texinfo export +@cindex Texinfo export, Info directory file +@cindex @code{install-info} parameters, in Texinfo export +@cindex Texinfo export, @code{install-info} parameters @cindex #+TEXINFO_DIR_CATEGORY @cindex #+TEXINFO_DIR_TITLE @cindex #+TEXINFO_DIR_DESC -You may ultimately want to install your new Info file to your system. You -can write an appropriate entry in the top level directory specifying its -category and title with, respectively, @code{#+TEXINFO_DIR_CATEGORY} and -@code{#+TEXINFO_DIR_TITLE}. Optionally, you can add a short description -using @code{#+TEXINFO_DIR_DESC}. The following example would write an entry -similar to Org's in the @samp{Top} node. +The end result of the Texinfo export process is the creation of an Info file. +This Info file's metadata has variables for category, title, and description: +@code{#+TEXINFO_DIR_CATEGORY}, @code{#+TEXINFO_DIR_TITLE}, and +@code{#+TEXINFO_DIR_DESC} that establish where in the Info hierarchy the file +fits. + +Here is an example that writes to the Info directory file: @example #+TEXINFO_DIR_CATEGORY: Emacs @@ -12976,34 +13627,38 @@ similar to Org's in the @samp{Top} node. #+TEXINFO_DIR_DESC: Outline-based notes management and organizer @end example -@node Headings and sectioning structure, Indices, Document preamble, Texinfo export +@node Headings and sectioning structure @subsection Headings and sectioning structure @vindex org-texinfo-classes @vindex org-texinfo-default-class @cindex #+TEXINFO_CLASS -@samp{texinfo} uses a pre-defined scheme, or class, to convert headlines into -Texinfo structuring commands. For example, a top level headline appears as -@code{@@chapter} if it should be numbered or as @code{@@unnumbered} -otherwise. If you need to use a different set of commands, e.g., to start -with @code{@@part} instead of @code{@@chapter}, install a new class in -@code{org-texinfo-classes}, then activate it with @code{#+TEXINFO_CLASS} -keyword. Export process defaults to @code{org-texinfo-default-class} when -there is no such keyword in the document. - -If a headline's level has no associated structuring command, or is below -a certain threshold @pxref{Export settings}, that headline becomes a list in -Texinfo output. +The Texinfo export back-end uses a pre-defined scheme to convert Org +headlines to an equivalent Texinfo structuring commands. A scheme like this +maps top-level headlines to numbered chapters tagged as @code{@@chapter} and +lower-level headlines to unnumbered chapters tagged as @code{@@unnumbered}. +To override such mappings to introduce @code{@@part} or other Texinfo +structuring commands, define a new class in @code{org-texinfo-classes}. +Activate the new class with the @code{#+TEXINFO_CLASS} keyword. When no new +class is defined and activated, the Texinfo export back-end defaults to the +@code{org-texinfo-default-class}. + +If an Org headline's level has no associated Texinfo structuring command, or +is below a certain threshold (@pxref{Export settings}), then the Texinfo +export back-end makes it into a list item. @cindex property, APPENDIX -As an exception, a headline with a non-nil @code{:APPENDIX:} property becomes -an appendix, independently on its level and the class used. +The Texinfo export back-end makes any headline with a non-@code{nil} +@code{:APPENDIX:} property into an appendix. This happens independent of the +Org headline level or the @code{#+TEXINFO_CLASS}. @cindex property, DESCRIPTION -Each regular sectioning structure creates a menu entry, named after the -heading. You can provide a different, e.g., shorter, title in -@code{:ALT_TITLE:} property (@pxref{Table of contents}). Optionally, you can -specify a description for the item in @code{:DESCRIPTION:} property. E.g., +The Texinfo export back-end creates a menu entry after the Org headline for +each regular sectioning structure. To override this with a shorter menu +entry, use the @code{:ALT_TITLE:} property (@pxref{Table of contents}). +Texinfo menu entries also have an option for a longer @code{:DESCRIPTION:} +property. Here's an example that uses both to override the default menu +entry: @example * Controlling Screen Display @@ -13013,30 +13668,51 @@ specify a description for the item in @code{:DESCRIPTION:} property. E.g., :END: @end example -@node Indices, Quoting Texinfo code, Headings and sectioning structure, Texinfo export +@cindex The Top node, in Texinfo export +@cindex Texinfo export, Top node +The text before the first headline belongs to the @samp{Top} node, i.e., the +node in which a reader enters an Info manual. As such, it is expected not to +appear in printed output generated from the @file{.texi} file. @inforef{The +Top Node,,texinfo}, for more information. + +@node Indices @subsection Indices @cindex #+CINDEX +@cindex concept index, in Texinfo export +@cindex Texinfo export, index, concept @cindex #+FINDEX +@cindex function index, in Texinfo export +@cindex Texinfo export, index, function @cindex #+KINDEX +@cindex keystroke index, in Texinfo export +@cindex Texinfo export, keystroke index @cindex #+PINDEX +@cindex program index, in Texinfo export +@cindex Texinfo export, program index @cindex #+TINDEX +@cindex data type index, in Texinfo export +@cindex Texinfo export, data type index @cindex #+VINDEX -Index entries are created using dedicated keywords. @samp{texinfo} back-end -provides one for each predefined type: @code{#+CINDEX}, @code{#+FINDEX}, -@code{#+KINDEX}, @code{#+PINDEX}, @code{#+TINDEX} and @code{#+VINDEX}. For -custom indices, you can write raw Texinfo code (@pxref{Quoting Texinfo -code}). +@cindex variable index, in Texinfo export +@cindex Texinfo export, variable index +The Texinfo export back-end recognizes these indexing keywords if used in the +Org file: @code{#+CINDEX}, @code{#+FINDEX}, @code{#+KINDEX}, @code{#+PINDEX}, +@code{#+TINDEX}, and @code{#+VINDEX}. Write their value as verbatim Texinfo +code; in particular, @samp{@{}, @samp{@}} and @samp{@@} characters need to be +escaped with @samp{@@} if they not belong to a Texinfo command. @example #+CINDEX: Defining indexing entries @end example @cindex property, INDEX -To generate an index, you need to set the @code{:INDEX:} property of -a headline to an appropriate abbreviation (e.g., @samp{cp} or @samp{vr}). -The headline is then exported as an unnumbered chapter or section command and -the index is inserted after its contents. +For the back-end to generate an index entry for a headline, set the +@code{:INDEX:} property to @samp{cp} or @samp{vr}. These abbreviations come +from Texinfo that stand for concept index and variable index. The Texinfo +manual has abbreviations for all other kinds of indexes. The back-end +exports the headline as an unnumbered chapter or section command, and then +inserts the index after its contents. @example * Concept Index @@ -13045,78 +13721,115 @@ the index is inserted after its contents. :END: @end example -@node Quoting Texinfo code, Texinfo specific attributes, Indices, Texinfo export +@node Quoting Texinfo code @subsection Quoting Texinfo code -It is possible to insert raw Texinfo code using any of the following -constructs +Use any of the following three methods to insert or escape raw Texinfo code: @cindex #+TEXINFO -@cindex #+BEGIN_TEXINFO +@cindex #+BEGIN_EXPORT texinfo @example Richard @@@@texinfo:@@sc@{@@@@Stallman@@@@texinfo:@}@@@@ commence' GNU. #+TEXINFO: @@need800 This paragraph is preceded by... -#+BEGIN_TEXINFO +#+BEGIN_EXPORT texinfo @@auindex Johnson, Mark @@auindex Lakoff, George -#+END_TEXINFO +#+END_EXPORT @end example -@node Texinfo specific attributes, An example, Quoting Texinfo code, Texinfo export -@subsection Texinfo specific attributes - -@cindex #+ATTR_TEXINFO -@samp{texinfo} back-end understands several attributes in plain lists and -tables. They must be specified using an @code{#+ATTR_TEXINFO} keyword, -written just above the list or table. - -@subsubheading Plain lists - -In Texinfo output, description lists appear as two-column tables, using the -default command @code{@@table}. You can use @code{@@ftable} or -@code{@@vtable}@footnote{For more information, @inforef{Two-column -Tables,,texinfo}.} instead with @code{:table-type} attribute. +@node Plain lists in Texinfo export +@subsection Plain lists in Texinfo export +@cindex #+ATTR_TEXINFO, in plain lists +The Texinfo export back-end by default converts description lists in the Org +file using the default command @code{@@table}, which results in a table with +two columns. To change this behavior, specify @code{:table-type} with +@code{@@ftable} or @code{@@vtable} attributes. For more information, +@inforef{Two-column Tables,,texinfo}. @vindex org-texinfo-def-table-markup -In any case, these constructs require a highlighting command for entries in -the list. You can provide one with @code{:indic} attribute. If you do not, -it defaults to the value stored in @code{org-texinfo-def-table-markup}, which -see. +The Texinfo export back-end by default also applies a text highlight based on +the defaults stored in @code{org-texinfo-def-table-markup}. To override the +default highlight command, specify another one with the @code{:indic} +attribute as shown in this example: @example #+ATTR_TEXINFO: :indic @@asis - foo :: This is the text for /foo/, with no highlighting. @end example -@subsubheading Tables +@node Tables in Texinfo export +@subsection Tables in Texinfo export +@cindex #+ATTR_TEXINFO, in tables -When exporting a table, column widths are deduced from the longest cell in -each column. You can also define them explicitly as fractions of the line -length, using @code{:columns} attribute. +When exporting tables, the Texinfo export back-end uses the widest cell width +in each column. To override this and instead specify as fractions of line +length, use the @code{:columns} attribute. See example below. @example #+ATTR_TEXINFO: :columns .5 .5 | a cell | another cell | @end example -@node An example, , Texinfo specific attributes, Texinfo export -@subsection An example +@node Images in Texinfo export +@subsection Images in Texinfo export +@cindex #+ATTR_TEXINFO, in images -Here is a thorough example, taken from @inforef{GNU Sample Texts,,texinfo}. +Insert a file link to the image in the Org file, and the Texinfo export +back-end inserts the image. These links must have the usual supported image +extensions and no descriptions. To scale the image, use @code{:width} and +@code{:height} attributes. For alternate text, use @code{:alt} and specify +the text using Texinfo code, as shown in the example: -@smallexample -#+MACRO: version 2.0 -#+MACRO: updated last updated 4 March 2014 +@example +#+ATTR_TEXINFO: :width 1in :alt Alternate @@i@{text@} +[[ridt.pdf]] +@end example -#+OPTIONS: ':t toc:t author:t email:t +@node Special blocks in Texinfo export +@subsection Special blocks +@cindex #+ATTR_TEXINFO, in special blocks + +The Texinfo export back-end converts special blocks to commands with the same +name. It also adds any @code{:options} attributes to the end of the command, +as shown in this example: + +@example +#+ATTR_TEXINFO: :options org-org-export-to-org ... +#+begin_defun +A somewhat obsessive function. +#+end_defun +@end example + +@noindent +becomes + +@example +@@defun org-org-export-to-org ... +A somewhat obsessive function. +@@end defun +@end example + +@node A Texinfo example +@subsection A Texinfo example + +Here is a more detailed example Org file. @inforef{GNU Sample +Texts,,texinfo} for an equivalent example using Texinfo code. + +@example #+TITLE: GNU Sample @{@{@{version@}@}@} +#+SUBTITLE: for version @{@{@{version@}@}@}, @{@{@{updated@}@}@} #+AUTHOR: A.U. Thor #+EMAIL: bug-sample@@gnu.org + +#+OPTIONS: ':t toc:t author:t email:t #+LANGUAGE: en +#+MACRO: version 2.0 +#+MACRO: updated last updated 4 March 2014 + #+TEXINFO_FILENAME: sample.info #+TEXINFO_HEADER: @@syncodeindex pg cp @@ -13125,7 +13838,9 @@ Here is a thorough example, taken from @inforef{GNU Sample Texts,,texinfo}. #+TEXINFO_DIR_DESC: Invoking sample #+TEXINFO_PRINTED_TITLE: GNU Sample -#+SUBTITLE: for version 2.0, last updated 4 March 2014 + +This manual is for GNU Sample (version @{@{@{version@}@}@}, +@{@{@{updated@}@}@}). * Copying :PROPERTIES: @@ -13135,8 +13850,7 @@ Here is a thorough example, taken from @inforef{GNU Sample Texts,,texinfo}. This manual is for GNU Sample (version @{@{@{version@}@}@}, @{@{@{updated@}@}@}), which is an example in the Texinfo documentation. - Copyright @@@@texinfo:@@copyright@{@}@@@@ 2013 Free Software Foundation, - Inc. + Copyright \copy 2016 Free Software Foundation, Inc. #+BEGIN_QUOTE Permission is granted to copy, distribute and/or modify this @@ -13167,9 +13881,9 @@ Here is a thorough example, taken from @inforef{GNU Sample Texts,,texinfo}. :PROPERTIES: :INDEX: cp :END: -@end smallexample +@end example -@node iCalendar export, Other built-in back-ends, Texinfo export, Exporting +@node iCalendar export @section iCalendar export @cindex iCalendar export @@ -13178,49 +13892,51 @@ Here is a thorough example, taken from @inforef{GNU Sample Texts,,texinfo}. @vindex org-icalendar-use-scheduled @vindex org-icalendar-categories @vindex org-icalendar-alarm-time -Some people use Org mode for keeping track of projects, but still prefer a -standard calendar application for anniversaries and appointments. In this -case it can be useful to show deadlines and other time-stamped items in Org -files in the calendar application. Org mode can export calendar information -in the standard iCalendar format. If you also want to have TODO entries -included in the export, configure the variable -@code{org-icalendar-include-todo}. Plain timestamps are exported as VEVENT, -and TODO items as VTODO@. It will also create events from deadlines that are -in non-TODO items. Deadlines and scheduling dates in TODO items will be used -to set the start and due dates for the TODO entry@footnote{See the variables -@code{org-icalendar-use-deadline} and @code{org-icalendar-use-scheduled}.}. -As categories, it will use the tags locally defined in the heading, and the -file/tree category@footnote{To add inherited tags or the TODO state, -configure the variable @code{org-icalendar-categories}.}. See the variable -@code{org-icalendar-alarm-time} for a way to assign alarms to entries with a -time. +A large part of Org mode's inter-operability success is its ability to easily +export to or import from external applications. The iCalendar export +back-end takes calendar data from Org files and exports to the standard +iCalendar format. + +The iCalendar export back-end can also incorporate TODO entries based on the +configuration of the @code{org-icalendar-include-todo} variable. The +back-end exports plain timestamps as VEVENT, TODO items as VTODO, and also +create events from deadlines that are in non-TODO items. The back-end uses +the deadlines and scheduling dates in Org TODO items for setting the start +and due dates for the iCalendar TODO entry. Consult the +@code{org-icalendar-use-deadline} and @code{org-icalendar-use-scheduled} +variables for more details. + +For tags on the headline, the iCalendar export back-end makes them into +iCalendar categories. To tweak the inheritance of tags and TODO states, +configure the variable @code{org-icalendar-categories}. To assign clock +alarms based on time, configure the @code{org-icalendar-alarm-time} variable. @vindex org-icalendar-store-UID @cindex property, ID -The iCalendar standard requires each entry to have a globally unique -identifier (UID). Org creates these identifiers during export. If you set -the variable @code{org-icalendar-store-UID}, the UID will be stored in the -@code{:ID:} property of the entry and re-used next time you report this -entry. Since a single entry can give rise to multiple iCalendar entries (as -a timestamp, a deadline, a scheduled item, and as a TODO item), Org adds -prefixes to the UID, depending on what triggered the inclusion of the entry. -In this way the UID remains unique, but a synchronization program can still -figure out from which entry all the different instances originate. +The iCalendar format standard requires globally unique identifier---UID---for +each entry. The iCalendar export back-end creates UIDs during export. To +save a copy of the UID in the Org file set the variable +@code{org-icalendar-store-UID}. The back-end looks for the @code{:ID:} +property of the entry for re-using the same UID for subsequent exports. + +Since a single Org entry can result in multiple iCalendar entries---as +timestamp, deadline, scheduled item, or TODO item---Org adds prefixes to the +UID, depending on which part of the Org entry triggered the creation of the +iCalendar entry. Prefixing ensures UIDs remains unique, yet enable +synchronization programs trace the connections. @table @kbd @orgcmd{C-c C-e c f,org-icalendar-export-to-ics} -Create iCalendar entries for the current buffer and store them in the same -directory, using a file extension @file{.ics}. +Create iCalendar entries from the current Org buffer and store them in the +same directory, using a file extension @file{.ics}. @orgcmd{C-c C-e c a, org-icalendar-export-agenda-files} @vindex org-agenda-files -Like @kbd{C-c C-e c f}, but do this for all files in -@code{org-agenda-files}. For each of these files, a separate iCalendar -file will be written. +Create iCalendar entries from Org files in @code{org-agenda-files} and store +in a separate iCalendar file for each Org file. @orgcmd{C-c C-e c c,org-icalendar-combine-agenda-files} @vindex org-icalendar-combined-agenda-file -Create a single large iCalendar file from all files in -@code{org-agenda-files} and write it to the file given by -@code{org-icalendar-combined-agenda-file}. +Create a combined iCalendar file from Org files in @code{org-agenda-files} +and write it to @code{org-icalendar-combined-agenda-file} file name. @end table @vindex org-use-property-inheritance @@ -13228,72 +13944,54 @@ Create a single large iCalendar file from all files in @cindex property, SUMMARY @cindex property, DESCRIPTION @cindex property, LOCATION -The export will honor SUMMARY, DESCRIPTION and LOCATION@footnote{The LOCATION -property can be inherited from higher in the hierarchy if you configure -@code{org-use-property-inheritance} accordingly.} properties if the selected -entries have them. If not, the summary will be derived from the headline, -and the description from the body (limited to -@code{org-icalendar-include-body} characters). - -How this calendar is best read and updated, depends on the application -you are using. The FAQ covers this issue. - -@node Other built-in back-ends, Export in foreign buffers, iCalendar export, Exporting +The iCalendar export back-end includes SUMMARY, DESCRIPTION and LOCATION +properties from the Org entries when exporting. To force the back-end to +inherit the LOCATION property, configure the +@code{org-use-property-inheritance} variable. + +When Org entries do not have SUMMARY, DESCRIPTION and LOCATION properties, +the iCalendar export back-end derives the summary from the headline, and +derives the description from the body of the Org item. The +@code{org-icalendar-include-body} variable limits the maximum number of +characters of the content are turned into its description. + +Exporting to iCalendar format depends in large part on the capabilities of +the destination application. Some are more lenient than others. Consult the +Org mode FAQ for advice on specific applications. + +@node Other built-in back-ends @section Other built-in back-ends @cindex export back-ends, built-in @vindex org-export-backends -On top of the aforementioned back-ends, Org comes with other built-in ones: +Other export back-ends included with Org are: @itemize @item @file{ox-man.el}: export to a man page. @end itemize -To activate these export back-end, customize @code{org-export-backends} or -load them directly with e.g., @code{(require 'ox-man)}. This will add new -keys in the export dispatcher (@pxref{The Export Dispatcher}). - -See the comment section of these files for more information on how to use -them. - -@node Export in foreign buffers, Advanced configuration, Other built-in back-ends, Exporting -@section Export in foreign buffers - -Most built-in back-ends come with a command to convert the selected region -into a selected format and replace this region by the exported output. Here -is a list of such conversion commands: +To activate such back-ends, either customize @code{org-export-backends} or +load directly with @code{(require 'ox-man)}. On successful load, the +back-end adds new keys in the export dispatcher (@pxref{The export +dispatcher}). -@table @code -@item org-html-convert-region-to-html -Convert the selected region into HTML. -@item org-latex-convert-region-to-latex -Convert the selected region into @LaTeX{}. -@item org-texinfo-convert-region-to-texinfo -Convert the selected region into @code{Texinfo}. -@item org-md-convert-region-to-md -Convert the selected region into @code{MarkDown}. -@end table +Follow the comment section of such files, for example, @file{ox-man.el}, for +usage and configuration details. -This is particularly useful for converting tables and lists in foreign -buffers. E.g., in an HTML buffer, you can turn on @code{orgstruct-mode}, then -use Org commands for editing a list, and finally select and convert the list -with @code{M-x org-html-convert-region-to-html RET}. - -@node Advanced configuration, , Export in foreign buffers, Exporting +@node Advanced configuration @section Advanced configuration @subheading Hooks @vindex org-export-before-processing-hook @vindex org-export-before-parsing-hook -Two hooks are run during the first steps of the export process. The first -one, @code{org-export-before-processing-hook} is called before expanding -macros, Babel code and include keywords in the buffer. The second one, -@code{org-export-before-parsing-hook}, as its name suggests, happens just -before parsing the buffer. Their main use is for heavy duties, that is -duties involving structural modifications of the document. For example, one -may want to remove every headline in the buffer during export. The following -code can achieve this: +The export process executes two hooks before the actual exporting begins. +The first hook, @code{org-export-before-processing-hook}, runs before any +expansions of macros, Babel code, and include keywords in the buffer. The +second hook, @code{org-export-before-parsing-hook}, runs before the buffer is +parsed. Both hooks are specified as functions, see example below. Their main +use is for heavy duty structural modifications of the Org content. For +example, removing every headline in the buffer during export: @lisp @group @@ -13307,86 +14005,83 @@ BACKEND is the export back-end being used, as a symbol." @end group @end lisp -Note that functions used in these hooks require a mandatory argument, -a symbol representing the back-end used. +Note that the hook function must have a mandatory argument that is a symbol +for the back-end. @subheading Filters @cindex Filters, exporting -Filters are lists of functions applied on a specific part of the output from -a given back-end. More explicitly, each time a back-end transforms an Org -object or element into another language, all functions within a given filter -type are called in turn on the string produced. The string returned by the -last function will be the one used in the final output. - -There are filters sets for each type of element or object, for plain text, -for the parse tree, for the export options and for the final output. They -are all named after the same scheme: @code{org-export-filter-TYPE-functions}, -where @code{TYPE} is the type targeted by the filter. Valid types are: +The Org export process relies on filters to process specific parts of +conversion process. Filters are just lists of functions to be applied to +certain parts for a given back-end. The output from the first function in +the filter is passed on to the next function in the filter. The final output +is the output from the final function in the filter. + +The Org export process has many filter sets applicable to different types of +objects, plain text, parse trees, export options, and final output formats. +The filters are named after the element type or object type: +@code{org-export-filter-TYPE-functions}, where @code{TYPE} is the type +targeted by the filter. Valid types are: @multitable @columnfractions .33 .33 .33 -@item bold +@item body +@tab bold @tab babel-call -@tab center-block -@item clock +@item center-block +@tab clock @tab code -@tab comment -@item comment-block -@tab diary-sexp +@item diary-sexp @tab drawer -@item dynamic-block -@tab entity +@tab dynamic-block +@item entity @tab example-block -@item export-block -@tab export-snippet +@tab export-block +@item export-snippet @tab final-output -@item fixed-width -@tab footnote-definition +@tab fixed-width +@item footnote-definition @tab footnote-reference -@item headline -@tab horizontal-rule +@tab headline +@item horizontal-rule @tab inline-babel-call -@item inline-src-block -@tab inlinetask +@tab inline-src-block +@item inlinetask @tab italic -@item item -@tab keyword +@tab item +@item keyword @tab latex-environment -@item latex-fragment -@tab line-break +@tab latex-fragment +@item line-break @tab link -@item node-property -@tab options +@tab node-property +@item options @tab paragraph -@item parse-tree -@tab plain-list +@tab parse-tree +@item plain-list @tab plain-text -@item planning -@tab property-drawer +@tab planning +@item property-drawer @tab quote-block -@item quote-section @tab radio-target -@tab section -@item special-block +@item section +@tab special-block @tab src-block -@tab statistics-cookie -@item strike-through +@item statistics-cookie +@tab strike-through @tab subscript -@tab superscript -@item table +@item superscript +@tab table @tab table-cell -@tab table-row -@item target +@item table-row +@tab target @tab timestamp -@tab underline -@item verbatim +@item underline +@tab verbatim @tab verse-block -@tab @end multitable -For example, the following snippet allows me to use non-breaking spaces in -the Org buffer and get them translated into @LaTeX{} without using the -@code{\nbsp} macro (where @code{_} stands for the non-breaking space): +Here is an example filter that replaces non-breaking spaces @code{~} in the +Org buffer with @code{_} for the @LaTeX{} back-end. @lisp @group @@ -13400,33 +14095,49 @@ the Org buffer and get them translated into @LaTeX{} without using the @end group @end lisp -Three arguments must be provided to a filter: the code being changed, the -back-end used, and some information about the export process. You can safely -ignore the third argument for most purposes. Note the use of -@code{org-export-derived-backend-p}, which ensures that the filter will only -be applied when using @code{latex} back-end or any other back-end derived -from it (e.g., @code{beamer}). +A filter requires three arguments: the code to be transformed, the name of +the back-end, and some optional information about the export process. The +third argument can be safely ignored. Note the use of +@code{org-export-derived-backend-p} predicate that tests for @code{latex} +back-end or any other back-end, such as @code{beamer}, derived from +@code{latex}. -@subheading Extending an existing back-end +@subheading Defining filters for individual files + +The Org export can filter not just for back-ends, but also for specific files +through the @code{#+BIND} keyword. Here is an example with two filters; one +removes brackets from time stamps, and the other removes strike-through text. +The filter functions are defined in a @samp{src} code block in the same Org +file, which is a handy location for debugging. + +@example +#+BIND: org-export-filter-timestamp-functions (tmp-f-timestamp) +#+BIND: org-export-filter-strike-through-functions (tmp-f-strike-through) +#+begin_src emacs-lisp :exports results :results none + (defun tmp-f-timestamp (s backend info) + (replace-regexp-in-string "&[lg]t;\\|[][]" "" s)) + (defun tmp-f-strike-through (s backend info) "") +#+end_src +@end example -This is obviously the most powerful customization, since the changes happen -at the parser level. Indeed, some export back-ends are built as extensions -of other ones (e.g., Markdown back-end an extension of HTML back-end). +@subheading Extending an existing back-end -Extending a back-end means that if an element type is not transcoded by the -new back-end, it will be handled by the original one. Hence you can extend -specific parts of a back-end without too much work. +Some parts of the conversion process can be extended for certain elements so +as to introduce a new or revised translation. That is how the HTML export +back-end was extended to handle Markdown format. The extensions work +seamlessly so any aspect of filtering not done by the extended back-end is +handled by the original back-end. Of all the export customization in Org, +extending is very powerful as it operates at the parser level. -As an example, imagine we want the @code{ascii} back-end to display the -language used in a source block, when it is available, but only when some -attribute is non-@code{nil}, like the following: +For this example, make the @code{ascii} back-end display the language used in +a source code block. Also make it display only when some attribute is +non-@code{nil}, like the following: @example #+ATTR_ASCII: :language t @end example -Because that back-end is lacking in that area, we are going to create a new -back-end, @code{my-ascii} that will do the job. +Then extend @code{ascii} back-end with a custom @code{my-ascii} back-end. @lisp @group @@ -13450,20 +14161,47 @@ channel." @end lisp The @code{my-ascii-src-block} function looks at the attribute above the -element. If it isn't true, it gives hand to the @code{ascii} back-end. -Otherwise, it creates a box around the code, leaving room for the language. -A new back-end is then created. It only changes its behavior when -translating @code{src-block} type element. Now, all it takes to use the new -back-end is calling the following from an Org buffer: +current element. If not true, hands over to @code{ascii} back-end. If true, +which it is in this example, it creates a box around the code and leaves room +for the inserting a string for language. The last form creates the new +back-end that springs to action only when translating @code{src-block} type +elements. + +To use the newly defined back-end, call the following from an Org buffer: @smalllisp (org-export-to-buffer 'my-ascii "*Org MY-ASCII Export*") @end smalllisp -It is obviously possible to write an interactive function for this, install -it in the export dispatcher menu, and so on. +Further steps to consider would be an interactive function, self-installing +an item in the export dispatcher menu, and other user-friendly improvements. + +@node Export in foreign buffers +@section Export in foreign buffers + +The export back-ends in Org often include commands to convert selected +regions. A convenient feature of this in-place conversion is that the +exported output replaces the original source. Here are such functions: + +@table @code +@item org-html-convert-region-to-html +Convert the selected region into HTML. +@item org-latex-convert-region-to-latex +Convert the selected region into @LaTeX{}. +@item org-texinfo-convert-region-to-texinfo +Convert the selected region into @code{Texinfo}. +@item org-md-convert-region-to-md +Convert the selected region into @code{MarkDown}. +@end table + +In-place conversions are particularly handy for quick conversion of tables +and lists in foreign buffers. For example, turn on the minor mode @code{M-x +orgstruct-mode} in an HTML buffer, then use the convenient Org keyboard +commands to create a list, select it, and covert it to HTML with @code{M-x +org-html-convert-region-to-html RET}. + -@node Publishing, Working With Source Code, Exporting, Top +@node Publishing @chapter Publishing @cindex publishing @@ -13485,7 +14223,7 @@ Publishing has been contributed to Org by David O'Toole. * Triggering publication:: Publication commands @end menu -@node Configuration, Uploading files, Publishing, Publishing +@node Configuration @section Configuration Publishing needs significant configuration to specify files, destination @@ -13502,7 +14240,7 @@ and many other properties of a project. * Generating an index:: An index that reaches across pages @end menu -@node Project alist, Sources and destinations, Configuration, Configuration +@node Project alist @subsection The variable @code{org-publish-project-alist} @cindex org-publish-project-alist @cindex projects, for publishing @@ -13529,7 +14267,7 @@ together files requiring different publishing options. When you publish such a ``meta-project'', all the components will also be published, in the sequence given. -@node Sources and destinations, Selecting files, Project alist, Configuration +@node Sources and destinations @subsection Sources and destinations for files @cindex directories, for publishing @@ -13548,17 +14286,17 @@ use external tools to upload your website (@pxref{Uploading files}). @item @code{:preparation-function} @tab Function or list of functions to be called before starting the publishing process, for example, to run @code{make} for updating files to be -published. The project property list is scoped into this call as the -variable @code{project-plist}. +published. Each preparation function is called with a single argument, the +project property list. @item @code{:completion-function} @tab Function or list of functions called after finishing the publishing -process, for example, to change permissions of the resulting files. The -project property list is scoped into this call as the variable -@code{project-plist}. +process, for example, to change permissions of the resulting files. Each +completion function is called with a single argument, the project property +list. @end multitable @noindent -@node Selecting files, Publishing action, Sources and destinations, Configuration +@node Selecting files @subsection Selecting files @cindex files, selecting for publishing @@ -13584,7 +14322,7 @@ and @code{:exclude}. @tab non-@code{nil} means, check base-directory recursively for files to publish. @end multitable -@node Publishing action, Publishing options, Selecting files, Configuration +@node Publishing action @subsection Publishing action @cindex action, for publishing @@ -13623,46 +14361,26 @@ and the path to the publishing directory of the output file. It should take the specified file, make the necessary transformation (if any) and place the result into the destination folder. -@node Publishing options, Publishing links, Publishing action, Configuration +@node Publishing options @subsection Options for the exporters @cindex options, for publishing -The property list can be used to set many export options for the exporters. -In most cases, these properties correspond to user variables in Org. The -first table below lists these properties along with the variable they belong -to. The second table list HTML specific properties. See the documentation -string of these options for details. +The property list can be used to set export options during the publishing +process. In most cases, these properties correspond to user variables in +Org. While some properties are available for all export back-ends, most of +them are back-end specific. The following sections list properties along +with the variable they belong to. See the documentation string of these +options for details. -@vindex org-display-custom-times -@vindex org-export-default-language -@vindex org-export-exclude-tags -@vindex org-export-headline-levels -@vindex org-export-preserve-breaks -@vindex org-export-publishing-directory -@vindex org-export-select-tags -@vindex org-export-with-archived-trees -@vindex org-export-with-author -@vindex org-export-with-creator -@vindex org-export-with-drawers -@vindex org-export-with-email -@vindex org-export-with-emphasize -@vindex org-export-with-fixed-width -@vindex org-export-with-footnotes -@vindex org-export-with-latex -@vindex org-export-with-planning -@vindex org-export-with-priority -@vindex org-export-with-section-numbers -@vindex org-export-with-special-strings -@vindex org-export-with-sub-superscripts -@vindex org-export-with-tables -@vindex org-export-with-tags -@vindex org-export-with-tasks -@vindex org-export-with-timestamps -@vindex org-export-with-toc -@vindex org-export-with-todo-keywords -@vindex user-mail-address +@vindex org-publish-project-alist +When a property is given a value in @code{org-publish-project-alist}, its +setting overrides the value of the corresponding user variable (if any) +during publishing. Options set within a file (@pxref{Export settings}), +however, override everything. + +@subsubheading Generic properties -@multitable @columnfractions 0.32 0.68 +@multitable {@code{:with-sub-superscript}} {@code{org-export-with-sub-superscripts}} @item @code{:archived-trees} @tab @code{org-export-with-archived-trees} @item @code{:exclude-tags} @tab @code{org-export-exclude-tags} @item @code{:headline-levels} @tab @code{org-export-headline-levels} @@ -13671,7 +14389,10 @@ string of these options for details. @item @code{:section-numbers} @tab @code{org-export-with-section-numbers} @item @code{:select-tags} @tab @code{org-export-select-tags} @item @code{:with-author} @tab @code{org-export-with-author} +@item @code{:with-broken-links} @tab @code{org-export-with-broken-links} +@item @code{:with-clocks} @tab @code{org-export-with-clocks} @item @code{:with-creator} @tab @code{org-export-with-creator} +@item @code{:with-date} @tab @code{org-export-with-date} @item @code{:with-drawers} @tab @code{org-export-with-drawers} @item @code{:with-email} @tab @code{org-export-with-email} @item @code{:with-emphasize} @tab @code{org-export-with-emphasize} @@ -13680,83 +14401,225 @@ string of these options for details. @item @code{:with-latex} @tab @code{org-export-with-latex} @item @code{:with-planning} @tab @code{org-export-with-planning} @item @code{:with-priority} @tab @code{org-export-with-priority} +@item @code{:with-properties} @tab @code{org-export-with-properties} @item @code{:with-special-strings} @tab @code{org-export-with-special-strings} @item @code{:with-sub-superscript} @tab @code{org-export-with-sub-superscripts} @item @code{:with-tables} @tab @code{org-export-with-tables} @item @code{:with-tags} @tab @code{org-export-with-tags} @item @code{:with-tasks} @tab @code{org-export-with-tasks} @item @code{:with-timestamps} @tab @code{org-export-with-timestamps} +@item @code{:with-title} @tab @code{org-export-with-title} @item @code{:with-toc} @tab @code{org-export-with-toc} @item @code{:with-todo-keywords} @tab @code{org-export-with-todo-keywords} @end multitable -@vindex org-html-doctype -@vindex org-html-container-element -@vindex org-html-html5-fancy -@vindex org-html-xml-declaration -@vindex org-html-link-up -@vindex org-html-link-home -@vindex org-html-link-org-files-as-html -@vindex org-html-link-use-abs-url -@vindex org-html-head -@vindex org-html-head-extra -@vindex org-html-inline-images -@vindex org-html-extension -@vindex org-html-preamble -@vindex org-html-postamble -@vindex org-html-table-default-attributes -@vindex org-html-table-row-tags -@vindex org-html-head-include-default-style -@vindex org-html-head-include-scripts -@multitable @columnfractions 0.32 0.68 -@item @code{:html-doctype} @tab @code{org-html-doctype} -@item @code{:html-container} @tab @code{org-html-container-element} -@item @code{:html-html5-fancy} @tab @code{org-html-html5-fancy} -@item @code{:html-xml-declaration} @tab @code{org-html-xml-declaration} -@item @code{:html-link-up} @tab @code{org-html-link-up} -@item @code{:html-link-home} @tab @code{org-html-link-home} -@item @code{:html-link-org-as-html} @tab @code{org-html-link-org-files-as-html} -@item @code{:html-link-use-abs-url} @tab @code{org-html-link-use-abs-url} -@item @code{:html-head} @tab @code{org-html-head} -@item @code{:html-head-extra} @tab @code{org-html-head-extra} -@item @code{:html-inline-images} @tab @code{org-html-inline-images} -@item @code{:html-extension} @tab @code{org-html-extension} -@item @code{:html-preamble} @tab @code{org-html-preamble} -@item @code{:html-postamble} @tab @code{org-html-postamble} -@item @code{:html-table-attributes} @tab @code{org-html-table-default-attributes} -@item @code{:html-table-row-tags} @tab @code{org-html-table-row-tags} +@subsubheading ASCII specific properties + +@multitable {@code{:ascii-table-keep-all-vertical-lines}} {@code{org-ascii-table-keep-all-vertical-lines}} +@item @code{:ascii-bullets} @tab @code{org-ascii-bullets} +@item @code{:ascii-caption-above} @tab @code{org-ascii-caption-above} +@item @code{:ascii-charset} @tab @code{org-ascii-charset} +@item @code{:ascii-global-margin} @tab @code{org-ascii-global-margin} +@item @code{:ascii-format-drawer-function} @tab @code{org-ascii-format-drawer-function} +@item @code{:ascii-format-inlinetask-function} @tab @code{org-ascii-format-inlinetask-function} +@item @code{:ascii-headline-spacing} @tab @code{org-ascii-headline-spacing} +@item @code{:ascii-indented-line-width} @tab @code{org-ascii-indented-line-width} +@item @code{:ascii-inlinetask-width} @tab @code{org-ascii-inlinetask-width} +@item @code{:ascii-inner-margin} @tab @code{org-ascii-inner-margin} +@item @code{:ascii-links-to-notes} @tab @code{org-ascii-links-to-notes} +@item @code{:ascii-list-margin} @tab @code{org-ascii-list-margin} +@item @code{:ascii-paragraph-spacing} @tab @code{org-ascii-paragraph-spacing} +@item @code{:ascii-quote-margin} @tab @code{org-ascii-quote-margin} +@item @code{:ascii-table-keep-all-vertical-lines} @tab @code{org-ascii-table-keep-all-vertical-lines} +@item @code{:ascii-table-use-ascii-art} @tab @code{org-ascii-table-use-ascii-art} +@item @code{:ascii-table-widen-columns} @tab @code{org-ascii-table-widen-columns} +@item @code{:ascii-text-width} @tab @code{org-ascii-text-width} +@item @code{:ascii-underline} @tab @code{org-ascii-underline} +@item @code{:ascii-verbatim-format} @tab @code{org-ascii-verbatim-format} +@end multitable + +@subsubheading Beamer specific properties + +@multitable {@code{:beamer-frame-default-options}} {@code{org-beamer-frame-default-options}} +@item @code{:beamer-theme} @tab @code{org-beamer-theme} +@item @code{:beamer-column-view-format} @tab @code{org-beamer-column-view-format} +@item @code{:beamer-environments-extra} @tab @code{org-beamer-environments-extra} +@item @code{:beamer-frame-default-options} @tab @code{org-beamer-frame-default-options} +@item @code{:beamer-outline-frame-options} @tab @code{org-beamer-outline-frame-options} +@item @code{:beamer-outline-frame-title} @tab @code{org-beamer-outline-frame-title} +@item @code{:beamer-subtitle-format} @tab @code{org-beamer-subtitle-format} +@end multitable + +@subsubheading HTML specific properties + +@multitable {@code{:html-table-use-header-tags-for-first-column}} {@code{org-html-table-use-header-tags-for-first-column}} +@item @code{:html-allow-name-attribute-in-anchors} @tab @code{org-html-allow-name-attribute-in-anchors} +@item @code{:html-checkbox-type} @tab @code{org-html-checkbox-type} +@item @code{:html-container} @tab @code{org-html-container-element} +@item @code{:html-divs} @tab @code{org-html-divs} +@item @code{:html-doctype} @tab @code{org-html-doctype} +@item @code{:html-extension} @tab @code{org-html-extension} +@item @code{:html-footnote-format} @tab @code{org-html-footnote-format} +@item @code{:html-footnote-separator} @tab @code{org-html-footnote-separator} +@item @code{:html-footnotes-section} @tab @code{org-html-footnotes-section} +@item @code{:html-format-drawer-function} @tab @code{org-html-format-drawer-function} +@item @code{:html-format-headline-function} @tab @code{org-html-format-headline-function} +@item @code{:html-format-inlinetask-function} @tab @code{org-html-format-inlinetask-function} +@item @code{:html-head-extra} @tab @code{org-html-head-extra} @item @code{:html-head-include-default-style} @tab @code{org-html-head-include-default-style} -@item @code{:html-head-include-scripts} @tab @code{org-html-head-include-scripts} +@item @code{:html-head-include-scripts} @tab @code{org-html-head-include-scripts} +@item @code{:html-head} @tab @code{org-html-head} +@item @code{:html-home/up-format} @tab @code{org-html-home/up-format} +@item @code{:html-html5-fancy} @tab @code{org-html-html5-fancy} +@item @code{:html-indent} @tab @code{org-html-indent} +@item @code{:html-infojs-options} @tab @code{org-html-infojs-options} +@item @code{:html-infojs-template} @tab @code{org-html-infojs-template} +@item @code{:html-inline-image-rules} @tab @code{org-html-inline-image-rules} +@item @code{:html-inline-images} @tab @code{org-html-inline-images} +@item @code{:html-link-home} @tab @code{org-html-link-home} +@item @code{:html-link-org-files-as-html} @tab @code{org-html-link-org-files-as-html} +@item @code{:html-link-up} @tab @code{org-html-link-up} +@item @code{:html-link-use-abs-url} @tab @code{org-html-link-use-abs-url} +@item @code{:html-mathjax-options} @tab @code{org-html-mathjax-options} +@item @code{:html-mathjax-template} @tab @code{org-html-mathjax-template} +@item @code{:html-metadata-timestamp-format} @tab @code{org-html-metadata-timestamp-format} +@item @code{:html-postamble-format} @tab @code{org-html-postamble-format} +@item @code{:html-postamble} @tab @code{org-html-postamble} +@item @code{:html-preamble-format} @tab @code{org-html-preamble-format} +@item @code{:html-preamble} @tab @code{org-html-preamble} +@item @code{:html-table-align-individual-fields} @tab @code{org-html-table-align-individual-fields} +@item @code{:html-table-attributes} @tab @code{org-html-table-default-attributes} +@item @code{:html-table-caption-above} @tab @code{org-html-table-caption-above} +@item @code{:html-table-data-tags} @tab @code{org-html-table-data-tags} +@item @code{:html-table-header-tags} @tab @code{org-html-table-header-tags} +@item @code{:html-table-row-tags} @tab @code{org-html-table-row-tags} +@item @code{:html-table-use-header-tags-for-first-column} @tab @code{org-html-table-use-header-tags-for-first-column} +@item @code{:html-tag-class-prefix} @tab @code{org-html-tag-class-prefix} +@item @code{:html-text-markup-alist} @tab @code{org-html-text-markup-alist} +@item @code{:html-todo-kwd-class-prefix} @tab @code{org-html-todo-kwd-class-prefix} +@item @code{:html-toplevel-hlevel} @tab @code{org-html-toplevel-hlevel} +@item @code{:html-use-infojs} @tab @code{org-html-use-infojs} +@item @code{:html-validation-link} @tab @code{org-html-validation-link} +@item @code{:html-viewport} @tab @code{org-html-viewport} +@item @code{:html-xml-declaration} @tab @code{org-html-xml-declaration} @end multitable -Most of the @code{org-export-with-*} variables have the same effect in each -exporter. +@subsubheading @LaTeX{} specific properties + +@multitable {@code{:latex-link-with-unknown-path-format}} {@code{org-latex-link-with-unknown-path-format}} +@item @code{:latex-active-timestamp-format} @tab @code{org-latex-active-timestamp-format} +@item @code{:latex-caption-above} @tab @code{org-latex-caption-above} +@item @code{:latex-classes} @tab @code{org-latex-classes} +@item @code{:latex-class} @tab @code{org-latex-default-class} +@item @code{:latex-compiler} @tab @code{org-latex-compiler} +@item @code{:latex-default-figure-position} @tab @code{org-latex-default-figure-position} +@item @code{:latex-default-table-environment} @tab @code{org-latex-default-table-environment} +@item @code{:latex-default-table-mode} @tab @code{org-latex-default-table-mode} +@item @code{:latex-diary-timestamp-format} @tab @code{org-latex-diary-timestamp-format} +@item @code{:latex-footnote-defined-format} @tab @code{org-latex-footnote-defined-format} +@item @code{:latex-footnote-separator} @tab @code{org-latex-footnote-separator} +@item @code{:latex-format-drawer-function} @tab @code{org-latex-format-drawer-function} +@item @code{:latex-format-headline-function} @tab @code{org-latex-format-headline-function} +@item @code{:latex-format-inlinetask-function} @tab @code{org-latex-format-inlinetask-function} +@item @code{:latex-hyperref-template} @tab @code{org-latex-hyperref-template} +@item @code{:latex-image-default-height} @tab @code{org-latex-image-default-height} +@item @code{:latex-image-default-option} @tab @code{org-latex-image-default-option} +@item @code{:latex-image-default-width} @tab @code{org-latex-image-default-width} +@item @code{:latex-images-centered} @tab @code{org-latex-images-centered} +@item @code{:latex-inactive-timestamp-format} @tab @code{org-latex-inactive-timestamp-format} +@item @code{:latex-inline-image-rules} @tab @code{org-latex-inline-image-rules} +@item @code{:latex-link-with-unknown-path-format} @tab @code{org-latex-link-with-unknown-path-format} +@item @code{:latex-listings-langs} @tab @code{org-latex-listings-langs} +@item @code{:latex-listings-options} @tab @code{org-latex-listings-options} +@item @code{:latex-listings} @tab @code{org-latex-listings} +@item @code{:latex-minted-langs} @tab @code{org-latex-minted-langs} +@item @code{:latex-minted-options} @tab @code{org-latex-minted-options} +@item @code{:latex-prefer-user-labels} @tab @code{org-latex-prefer-user-labels} +@item @code{:latex-subtitle-format} @tab @code{org-latex-subtitle-format} +@item @code{:latex-subtitle-separate} @tab @code{org-latex-subtitle-separate} +@item @code{:latex-table-scientific-notation} @tab @code{org-latex-table-scientific-notation} +@item @code{:latex-tables-booktabs} @tab @code{org-latex-tables-booktabs} +@item @code{:latex-tables-centered} @tab @code{org-latex-tables-centered} +@item @code{:latex-text-markup-alist} @tab @code{org-latex-text-markup-alist} +@item @code{:latex-title-command} @tab @code{org-latex-title-command} +@item @code{:latex-toc-command} @tab @code{org-latex-toc-command} +@end multitable -@vindex org-publish-project-alist -When a property is given a value in @code{org-publish-project-alist}, its -setting overrides the value of the corresponding user variable (if any) -during publishing. Options set within a file (@pxref{Export settings}), -however, override everything. +@subsubheading Markdown specific properties + +@multitable {@code{:md-footnotes-section}} {@code{org-md-footnotes-section}} +@item @code{:md-footnote-format} @tab @code{org-md-footnote-format} +@item @code{:md-footnotes-section} @tab @code{org-md-footnotes-section} +@item @code{:md-headline-style} @tab @code{org-md-headline-style} +@end multitable + +@subsubheading ODT specific properties + +@multitable {@code{:odt-format-inlinetask-function}} {@code{org-odt-format-inlinetask-function}} +@item @code{:odt-content-template-file} @tab @code{org-odt-content-template-file} +@item @code{:odt-display-outline-level} @tab @code{org-odt-display-outline-level} +@item @code{:odt-fontify-srcblocks} @tab @code{org-odt-fontify-srcblocks} +@item @code{:odt-format-drawer-function} @tab @code{org-odt-format-drawer-function} +@item @code{:odt-format-headline-function} @tab @code{org-odt-format-headline-function} +@item @code{:odt-format-inlinetask-function} @tab @code{org-odt-format-inlinetask-function} +@item @code{:odt-inline-formula-rules} @tab @code{org-odt-inline-formula-rules} +@item @code{:odt-inline-image-rules} @tab @code{org-odt-inline-image-rules} +@item @code{:odt-pixels-per-inch} @tab @code{org-odt-pixels-per-inch} +@item @code{:odt-styles-file} @tab @code{org-odt-styles-file} +@item @code{:odt-table-styles} @tab @code{org-odt-table-styles} +@item @code{:odt-use-date-fields} @tab @code{org-odt-use-date-fields} +@end multitable + +@subsubheading Texinfo specific properties + +@multitable {@code{:texinfo-link-with-unknown-path-format}} {@code{org-texinfo-link-with-unknown-path-format}} +@item @code{:texinfo-active-timestamp-format} @tab @code{org-texinfo-active-timestamp-format} +@item @code{:texinfo-classes} @tab @code{org-texinfo-classes} +@item @code{:texinfo-class} @tab @code{org-texinfo-default-class} +@item @code{:texinfo-def-table-markup} @tab @code{org-texinfo-def-table-markup} +@item @code{:texinfo-diary-timestamp-format} @tab @code{org-texinfo-diary-timestamp-format} +@item @code{:texinfo-filename} @tab @code{org-texinfo-filename} +@item @code{:texinfo-format-drawer-function} @tab @code{org-texinfo-format-drawer-function} +@item @code{:texinfo-format-headline-function} @tab @code{org-texinfo-format-headline-function} +@item @code{:texinfo-format-inlinetask-function} @tab @code{org-texinfo-format-inlinetask-function} +@item @code{:texinfo-inactive-timestamp-format} @tab @code{org-texinfo-inactive-timestamp-format} +@item @code{:texinfo-link-with-unknown-path-format} @tab @code{org-texinfo-link-with-unknown-path-format} +@item @code{:texinfo-node-description-column} @tab @code{org-texinfo-node-description-column} +@item @code{:texinfo-table-scientific-notation} @tab @code{org-texinfo-table-scientific-notation} +@item @code{:texinfo-tables-verbatim} @tab @code{org-texinfo-tables-verbatim} +@item @code{:texinfo-text-markup-alist} @tab @code{org-texinfo-text-markup-alist} +@end multitable -@node Publishing links, Sitemap, Publishing options, Configuration +@node Publishing links @subsection Links between published files @cindex links, publishing To create a link from one Org file to another, you would use something like -@samp{[[file:foo.org][The foo]]} or simply @samp{file:foo.org.} -(@pxref{Hyperlinks}). When published, this link becomes a link to -@file{foo.html}. You can thus interlink the pages of your "org web" project -and the links will work as expected when you publish them to HTML@. If you -also publish the Org source file and want to link to it, use an @code{http:} -link instead of a @code{file:} link, because @code{file:} links are converted -to link to the corresponding @file{html} file. +@samp{[[file:foo.org][The foo]]} or simply @samp{file:foo.org} +(@pxref{External links}). When published, this link becomes a link to +@file{foo.html}. You can thus interlink the pages of your ``org web'' +project and the links will work as expected when you publish them to HTML. +If you also publish the Org source file and want to link to it, use an +@code{http:} link instead of a @code{file:} link, because @code{file:} links +are converted to link to the corresponding @file{html} file. You may also link to related files, such as images. Provided you are careful with relative file names, and provided you have also configured Org to upload the related files, these links will work too. See @ref{Complex example}, for an example of this usage. -@node Sitemap, Generating an index, Publishing links, Configuration +Eventually, links between published documents can contain some search options +(@pxref{Search options}), which will be resolved to the appropriate location +in the linked file. For example, once published to HTML, the following links +all point to a dedicated anchor in @file{foo.html}. + +@example +[[file:foo.org::*heading]] +[[file:foo.org::#custom-id]] +[[file:foo.org::target]] +@end example + +@node Sitemap @subsection Generating a sitemap @cindex sitemap, of published pages @@ -13816,7 +14679,7 @@ Defaults to @code{nil}. @end multitable -@node Generating an index, , Sitemap, Configuration +@node Generating an index @subsection Generating an index @cindex index, in a publishing project @@ -13833,7 +14696,17 @@ The file will be created when first publishing a project with the "theindex.inc"}. You can then build around this include statement by adding a title, style information, etc. -@node Uploading files, Sample configuration, Configuration, Publishing +@cindex #+INDEX +Index entries are specified with @code{#+INDEX} keyword. An entry that +contains an exclamation mark will create a sub item. + +@example +* Curriculum Vitae +#+INDEX: CV +#+INDEX: Application!CV +@end example + +@node Uploading files @section Uploading files @cindex rsync @cindex unison @@ -13866,7 +14739,7 @@ benefit of re-including any changed external files such as source example files you might include with @code{#+INCLUDE:}. The timestamp mechanism in Org is not smart enough to detect if included files have been modified. -@node Sample configuration, Triggering publication, Uploading files, Publishing +@node Sample configuration @section Sample configuration Below we provide two example configurations. The first one is a simple @@ -13878,7 +14751,7 @@ more complex, with a multi-component project. * Complex example:: A multi-component publishing example @end menu -@node Simple example, Complex example, Sample configuration, Sample configuration +@node Simple example @subsection Example: simple publishing configuration This example publishes a set of Org files to the @file{public_html} @@ -13896,7 +14769,7 @@ directory on the local machine. type=\"text/css\"/>"))) @end lisp -@node Complex example, , Simple example, Sample configuration +@node Complex example @subsection Example: complex publishing configuration This more complicated example publishes an entire website, including @@ -13946,7 +14819,7 @@ right place on the web server, and publishing images to it. ("website" :components ("orgfiles" "images" "other")))) @end lisp -@node Triggering publication, , Sample configuration, Publishing +@node Triggering publication @section Triggering publication Once properly configured, Org can publish with the following commands: @@ -13970,17 +14843,20 @@ above, or by customizing the variable @code{org-publish-use-timestamps-flag}. This may be necessary in particular if files include other files via @code{#+SETUPFILE:} or @code{#+INCLUDE:}. -@comment node-name, next, previous, up -@comment Working With Source Code, Miscellaneous, Publishing, Top -@node Working With Source Code, Miscellaneous, Publishing, Top +@node Working with source code @chapter Working with source code @cindex Schulte, Eric @cindex Davison, Dan @cindex source code, working with -Source code can be included in Org mode documents using a @samp{src} block, -e.g.: +Source code here refers to any code typed in Org mode documents. Org can +manage source code in any Org file once such code is tagged with begin and +end markers. Working with source code begins with tagging source code +blocks. Tagged @samp{src} code blocks are not restricted to the preamble or +the end of an Org document; they can go anywhere---with a few exceptions, +such as not inside comments and fixed width areas. Here's a sample +@samp{src} code block in emacs-lisp: @example #+BEGIN_SRC emacs-lisp @@ -13990,14 +14866,57 @@ e.g.: #+END_SRC @end example -Org mode provides a number of features for working with live source code, -including editing of code blocks in their native major-mode, evaluation of -code blocks, converting code blocks into source files (known as @dfn{tangling} -in literate programming), and exporting code blocks and their -results in several formats. This functionality was contributed by Eric -Schulte and Dan Davison, and was originally named Org-babel. - -The following sections describe Org mode's code block handling facilities. +Org can take the code in the block between the @samp{#+BEGIN_SRC} and +@samp{#+END_SRC} tags, and format, compile, execute, and show the results. +Org can simplify many housekeeping tasks essential to modern code +maintenance. That's why these blocks in Org mode literature are sometimes +referred to as @samp{live code} blocks (as compared to the static text and +documentation around it). Users can control how @samp{live} they want each +block by tweaking the headers for compiling, execution, extraction. + +Org's @samp{src} code block type is one of many block types, such as quote, +export, verse, latex, example, and verbatim. This section pertains to +@samp{src} code blocks between @samp{#+BEGIN_SRC} and @samp{#+END_SRC} + +For editing @samp{src} code blocks, Org provides native Emacs major-modes. +That leverages the latest Emacs features for that source code language mode. + +For exporting, Org can then extract @samp{src} code blocks into compilable +source files (in a conversion process known as @dfn{tangling} in literate +programming terminology). + +For publishing, Org's back-ends can handle the @samp{src} code blocks and the +text for output to a variety of formats with native syntax highlighting. + +For executing the source code in the @samp{src} code blocks, Org provides +facilities that glue the tasks of compiling, collecting the results of the +execution, and inserting them back to the Org file. Besides text output, +results may include links to other data types that Emacs can handle: audio, +video, and graphics. + +An important feature of Org's execution of the @samp{src} code blocks is +passing variables, functions, and results between @samp{src} blocks. Such +interoperability uses a common syntax even if these @samp{src} blocks are in +different source code languages. The integration extends to linking the +debugger's error messages to the line in the @samp{src} code block in the Org +file. That should partly explain why this functionality by the original +contributors, Eric Schulte and Dan Davison, was called @samp{Org Babel}. + +In literate programming, the main appeal is code and documentation +co-existing in one file. Org mode takes this several steps further. First +by enabling execution, and then by inserting results of that execution back +into the Org file. Along the way, Org provides extensive formatting +features, including handling tables. Org handles multiple source code +languages in one file, and provides a common syntax for passing variables, +functions, and results between @samp{src} code blocks. + +Org mode fulfills the promise of easy verification and maintenance of +publishing reproducible research by keeping all these in the same file: text, +data, code, configuration settings of the execution environment, the results +of the execution, and associated narratives, claims, references, and internal +and external links. + +Details of Org's facilities for working with source code are shown next. @menu * Structure of code blocks:: Code block syntax described @@ -14014,19 +14933,18 @@ The following sections describe Org mode's code block handling facilities. * Batch execution:: Call functions from the command line @end menu -@comment node-name, next, previous, up -@comment Structure of code blocks, Editing source code, Working With Source Code, Working With Source Code -@node Structure of code blocks, Editing source code, Working With Source Code, Working With Source Code +@node Structure of code blocks @section Structure of code blocks @cindex code block, structure @cindex source code, block structure @cindex #+NAME @cindex #+BEGIN_SRC -Live code blocks can be specified with a @samp{src} block or -inline.@footnote{Note that @samp{src} blocks may be inserted using Org mode's -@ref{Easy Templates} system} The structure of a @samp{src} block is +Org offers two ways to structure source code in Org documents: in a +@samp{src} block, and directly inline. Both specifications are shown below. + +A @samp{src} block conforms to this structure: @example #+NAME: @@ -14035,12 +14953,15 @@ inline.@footnote{Note that @samp{src} blocks may be inserted using Org mode's #+END_SRC @end example -The @code{#+NAME:} line is optional, and can be used to name the code -block. Live code blocks require that a language be specified on the -@code{#+BEGIN_SRC} line. Switches and header arguments are optional. -@cindex source code, inline +Org mode's templates system (@pxref{Easy templates}) speeds up creating +@samp{src} code blocks with just three keystrokes. Do not be put-off by +having to remember the source block syntax. Org also works with other +completion systems in Emacs, some of which predate Org and have custom +domain-specific languages for defining templates. Regular use of templates +reduces errors, increases accuracy, and maintains consistency. -Live code blocks can also be specified inline using +@cindex source code, inline +An inline code block conforms to this structure: @example src_@{@} @@ -14053,36 +14974,39 @@ src_[
]@{@} @end example @table @code -@item <#+NAME: name> -This line associates a name with the code block. This is similar to the -@code{#+NAME: Name} lines that can be used to name tables in Org mode -files. Referencing the name of a code block makes it possible to evaluate -the block from other places in the file, from other files, or from Org mode -table formulas (see @ref{The spreadsheet}). Names are assumed to be unique -and the behavior of Org mode when two or more blocks share the same name is -undefined. +@item #+NAME: +Optional. Names the @samp{src} block so it can be called, like a function, +from other @samp{src} blocks or inline blocks to evaluate or to capture the +results. Code from other blocks, other files, and from table formulas +(@pxref{The spreadsheet}) can use the name to reference a @samp{src} block. +This naming serves the same purpose as naming Org tables. Org mode requires +unique names. For duplicate names, Org mode's behavior is undefined. @cindex #+NAME +@item #+BEGIN_SRC +@item #+END_SRC +Mandatory. They mark the start and end of a block that Org requires. The +@code{#+BEGIN_SRC} line takes additional arguments, as described next. +@cindex begin block, end block @item -The language of the code in the block (see @ref{Languages}). +Mandatory for live code blocks. It is the identifier of the source code +language in the block. @xref{Languages}, for identifiers of supported +languages. @cindex source code, language @item -Optional switches control code block export (see the discussion of switches in -@ref{Literal examples}) +Optional. Switches provide finer control of the code execution, export, and +format (see the discussion of switches in @ref{Literal examples}) @cindex source code, switches @item
-Optional header arguments control many aspects of evaluation, export and -tangling of code blocks (see @ref{Header arguments}). -Header arguments can also be set on a per-buffer or per-subtree -basis using properties. +Optional. Heading arguments control many aspects of evaluation, export and +tangling of code blocks (@pxref{Header arguments}). Using Org's properties +feature, header arguments can be selectively applied to the entire buffer or +specific sub-trees of the Org document. @item source code, header arguments @item -Source code in the specified language. +Source code in the dialect of the specified language identifier. @end table -@comment node-name, next, previous, up -@comment Editing source code, Exporting code blocks, Structure of code blocks, Working With Source Code - -@node Editing source code, Exporting code blocks, Structure of code blocks, Working With Source Code +@node Editing source code @section Editing source code @cindex code block, editing @cindex source code, editing @@ -14090,116 +15014,150 @@ Source code in the specified language. @vindex org-edit-src-auto-save-idle-delay @vindex org-edit-src-turn-on-auto-save @kindex C-c ' -Use @kbd{C-c '} to edit the current code block. This brings up a language -major-mode edit buffer containing the body of the code block. Manually -saving this buffer with @key{C-x C-s} will write the contents back to the Org -buffer. You can also set @code{org-edit-src-auto-save-idle-delay} to save the -base buffer after some idle delay, or @code{org-edit-src-turn-on-auto-save} -to auto-save this buffer into a separate file using @code{auto-save-mode}. -Use @kbd{C-c '} again to exit. - -The @code{org-src-mode} minor mode will be active in the edit buffer. The -following variables can be used to configure the behavior of the edit -buffer. See also the customization group @code{org-edit-structure} for -further configuration options. +@kbd{C-c '} for editing the current code block. It opens a new major-mode +edit buffer containing the body of the @samp{src} code block, ready for any +edits. @kbd{C-c '} again to close the buffer and return to the Org buffer. + +@key{C-x C-s} saves the buffer and updates the contents of the Org buffer. + +Set @code{org-edit-src-auto-save-idle-delay} to save the base buffer after +a certain idle delay time. + +Set @code{org-edit-src-turn-on-auto-save} to auto-save this buffer into a +separate file using @code{auto-save-mode}. + +@kbd{C-c '} to close the major-mode buffer and return back to the Org buffer. + +While editing the source code in the major-mode, the @code{org-src-mode} +minor mode remains active. It provides these customization variables as +described below. For even more variables, look in the customization +group @code{org-edit-structure}. @table @code @item org-src-lang-modes -If an Emacs major-mode named @code{-mode} exists, where -@code{} is the language named in the header line of the code block, -then the edit buffer will be placed in that major-mode. This variable -can be used to map arbitrary language names to existing major modes. +If an Emacs major-mode named @code{-mode} exists, where @code{} +is the language identifier from code block's header line, then the edit +buffer uses that major-mode. Use this variable to arbitrarily map language +identifiers to major modes. @item org-src-window-setup -Controls the way Emacs windows are rearranged when the edit buffer is created. +For specifying Emacs window arrangement when the new edit buffer is created. @item org-src-preserve-indentation -By default, the value is @code{nil}, which means that when code blocks are -evaluated during export or tangled, they are re-inserted into the code block, -which may replace sequences of spaces with tab characters. When non-nil, -whitespace in code blocks will be preserved during export or tangling, -exactly as it appears. This variable is especially useful for tangling -languages such as Python, in which whitespace indentation in the output is -critical. +@cindex indentation, in source blocks +Default is @code{nil}. Source code is indented. This indentation applies +during export or tangling, and depending on the context, may alter leading +spaces and tabs. When non-@code{nil}, source code is aligned with the +leftmost column. No lines are modified during export or tangling, which is +very useful for white-space sensitive languages, such as Python. @item org-src-ask-before-returning-to-edit-buffer -By default, Org will ask before returning to an open edit buffer. Set this -variable to @code{nil} to switch without asking. +When @code{nil}, Org returns to the edit buffer without further prompts. The +default prompts for a confirmation. @end table -To turn on native code fontification in the @emph{Org} buffer, configure the -variable @code{org-src-fontify-natively}. +Set @code{org-src-fontify-natively} to non-@code{nil} to turn on native code +fontification in the @emph{Org} buffer. Fontification of @samp{src} code +blocks can give visual separation of text and code on the display page. To +further customize the appearance of @code{org-block} for specific languages, +customize @code{org-src-block-faces}. The following example shades the +background of regular blocks, and colors source blocks only for Python and +Emacs-Lisp languages. +@lisp +(require 'color) +(set-face-attribute 'org-block nil :background + (color-darken-name + (face-attribute 'default :background) 3)) -@comment node-name, next, previous, up -@comment Exporting code blocks, Extracting source code, Editing source code, Working With Source Code +(setq org-src-block-faces '(("emacs-lisp" (:background "#EEE2FF")) + ("python" (:background "#E5FFB8")))) +@end lisp -@node Exporting code blocks, Extracting source code, Editing source code, Working With Source Code +@node Exporting code blocks @section Exporting code blocks @cindex code block, exporting @cindex source code, exporting -It is possible to export the @emph{code} of code blocks, the @emph{results} -of code block evaluation, @emph{both} the code and the results of code block -evaluation, or @emph{none}. For most languages, the default exports code. -However, for some languages (e.g., @code{ditaa}) the default exports the -results of code block evaluation. For information on exporting code block -bodies, see @ref{Literal examples}. +Org can flexibly export just the @emph{code} from the code blocks, just the +@emph{results} of evaluation of the code block, @emph{both} the code and the +results of the code block evaluation, or @emph{none}. Org defaults to +exporting @emph{code} for most languages. For some languages, such as +@code{ditaa}, Org defaults to @emph{results}. To export just the body of +code blocks, @pxref{Literal examples}. To selectively export sub-trees of +an Org document, @pxref{Exporting}. -The @code{:exports} header argument can be used to specify export -behavior: +The @code{:exports} header arguments control exporting code blocks only and +not inline code: @subsubheading Header arguments: @table @code +@cindex @code{:exports}, src header argument @item :exports code -The default in most languages. The body of the code block is exported, as -described in @ref{Literal examples}. +This is the default for most languages where the body of the code block is +exported. See @ref{Literal examples} for more. @item :exports results -The code block will be evaluated and the results will be placed in the -Org mode buffer for export, either updating previous results of the code -block located anywhere in the buffer or, if no previous results exist, -placing the results immediately after the code block. The body of the code -block will not be exported. +On export, Org includes only the results and not the code block. After each +evaluation, Org inserts the results after the end of code block in the Org +buffer. By default, Org replaces any previous results. Org can also append +results. @item :exports both -Both the code block and its results will be exported. +Org exports both the code block and the results. @item :exports none -Neither the code block nor its results will be exported. +Org does not export the code block nor the results. @end table -It is possible to inhibit the evaluation of code blocks during export. -Setting the @code{org-export-babel-evaluate} variable to @code{nil} will -ensure that no code blocks are evaluated as part of the export process. This -can be useful in situations where potentially untrusted Org mode files are -exported in an automated fashion, for example when Org mode is used as the -markup language for a wiki. It is also possible to set this variable to -@code{'inline-only}. In that case, only inline code blocks will be -evaluated, in order to insert their results. Non-inline code blocks are -assumed to have their results already inserted in the buffer by manual -evaluation. This setting is useful to avoid expensive recalculations during -export, not to provide security. - -@comment node-name, next, previous, up -@comment Extracting source code, Evaluating code blocks, Exporting code blocks, Working With Source Code -@node Extracting source code, Evaluating code blocks, Exporting code blocks, Working With Source Code +@vindex org-export-babel-evaluate +To stop Org from evaluating code blocks during export, set +@code{org-export-babel-evaluate} variable to @code{nil}. + +Turning off evaluation comes in handy when batch processing. For example, +markup languages for wikis, which have a high risk of untrusted code. +Stopping code block evaluation also stops evaluation of all header arguments +of the code block. This may not be desirable in some circumstances. So +during export, to allow evaluation of just the header arguments but not any +code evaluation in the source block, set @code{:eval never-export} +(@pxref{eval}). + +To evaluate just the inline code blocks, set @code{org-export-babel-evaluate} +to @code{inline-only}. Isolating the option to allow inline evaluations +separate from @samp{src} code block evaluations during exports is not for +security but for avoiding any delays due to recalculations, such as calls to +a remote database. + +Org never evaluates code blocks in commented sub-trees when exporting +(@pxref{Comment lines}). On the other hand, Org does evaluate code blocks in +sub-trees excluded from export (@pxref{Export settings}). + +@node Extracting source code @section Extracting source code @cindex tangling @cindex source code, extracting @cindex code block, extracting source code -Creating pure source code files by extracting code from source blocks is -referred to as ``tangling''---a term adopted from the literate programming -community. During ``tangling'' of code blocks their bodies are expanded -using @code{org-babel-expand-src-block} which can expand both variable and -``noweb'' style references (see @ref{Noweb reference syntax}). +Extracting source code from code blocks is a basic task in literate +programming. Org has features to make this easy. In literate programming +parlance, documents on creation are @emph{woven} with code and documentation, +and on export, the code is @emph{tangled} for execution by a computer. Org +facilitates weaving and tangling for producing, maintaining, sharing, and +exporting literate programming documents. Org provides extensive +customization options for extracting source code. + +When Org tangles @samp{src} code blocks, it expands, merges, and transforms +them. Then Org recomposes them into one or more separate files, as +configured through the options. During this @emph{tangling} process, Org +expands variables in the source code, and resolves any ``noweb'' style +references (@pxref{Noweb reference syntax}). @subsubheading Header arguments @table @code +@cindex @code{:tangle}, src header argument @item :tangle no -The default. The code block is not included in the tangled output. +By default, Org does not tangle the @samp{src} code block on export. @item :tangle yes -Include the code block in the tangled output. The output file name is the -name of the org file with the extension @samp{.org} replaced by the extension -for the block language. +Org extracts the contents of the code block for the tangled output. By +default, the output file name is the same as the Org file but with a file +extension derived from the language identifier of the @samp{src} code block. @item :tangle filename -Include the code block in the tangled output to file @samp{filename}. +Override the default file name with this one for the tangled output. @end table @kindex C-c C-v t @@ -14209,7 +15167,7 @@ Include the code block in the tangled output to file @samp{filename}. @item org-babel-tangle Tangle the current file. Bound to @kbd{C-c C-v t}. -With prefix argument only tangle the current code block. +With prefix argument only tangle the current @samp{src} code block. @item org-babel-tangle-file Choose a file to tangle. Bound to @kbd{C-c C-v f}. @end table @@ -14218,72 +15176,67 @@ Choose a file to tangle. Bound to @kbd{C-c C-v f}. @table @code @item org-babel-post-tangle-hook -This hook is run from within code files tangled by @code{org-babel-tangle}. -Example applications could include post-processing, compilation or evaluation -of tangled code files. +This hook runs from within code tangled by @code{org-babel-tangle}, making it +suitable for post-processing, compilation, and evaluation of code in the +tangled files. @end table @subsubheading Jumping between code and Org -When tangling code from an Org-mode buffer to a source code file, you'll -frequently find yourself viewing the file of tangled source code (e.g., many -debuggers point to lines of the source code file). It is useful to be able -to navigate from the tangled source to the Org-mode buffer from which the -code originated. +Debuggers normally link errors and messages back to the source code. But for +tangled files, we want to link back to the Org file, not to the tangled +source file. To make this extra jump, Org uses +@code{org-babel-tangle-jump-to-org} function with two additional source code +block header arguments: One, set @code{padline} (@pxref{padline}) to true +(the default setting). Two, set @code{comments} (@pxref{comments}) to +@code{link}, which makes Org insert links to the Org file. -The @code{org-babel-tangle-jump-to-org} function provides this jumping from -code to Org-mode functionality. Two header arguments are required for -jumping to work, first the @code{padline} (@ref{padline}) option must be set -to true (the default setting), second the @code{comments} (@ref{comments}) -header argument must be set to @code{links}, which will insert comments into -the source code buffer which point back to the original Org-mode file. - -@node Evaluating code blocks, Library of Babel, Extracting source code, Working With Source Code +@node Evaluating code blocks @section Evaluating code blocks @cindex code block, evaluating @cindex source code, evaluating @cindex #+RESULTS -Code blocks can be evaluated@footnote{Whenever code is evaluated there is a -potential for that code to do harm. Org mode provides safeguards to ensure -that code is only evaluated after explicit confirmation from the user. For -information on these safeguards (and on how to disable them) see @ref{Code -evaluation security}.} and the results of evaluation optionally placed in the -Org mode buffer. The results of evaluation are placed following a line that -begins by default with @code{#+RESULTS} and optionally a cache identifier -and/or the name of the evaluated code block. The default value of -@code{#+RESULTS} can be changed with the customizable variable -@code{org-babel-results-keyword}. - -By default, the evaluation facility is only enabled for Lisp code blocks -specified as @code{emacs-lisp}. However, source code blocks in many languages -can be evaluated within Org mode (see @ref{Languages} for a list of supported -languages and @ref{Structure of code blocks} for information on the syntax -used to define a code block). +A note about security: With code evaluation comes the risk of harm. Org +safeguards by prompting for user's permission before executing any code in +the source block. To customize this safeguard (or disable it) see @ref{Code +evaluation security}. + +Org captures the results of the @samp{src} code block evaluation and inserts +them in the Org file, right after the @samp{src} code block. The insertion +point is after a newline and the @code{#+RESULTS} label. Org creates the +@code{#+RESULTS} label if one is not already there. + +By default, Org enables only @code{emacs-lisp} @samp{src} code blocks for +execution. See @ref{Languages} for identifiers to enable other languages. @kindex C-c C-c -There are a number of ways to evaluate code blocks. The simplest is to press -@kbd{C-c C-c} or @kbd{C-c C-v e} with the point on a code block@footnote{The -option @code{org-babel-no-eval-on-ctrl-c-ctrl-c} can be used to remove code -evaluation from the @kbd{C-c C-c} key binding.}. This will call the -@code{org-babel-execute-src-block} function to evaluate the block and insert -its results into the Org mode buffer. -@cindex #+CALL +Org provides many ways to execute @samp{src} code blocks. @kbd{C-c C-c} or +@kbd{C-c C-v e} with the point on a @samp{src} code block@footnote{The option +@code{org-babel-no-eval-on-ctrl-c-ctrl-c} can be used to remove code +evaluation from the @kbd{C-c C-c} key binding.} calls the +@code{org-babel-execute-src-block} function, which executes the code in the +block, collects the results, and inserts them in the buffer. -It is also possible to evaluate named code blocks from anywhere in an Org -mode buffer or an Org mode table. Live code blocks located in the current -Org mode buffer or in the ``Library of Babel'' (see @ref{Library of Babel}) -can be executed. Named code blocks can be executed with a separate -@code{#+CALL:} line or inline within a block of text. +@cindex #+CALL +By calling a named code block@footnote{Actually, the constructs call_() +and src_@{@} are not evaluated when they appear in a keyword line +(i.e. lines starting with @code{#+KEYWORD:}, @pxref{In-buffer settings}).} +from an Org mode buffer or a table. Org can call the named @samp{src} code +blocks from the current Org mode buffer or from the ``Library of Babel'' +(@pxref{Library of Babel}). Whether inline syntax or the @code{#+CALL:} +syntax is used, the result is wrapped based on the variable +@code{org-babel-inline-result-wrap}, which by default is set to @code{"=%s="} +to produce verbatim text suitable for markup. -The syntax of the @code{#+CALL:} line is +The syntax for @code{#+CALL:} is @example #+CALL: () #+CALL: []() @end example -The syntax for inline evaluation of named code blocks is +The syntax for inline named code block is @example ... call_() ... @@ -14292,98 +15245,88 @@ The syntax for inline evaluation of named code blocks is @table @code @item -The name of the code block to be evaluated (see @ref{Structure of code blocks}). +This is the name of the code block to be evaluated (@pxref{Structure of +code blocks}). @item -Arguments specified in this section will be passed to the code block. These -arguments use standard function call syntax, rather than -header argument syntax. For example, a @code{#+CALL:} line that passes the -number four to a code block named @code{double}, which declares the header -argument @code{:var n=2}, would be written as @code{#+CALL: double(n=4)}. +Org passes arguments to the code block using standard function call syntax. +For example, a @code{#+CALL:} line that passes @samp{4} to a code block named +@code{double}, which declares the header argument @code{:var n=2}, would be +written as @code{#+CALL: double(n=4)}. Note how this function call syntax is +different from the header argument syntax. @item -Inside header arguments are passed through and applied to the named code -block. These arguments use header argument syntax rather than standard -function call syntax. Inside header arguments affect how the code block is -evaluated. For example, @code{[:results output]} will collect the results of -everything printed to @code{STDOUT} during execution of the code block. +Org passes inside header arguments to the named @samp{src} code block using +the header argument syntax. Inside header arguments apply to code block +evaluation. For example, @code{[:results output]} collects results printed +to @code{STDOUT} during code execution of that block. Note how this header +argument syntax is different from the function call syntax. @item -End header arguments are applied to the calling instance and do not affect -evaluation of the named code block. They affect how the results are -incorporated into the Org mode buffer and how the call line is exported. For -example, @code{:results html} will insert the results of the call line -evaluation in the Org buffer, wrapped in a @code{BEGIN_HTML:} block. - -For more examples of passing header arguments to @code{#+CALL:} lines see -@ref{Header arguments in function calls}. +End header arguments affect the results returned by the code block. For +example, @code{:results html} wraps the results in a @code{BEGIN_EXPORT html} +block before inserting the results in the Org buffer. + +For more examples of header arguments for @code{#+CALL:} lines, +@pxref{Arguments in function calls}. @end table -@node Library of Babel, Languages, Evaluating code blocks, Working With Source Code +@node Library of Babel @section Library of Babel @cindex babel, library of @cindex source code, library @cindex code block, library -The ``Library of Babel'' consists of code blocks that can be called from any -Org mode file. Code blocks defined in the ``Library of Babel'' can be called -remotely as if they were in the current Org mode buffer (see @ref{Evaluating -code blocks} for information on the syntax of remote code block evaluation). - - -The central repository of code blocks in the ``Library of Babel'' is housed -in an Org mode file located in the @samp{contrib} directory of Org mode. - -Users can add code blocks they believe to be generally useful to their -``Library of Babel.'' The code blocks can be stored in any Org mode file and -then loaded into the library with @code{org-babel-lob-ingest}. - +The ``Library of Babel'' is a collection of code blocks. Like a function +library, these code blocks can be called from other Org files. This +collection is in a repository file in Org mode format in the @samp{doc} +directory of Org mode installation. For remote code block evaluation syntax, +@pxref{Evaluating code blocks}. @kindex C-c C-v i -Code blocks located in any Org mode file can be loaded into the ``Library of -Babel'' with the @code{org-babel-lob-ingest} function, bound to @kbd{C-c C-v -i}. +For any user to add code to the library, first save the code in regular +@samp{src} code blocks of an Org file, and then load the Org file with +@code{org-babel-lob-ingest}, which is bound to @kbd{C-c C-v i}. -@node Languages, Header arguments, Library of Babel, Working With Source Code +@node Languages @section Languages @cindex babel, languages @cindex source code, languages @cindex code block, languages -Code blocks in the following languages are supported. +Org supports the following languages for the @samp{src} code blocks: -@multitable @columnfractions 0.28 0.3 0.22 0.2 -@item @b{Language} @tab @b{Identifier} @tab @b{Language} @tab @b{Identifier} +@multitable @columnfractions 0.25 0.25 0.25 0.25 +@headitem @b{Language} @tab @b{Identifier} @tab @b{Language} @tab @b{Identifier} @item Asymptote @tab asymptote @tab Awk @tab awk -@item Emacs Calc @tab calc @tab C @tab C -@item C++ @tab C++ @tab Clojure @tab clojure -@item CSS @tab css @tab ditaa @tab ditaa -@item Graphviz @tab dot @tab Emacs Lisp @tab emacs-lisp +@item C @tab C @tab C++ @tab C++ +@item Clojure @tab clojure @tab CSS @tab css +@item D @tab d @tab ditaa @tab ditaa +@item Graphviz @tab dot @tab Emacs Calc @tab calc +@item Emacs Lisp @tab emacs-lisp @tab Fortran @tab fortran @item gnuplot @tab gnuplot @tab Haskell @tab haskell -@item Java @tab java @tab @tab -@item Javascript @tab js @tab LaTeX @tab latex -@item Ledger @tab ledger @tab Lisp @tab lisp -@item Lilypond @tab lilypond @tab MATLAB @tab matlab +@item Java @tab java @tab Javascript @tab js +@item LaTeX @tab latex @tab Ledger @tab ledger +@item Lisp @tab lisp @tab Lilypond @tab lilypond +@item Lua @tab lua @tab MATLAB @tab matlab @item Mscgen @tab mscgen @tab Objective Caml @tab ocaml @item Octave @tab octave @tab Org mode @tab org @item Oz @tab oz @tab Perl @tab perl -@item Plantuml @tab plantuml @tab Python @tab python -@item R @tab R @tab Ruby @tab ruby -@item Sass @tab sass @tab Scheme @tab scheme -@item GNU Screen @tab screen @tab shell @tab sh +@item Plantuml @tab plantuml @tab Processing.js @tab processing +@item Python @tab python @tab R @tab R +@item Ruby @tab ruby @tab Sass @tab sass +@item Scheme @tab scheme @tab GNU Screen @tab screen +@item Sed @tab sed @tab shell @tab sh @item SQL @tab sql @tab SQLite @tab sqlite @end multitable -Language-specific documentation is available for some languages. If -available, it can be found at +Additional documentation for some languages are at @uref{http://orgmode.org/worg/org-contrib/babel/languages.html}. -The option @code{org-babel-load-languages} controls which languages are -enabled for evaluation (by default only @code{emacs-lisp} is enabled). This -variable can be set using the customization interface or by adding code like -the following to your emacs configuration. +By default, only @code{emacs-lisp} is enabled for evaluation. To enable or +disable other languages, customize the @code{org-babel-load-languages} +variable either through the Emacs customization interface, or by adding code +to the init file as shown next: -@quotation -The following disables @code{emacs-lisp} evaluation and enables evaluation of -@code{R} code blocks. -@end quotation +In this example, evaluation is disabled for @code{emacs-lisp}, and enabled +for @code{R}. @lisp (org-babel-do-load-languages @@ -14392,55 +15335,54 @@ The following disables @code{emacs-lisp} evaluation and enables evaluation of (R . t))) @end lisp -It is also possible to enable support for a language by loading the related -elisp file with @code{require}. - -@quotation -The following adds support for evaluating @code{clojure} code blocks. -@end quotation +Note that this is not the only way to enable a language. Org also enables +languages when loaded with @code{require} statement. For example, the +following enables execution of @code{clojure} code blocks: @lisp (require 'ob-clojure) @end lisp -@node Header arguments, Results of evaluation, Languages, Working With Source Code +@node Header arguments @section Header arguments @cindex code block, header arguments @cindex source code, block header arguments -Code block functionality can be configured with header arguments. This -section provides an overview of the use of header arguments, and then -describes each header argument in detail. +Details of configuring header arguments are shown here. @menu * Using header arguments:: Different ways to set header arguments * Specific header arguments:: List of header arguments @end menu -@node Using header arguments, Specific header arguments, Header arguments, Header arguments +@node Using header arguments @subsection Using header arguments -The values of header arguments can be set in several way. When the header -arguments in each layer have been determined, they are combined in order from -the first, least specific (having the lowest priority) up to the last, most -specific (having the highest priority). A header argument with a higher -priority replaces the same header argument specified at lower priority. +Since header arguments can be set in several ways, Org prioritizes them in +case of overlaps or conflicts by giving local settings a higher priority. +Header values in function calls, for example, override header values from +global defaults. @menu -* System-wide header arguments:: Set global default values -* Language-specific header arguments:: Set default values by language -* Header arguments in Org mode properties:: Set default values for a buffer or heading -* Language-specific header arguments in Org mode properties:: Set language-specific default values for a buffer or heading -* Code block specific header arguments:: The most common way to set values -* Header arguments in function calls:: The most specific level +* System-wide header arguments:: Set globally, language-specific +* Language-specific header arguments:: Set in the Org file's headers +* Header arguments in Org mode properties:: Set in the Org file +* Language-specific mode properties:: +* Code block specific header arguments:: The most commonly used method +* Arguments in function calls:: The most specific level, takes highest priority @end menu -@node System-wide header arguments, Language-specific header arguments, Using header arguments, Using header arguments +@node System-wide header arguments @subsubheading System-wide header arguments @vindex org-babel-default-header-args System-wide values of header arguments can be specified by adapting the @code{org-babel-default-header-args} variable: +@cindex @code{:session}, src header argument +@cindex @code{:results}, src header argument +@cindex @code{:exports}, src header argument +@cindex @code{:cache}, src header argument +@cindex @code{:noweb}, src header argument @example :session => "none" :results => "replace" @@ -14449,10 +15391,8 @@ System-wide values of header arguments can be specified by adapting the :noweb => "no" @end example -For example, the following example could be used to set the default value of -@code{:noweb} header arguments to @code{yes}. This would have the effect of -expanding @code{:noweb} references by default when evaluating source code -blocks. +This example sets @code{:noweb} header arguments to @code{yes}, which makes +Org expand @code{:noweb} references by default. @lisp (setq org-babel-default-header-args @@ -14460,48 +15400,40 @@ blocks. (assq-delete-all :noweb org-babel-default-header-args))) @end lisp -@node Language-specific header arguments, Header arguments in Org mode properties, System-wide header arguments, Using header arguments +@node Language-specific header arguments @subsubheading Language-specific header arguments -Each language can define its own set of default header arguments in variable -@code{org-babel-default-header-args:}, where @code{} is the name -of the language. See the language-specific documentation available online at -@uref{http://orgmode.org/worg/org-contrib/babel}. +Each language can have separate default header arguments by customizing the +variable @code{org-babel-default-header-args:}, where @code{} is +the name of the language. For details, see the language-specific online +documentation at @uref{http://orgmode.org/worg/org-contrib/babel}. -@node Header arguments in Org mode properties, Language-specific header arguments in Org mode properties, Language-specific header arguments, Using header arguments +@node Header arguments in Org mode properties @subsubheading Header arguments in Org mode properties -Buffer-wide header arguments may be specified as properties through the use -of @code{#+PROPERTY:} lines placed anywhere in an Org mode file (see -@ref{Property syntax}). +For header arguments applicable to the buffer, use @code{#+PROPERTY:} lines +anywhere in the Org mode file (@pxref{Property syntax}). -For example the following would set @code{session} to @code{*R*} (only for R -code blocks), and @code{results} to @code{silent} for every code block in the -buffer, ensuring that all execution took place in the same session, and no -results would be inserted into the buffer. +The following example sets only for @samp{R} code blocks to @code{session}, +making all the @samp{R} code blocks execute in the same session. Setting +@code{results} to @code{silent} ignores the results of executions for all +blocks, not just @samp{R} code blocks; no results inserted for any block. @example #+PROPERTY: header-args:R :session *R* #+PROPERTY: header-args :results silent @end example -Header arguments read from Org mode properties can also be set on a -per-subtree basis using property drawers (see @ref{Property syntax}). @vindex org-use-property-inheritance -When properties are used to set default header arguments, they are always -looked up with inheritance, regardless of the value of -@code{org-use-property-inheritance}. Properties are evaluated as seen by the -outermost call or source block.@footnote{The deprecated syntax for default -header argument properties, using the name of the header argument as a -property name directly, evaluates the property as seen by the corresponding -source block definition. This behavior has been kept for backwards -compatibility.} +Header arguments set through Org's property drawers (@pxref{Property syntax}) +apply at the sub-tree level on down. Since these property drawers can appear +anywhere in the file hierarchy, Org uses outermost call or source block to +resolve the values. Org ignores @code{org-use-property-inheritance} setting. -In the following example the value of -the @code{:cache} header argument will default to @code{yes} in all code -blocks in the subtree rooted at the following heading: +In this example, @code{:cache} defaults to @code{yes} for all code blocks in +the sub-tree starting with @samp{sample header}. @example -* outline header +* sample header :PROPERTIES: :header-args: :cache yes :END: @@ -14509,17 +15441,16 @@ blocks in the subtree rooted at the following heading: @kindex C-c C-x p @vindex org-babel-default-header-args -Properties defined in this way override the properties set in -@code{org-babel-default-header-args} and are applied for all activated -languages. It is convenient to use the @code{org-set-property} function -bound to @kbd{C-c C-x p} to set properties in Org mode documents. +Properties defined through @code{org-set-property} function, bound to +@kbd{C-c C-x p}, apply to all active languages. They override properties set +in @code{org-babel-default-header-args}. -@node Language-specific header arguments in Org mode properties, Code block specific header arguments, Header arguments in Org mode properties, Using header arguments -@subsubheading Language-specific header arguments in Org mode properties +@node Language-specific mode properties +@subsubheading Language-specific mode properties Language-specific header arguments are also read from properties -@code{header-args:} where @code{} is the name of the language -targeted. As an example +@code{header-args:} where @code{} is the language identifier. +For example, @example * Heading @@ -14533,24 +15464,21 @@ targeted. As an example :END: @end example -would independently set a default session header argument for R and clojure -for calls and source blocks under subtree ``Heading'' and change to a -different clojure setting for evaluations under subtree ``Subheading'', while -the R session is inherited from ``Heading'' and therefore unchanged. +would force separate sessions for clojure blocks in Heading and Subheading, +but use the same session for all @samp{R} blocks. Blocks in Subheading +inherit settings from Heading. -@node Code block specific header arguments, Header arguments in function calls, Language-specific header arguments in Org mode properties, Using header arguments +@node Code block specific header arguments @subsubheading Code block specific header arguments -The most common way to assign values to header arguments is at the -code block level. This can be done by listing a sequence of header -arguments and their values as part of the @code{#+BEGIN_SRC} line. -Properties set in this way override both the values of -@code{org-babel-default-header-args} and header arguments specified as -properties. In the following example, the @code{:results} header argument -is set to @code{silent}, meaning the results of execution will not be -inserted in the buffer, and the @code{:exports} header argument is set to -@code{code}, meaning only the body of the code block will be -preserved on export to HTML or @LaTeX{}. +Header arguments are most commonly set at the @samp{src} code block level, on +the @code{#+BEGIN_SRC} line. Arguments set at this level take precedence +over those set in the @code{org-babel-default-header-args} variable, and also +those set as header properties. + +In the following example, setting @code{results} to @code{silent} makes it +ignore results of the code execution. Setting @code{:exports} to @code{code} +exports only the body of the @samp{src} code block to HTML or @LaTeX{}.: @example #+NAME: factorial @@ -14559,93 +15487,93 @@ fac 0 = 1 fac n = n * fac (n-1) #+END_SRC @end example -Similarly, it is possible to set header arguments for inline code blocks + +The same header arguments in an inline @samp{src} code block: @example src_haskell[:exports both]@{fac 5@} @end example -Code block header arguments can span multiple lines using @code{#+HEADER:} or -@code{#+HEADERS:} lines preceding a code block or nested between the -@code{#+NAME:} line and the @code{#+BEGIN_SRC} line of a named code block. +Code block header arguments can span multiple lines using @code{#+HEADER:} on +each line. Note that Org currently accepts the plural spelling of +@code{#+HEADER:} only as a convenience for backward-compatibility. It may be +removed at some point. + @cindex #+HEADER: -@cindex #+HEADERS: -Multi-line header arguments on an un-named code block: +Multi-line header arguments on an unnamed @samp{src} code block: @example - #+HEADERS: :var data1=1 - #+BEGIN_SRC emacs-lisp :var data2=2 +#+HEADER: :var data1=1 +#+BEGIN_SRC emacs-lisp :var data2=2 (message "data1:%S, data2:%S" data1 data2) - #+END_SRC +#+END_SRC - #+RESULTS: - : data1:1, data2:2 +#+RESULTS: +: data1:1, data2:2 @end example -Multi-line header arguments on a named code block: +Multi-line header arguments on a named @samp{src} code block: @example - #+NAME: named-block - #+HEADER: :var data=2 - #+BEGIN_SRC emacs-lisp - (message "data:%S" data) - #+END_SRC +#+NAME: named-block +#+HEADER: :var data=2 +#+BEGIN_SRC emacs-lisp + (message "data:%S" data) +#+END_SRC - #+RESULTS: named-block - : data:2 +#+RESULTS: named-block + : data:2 @end example -@node Header arguments in function calls, , Code block specific header arguments, Using header arguments -@comment node-name, next, previous, up -@subsubheading Header arguments in function calls +@node Arguments in function calls +@subsubheading Arguments in function calls -At the most specific level, header arguments for ``Library of Babel'' or -@code{#+CALL:} lines can be set as shown in the two examples below. For more -information on the structure of @code{#+CALL:} lines see @ref{Evaluating code -blocks}. +Header arguments in function calls are the most specific and override all +other settings in case of an overlap. They get the highest priority. Two +@code{#+CALL:} examples are shown below. For the complete syntax of +@code{#+CALL:} lines, see @ref{Evaluating code blocks}. -The following will apply the @code{:exports results} header argument to the +In this example, @code{:exports results} header argument is applied to the evaluation of the @code{#+CALL:} line. @example #+CALL: factorial(n=5) :exports results @end example -The following will apply the @code{:session special} header argument to the -evaluation of the @code{factorial} code block. +In this example, @code{:session special} header argument is applied to the +evaluation of @code{factorial} code block. @example #+CALL: factorial[:session special](n=5) @end example -@node Specific header arguments, , Using header arguments, Header arguments +@node Specific header arguments @subsection Specific header arguments -Header arguments consist of an initial colon followed by the name of the -argument in lowercase letters. The following header arguments are defined: +Org comes with many header arguments common to all languages. New header +arguments are added for specific languages as they become available for use +in @samp{src} code blocks. A header argument is specified with an initial +colon followed by the argument's name in lowercase. Common header arguments +are: @menu -* var:: Pass arguments to code blocks -* results:: Specify the type of results and how they will - be collected and handled -* file:: Specify a path for file output +* var:: Pass arguments to @samp{src} code blocks +* results:: Specify results type; how to collect +* file:: Specify a path for output file * file-desc:: Specify a description for file results -* dir:: Specify the default (possibly remote) - directory for code block execution -* exports:: Export code and/or results -* tangle:: Toggle tangling and specify file name -* mkdirp:: Toggle creation of parent directories of target - files during tangling -* comments:: Toggle insertion of comments in tangled - code files -* padline:: Control insertion of padding lines in tangled - code files -* no-expand:: Turn off variable assignment and noweb - expansion during tangling +* file-ext:: Specify an extension for file output +* output-dir:: Specify a directory for output file +* dir:: Specify the default directory for code block execution +* exports:: Specify exporting code, results, both, none +* tangle:: Toggle tangling; or specify file name +* mkdirp:: Toggle for parent directory creation for target files during tangling +* comments:: Toggle insertion of comments in tangled code files +* padline:: Control insertion of padding lines in tangled code files +* no-expand:: Turn off variable assignment and noweb expansion during tangling * session:: Preserve the state of code evaluation * noweb:: Toggle expansion of noweb references * noweb-ref:: Specify block's noweb reference resolution target -* noweb-sep:: String used to separate noweb references +* noweb-sep:: String to separate noweb references * cache:: Avoid re-evaluating unchanged code blocks * sep:: Delimiter for writing tabular results outside Org * hlines:: Handle horizontal lines in tables @@ -14655,45 +15583,46 @@ argument in lowercase letters. The following header arguments are defined: * tangle-mode:: Set permission of tangled files * eval:: Limit evaluation of specific code blocks * wrap:: Mark source block evaluation results -* post:: Post processing of code block results -* prologue:: Text to prepend to code block body -* epilogue:: Text to append to code block body +* post:: Post processing of results of code block evaluation +* prologue:: Text to prepend to body of code block +* epilogue:: Text to append to body of code block @end menu -Additional header arguments are defined on a language-specific basis, see -@ref{Languages}. +For language-specific header arguments, see @ref{Languages}. -@node var, results, Specific header arguments, Specific header arguments +@node var @subsubsection @code{:var} -The @code{:var} header argument is used to pass arguments to code blocks. -The specifics of how arguments are included in a code block vary by language; -these are addressed in the language-specific documentation. However, the -syntax used to specify arguments is the same across all languages. In every -case, variables require a default value when they are declared. +@cindex @code{:var}, src header argument +Use @code{:var} for passing arguments to @samp{src} code blocks. The +specifics of variables in @samp{src} code blocks vary by the source language +and are covered in the language-specific documentation. The syntax for +@code{:var}, however, is the same for all languages. This includes declaring +a variable, and assigning a default value. -The values passed to arguments can either be literal values, references, or -Emacs Lisp code (see @ref{var, Emacs Lisp evaluation of variables}). -References include anything in the Org mode file that takes a @code{#+NAME:} -or @code{#+RESULTS:} line: tables, lists, @code{#+BEGIN_EXAMPLE} blocks, -other code blocks and the results of other code blocks. +Arguments can take values as literals, or as references, or even as Emacs +Lisp code (@pxref{var, Emacs Lisp evaluation of variables}). References are +names from the Org file from the lines @code{#+NAME:} or @code{#+RESULTS:}. +References can also refer to tables, lists, @code{#+BEGIN_EXAMPLE} blocks, +other types of @samp{src} code blocks, or the results of execution of +@samp{src} code blocks. -Note: When a reference is made to another code block, the referenced block -will be evaluated unless it has current cached results (see @ref{cache}). +For better performance, Org can cache results of evaluations. But caching +comes with severe limitations (@pxref{cache}). -Argument values can be indexed in a manner similar to arrays (see @ref{var, -Indexable variable values}). +Argument values are indexed like arrays (@pxref{var, Indexable variable +values}). -The following syntax is used to pass arguments to code blocks using the -@code{:var} header argument. +The following syntax is used to pass arguments to @samp{src} code blocks +using the @code{:var} header argument. @example :var name=assign @end example -The argument, @code{assign}, can either be a literal value, such as a string -@samp{"string"} or a number @samp{9}, or a reference to a table, a list, a -literal example, another code block (with or without arguments), or the -results of evaluating another code block. +The @code{assign} is a literal value, such as a string @samp{"string"}, a +number @samp{9}, a reference to a table, a list, a literal example, another +code block (with or without arguments), or the results from evaluating a code +block. Here are examples of passing values by reference: @@ -14719,8 +15648,8 @@ an Org mode table named with either a @code{#+NAME:} line @end example @item list -a simple list named with a @code{#+NAME:} line (note that nesting is not -carried through to the source code block) +a simple list named with a @code{#+NAME:} line. Note that only the top level +list items are passed along. Nested list items are ignored. @example #+NAME: example-list @@ -14751,9 +15680,9 @@ optionally followed by parentheses @end example @item code block with arguments -a code block name, as assigned by @code{#+NAME:}, followed by parentheses and -optional arguments passed within the parentheses following the -code block name using standard function call syntax +a @samp{src} code block name, as assigned by @code{#+NAME:}, followed by +parentheses and optional arguments passed within the parentheses following +the @samp{src} code block name using standard function call syntax @example #+NAME: double @@ -14765,7 +15694,7 @@ code block name using standard function call syntax : 16 #+NAME: squared -#+BEGIN_SRC emacs-lisp :var input=double(input=1) +#+BEGIN_SRC emacs-lisp :var input=double(input=2) (* input input) #+END_SRC @@ -14797,14 +15726,14 @@ on two lines @end table @subsubheading Indexable variable values -It is possible to reference portions of variable values by ``indexing'' into -the variables. Indexes are 0 based with negative values counting back from -the end. If an index is separated by @code{,}s then each subsequent section -will index into the next deepest nesting or dimension of the value. Note -that this indexing occurs @emph{before} other table related header arguments -like @code{:hlines}, @code{:colnames} and @code{:rownames} are applied. The -following example assigns the last cell of the first row the table -@code{example-table} to the variable @code{data}: +Indexing variable values enables referencing portions of a variable. Indexes +are 0 based with negative values counting backwards from the end. If an +index is separated by @code{,}s then each subsequent section will index as +the next dimension. Note that this indexing occurs @emph{before} other +table-related header arguments are applied, such as @code{:hlines}, +@code{:colnames} and @code{:rownames}. The following example assigns the +last cell of the first row the table @code{example-table} to the variable +@code{data}: @example #+NAME: example-table @@ -14844,10 +15773,9 @@ to @code{data}. | 4 | d | @end example -Additionally, an empty index, or the single character @code{*}, are both -interpreted to mean the entire range and as such are equivalent to -@code{0:-1}, as shown in the following example in which the entire first -column is referenced. +To pick the entire range, use an empty index, or the single character +@code{*}. @code{0:-1} does the same thing. Example below shows how to +reference the first column only. @example #+NAME: example-table @@ -14864,9 +15792,9 @@ column is referenced. | 1 | 2 | 3 | 4 | @end example -It is possible to index into the results of code blocks as well as tables. -Any number of dimensions can be indexed. Dimensions are separated from one -another by commas, as shown in the following example. +Index referencing can be used for tables and code blocks. Index referencing +can handle any number of dimensions. Commas delimit multiple dimensions, as +shown below. @example #+NAME: 3D @@ -14886,14 +15814,13 @@ another by commas, as shown in the following example. @subsubheading Emacs Lisp evaluation of variables -Emacs lisp code can be used to initialize variable values. When a variable -value starts with @code{(}, @code{[}, @code{'} or @code{`} it will be -evaluated as Emacs Lisp and the result of the evaluation will be assigned as -the variable value. The following example demonstrates use of this -evaluation to reliably pass the file-name of the Org mode buffer to a code -block---note that evaluation of header arguments is guaranteed to take place -in the original Org mode file, while there is no such guarantee for -evaluation of the code block body. +Emacs lisp code can set the values for variables. To differentiate a value +from lisp code, Org interprets any value starting with @code{(}, @code{[}, +@code{'} or @code{`} as Emacs Lisp code. The result of evaluating that code +is then assigned to the value of that variable. The following example shows +how to reliably query and pass file name of the Org mode buffer to a code +block using headers. We need reliability here because the file's name could +change once the code in the block starts executing. @example #+BEGIN_SRC sh :var filename=(buffer-file-name) :exports both @@ -14901,14 +15828,14 @@ evaluation of the code block body. #+END_SRC @end example -Note that values read from tables and lists will not be evaluated as -Emacs Lisp, as shown in the following example. +Note that values read from tables and lists will not be mistakenly evaluated +as Emacs Lisp code, as illustrated in the following example. @example #+NAME: table | (a b c) | -#+HEADERS: :var data=table[0,0] +#+HEADER: :var data=table[0,0] #+BEGIN_SRC perl $data #+END_SRC @@ -14917,167 +15844,171 @@ Emacs Lisp, as shown in the following example. : (a b c) @end example -@node results, file, var, Specific header arguments +@node results @subsubsection @code{:results} +@cindex @code{:results}, src header argument -There are four classes of @code{:results} header argument. Only one option -per class may be supplied per code block. +There are four classes of @code{:results} header arguments. Each @samp{src} +code block can take only one option per class. @itemize @bullet @item -@b{collection} header arguments specify how the results should be collected -from the code block +@b{collection} for how the results should be collected from the @samp{src} +code block @item -@b{type} header arguments specify what type of result the code block will -return---which has implications for how they will be processed before -insertion into the Org mode buffer +@b{type} for which type of result the code block will return; affects how Org +processes and inserts results in the Org buffer @item -@b{format} header arguments specify what type of result the code block will -return---which has implications for how they will be inserted into the -Org mode buffer +@b{format} for the result; affects how Org processes and inserts results in +the Org buffer @item -@b{handling} header arguments specify how the results of evaluating the code -block should be handled. +@b{handling} for processing results after evaluation of the @samp{src} code +block @end itemize @subsubheading Collection -The following options are mutually exclusive, and specify how the results -should be collected from the code block. +Collection options specify the results. Choose one of the options; they are +mutually exclusive. @itemize @bullet @item @code{value} -This is the default. The result is the value of the last statement in the -code block. This header argument places the evaluation in functional -mode. Note that in some languages, e.g., Python, use of this result type -requires that a @code{return} statement be included in the body of the source -code block. E.g., @code{:results value}. +Default. Functional mode. Result is the value returned by the last +statement in the @samp{src} code block. Languages like Python may require an +explicit @code{return} statement in the @samp{src} code block. Usage +example: @code{:results value}. @item @code{output} -The result is the collection of everything printed to STDOUT during the -execution of the code block. This header argument places the -evaluation in scripting mode. E.g., @code{:results output}. +Scripting mode. Result is collected from STDOUT during execution of the code +in the @samp{src} code block. Usage example: @code{:results output}. @end itemize @subsubheading Type - -The following options are mutually exclusive and specify what type of results -the code block will return. By default, results are inserted as either a -table or scalar depending on their value. +Type tells what result types to expect from the execution of the code +block. Choose one of the options; they are mutually exclusive. The default +behavior is to automatically determine the result type. @itemize @bullet @item @code{table}, @code{vector} -The results should be interpreted as an Org mode table. If a single value is -returned, it will be converted into a table with one row and one column. -E.g., @code{:results value table}. +Interpret the results as an Org table. If the result is a single value, +create a table with one row and one column. Usage example: @code{:results +value table}. @item @code{list} -The results should be interpreted as an Org mode list. If a single scalar -value is returned it will be converted into a list with only one element. +Interpret the results as an Org list. If the result is a single value, +create a list of one element. @item @code{scalar}, @code{verbatim} -The results should be interpreted literally---they will not be -converted into a table. The results will be inserted into the Org mode -buffer as quoted text. E.g., @code{:results value verbatim}. +Interpret literally and insert as quoted text. Do not create a table. Usage +example: @code{:results value verbatim}. @item @code{file} -The results will be interpreted as the path to a file, and will be inserted -into the Org mode buffer as a file link. E.g., @code{:results value file}. +Interpret as path to a file. Inserts a link to the file. Usage example: +@code{:results value file}. @end itemize @subsubheading Format - -The following options are mutually exclusive and specify what type of results -the code block will return. By default, results are inserted according to the -type as specified above. +Format pertains to the type of the result returned by the @samp{src} code +block. Choose one of the options; they are mutually exclusive. The default +follows from the type specified above. @itemize @bullet @item @code{raw} -The results are interpreted as raw Org mode code and are inserted directly -into the buffer. If the results look like a table they will be aligned as -such by Org mode. E.g., @code{:results value raw}. +Interpreted as raw Org mode. Inserted directly into the buffer. Aligned if +it is a table. Usage example: @code{:results value raw}. @item @code{org} -The results are will be enclosed in a @code{BEGIN_SRC org} block. -They are not comma-escaped by default but they will be if you hit @kbd{TAB} -in the block and/or if you export the file. E.g., @code{:results value org}. +Results enclosed in a @code{BEGIN_SRC org} block. For comma-escape, either +@kbd{TAB} in the block, or export the file. Usage example: @code{:results +value org}. @item @code{html} -Results are assumed to be HTML and will be enclosed in a @code{BEGIN_HTML} -block. E.g., @code{:results value html}. +Results enclosed in a @code{BEGIN_EXPORT html} block. Usage example: +@code{:results value html}. @item @code{latex} -Results assumed to be @LaTeX{} and are enclosed in a @code{BEGIN_LaTeX} block. -E.g., @code{:results value latex}. +Results enclosed in a @code{BEGIN_EXPORT latex} block. Usage example: +@code{:results value latex}. @item @code{code} -Result are assumed to be parsable code and are enclosed in a code block. -E.g., @code{:results value code}. +Result enclosed in a @samp{src} code block. Useful for parsing. Usage +example: @code{:results value code}. @item @code{pp} -The result is converted to pretty-printed code and is enclosed in a code -block. This option currently supports Emacs Lisp, Python, and Ruby. E.g., +Result converted to pretty-print source code. Enclosed in a @samp{src} code +block. Languages supported: Emacs Lisp, Python, and Ruby. Usage example: @code{:results value pp}. @item @code{drawer} -The result is wrapped in a RESULTS drawer. This can be useful for -inserting @code{raw} or @code{org} syntax results in such a way that their -extent is known and they can be automatically removed or replaced. +Result wrapped in a RESULTS drawer. Useful for containing @code{raw} or +@code{org} results for later scripting and automated processing. Usage +example: @code{:results value drawer}. @end itemize @subsubheading Handling -The following results options indicate what happens with the -results once they are collected. +Handling options after collecting the results. @itemize @bullet @item @code{silent} -The results will be echoed in the minibuffer but will not be inserted into -the Org mode buffer. E.g., @code{:results output silent}. +Do not insert results in the Org mode buffer, but echo them in the +minibuffer. Usage example: @code{:results output silent}. @item @code{replace} -The default value. Any existing results will be removed, and the new results -will be inserted into the Org mode buffer in their place. E.g., -@code{:results output replace}. +Default. Insert results in the Org buffer. Remove previous results. Usage +example: @code{:results output replace}. @item @code{append} -If there are pre-existing results of the code block then the new results will -be appended to the existing results. Otherwise the new results will be -inserted as with @code{replace}. +Append results to the Org buffer. Latest results are at the bottom. Does +not remove previous results. Usage example: @code{:results output append}. @item @code{prepend} -If there are pre-existing results of the code block then the new results will -be prepended to the existing results. Otherwise the new results will be -inserted as with @code{replace}. +Prepend results to the Org buffer. Latest results are at the top. Does not +remove previous results. Usage example: @code{:results output prepend}. @end itemize -@node file, file-desc, results, Specific header arguments +@node file @subsubsection @code{:file} +@cindex @code{:file}, src header argument + +An external @code{:file} that saves the results of execution of the code +block. The @code{:file} is either a file name or two strings, where the +first is the file name and the second is the description. A link to the file +is inserted. It uses an Org mode style @code{[[file:]]} link (@pxref{Link +format}). Some languages, such as @samp{R}, @samp{dot}, @samp{ditaa}, and +@samp{gnuplot}, automatically wrap the source code in additional boilerplate +code. Such code wrapping helps recreate the output, especially graphics +output, by executing just the @code{:file} contents. + +@node file-desc +@subsubsection @code{:file-desc} -The header argument @code{:file} is used to specify an external file in which -to save code block results. After code block evaluation an Org mode style -@code{[[file:]]} link (see @ref{Link format}) to the file will be inserted -into the Org mode buffer. Some languages including R, gnuplot, dot, and -ditaa provide special handling of the @code{:file} header argument -automatically wrapping the code block body in the boilerplate code required -to save output to the specified file. This is often useful for saving -graphical output of a code block to the specified file. +A description of the results file. Org uses this description for the link +(see @ref{Link format}) it inserts in the Org file. If the @code{:file-desc} +has no value, Org will use file name for both the ``link'' and the +``description'' portion of the Org mode link. -The argument to @code{:file} should be either a string specifying the path to -a file, or a list of two strings in which case the first element of the list -should be the path to a file and the second a description for the link. +@node file-ext +@subsubsection @code{:file-ext} +@cindex @code{:file-ext}, src header argument -@node file-desc, dir, file, Specific header arguments -@subsubsection @code{:file-desc} +File name extension for the output file. Org generates the file's complete +name, and extension by combining @code{:file-ext}, @code{#+NAME:} of the +source block, and the @ref{output-dir} header argument. To override this +auto generated file name, use the @code{:file} header argument. -The value of the @code{:file-desc} header argument is used to provide a -description for file code block results which are inserted as Org mode links -(see @ref{Link format}). If the @code{:file-desc} header argument is given -with no value the link path will be placed in both the ``link'' and the -``description'' portion of the Org mode link. +@node output-dir +@subsubsection @code{:output-dir} +@cindex @code{:output-dir}, src header argument -@node dir, exports, file-desc, Specific header arguments +Specifies the @code{:output-dir} for the results file. Org accepts an +absolute path (beginning with @code{/}) or a relative directory (without +@code{/}). The value can be combined with @code{#+NAME:} of the source block +and @ref{file} or @ref{file-ext} header arguments. + +@node dir @subsubsection @code{:dir} and remote execution +@cindex @code{:dir}, src header argument While the @code{:file} header argument can be used to specify the path to the -output file, @code{:dir} specifies the default directory during code block -execution. If it is absent, then the directory associated with the current -buffer is used. In other words, supplying @code{:dir path} temporarily has -the same effect as changing the current directory with @kbd{M-x cd path RET}, and -then not supplying @code{:dir}. Under the surface, @code{:dir} simply sets -the value of the Emacs variable @code{default-directory}. +output file, @code{:dir} specifies the default directory during @samp{src} +code block execution. If it is absent, then the directory associated with +the current buffer is used. In other words, supplying @code{:dir path} +temporarily has the same effect as changing the current directory with +@kbd{M-x cd path RET}, and then not supplying @code{:dir}. Under the +surface, @code{:dir} simply sets the value of the Emacs variable +@code{default-directory}. -When using @code{:dir}, you should supply a relative path for file output -(e.g., @code{:file myfile.jpg} or @code{:file results/myfile.jpg}) in which -case that path will be interpreted relative to the default directory. +When using @code{:dir}, relative paths (for example, @code{:file myfile.jpg} +or @code{:file results/myfile.jpg}) become relative to the default directory. -In other words, if you want your plot to go into a folder called @file{Work} -in your home directory, you could use +For example, to save the plot file in the @samp{Work} folder of the home +directory (notice tilde is expanded): @example #+BEGIN_SRC R :file myplot.png :dir ~/Work @@ -15086,8 +16017,8 @@ matplot(matrix(rnorm(100), 10), type="l") @end example @subsubheading Remote execution -A directory on a remote machine can be specified using tramp file syntax, in -which case the code will be evaluated on the remote machine. An example is +To evaluate the @samp{src} code block on a remote machine, supply a remote s +directory name using @samp{Tramp} syntax. For example: @example #+BEGIN_SRC R :file plot.png :dir /scp:dand@@yakuba.princeton.edu: @@ -15095,189 +16026,196 @@ plot(1:10, main=system("hostname", intern=TRUE)) #+END_SRC @end example -Text results will be returned to the local Org mode buffer as usual, and file -output will be created on the remote machine with relative paths interpreted -relative to the remote directory. An Org mode link to the remote file will be -created. - -So, in the above example a plot will be created on the remote machine, -and a link of the following form will be inserted in the org buffer: +Org first captures the text results as usual for insertion in the Org file. +Then Org also inserts a link to the remote file, thanks to Emacs +@samp{Tramp}. Org constructs the remote path to the file name from +@code{:dir} and @code{default-directory}, as illustrated here: @example [[file:/scp:dand@@yakuba.princeton.edu:/home/dand/plot.png][plot.png]] @end example -Most of this functionality follows immediately from the fact that @code{:dir} -sets the value of the Emacs variable @code{default-directory}, thanks to -tramp. Those using XEmacs, or GNU Emacs prior to version 23 may need to -install tramp separately in order for these features to work correctly. -@subsubheading Further points +@subsubheading Some more warnings @itemize @bullet @item -If @code{:dir} is used in conjunction with @code{:session}, although it will -determine the starting directory for a new session as expected, no attempt is -currently made to alter the directory associated with an existing session. +When @code{:dir} is used with @code{:session}, Org sets the starting +directory for a new session. But Org will not alter the directory of an +already existing session. @item -@code{:dir} should typically not be used to create files during export with -@code{:exports results} or @code{:exports both}. The reason is that, in order -to retain portability of exported material between machines, during export -links inserted into the buffer will @emph{not} be expanded against @code{default -directory}. Therefore, if @code{default-directory} is altered using -@code{:dir}, it is probable that the file will be created in a location to -which the link does not point. +Do not use @code{:dir} with @code{:exports results} or with @code{:exports +both} to avoid Org inserting incorrect links to remote files. That is because +Org does not expand @code{default directory} to avoid some underlying +portability issues. @end itemize -@node exports, tangle, dir, Specific header arguments +@node exports @subsubsection @code{:exports} +@cindex @code{:exports}, src header argument -The @code{:exports} header argument specifies what should be included in HTML -or @LaTeX{} exports of the Org mode file. +The @code{:exports} header argument is to specify if that part of the Org +file is exported to, say, HTML or @LaTeX{} formats. Note that +@code{:exports} affects only @samp{src} code blocks and not inline code. @itemize @bullet @item @code{code} -The default. The body of code is included into the exported file. E.g., +The default. The body of code is included into the exported file. Example: @code{:exports code}. @item @code{results} -The result of evaluating the code is included in the exported file. E.g., -@code{:exports results}. +The results of evaluation of the code is included in the exported file. +Example: @code{:exports results}. @item @code{both} -Both the code and results are included in the exported file. E.g., -@code{:exports both}. +Both the code and results of evaluation are included in the exported file. +Example: @code{:exports both}. @item @code{none} -Nothing is included in the exported file. E.g., @code{:exports none}. +Neither the code nor the results of evaluation is included in the exported +file. Whether the code is evaluated at all depends on other +options. Example: @code{:exports none}. @end itemize -@node tangle, mkdirp, exports, Specific header arguments +@node tangle @subsubsection @code{:tangle} +@cindex @code{:tangle}, src header argument -The @code{:tangle} header argument specifies whether or not the code -block should be included in tangled extraction of source code files. +The @code{:tangle} header argument specifies if the @samp{src} code block is +exported to source file(s). @itemize @bullet @item @code{tangle} -The code block is exported to a source code file named after the full path -(including the directory) and file name (w/o extension) of the Org mode file. -E.g., @code{:tangle yes}. +Export the @samp{src} code block to source file. The file name for the +source file is derived from the name of the Org file, and the file extension +is derived from the source code language identifier. Example: @code{:tangle +yes}. @item @code{no} -The default. The code block is not exported to a source code file. -E.g., @code{:tangle no}. +The default. Do not extract the code a source code file. Example: +@code{:tangle no}. @item other -Any other string passed to the @code{:tangle} header argument is interpreted -as a path (directory and file name relative to the directory of the Org mode -file) to which the block will be exported. E.g., @code{:tangle path}. +Export the @samp{src} code block to source file whose file name is derived +from any string passed to the @code{:tangle} header argument. Org derives +the file name as being relative to the directory of the Org file's location. +Example: @code{:tangle path}. @end itemize -@node mkdirp, comments, tangle, Specific header arguments +@node mkdirp @subsubsection @code{:mkdirp} +@cindex @code{:mkdirp}, src header argument -The @code{:mkdirp} header argument can be used to create parent directories -of tangled files when missing. This can be set to @code{yes} to enable -directory creation or to @code{no} to inhibit directory creation. +The @code{:mkdirp} header argument creates parent directories for tangled +files if the directory does not exist. @code{yes} enables directory creation +and @code{no} inhibits directory creation. -@node comments, padline, mkdirp, Specific header arguments +@node comments @subsubsection @code{:comments} -By default code blocks are tangled to source-code files without any insertion -of comments beyond those which may already exist in the body of the code -block. The @code{:comments} header argument can be set as follows to control -the insertion of extra comments into the tangled code file. +@cindex @code{:comments}, src header argument +Controls inserting comments into tangled files. These are above and beyond +whatever comments may already exist in the @samp{src} code block. @itemize @bullet @item @code{no} -The default. No extra comments are inserted during tangling. +The default. Do not insert any extra comments during tangling. @item @code{link} -The code block is wrapped in comments which contain pointers back to the -original Org file from which the code was tangled. +Wrap the @samp{src} code block in comments. Include links pointing back to +the place in the Org file from where the code was tangled. @item @code{yes} -A synonym for ``link'' to maintain backwards compatibility. +Kept for backward compatibility; same as ``link''. @item @code{org} -Include text from the Org mode file as a comment. -The text is picked from the leading context of the tangled code and is -limited by the nearest headline or source block as the case may be. +Nearest headline text from Org file is inserted as comment. The exact text +that is inserted is picked from the leading context of the source block. @item @code{both} -Turns on both the ``link'' and ``org'' comment options. +Includes both ``link'' and ``org'' comment options. @item @code{noweb} -Turns on the ``link'' comment option, and additionally wraps expanded noweb -references in the code block body in link comments. +Includes ``link'' comment option, expands noweb references, and wraps them in +link comments inside the body of the @samp{src} code block. @end itemize -@node padline, no-expand, comments, Specific header arguments +@node padline @subsubsection @code{:padline} -Control in insertion of padding lines around code block bodies in tangled -code files. The default value is @code{yes} which results in insertion of -newlines before and after each tangled code block. The following arguments -are accepted. - +@cindex @code{:padline}, src header argument +Control insertion of newlines to pad @samp{src} code blocks in the tangled +file. @itemize @bullet @item @code{yes} -Insert newlines before and after each code block body in tangled code files. +Default. Insert a newline before and after each @samp{src} code block in the +tangled file. @item @code{no} -Do not insert any newline padding in tangled output. +Do not insert newlines to pad the tangled @samp{src} code blocks. @end itemize -@node no-expand, session, padline, Specific header arguments +@node no-expand @subsubsection @code{:no-expand} - -By default, code blocks are expanded with @code{org-babel-expand-src-block} -during tangling. This has the effect of assigning values to variables -specified with @code{:var} (see @ref{var}), and of replacing ``noweb'' -references (see @ref{Noweb reference syntax}) with their targets. The -@code{:no-expand} header argument can be used to turn off this behavior. - -@node session, noweb, no-expand, Specific header arguments +@cindex @code{:no-expand}, src header argument + +By default Org expands @samp{src} code blocks during tangling. The +@code{:no-expand} header argument turns off such expansions. Note that one +side-effect of expansion by @code{org-babel-expand-src-block} also assigns +values to @code{:var} (@pxref{var}) variables. Expansions also replace +``noweb'' references with their targets (@pxref{Noweb reference syntax}). +Some of these expansions may cause premature assignment, hence this option. +This option makes a difference only for tangling. It has no effect when +exporting since @samp{src} code blocks for execution have to be expanded +anyway. + +@node session @subsubsection @code{:session} +@cindex @code{:session}, src header argument -The @code{:session} header argument starts a session for an interpreted -language where state is preserved. +The @code{:session} header argument is for running multiple source code +blocks under one session. Org runs @samp{src} code blocks with the same +session name in the same interpreter process. -By default, a session is not started. - -A string passed to the @code{:session} header argument will give the session -a name. This makes it possible to run concurrent sessions for each -interpreted language. +@itemize @bullet +@item @code{none} +Default. Each @samp{src} code block gets a new interpreter process to +execute. The process terminates once the block is evaluated. +@item @code{other} +Any string besides @code{none} turns that string into the name of that +session. For example, @code{:session mysession} names it @samp{mysession}. +If @code{:session} has no argument, then the session name is derived from the +source language identifier. Subsequent blocks with the same source code +language use the same session. Depending on the language, state variables, +code from other blocks, and the overall interpreted environment may be +shared. Some interpreted languages support concurrent sessions when +subsequent source code language blocks change session names. +@end itemize -@node noweb, noweb-ref, session, Specific header arguments +@node noweb @subsubsection @code{:noweb} +@cindex @code{:noweb}, src header argument The @code{:noweb} header argument controls expansion of ``noweb'' syntax -references (see @ref{Noweb reference syntax}) when the code block is -evaluated, tangled, or exported. The @code{:noweb} header argument can have -one of the five values: @code{no}, @code{yes}, @code{tangle}, or -@code{no-export} @code{strip-export}. +references (@pxref{Noweb reference syntax}). Expansions occur when source +code blocks are evaluated, tangled, or exported. @itemize @bullet @item @code{no} -The default. ``Noweb'' syntax references in the body of the code block will -not be expanded before the code block is evaluated, tangled or exported. +Default. No expansion of ``Noweb'' syntax references in the body of the code +when evaluating, tangling, or exporting. @item @code{yes} -``Noweb'' syntax references in the body of the code block will be -expanded before the code block is evaluated, tangled or exported. +Expansion of ``Noweb'' syntax references in the body of the @samp{src} code +block when evaluating, tangling, or exporting. @item @code{tangle} -``Noweb'' syntax references in the body of the code block will be expanded -before the code block is tangled. However, ``noweb'' syntax references will -not be expanded when the code block is evaluated or exported. +Expansion of ``Noweb'' syntax references in the body of the @samp{src} code +block when tangling. No expansion when evaluating or exporting. @item @code{no-export} -``Noweb'' syntax references in the body of the code block will be expanded -before the block is evaluated or tangled. However, ``noweb'' syntax -references will not be expanded when the code block is exported. +Expansion of ``Noweb'' syntax references in the body of the @samp{src} code +block when evaluating or tangling. No expansion when exporting. @item @code{strip-export} -``Noweb'' syntax references in the body of the code block will be expanded -before the block is evaluated or tangled. However, ``noweb'' syntax -references will be removed when the code block is exported. +Expansion of ``Noweb'' syntax references in the body of the @samp{src} code +block when expanding prior to evaluating or tangling. Removes ``noweb'' +syntax references when exporting. @item @code{eval} -``Noweb'' syntax references in the body of the code block will only be -expanded before the block is evaluated. +Expansion of ``Noweb'' syntax references in the body of the @samp{src} code +block only before evaluating. @end itemize @subsubheading Noweb prefix lines -Noweb insertions are now placed behind the line prefix of the -@code{<>}. -This behavior is illustrated in the following example. Because the -@code{<>} noweb reference appears behind the SQL comment syntax, -each line of the expanded noweb reference will be commented. +Noweb insertions now honor prefix characters that appear before +@code{<>}. This behavior is illustrated in the following example. +Because the @code{<>} noweb reference appears behind the SQL comment +syntax, each line of the expanded noweb reference will be commented. -This code block: +This @samp{src} code block: @example -- <> @@ -15290,23 +16228,20 @@ expands to: -- multi-line body of example @end example -Note that noweb replacement text that does not contain any newlines will not -be affected by this change, so it is still possible to use inline noweb -references. +Since this change will not affect noweb replacement text without newlines in +them, inline noweb references are acceptable. -@node noweb-ref, noweb-sep, noweb, Specific header arguments +@node noweb-ref @subsubsection @code{:noweb-ref} -When expanding ``noweb'' style references the bodies of all code block with -@emph{either} a block name matching the reference name @emph{or} a -@code{:noweb-ref} header argument matching the reference name will be -concatenated together to form the replacement text. +@cindex @code{:noweb-ref}, src header argument -By setting this header argument at the sub-tree or file level, simple code -block concatenation may be achieved. For example, when tangling the -following Org mode file, the bodies of code blocks will be concatenated into -the resulting pure code file@footnote{(The example needs property inheritance -to be turned on for the @code{noweb-ref} property, see @ref{Property -inheritance}).}. +When expanding ``noweb'' style references, Org concatenates @samp{src} code +blocks by matching the reference name to either the block name or the +@code{:noweb-ref} header argument. + +For simple concatenation, set this @code{:noweb-ref} header argument at the +sub-tree or file level. In the example Org file shown next, the body of the +source code in each block is extracted for concatenation to a pure code file. @example #+BEGIN_SRC sh :tangle yes :noweb yes :shebang #!/bin/sh @@ -15314,7 +16249,7 @@ inheritance}).}. #+END_SRC * the mount point of the fullest disk :PROPERTIES: - :noweb-ref: fullest-disk + :header-args: :noweb-ref fullest-disk :END: ** query all mounted disks @@ -15333,45 +16268,60 @@ inheritance}).}. #+END_SRC @end example -The @code{:noweb-sep} (see @ref{noweb-sep}) header argument holds the string -used to separate accumulate noweb references like those above. By default a -newline is used. - -@node noweb-sep, cache, noweb-ref, Specific header arguments +@node noweb-sep @subsubsection @code{:noweb-sep} +@cindex @code{:noweb-sep}, src header argument -The @code{:noweb-sep} header argument holds the string used to separate -accumulate noweb references (see @ref{noweb-ref}). By default a newline is -used. +By default a newline separates each noweb reference concatenation. To change +this newline separator, edit the @code{:noweb-sep} (@pxref{noweb-sep}) header +argument. -@node cache, sep, noweb-sep, Specific header arguments +@node cache @subsubsection @code{:cache} - -The @code{:cache} header argument controls the use of in-buffer caching of -the results of evaluating code blocks. It can be used to avoid re-evaluating -unchanged code blocks. Note that the @code{:cache} header argument will not -attempt to cache results when the @code{:session} header argument is used, -because the results of the code block execution may be stored in the session -outside of the Org mode buffer. The @code{:cache} header argument can have -one of two values: @code{yes} or @code{no}. +@cindex @code{:cache}, src header argument + +The @code{:cache} header argument is for caching results of evaluating code +blocks. Caching results can avoid re-evaluating @samp{src} code blocks that +have not changed since the previous run. To benefit from the cache and avoid +redundant evaluations, the source block must have a result already present in +the buffer, and neither the header arguments (including the value of +@code{:var} references) nor the text of the block itself has changed since +the result was last computed. This feature greatly helps avoid long-running +calculations. For some edge cases, however, the cached results may not be +reliable. + +The caching feature is best for when @samp{src} blocks are pure functions, +that is functions that return the same value for the same input arguments +(@pxref{var}), and that do not have side effects, and do not rely on external +variables other than the input arguments. Functions that depend on a timer, +file system objects, and random number generators are clearly unsuitable for +caching. + +A note of warning: when @code{:cache} is used for a @code{:session}, caching +may cause unexpected results. + +When the caching mechanism tests for any source code changes, it will not +expand ``noweb'' style references (@pxref{Noweb reference syntax}). For +reasons why, see @uref{http://thread.gmane.org/gmane.emacs.orgmode/79046}. + +The @code{:cache} header argument can have one of two values: @code{yes} or +@code{no}. @itemize @bullet @item @code{no} -The default. No caching takes place, and the code block will be evaluated -every time it is called. +Default. No caching of results; @samp{src} code block evaluated every time. @item @code{yes} -Every time the code block is run a SHA1 hash of the code and arguments -passed to the block will be generated. This hash is packed into the -@code{#+RESULTS:} line and will be checked on subsequent -executions of the code block. If the code block has not -changed since the last time it was evaluated, it will not be re-evaluated. +Whether to run the code or return the cached results is determined by +comparing the SHA1 hash value of the combined @samp{src} code block and +arguments passed to it. This hash value is packed on the @code{#+RESULTS:} +line from previous evaluation. When hash values match, Org does not evaluate +the @samp{src} code block. When hash values mismatch, Org evaluates the +@samp{src} code block, inserts the results, recalculates the hash value, and +updates @code{#+RESULTS:} line. @end itemize -Code block caches notice if the value of a variable argument -to the code block has changed. If this is the case, the cache is -invalidated and the code block is re-run. In the following example, -@code{caller} will not be re-run unless the results of @code{random} have -changed since it was last run. +In this example, both functions are cached. But @code{caller} runs only if +the result from @code{random} has changed since the last run. @example #+NAME: random @@ -15391,32 +16341,31 @@ changed since it was last run. 0.254227238707244 @end example -@node sep, hlines, cache, Specific header arguments +@node sep @subsubsection @code{:sep} +@cindex @code{:sep}, src header argument -The @code{:sep} header argument can be used to control the delimiter used -when writing tabular results out to files external to Org mode. This is used -either when opening tabular results of a code block by calling the -@code{org-open-at-point} function bound to @kbd{C-c C-o} on the code block, -or when writing code block results to an external file (see @ref{file}) -header argument. +The @code{:sep} header argument is the delimiter for saving results as tables +to files (@pxref{file}) external to Org mode. Org defaults to tab delimited +output. The function, @code{org-open-at-point}, which is bound to @kbd{C-c +C-o}, also uses @code{:sep} for opening tabular results. -By default, when @code{:sep} is not specified output tables are tab -delimited. - -@node hlines, colnames, sep, Specific header arguments +@node hlines @subsubsection @code{:hlines} +@cindex @code{:hlines}, src header argument -Tables are frequently represented with one or more horizontal lines, or -hlines. The @code{:hlines} argument to a code block accepts the -values @code{yes} or @code{no}, with a default value of @code{no}. +In-between each table row or below the table headings, sometimes results have +horizontal lines, which are also known as hlines. The @code{:hlines} +argument with the value @code{yes} accepts such lines. The default is +@code{no}. @itemize @bullet @item @code{no} -Strips horizontal lines from the input table. In most languages this is the -desired effect because an @code{hline} symbol is interpreted as an unbound -variable and raises an error. Setting @code{:hlines no} or relying on the -default value yields the following results. +Strips horizontal lines from the input table. For most code, this is +desirable, or else those @code{hline} symbols raise unbound variable errors. + +The default is @code{:hlines no}. The example shows hlines removed from the +input table. @example #+NAME: many-cols @@ -15438,7 +16387,7 @@ default value yields the following results. @end example @item @code{yes} -Leaves hlines in the table. Setting @code{:hlines yes} has this effect. +For @code{:hlines yes}, the example shows hlines unchanged. @example #+NAME: many-cols @@ -15462,20 +16411,20 @@ Leaves hlines in the table. Setting @code{:hlines yes} has this effect. @end example @end itemize -@node colnames, rownames, hlines, Specific header arguments +@node colnames @subsubsection @code{:colnames} +@cindex @code{:colnames}, src header argument -The @code{:colnames} header argument accepts the values @code{yes}, -@code{no}, or @code{nil} for unassigned. The default value is @code{nil}. -Note that the behavior of the @code{:colnames} header argument may differ -across languages. +The @code{:colnames} header argument accepts @code{yes}, @code{no}, or +@code{nil} values. The default value is @code{nil}, which is unassigned. +But this header argument behaves differently depending on the source code +language. @itemize @bullet @item @code{nil} -If an input table looks like it has column names -(because its second row is an hline), then the column -names will be removed from the table before -processing, then reapplied to the results. +If an input table has column names (because the second row is an hline), then +Org removes the column names, processes the table, puts back the column +names, and then writes the table to the results block. @example #+NAME: less-cols @@ -15496,33 +16445,36 @@ processing, then reapplied to the results. | c* | @end example -Please note that column names are not removed before the table is indexed -using variable indexing @xref{var, Indexable variable values}. +Note that column names have to accounted for when using variable indexing +(@pxref{var, Indexable variable values}) because column names are not removed +for indexing. @item @code{no} -No column name pre-processing takes place +Do not pre-process column names. @item @code{yes} -Column names are removed and reapplied as with @code{nil} even if the table -does not ``look like'' it has column names (i.e., the second row is not an -hline) +For an input table that has no hlines, process it like the @code{nil} +value. That is, Org removes the column names, processes the table, puts back +the column names, and then writes the table to the results block. @end itemize -@node rownames, shebang, colnames, Specific header arguments +@node rownames @subsubsection @code{:rownames} +@cindex @code{:rownames}, src header argument -The @code{:rownames} header argument can take on the values @code{yes} or -@code{no}, with a default value of @code{no}. Note that Emacs Lisp code -blocks ignore the @code{:rownames} header argument entirely given the ease -with which tables with row names may be handled directly in Emacs Lisp. +The @code{:rownames} header argument can take on values @code{yes} or +@code{no} values. The default is @code{no}. Note that @code{emacs-lisp} +code blocks ignore @code{:rownames} header argument because of the ease of +table-handling in Emacs. @itemize @bullet @item @code{no} -No row name pre-processing will take place. +Org will not pre-process row names. @item @code{yes} -The first column of the table is removed from the table before processing, -and is then reapplied to the results. +If an input table has row names, then Org removes the row names, processes +the table, puts back the row names, and then writes the table to the results +block. @example #+NAME: with-rownames @@ -15539,82 +16491,88 @@ and is then reapplied to the results. | two | 16 | 17 | 18 | 19 | 20 | @end example -Please note that row names are not removed before the table is indexed using -variable indexing @xref{var, Indexable variable values}. +Note that row names have to accounted for when using variable indexing +(@pxref{var, Indexable variable values}) because row names are not removed +for indexing. @end itemize -@node shebang, tangle-mode, rownames, Specific header arguments +@node shebang @subsubsection @code{:shebang} +@cindex @code{:shebang}, src header argument -Setting the @code{:shebang} header argument to a string value -(e.g., @code{:shebang "#!/bin/bash"}) causes the string to be inserted as the -first line of any tangled file holding the code block, and the file -permissions of the tangled file are set to make it executable. +This header argument can turn results into executable script files. By +setting the @code{:shebang} header argument to a string value (for example, +@code{:shebang "#!/bin/bash"}), Org inserts that string as the first line of +the tangled file that the @samp{src} code block is extracted to. Org then +turns on the tangled file's executable permission. - -@node tangle-mode, eval, shebang, Specific header arguments +@node tangle-mode @subsubsection @code{:tangle-mode} +@cindex @code{:tangle-mode}, src header argument + +The @code{tangle-mode} header argument specifies what permissions to set for +tangled files by @code{set-file-modes}. For example, to make read-only +tangled file, use @code{:tangle-mode (identity #o444)}. To make it +executable, use @code{:tangle-mode (identity #o755)}. + +On @samp{src} code blocks with @code{shebang} (@pxref{shebang}) header +argument, Org will automatically set the tangled file to executable +permissions. But this can be overridden with custom permissions using +@code{tangle-mode} header argument. + +When multiple @samp{src} code blocks tangle to a single file with different +and conflicting @code{tangle-mode} header arguments, Org's behavior is +undefined. -The @code{tangle-mode} header argument controls the permission set on tangled -files. The value of this header argument will be passed to -@code{set-file-modes}. For example, to set a tangled file as read only use -@code{:tangle-mode (identity #o444)}, or to set a tangled file as executable -use @code{:tangle-mode (identity #o755)}. Blocks with @code{shebang} -(@ref{shebang}) header arguments will automatically be made executable unless -the @code{tangle-mode} header argument is also used. The behavior is -undefined if multiple code blocks with different values for the -@code{tangle-mode} header argument are tangled to the same file. - -@node eval, wrap, tangle-mode, Specific header arguments +@node eval @subsubsection @code{:eval} -The @code{:eval} header argument can be used to limit the evaluation of -specific code blocks. The @code{:eval} header argument can be useful for -protecting against the evaluation of dangerous code blocks or to ensure that -evaluation will require a query regardless of the value of the -@code{org-confirm-babel-evaluate} variable. The possible values of -@code{:eval} and their effects are shown below. +@cindex @code{:eval}, src header argument +The @code{:eval} header argument can limit evaluation of specific code +blocks. It is useful for protection against evaluating untrusted @samp{src} +code blocks by prompting for a confirmation. This protection is independent +of the @code{org-confirm-babel-evaluate} setting. @table @code @item never or no -The code block will not be evaluated under any circumstances. +Org will never evaluate this @samp{src} code block. @item query -Evaluation of the code block will require a query. +Org prompts the user for permission to evaluate this @samp{src} code block. @item never-export or no-export -The code block will not be evaluated during export but may still be called -interactively. +Org will not evaluate this @samp{src} code block when exporting, yet the user +can evaluate this source block interactively. @item query-export -Evaluation of the code block during export will require a query. +Org prompts the user for permission to export this @samp{src} code block. @end table -If this header argument is not set then evaluation is determined by the value -of the @code{org-confirm-babel-evaluate} variable see @ref{Code evaluation -security}. +If @code{:eval} header argument is not set for a source block, then Org +determines whether to evaluate from the @code{org-confirm-babel-evaluate} +variable (@pxref{Code evaluation security}). -@node wrap, post, eval, Specific header arguments +@node wrap @subsubsection @code{:wrap} -The @code{:wrap} header argument is used to mark the results of source block -evaluation. The header argument can be passed a string that will be appended -to @code{#+BEGIN_} and @code{#+END_}, which will then be used to wrap the -results. If not string is specified then the results will be wrapped in a -@code{#+BEGIN/END_RESULTS} block. +@cindex @code{:wrap}, src header argument +The @code{:wrap} header argument marks the results block by appending strings +to @code{#+BEGIN_} and @code{#+END_}. If no string is specified, Org wraps +the results in a @code{#+BEGIN/END_RESULTS} block. -@node post, prologue, wrap, Specific header arguments +@node post @subsubsection @code{:post} -The @code{:post} header argument is used to post-process the results of a -code block execution. When a post argument is given, the results of the code -block will temporarily be bound to the @code{*this*} variable. This variable -may then be included in header argument forms such as those used in @ref{var} -header argument specifications allowing passing of results to other code -blocks, or direct execution via Emacs Lisp. - -The following example illustrates the usage of the @code{:post} header -argument. +@cindex @code{:post}, src header argument +The @code{:post} header argument is for post-processing results from +@samp{src} block evaluation. When @code{:post} has any value, Org binds the +results to @code{*this*} variable for easy passing to @ref{var} header +argument specifications. That makes results available to other @samp{src} +code blocks, or for even direct Emacs Lisp code execution. + +The following two examples illustrate @code{:post} header argument in action. +The first one shows how to attach @code{#+ATTR_LATEX:} line using +@code{:post}. @example #+name: attr_wrap #+begin_src sh :var data="" :var width="\\textwidth" :results output - echo "#+ATTR_LATEX :width $width" + echo "#+ATTR_LATEX: :width $width" echo "$data" #+end_src @@ -15634,33 +16592,65 @@ argument. :END: @end example -@node prologue, epilogue, post, Specific header arguments +The second example shows use of @code{:colnames} in @code{:post} to pass +data between @samp{src} code blocks. + +@example +#+name: round-tbl +#+begin_src emacs-lisp :var tbl="" fmt="%.3f" + (mapcar (lambda (row) + (mapcar (lambda (cell) + (if (numberp cell) + (format fmt cell) + cell)) + row)) + tbl) +#+end_src + +#+begin_src R :colnames yes :post round-tbl[:colnames yes](*this*) +set.seed(42) +data.frame(foo=rnorm(1)) +#+end_src + +#+RESULTS: +| foo | +|-------| +| 1.371 | +@end example + +@node prologue @subsubsection @code{:prologue} -The value of the @code{prologue} header argument will be prepended to the -code block body before execution. For example, @code{:prologue "reset"} may -be used to reset a gnuplot session before execution of a particular code -block, or the following configuration may be used to do this for all gnuplot -code blocks. Also see @ref{epilogue}. +@cindex @code{:prologue}, src header argument +The @code{prologue} header argument is for appending to the top of the code +block for execution. For example, a clear or reset code at the start of new +execution of a @samp{src} code block. A @code{reset} for @samp{gnuplot}: +@code{:prologue "reset"}. See also @ref{epilogue}. @lisp (add-to-list 'org-babel-default-header-args:gnuplot '((:prologue . "reset"))) @end lisp -@node epilogue, , prologue, Specific header arguments +@node epilogue @subsubsection @code{:epilogue} -The value of the @code{epilogue} header argument will be appended to the code -block body before execution. Also see @ref{prologue}. +@cindex @code{:epilogue}, src header argument +The value of the @code{epilogue} header argument is for appending to the end +of the code block for execution. See also @ref{prologue}. -@node Results of evaluation, Noweb reference syntax, Header arguments, Working With Source Code +@node Results of evaluation @section Results of evaluation @cindex code block, results of evaluation @cindex source code, results of evaluation -The way in which results are handled depends on whether a session is invoked, -as well as on whether @code{:results value} or @code{:results output} is -used. The following table shows the table possibilities. For a full listing -of the possible results header arguments see @ref{results}. +How Org handles results of a code block execution depends on many header +arguments working together. Here is only a summary of these. For an +enumeration of all the header arguments that affect results, see +@ref{results}. + +The primary determinant is the execution context. Is it in a @code{:session} +or not? Orthogonal to that is if the expected result is a @code{:results +value} or @code{:results output}, which is a concatenation of output from +start to finish of the @samp{src} code block's evaluation. @multitable @columnfractions 0.26 0.33 0.41 @item @tab @b{Non-session} @tab @b{Session} @@ -15668,51 +16658,54 @@ of the possible results header arguments see @ref{results}. @item @code{:results output} @tab contents of STDOUT @tab concatenation of interpreter output @end multitable -Note: With @code{:results value}, the result in both @code{:session} and -non-session is returned to Org mode as a table (a one- or two-dimensional -vector of strings or numbers) when appropriate. +For @code{:session} and non-session, the @code{:results value} turns the +results into an Org mode table format. Single values are wrapped in a one +dimensional vector. Rows and columns of a table are wrapped in a +two-dimensional vector. @subsection Non-session @subsubsection @code{:results value} -This is the default. Internally, the value is obtained by wrapping the code -in a function definition in the external language, and evaluating that -function. Therefore, code should be written as if it were the body of such a -function. In particular, note that Python does not automatically return a -value from a function unless a @code{return} statement is present, and so a -@samp{return} statement will usually be required in Python. +@cindex @code{:results}, src header argument +Default. Org gets the value by wrapping the code in a function definition in +the language of the @samp{src} block. That is why when using @code{:results +value}, code should execute like a function and return a value. For +languages like Python, an explicit @code{return} statement is mandatory when +using @code{:results value}. -This is the only one of the four evaluation contexts in which the code is -automatically wrapped in a function definition. +This is one of four evaluation contexts where Org automatically wraps the +code in a function definition. @subsubsection @code{:results output} -The code is passed to the interpreter as an external process, and the -contents of the standard output stream are returned as text. (In certain -languages this also contains the error output stream; this is an area for -future work.) +@cindex @code{:results}, src header argument +For @code{:results output}, the code is passed to an external process running +the interpreter. Org returns the contents of the standard output stream as +as text results. @subsection Session @subsubsection @code{:results value} -The code is passed to an interpreter running as an interactive Emacs inferior -process. Only languages which provide tools for interactive evaluation of -code have session support, so some language (e.g., C and ditaa) do not -support the @code{:session} header argument, and in other languages (e.g., -Python and Haskell) which have limitations on the code which may be entered -into interactive sessions, those limitations apply to the code in code blocks -using the @code{:session} header argument as well. - -Unless the @code{:results output} option is supplied (see below) the result -returned is the result of the last evaluation performed by the -interpreter. (This is obtained in a language-specific manner: the value of -the variable @code{_} in Python and Ruby, and the value of @code{.Last.value} -in R). +@cindex @code{:results}, src header argument +For @code{:results value} from a @code{:session}, Org passes the code to an +interpreter running as an interactive Emacs inferior process. So only +languages that provide interactive evaluation can have session support. Not +all languages provide this support, such as @samp{C} and @samp{ditaa}. Even +those that do support, such as @samp{Python} and @samp{Haskell}, they impose +limitations on allowable language constructs that can run interactively. Org +inherits those limitations for those @samp{src} code blocks running in a +@code{:session}. + +Org gets the value from the source code interpreter's last statement +output. Org has to use language-specific methods to obtain the value. For +example, from the variable @code{_} in @samp{Python} and @samp{Ruby}, and the +value of @code{.Last.value} in @samp{R}). @subsubsection @code{:results output} -The code is passed to the interpreter running as an interactive Emacs -inferior process. The result returned is the concatenation of the sequence of -(text) output from the interactive interpreter. Notice that this is not -necessarily the same as what would be sent to @code{STDOUT} if the same code -were passed to a non-interactive interpreter running as an external -process. For example, compare the following two blocks: +@cindex @code{:results}, src header argument +For @code{:results output}, Org passes the code to the interpreter running as +an interactive Emacs inferior process. Org concatenates whatever text output +emitted by the interpreter to return the collection as a result. Note that +this collection is not the same as collected from @code{STDOUT} of a +non-interactive interpreter running as an external process. Compare for +example these two blocks: @example #+BEGIN_SRC python :results output @@ -15726,7 +16719,8 @@ process. For example, compare the following two blocks: : bye @end example -In non-session mode, the ``2'' is not printed and does not appear. +In the above non-session mode, the ``2'' is not printed; so does not appear +in results. @example #+BEGIN_SRC python :results output :session @@ -15741,60 +16735,61 @@ In non-session mode, the ``2'' is not printed and does not appear. : bye @end example -But in @code{:session} mode, the interactive interpreter receives input ``2'' -and prints out its value, ``2''. (Indeed, the other print statements are -unnecessary here). +In the above @code{:session} mode, the interactive interpreter receives and +prints ``2''. Results show that. -@node Noweb reference syntax, Key bindings and useful functions, Results of evaluation, Working With Source Code +@node Noweb reference syntax @section Noweb reference syntax @cindex code block, noweb reference @cindex syntax, noweb @cindex source code, noweb reference -The ``noweb'' (see @uref{http://www.cs.tufts.edu/~nr/noweb/}) Literate -Programming system allows named blocks of code to be referenced by using the -familiar Noweb syntax: +Org supports named blocks in ``noweb'' style syntax. For ``noweb'' literate +programming details, see @uref{http://www.cs.tufts.edu/~nr/noweb/}). @example <> @end example -When a code block is tangled or evaluated, whether or not ``noweb'' -references are expanded depends upon the value of the @code{:noweb} header -argument. If @code{:noweb yes}, then a Noweb reference is expanded before -evaluation. If @code{:noweb no}, the default, then the reference is not -expanded before evaluation. See the @ref{noweb-ref} header argument for -a more flexible way to resolve noweb references. +For the header argument @code{:noweb yes}, Org expands ``noweb'' style +references in the @samp{src} code block before evaluation. + +For the header argument @code{:noweb no}, Org does not expand ``noweb'' style +references in the @samp{src} code block before evaluation. + +The default is @code{:noweb no}. + +Org offers a more flexible way to resolve ``noweb'' style references +(@pxref{noweb-ref}). -It is possible to include the @emph{results} of a code block rather than the -body. This is done by appending parenthesis to the code block name which may -optionally contain arguments to the code block as shown below. +Org can handle naming of @emph{results} block, rather than the body of the +@samp{src} code block, using ``noweb'' style references. + +For ``noweb'' style reference, append parenthesis to the code block name for +arguments, as shown in this example: @example <> @end example -Note: the default value, @code{:noweb no}, was chosen to ensure that -correct code is not broken in a language, such as Ruby, where -@code{<>} is a syntactically valid construct. If @code{<>} is not -syntactically valid in languages that you use, then please consider setting -the default value. +Note: Org defaults to @code{:noweb no} so as not to cause errors in languages +such as @samp{Ruby} where ``noweb'' syntax is equally valid characters. For +example, @code{<>}. Change Org's default to @code{:noweb yes} for +languages where there is no risk of confusion. -Note: if noweb tangling is slow in large Org mode files consider setting the +For faster tangling of large Org mode files, set @code{org-babel-use-quick-and-dirty-noweb-expansion} variable to @code{t}. -This will result in faster noweb reference resolution at the expense of not -correctly resolving inherited values of the @code{:noweb-ref} header -argument. +The speedup comes at the expense of not correctly resolving inherited values +of the @code{:noweb-ref} header argument. -@node Key bindings and useful functions, Batch execution, Noweb reference syntax, Working With Source Code + +@node Key bindings and useful functions @section Key bindings and useful functions @cindex code block, key bindings -Many common Org mode key sequences are re-bound depending on -the context. +Many common Org mode key sequences are re-bound depending on the context. -Within a code block, the following key bindings -are active: +Active key bindings in code blocks: @multitable @columnfractions 0.25 0.75 @kindex C-c C-c @@ -15807,9 +16802,9 @@ are active: @item @kbd{M-@key{down}} @tab @code{org-babel-switch-to-session} @end multitable -In an Org mode buffer, the following key bindings are active: +Active key bindings in Org mode buffer: -@multitable @columnfractions 0.45 0.55 +@multitable @columnfractions 0.5 0.5 @kindex C-c C-v p @kindex C-c C-v C-p @item @kbd{C-c C-v p} @ @ @r{or} @ @ @kbd{C-c C-v C-p} @tab @code{org-babel-previous-src-block} @@ -15878,8 +16873,7 @@ In an Org mode buffer, the following key bindings are active: @item @kbd{C-c C-v x} @ @ @r{or} @ @ @kbd{C-c C-v C-x} @tab @code{org-babel-do-key-sequence-in-edit-buffer} @end multitable -@c When possible these keybindings were extended to work when the control key is -@c kept pressed, resulting in the following additional keybindings. +@c Extended key bindings when control key is kept pressed: @c @multitable @columnfractions 0.25 0.75 @c @item @kbd{C-c C-v C-a} @tab @code{org-babel-sha1-hash} @@ -15892,15 +16886,18 @@ In an Org mode buffer, the following key bindings are active: @c @item @kbd{C-c C-v C-z} @tab @code{org-babel-switch-to-session} @c @end multitable -@node Batch execution, , Key bindings and useful functions, Working With Source Code +@node Batch execution @section Batch execution @cindex code block, batch execution @cindex source code, batch execution -It is possible to call functions from the command line. This shell -script calls @code{org-babel-tangle} on every one of its arguments. +Org mode features, including working with source code facilities can be +invoked from the command line. This enables building shell scripts for batch +processing, running automated system tasks, and expanding Org mode's +usefulness. -Be sure to adjust the paths to fit your system. +The sample script shows batch processing of multiple files using +@code{org-babel-tangle}. @example #!/bin/sh @@ -15917,35 +16914,33 @@ for i in $@@; do done emacs -Q --batch \ ---eval "(progn -(add-to-list 'load-path (expand-file-name \"~/src/org/lisp/\")) -(add-to-list 'load-path (expand-file-name \"~/src/org/contrib/lisp/\" t)) -(require 'org)(require 'org-exp)(require 'ob)(require 'ob-tangle) -(mapc (lambda (file) - (find-file (expand-file-name file \"$DIR\")) - (org-babel-tangle) - (kill-buffer)) '($FILES)))" 2>&1 |grep tangled + --eval "(progn + (require 'org)(require 'ob)(require 'ob-tangle) + (mapc (lambda (file) + (find-file (expand-file-name file \"$DIR\")) + (org-babel-tangle) + (kill-buffer)) '($FILES)))" 2>&1 |grep -i tangled @end example -@node Miscellaneous, Hacking, Working With Source Code, Top +@node Miscellaneous @chapter Miscellaneous @menu -* Completion:: M-TAB knows what you need -* Easy Templates:: Quick insertion of structural elements +* Completion:: M-TAB guesses completions +* Easy templates:: Quick insertion of structural elements * Speed keys:: Electric commands at the beginning of a headline * Code evaluation security:: Org mode files evaluate inline code -* Customization:: Adapting Org to your taste +* Customization:: Adapting Org to changing tastes * In-buffer settings:: Overview of the #+KEYWORDS * The very busy C-c C-c key:: When in doubt, press C-c C-c * Clean view:: Getting rid of leading stars in the outline * TTY keys:: Using Org on a tty -* Interaction:: Other Emacs packages +* Interaction:: With other Emacs packages * org-crypt:: Encrypting Org files @end menu -@node Completion, Easy Templates, Miscellaneous, Miscellaneous +@node Completion @section Completion @cindex completion, of @TeX{} symbols @cindex completion, of TODO keywords @@ -15961,15 +16956,13 @@ emacs -Q --batch \ @cindex tag completion @cindex link abbreviations, completion of -Emacs would not be Emacs without completion, and Org mode uses it whenever it -makes sense. If you prefer an @i{iswitchb}- or @i{ido}-like interface for -some of the completion prompts, you can specify your preference by setting at -most one of the variables @code{org-completion-use-iswitchb} -@code{org-completion-use-ido}. - -Org supports in-buffer completion. This type of completion does -not make use of the minibuffer. You simply type a few letters into -the buffer and use the key to complete text right there. +Org has in-buffer completions. Unlike minibuffer completions, which are +useful for quick command interactions, Org's in-buffer completions are more +suitable for content creation in Org documents. Type one or more letters and +invoke the hot key to complete the text in-place. Depending on the context +and the keys, Org will offer different types of completions. No minibuffer +is involved. Such mode-specific hot keys have become an integral part of +Emacs and Org provides several shortcuts. @table @kbd @kindex M-@key{TAB} @@ -15996,112 +16989,123 @@ buffer. After @samp{[}, complete link abbreviations (@pxref{Link abbreviations}). @item After @samp{#+}, complete the special keywords like @samp{TYP_TODO} or -@samp{OPTIONS} which set file-specific options for Org mode. When the -option keyword is already complete, pressing @kbd{M-@key{TAB}} again -will insert example settings for this keyword. +file-specific @samp{OPTIONS}. After option keyword is complete, pressing +@kbd{M-@key{TAB}} again will insert example settings for that option. @item -In the line after @samp{#+STARTUP: }, complete startup keywords, -i.e., valid keys for this line. +After @samp{#+STARTUP: }, complete startup keywords. @item -Elsewhere, complete dictionary words using Ispell. +When the point is anywhere else, complete dictionary words using Ispell. @end itemize +@kindex C-M-i +If your desktop intercepts the combo @kbd{M-@key{TAB}} to switch windows, use +@kbd{C-M-i} or @kbd{@key{ESC} @key{TAB}} as an alternative or customize your +environment. @end table -@node Easy Templates, Speed keys, Completion, Miscellaneous -@section Easy Templates +@node Easy templates +@section Easy templates @cindex template insertion @cindex insertion, of templates -Org mode supports insertion of empty structural elements (like -@code{#+BEGIN_SRC} and @code{#+END_SRC} pairs) with just a few key -strokes. This is achieved through a native template expansion mechanism. -Note that Emacs has several other template mechanisms which could be used in -a similar way, for example @file{yasnippet}. +With just a few keystrokes, Org's easy templates inserts empty pairs of +structural elements, such as @code{#+BEGIN_SRC} and @code{#+END_SRC}. Easy +templates use an expansion mechanism, which is native to Org, in a process +similar to @file{yasnippet} and other Emacs template expansion packages. + +@kbd{@key{<}} @kbd{@key{s}} @kbd{@key{TAB}} completes the @samp{src} code +block. + +@kbd{<} @kbd{l} @kbd{@key{TAB}} + +expands to: + +#+BEGIN_EXPORT latex -To insert a structural element, type a @samp{<}, followed by a template -selector and @kbd{@key{TAB}}. Completion takes effect only when the above -keystrokes are typed on a line by itself. +#+END_EXPORT -The following template selectors are currently supported. +Org comes with these pre-defined easy templates: @multitable @columnfractions 0.1 0.9 -@item @kbd{s} @tab @code{#+BEGIN_SRC ... #+END_SRC} +@item @kbd{s} @tab @code{#+BEGIN_SRC ... #+END_SRC} @item @kbd{e} @tab @code{#+BEGIN_EXAMPLE ... #+END_EXAMPLE} -@item @kbd{q} @tab @code{#+BEGIN_QUOTE ... #+END_QUOTE} -@item @kbd{v} @tab @code{#+BEGIN_VERSE ... #+END_VERSE} -@item @kbd{c} @tab @code{#+BEGIN_CENTER ... #+END_CENTER} -@item @kbd{l} @tab @code{#+BEGIN_LaTeX ... #+END_LaTeX} -@item @kbd{L} @tab @code{#+LaTeX:} -@item @kbd{h} @tab @code{#+BEGIN_HTML ... #+END_HTML} +@item @kbd{q} @tab @code{#+BEGIN_QUOTE ... #+END_QUOTE} +@item @kbd{v} @tab @code{#+BEGIN_VERSE ... #+END_VERSE} +@item @kbd{c} @tab @code{#+BEGIN_CENTER ... #+END_CENTER} +@item @kbd{l} @tab @code{#+BEGIN_EXPORT latex ... #+END_EXPORT} +@item @kbd{L} @tab @code{#+LATEX:} +@item @kbd{h} @tab @code{#+BEGIN_EXPORT html ... #+END_EXPORT} @item @kbd{H} @tab @code{#+HTML:} -@item @kbd{a} @tab @code{#+BEGIN_ASCII ... #+END_ASCII} +@item @kbd{a} @tab @code{#+BEGIN_EXPORT ascii ... #+END_EXPORT} @item @kbd{A} @tab @code{#+ASCII:} @item @kbd{i} @tab @code{#+INDEX:} line @item @kbd{I} @tab @code{#+INCLUDE:} line @end multitable -For example, on an empty line, typing "Customization} menu. Many -settings can also be activated on a per-file basis, by putting special -lines into the buffer (@pxref{In-buffer settings}). +Org has more than 500 variables for customization. They can be accessed +through the usual @kbd{M-x org-customize RET} command. Or through the Org +menu, @code{Org->Customization->Browse Org Group}. Org also has per-file +settings for some variables (@pxref{In-buffer settings}). -@node In-buffer settings, The very busy C-c C-c key, Customization, Miscellaneous +@node In-buffer settings @section Summary of in-buffer settings @cindex in-buffer settings @cindex special keywords +In-buffer settings start with @samp{#+}, followed by a keyword, a colon, and +then a word for each setting. Org accepts multiple settings on the same +line. Org also accepts multiple lines for a keyword. This manual describes +these settings throughout. A summary follows here. -Org mode uses special lines in the buffer to define settings on a -per-file basis. These lines start with a @samp{#+} followed by a -keyword, a colon, and then individual words defining a setting. Several -setting words can be in the same line, but you can also have multiple -lines for the keyword. While these settings are described throughout -the manual, here is a summary. After changing any of those lines in the -buffer, press @kbd{C-c C-c} with the cursor still in the line to -activate the changes immediately. Otherwise they become effective only -when the file is visited again in a new Emacs session. +@kbd{C-c C-c} activates any changes to the in-buffer settings. Closing and +reopening the Org file in Emacs also activates the changes. @vindex org-archive-location @table @kbd @item #+ARCHIVE: %s_done:: -This line sets the archive location for the agenda file. It applies for -all subsequent lines until the next @samp{#+ARCHIVE} line, or the end -of the file. The first such line also applies to any entries before it. +Sets the archive location of the agenda file. This location applies to the +lines until the next @samp{#+ARCHIVE} line, if any, in the Org file. The +first archive location in the Org file also applies to any entries before it. The corresponding variable is @code{org-archive-location}. @item #+CATEGORY: -This line sets the category for the agenda file. The category applies -for all subsequent lines until the next @samp{#+CATEGORY} line, or the -end of the file. The first such line also applies to any entries before it. +Sets the category of the agenda file, which applies to the entire document. @item #+COLUMNS: %25ITEM ... @cindex property, COLUMNS -Set the default format for columns view. This format applies when -columns view is invoked in locations where no @code{COLUMNS} property -applies. +Sets the default format for columns view. Org uses this format for column +views where there is no @code{COLUMNS} property. @item #+CONSTANTS: name1=value1 ... @vindex org-table-formula-constants @vindex org-table-formula -Set file-local values for constants to be used in table formulas. This -line sets the local variable @code{org-table-formula-constants-local}. -The global version of this variable is -@code{org-table-formula-constants}. +Set file-local values for constants that table formulas can use. This line +sets the local variable @code{org-table-formula-constants-local}. The global +version of this variable is @code{org-table-formula-constants}. @item #+FILETAGS: :tag1:tag2:tag3: -Set tags that can be inherited by any entry in the file, including the +Set tags that all entries in the file will inherit from here, including the top-level entries. -@item #+DRAWERS: NAME1 ... -@vindex org-drawers -Set the file-local set of additional drawers. The corresponding global -variable is @code{org-drawers}. @item #+LINK: linkword replace @vindex org-link-abbrev-alist -These lines (several are allowed) specify link abbreviations. -@xref{Link abbreviations}. The corresponding variable is -@code{org-link-abbrev-alist}. +Each line specifies one abbreviation for one link. Use multiple +@code{#+LINK:} lines for more, @pxref{Link abbreviations}. The corresponding +variable is @code{org-link-abbrev-alist}. @item #+PRIORITIES: highest lowest default @vindex org-highest-priority @vindex org-lowest-priority @@ -16202,22 +17193,22 @@ This line sets a default inheritance value for entries in the current buffer, most useful for specifying the allowed values of a property. @cindex #+SETUPFILE @item #+SETUPFILE: file -This line defines a file that holds more in-buffer setup. Normally this is -entirely ignored. Only when the buffer is parsed for option-setting lines -(i.e., when starting Org mode for a file, when pressing @kbd{C-c C-c} in a -settings line, or when exporting), then the contents of this file are parsed -as if they had been included in the buffer. In particular, the file can be -any other Org mode file with internal setup. You can visit the file the -cursor is in the line with @kbd{C-c '}. +The setup file is for additional in-buffer settings. Org loads this file and +parses it for any settings in it only when Org opens the main file. @kbd{C-c +C-c} on the settings line will also parse and load. Org also parses and +loads the file during normal exporting process. Org parses the contents of +this file as if it was included in the buffer. It can be another Org file. +To visit the file, @kbd{C-c '} while the cursor is on the line with the file +name. @item #+STARTUP: @cindex #+STARTUP -This line sets options to be used at startup of Org mode, when an -Org file is being visited. +Startup options Org uses when first visiting a file. The first set of options deals with the initial visibility of the outline tree. The corresponding variable for global default settings is -@code{org-startup-folded}, with a default value @code{t}, which means -@code{overview}. +@code{org-startup-folded} with a default value of @code{t}, which is the same +as @code{overview}. + @vindex org-startup-folded @cindex @code{overview}, STARTUP keyword @cindex @code{content}, STARTUP keyword @@ -16234,17 +17225,17 @@ showeverything @r{show even drawer contents} @cindex @code{indent}, STARTUP keyword @cindex @code{noindent}, STARTUP keyword Dynamic virtual indentation is controlled by the variable -@code{org-startup-indented}@footnote{Emacs 23 and Org mode 6.29 are required} +@code{org-startup-indented} @example indent @r{start with @code{org-indent-mode} turned on} noindent @r{start with @code{org-indent-mode} turned off} @end example @vindex org-startup-align-all-tables -Then there are options for aligning tables upon visiting a file. This -is useful in files containing narrowed table columns. The corresponding -variable is @code{org-startup-align-all-tables}, with a default value -@code{nil}. +Aligns tables consistently upon visiting a file; useful for restoring +narrowed table columns. The corresponding variable is +@code{org-startup-align-all-tables} with @code{nil} as default value. + @cindex @code{align}, STARTUP keyword @cindex @code{noalign}, STARTUP keyword @example @@ -16253,9 +17244,9 @@ noalign @r{don't align tables on startup} @end example @vindex org-startup-with-inline-images -When visiting a file, inline images can be automatically displayed. The -corresponding variable is @code{org-startup-with-inline-images}, with a -default value @code{nil} to avoid delays when visiting a file. +Whether Org should automatically display inline images. The corresponding +variable is @code{org-startup-with-inline-images}, with a default value +@code{nil} to avoid delays when visiting a file. @cindex @code{inlineimages}, STARTUP keyword @cindex @code{noinlineimages}, STARTUP keyword @example @@ -16264,10 +17255,9 @@ noinlineimages @r{don't show inline images on startup} @end example @vindex org-startup-with-latex-preview -When visiting a file, @LaTeX{} fragments can be converted to images -automatically. The variable @code{org-startup-with-latex-preview} which -controls this behavior, is set to @code{nil} by default to avoid delays on -startup. +Whether Org should automatically convert @LaTeX{} fragments to images. The +variable @code{org-startup-with-latex-preview}, which controls this setting, +is set to @code{nil} by default to avoid startup delays. @cindex @code{latexpreview}, STARTUP keyword @cindex @code{nolatexpreview}, STARTUP keyword @example @@ -16328,21 +17318,21 @@ nologstatesreversed @r{do not reverse the order of states notes} @vindex org-hide-leading-stars @vindex org-odd-levels-only -Here are the options for hiding leading stars in outline headings, and for -indenting outlines. The corresponding variables are -@code{org-hide-leading-stars} and @code{org-odd-levels-only}, both with a -default setting @code{nil} (meaning @code{showstars} and @code{oddeven}). +These options hide leading stars in outline headings, and indent outlines. +The corresponding variables are @code{org-hide-leading-stars} and +@code{org-odd-levels-only}, both with a default setting of @code{nil} +(meaning @code{showstars} and @code{oddeven}). @cindex @code{hidestars}, STARTUP keyword @cindex @code{showstars}, STARTUP keyword @cindex @code{odd}, STARTUP keyword @cindex @code{even}, STARTUP keyword @example -hidestars @r{make all but one of the stars starting a headline invisible.} -showstars @r{show all stars starting a headline} -indent @r{virtual indentation according to outline level} -noindent @r{no virtual indentation according to outline level} -odd @r{allow only odd outline levels (1,3,...)} -oddeven @r{allow all outline levels} +hidestars @r{hide all stars on the headline except one.} +showstars @r{show all stars on the headline} +indent @r{virtual indents according to the outline level} +noindent @r{no virtual indents} +odd @r{show odd outline levels only (1,3,...)} +oddeven @r{show all outline levels} @end example @vindex org-put-time-stamp-overlays @@ -16368,8 +17358,8 @@ constSI @r{@file{constants.el} should use the SI unit system} @vindex org-footnote-define-inline @vindex org-footnote-auto-label @vindex org-footnote-auto-adjust -To influence footnote settings, use the following keywords. The -corresponding variables are @code{org-footnote-define-inline}, +For footnote settings, use the following keywords. The corresponding +variables are @code{org-footnote-define-inline}, @code{org-footnote-auto-label}, and @code{org-footnote-auto-adjust}. @cindex @code{fninline}, STARTUP keyword @cindex @code{nofninline}, STARTUP keyword @@ -16414,67 +17404,57 @@ entitiesplain @r{Leave entities plain} @item #+TAGS: TAG1(c1) TAG2(c2) @vindex org-tag-alist -These lines (several such lines are allowed) specify the valid tags in -this file, and (potentially) the corresponding @emph{fast tag selection} -keys. The corresponding variable is @code{org-tag-alist}. +These lines specify valid tags for this file. Org accepts multiple tags +lines. Tags could correspond to the @emph{fast tag selection} keys. The +corresponding variable is @code{org-tag-alist}. @cindex #+TBLFM @item #+TBLFM: -This line contains the formulas for the table directly above the line. - -Table can have multiple lines containing @samp{#+TBLFM:}. Note -that only the first line of @samp{#+TBLFM:} will be applied when -you recalculate the table. For more details see @ref{Using -multiple #+TBLFM lines} in @ref{Editing and debugging formulas}. - +This line is for formulas for the table directly above. A table can have +multiple @samp{#+TBLFM:} lines. On table recalculation, Org applies only the +first @samp{#+TBLFM:} line. For details see @ref{Using multiple #+TBLFM +lines} in @ref{Editing and debugging formulas}. @item #+TITLE:, #+AUTHOR:, #+EMAIL:, #+LANGUAGE:, #+DATE:, @itemx #+OPTIONS:, #+BIND:, -@itemx #+DESCRIPTION:, #+KEYWORDS:, -@itemx #+LaTeX_HEADER:, #+LaTeX_HEADER_EXTRA:, -@itemx #+HTML_HEAD:, #+HTML_HEAD_EXTRA:, #+HTML_LINK_UP:, #+HTML_LINK_HOME:, @itemx #+SELECT_TAGS:, #+EXCLUDE_TAGS: These lines provide settings for exporting files. For more details see @ref{Export settings}. @item #+TODO: #+SEQ_TODO: #+TYP_TODO: @vindex org-todo-keywords -These lines set the TODO keywords and their interpretation in the -current file. The corresponding variable is @code{org-todo-keywords}. +These lines set the TODO keywords and their significance to the current file. +The corresponding variable is @code{org-todo-keywords}. @end table -@node The very busy C-c C-c key, Clean view, In-buffer settings, Miscellaneous +@node The very busy C-c C-c key @section The very busy C-c C-c key @kindex C-c C-c @cindex C-c C-c, overview -The key @kbd{C-c C-c} has many purposes in Org, which are all -mentioned scattered throughout this manual. One specific function of -this key is to add @emph{tags} to a headline (@pxref{Tags}). In many -other circumstances it means something like @emph{``Hey Org, look -here and update according to what you see here''}. Here is a summary of -what this means in different contexts. +The @kbd{C-c C-c} key in Org serves many purposes depending on the context. +It is probably the most over-worked, multi-purpose key combination in Org. +Its uses are well-documented through out this manual, but here is a +consolidated list for easy reference. @itemize @minus @item -If there are highlights in the buffer from the creation of a sparse -tree, or from clock display, remove these highlights. +If any highlights shown in the buffer from the creation of a sparse tree, or +from clock display, remove such highlights. @item -If the cursor is in one of the special @code{#+KEYWORD} lines, this -triggers scanning the buffer for these lines and updating the -information. +If the cursor is in one of the special @code{#+KEYWORD} lines, scan the +buffer for these lines and update the information. @item -If the cursor is inside a table, realign the table. This command -works even if the automatic table editor has been turned off. +If the cursor is inside a table, realign the table. The table realigns even +if automatic table editor is turned off. @item If the cursor is on a @code{#+TBLFM} line, re-apply the formulas to the entire table. @item -If the current buffer is a capture buffer, close the note and file it. -With a prefix argument, file it, without further interaction, to the -default location. +If the current buffer is a capture buffer, close the note and file it. With +a prefix argument, also jump to the target location after saving the note. @item If the cursor is on a @code{<<>>}, update radio targets and corresponding links in this buffer. @item -If the cursor is in a property line or at the start or end of a property +If the cursor is on a property line or at the start or end of a property drawer, offer property commands. @item If the cursor is at a footnote reference, go to the corresponding @@ -16494,18 +17474,18 @@ block is updated. If the cursor is at a timestamp, fix the day name in the timestamp. @end itemize -@node Clean view, TTY keys, The very busy C-c C-c key, Miscellaneous +@node Clean view @section A cleaner outline view @cindex hiding leading stars @cindex dynamic indentation @cindex odd-levels-only outlines @cindex clean outline view -Some people find it noisy and distracting that the Org headlines start with a -potentially large number of stars, and that text below the headlines is not -indented. While this is no problem when writing a @emph{book-like} document -where the outline headings are really section headings, in a more -@emph{list-oriented} outline, indented structure is a lot cleaner: +Org's default outline with stars and no indents can become too cluttered for +short documents. For @emph{book-like} long documents, the effect is not as +noticeable. Org provides an alternate stars and indentation scheme, as shown +on the right in the following table. It uses only one star and indents text +to line with the heading: @example @group @@ -16521,38 +17501,40 @@ more text | more text @noindent -If you are using at least Emacs 23.2@footnote{Emacs 23.1 can actually crash -with @code{org-indent-mode}} and version 6.29 of Org, this kind of view can -be achieved dynamically at display time using @code{org-indent-mode}. In -this minor mode, all lines are prefixed for display with the necessary amount -of space@footnote{@code{org-indent-mode} also sets the @code{wrap-prefix} -property, such that @code{visual-line-mode} (or purely setting -@code{word-wrap}) wraps long lines (including headlines) correctly indented. -}. Also headlines are prefixed with additional stars, so that the amount of -indentation shifts by two@footnote{See the variable -@code{org-indent-indentation-per-level}.} spaces per level. All headline -stars but the last one are made invisible using the @code{org-hide} -face@footnote{Turning on @code{org-indent-mode} sets +To turn this mode on, use the minor mode, @code{org-indent-mode}. Text lines +that are not headlines are prefixed with spaces to vertically align with the +headline text@footnote{The @code{org-indent-mode} also sets the +@code{wrap-prefix} correctly for indenting and wrapping long lines of +headlines or text. This minor mode handles @code{visual-line-mode} and +directly applied settings through @code{word-wrap}.}. + +To make more horizontal space, the headlines are shifted by two stars. This +can be configured by the @code{org-indent-indentation-per-level} variable. +Only one star on each headline is visible, the rest are masked with the same +font color as the background. This font face can be configured with the +@code{org-hide} variable. + +Note that turning on @code{org-indent-mode} sets @code{org-hide-leading-stars} to @code{t} and @code{org-adapt-indentation} to -@code{nil}.}; see below under @samp{2.} for more information on how this -works. You can turn on @code{org-indent-mode} for all files by customizing -the variable @code{org-startup-indented}, or you can turn it on for -individual files using +@code{nil}; @samp{2.} below shows how this works. + +To globally turn on @code{org-indent-mode} for all files, customize the +variable @code{org-startup-indented}. + +To turn on indenting for individual files, use @code{#+STARTUP} option as +follows: @example #+STARTUP: indent @end example -If you want a similar effect in an earlier version of Emacs and/or Org, or if -you want the indentation to be hard space characters so that the plain text -file looks as similar as possible to the Emacs display, Org supports you in -the following way: +Indent on startup makes Org use hard spaces to align text with headings as +shown in examples below. @enumerate @item @emph{Indentation of text below headlines}@* -You may indent text below each headline to make the left boundary line up -with the headline, like +Indent text to align with the headline. @example *** 3rd level @@ -16560,23 +17542,21 @@ with the headline, like @end example @vindex org-adapt-indentation -Org supports this with paragraph filling, line wrapping, and structure -editing@footnote{See also the variable @code{org-adapt-indentation}.}, -preserving or adapting the indentation as appropriate. +Org adapts indentations with paragraph filling, line wrapping, and structure +editing@footnote{Also see the variable @code{org-adapt-indentation}.}. @item @vindex org-hide-leading-stars -@emph{Hiding leading stars}@* You can modify the display in such a way that -all leading stars become invisible. To do this in a global way, configure -the variable @code{org-hide-leading-stars} or change this on a per-file basis -with +@emph{Hiding leading stars}@* Org can make leading stars invisible. For +global preference, configure the variable @code{org-hide-leading-stars}. For +per-file preference, use these file @code{#+STARTUP} options: @example #+STARTUP: hidestars #+STARTUP: showstars @end example -With hidden stars, the tree becomes: +With stars hidden, the tree is shown as: @example @group @@ -16589,50 +17569,39 @@ With hidden stars, the tree becomes: @noindent @vindex org-hide @r{(face)} -The leading stars are not truly replaced by whitespace, they are only -fontified with the face @code{org-hide} that uses the background color as -font color. If you are not using either white or black background, you may -have to customize this face to get the wanted effect. Another possibility is -to set this font such that the extra stars are @i{almost} invisible, for -example using the color @code{grey90} on a white background. +Because Org makes the font color same as the background color to hide to +stars, sometimes @code{org-hide} face may need tweaking to get the effect +right. For some black and white combinations, @code{grey90} on a white +background might mask the stars better. @item @vindex org-odd-levels-only -Things become cleaner still if you skip all the even levels and use only odd -levels 1, 3, 5..., effectively adding two stars to go from one outline level -to the next@footnote{When you need to specify a level for a property search -or refile targets, @samp{LEVEL=2} will correspond to 3 stars, etc.}. In this -way we get the outline view shown at the beginning of this section. In order -to make the structure editing and export commands handle this convention -correctly, configure the variable @code{org-odd-levels-only}, or set this on -a per-file basis with one of the following lines: +Using stars for only odd levels, 1, 3, 5, @dots{}, can also clean up the +clutter. This removes two stars from each level@footnote{Because +@samp{LEVEL=2} has 3 stars, @samp{LEVEL=3} has 4 stars, and so on}. For Org +to properly handle this cleaner structure during edits and exports, configure +the variable @code{org-odd-levels-only}. To set this per-file, use either +one of the following lines: @example #+STARTUP: odd #+STARTUP: oddeven @end example -You can convert an Org file from single-star-per-level to the -double-star-per-level convention with @kbd{M-x org-convert-to-odd-levels -RET} in that file. The reverse operation is @kbd{M-x -org-convert-to-oddeven-levels}. +To switch between single and double stars layouts, use @kbd{M-x +org-convert-to-odd-levels RET} and @kbd{M-x org-convert-to-oddeven-levels}. @end enumerate -@node TTY keys, Interaction, Clean view, Miscellaneous +@node TTY keys @section Using Org on a tty @cindex tty key bindings -Because Org contains a large number of commands, by default many of -Org's core commands are bound to keys that are generally not -accessible on a tty, such as the cursor keys (@key{left}, @key{right}, -@key{up}, @key{down}), @key{TAB} and @key{RET}, in particular when used -together with modifiers like @key{Meta} and/or @key{Shift}. To access -these commands on a tty when special keys are unavailable, the following -alternative bindings can be used. The tty bindings below will likely be -more cumbersome; you may find for some of the bindings below that a -customized workaround suits you better. For example, changing a timestamp -is really only fun with @kbd{S-@key{cursor}} keys, whereas on a -tty you would rather use @kbd{C-c .} to re-insert the timestamp. +Org provides alternative key bindings for TTY and modern mobile devices that +cannot handle cursor keys and complex modifier key chords. Some of these +workarounds may be more cumbersome than necessary. Users should look into +customizing these further based on their usage needs. For example, the +normal @kbd{S-@key{cursor}} for editing timestamp might be better with +@kbd{C-c .} chord. @multitable @columnfractions 0.15 0.2 0.1 0.2 @item @b{Default} @tab @b{Alternative 1} @tab @b{Speed key} @tab @b{Alternative 2} @@ -16657,74 +17626,62 @@ tty you would rather use @kbd{C-c .} to re-insert the timestamp. @end multitable -@node Interaction, org-crypt, TTY keys, Miscellaneous +@node Interaction @section Interaction with other packages @cindex packages, interaction with other -Org lives in the world of GNU Emacs and interacts in various ways -with other code out there. +Org's compatibility and the level of interaction with other Emacs packages +are documented here. + @menu * Cooperation:: Packages Org cooperates with * Conflicts:: Packages that lead to conflicts @end menu -@node Cooperation, Conflicts, Interaction, Interaction +@node Cooperation @subsection Packages that Org cooperates with @table @asis @cindex @file{calc.el} @cindex Gillespie, Dave @item @file{calc.el} by Dave Gillespie -Org uses the Calc package for implementing spreadsheet -functionality in its tables (@pxref{The spreadsheet}). Org -checks for the availability of Calc by looking for the function -@code{calc-eval} which will have been autoloaded during setup if Calc has -been installed properly. As of Emacs 22, Calc is part of the Emacs -distribution. Another possibility for interaction between the two -packages is using Calc for embedded calculations. @xref{Embedded Mode, -, Embedded Mode, calc, GNU Emacs Calc Manual}. +Org uses the Calc package for tables to implement spreadsheet functionality +(@pxref{The spreadsheet}). Org also uses Calc for embedded calculations. +@xref{Embedded Mode, , Embedded Mode, calc, GNU Emacs Calc Manual}. @item @file{constants.el} by Carsten Dominik @cindex @file{constants.el} @cindex Dominik, Carsten @vindex org-table-formula-constants -In a table formula (@pxref{The spreadsheet}), it is possible to use -names for natural constants or units. Instead of defining your own -constants in the variable @code{org-table-formula-constants}, install -the @file{constants} package which defines a large number of constants -and units, and lets you use unit prefixes like @samp{M} for -@samp{Mega}, etc. You will need version 2.0 of this package, available -at @url{http://www.astro.uva.nl/~dominik/Tools}. Org checks for -the function @code{constants-get}, which has to be autoloaded in your -setup. See the installation instructions in the file -@file{constants.el}. +Org can use names for constants in formulas in tables. Org can also use +calculation suffixes for units, such as @samp{M} for @samp{Mega}. For a +standard collection of such constants, install the @file{constants} package. +Install version 2.0 of this package, available at +@url{http://www.astro.uva.nl/~dominik/Tools}. Org checks if the function +@code{constants-get} has been autoloaded. Installation instructions are in +the file, @file{constants.el}. @item @file{cdlatex.el} by Carsten Dominik @cindex @file{cdlatex.el} @cindex Dominik, Carsten -Org mode can make use of the CD@LaTeX{} package to efficiently enter -@LaTeX{} fragments into Org files. See @ref{CDLaTeX mode}. +Org mode can use CD@LaTeX{} package to efficiently enter @LaTeX{} fragments +into Org files (@pxref{CDLaTeX mode}). @item @file{imenu.el} by Ake Stenhoff and Lars Lindberg @cindex @file{imenu.el} -Imenu allows menu access to an index of items in a file. Org mode -supports Imenu---all you need to do to get the index is the following: +Imenu creates dynamic menus based on an index of items in a file. Org mode +supports Imenu menus. Enable it with a mode hook as follows: @lisp (add-hook 'org-mode-hook (lambda () (imenu-add-to-menubar "Imenu"))) @end lisp @vindex org-imenu-depth -By default the index is two levels deep---you can modify the depth using -the option @code{org-imenu-depth}. -@item @file{remember.el} by John Wiegley -@cindex @file{remember.el} -@cindex Wiegley, John -Org used to use this package for capture, but no longer does. +By default the Imenu index is two levels deep. Change the index depth using +thes variable, @code{org-imenu-depth}. @item @file{speedbar.el} by Eric M. Ludlam @cindex @file{speedbar.el} @cindex Ludlam, Eric M. -Speedbar is a package that creates a special frame displaying files and -index items in files. Org mode supports Speedbar and allows you to -drill into Org files directly from the Speedbar. It also allows you to -restrict the scope of agenda commands to a file or a subtree by using -the command @kbd{<} in the Speedbar frame. +Speedbar package creates a special Emacs frame for displaying files and index +items in files. Org mode supports Speedbar; users can drill into Org files +directly from the Speedbar. The @kbd{<} in the Speedbar frame tweeks the +agenda commands to that file or to a subtree. @cindex @file{table.el} @item @file{table.el} by Takaaki Ota @kindex C-c C-c @@ -16733,13 +17690,11 @@ the command @kbd{<} in the Speedbar frame. @cindex Ota, Takaaki Complex ASCII tables with automatic line wrapping, column- and row-spanning, -and alignment can be created using the Emacs table package by Takaaki Ota -(@uref{http://sourceforge.net/projects/table}, and also part of Emacs 22). -Org mode will recognize these tables and export them properly. Because of -interference with other Org mode functionality, you unfortunately cannot edit -these tables directly in the buffer. Instead, you need to use the command -@kbd{C-c '} to edit them, similar to source code snippets. - +and alignment can be created using the Emacs table package by Takaaki Ota. +Org mode recognizes such tables and export them properly. @kbd{C-c '} to +edit these tables in a special buffer, much like Org's @samp{src} code +blocks. Because of interference with other Org mode functionality, Takaaki +Ota tables cannot be edited directly in the Org buffer. @table @kbd @orgcmd{C-c ',org-edit-special} Edit a @file{table.el} table. Works when the cursor is in a table.el table. @@ -16747,50 +17702,37 @@ Edit a @file{table.el} table. Works when the cursor is in a table.el table. @orgcmd{C-c ~,org-table-create-with-table.el} Insert a @file{table.el} table. If there is already a table at point, this command converts it between the @file{table.el} format and the Org mode -format. See the documentation string of the command -@code{org-convert-table} for the restrictions under which this is -possible. +format. See the documentation string of the command @code{org-convert-table} +for details. @end table -@file{table.el} is part of Emacs since Emacs 22. -@item @file{footnote.el} by Steven L. Baur -@cindex @file{footnote.el} -@cindex Baur, Steven L. -Org mode recognizes numerical footnotes as provided by this package. -However, Org mode also has its own footnote support (@pxref{Footnotes}), -which makes using @file{footnote.el} unnecessary. @end table -@node Conflicts, , Cooperation, Interaction -@subsection Packages that lead to conflicts with Org mode +@node Conflicts +@subsection Packages that conflict with Org mode @table @asis @cindex @code{shift-selection-mode} @vindex org-support-shift-select -In Emacs 23, @code{shift-selection-mode} is on by default, meaning that -cursor motions combined with the shift key should start or enlarge regions. -This conflicts with the use of @kbd{S-@key{cursor}} commands in Org to change -timestamps, TODO keywords, priorities, and item bullet types if the cursor is -at such a location. By default, @kbd{S-@key{cursor}} commands outside -special contexts don't do anything, but you can customize the variable -@code{org-support-shift-select}. Org mode then tries to accommodate shift -selection by (i) using it outside of the special contexts where special -commands apply, and by (ii) extending an existing active region even if the -cursor moves across a special context. +In Emacs, @code{shift-selection-mode} combines cursor motions with shift key +to enlarge regions. Emacs sets this mode by default. This conflicts with +Org's use of @kbd{S-@key{cursor}} commands to change timestamps, TODO +keywords, priorities, and item bullet types, etc. Since @kbd{S-@key{cursor}} +commands outside of specific contexts don't do anything, Org offers the +variable @code{org-support-shift-select} for customization. Org mode +accommodates shift selection by (i) making it available outside of the +special contexts where special commands apply, and (ii) extending an +existing active region even if the cursor moves across a special context. @item @file{CUA.el} by Kim. F. Storm @cindex @file{CUA.el} @cindex Storm, Kim. F. @vindex org-replace-disputed-keys -Key bindings in Org conflict with the @kbd{S-} keys used by CUA mode -(as well as @code{pc-select-mode} and @code{s-region-mode}) to select and extend the -region. In fact, Emacs 23 has this built-in in the form of -@code{shift-selection-mode}, see previous paragraph. If you are using Emacs -23, you probably don't want to use another package for this purpose. However, -if you prefer to leave these keys to a different package while working in -Org mode, configure the variable @code{org-replace-disputed-keys}. When set, -Org will move the following key bindings in Org files, and in the agenda -buffer (but not during date selection). +Org key bindings conflict with @kbd{S-} keys used by CUA mode. For +Org to relinquish these bindings to CUA mode, configure the variable +@code{org-replace-disputed-keys}. When set, Org moves the following key +bindings in Org files, and in the agenda buffer (but not during date +selection). @example S-UP @result{} M-p S-DOWN @result{} M-n @@ -16799,9 +17741,8 @@ C-S-LEFT @result{} M-S-- C-S-RIGHT @result{} M-S-+ @end example @vindex org-disputed-keys -Yes, these are unfortunately more difficult to remember. If you want -to have other replacement keys, look at the variable -@code{org-disputed-keys}. +Yes, these are unfortunately more difficult to remember. To define a +different replacement keys, look at the variable @code{org-disputed-keys}. @item @file{ecomplete.el} by Lars Magne Ingebrigtsen @email{larsi@@gnus.org} @cindex @file{ecomplete.el} @@ -16819,9 +17760,8 @@ manually when needed in the messages body. @cindex @file{filladapt.el} Org mode tries to do the right thing when filling paragraphs, list items and -other elements. Many users reported they had problems using both -@file{filladapt.el} and Org mode, so a safe thing to do is to disable it like -this: +other elements. Many users reported problems using both @file{filladapt.el} +and Org mode, so a safe thing to do is to disable filladapt like this: @lisp (add-hook 'org-mode-hook 'turn-off-filladapt-mode) @@ -16836,20 +17776,19 @@ fixed this problem: @lisp (add-hook 'org-mode-hook (lambda () - (org-set-local 'yas/trigger-key [tab]) + (setq-local yas/trigger-key [tab]) (define-key yas/keymap [tab] 'yas/next-field-or-maybe-expand))) @end lisp The latest version of yasnippet doesn't play well with Org mode. If the -above code does not fix the conflict, start by defining the following -function: +above code does not fix the conflict, first define the following function: @lisp (defun yas/org-very-safe-expand () (let ((yas/fallback-behavior 'return-nil)) (yas/expand))) @end lisp -Then, tell Org mode what to do with the new function: +Then tell Org mode to use that function: @lisp (add-hook 'org-mode-hook @@ -16892,21 +17831,19 @@ another key for this command, or override the key in @end table -@node org-crypt, , Interaction, Miscellaneous +@node org-crypt @section org-crypt.el @cindex @file{org-crypt.el} @cindex @code{org-decrypt-entry} -Org-crypt will encrypt the text of an entry, but not the headline, or -properties. Org-crypt uses the Emacs EasyPG library to encrypt and decrypt -files. +Org crypt encrypts the text of an Org entry, but not the headline, or +properties. Org crypt uses the Emacs EasyPG library to encrypt and decrypt. Any text below a headline that has a @samp{:crypt:} tag will be automatically -be encrypted when the file is saved. If you want to use a different tag just -customize the @code{org-crypt-tag-matcher} setting. +be encrypted when the file is saved. To use a different tag, customize the +@code{org-crypt-tag-matcher} variable. -To use org-crypt it is suggested that you have the following in your -@file{.emacs}: +Suggested Org crypt settings in Emacs init file: @lisp (require 'org-crypt) @@ -16928,14 +17865,14 @@ To use org-crypt it is suggested that you have the following in your ;; # -*- buffer-auto-save-file-name: nil; -*- @end lisp -Excluding the crypt tag from inheritance prevents already encrypted text -being encrypted again. +Excluding the crypt tag from inheritance prevents encrypting previously +encrypted text. -@node Hacking, MobileOrg, Miscellaneous, Top +@node Hacking @appendix Hacking @cindex hacking -This appendix covers some aspects where users can extend the functionality of +This appendix covers some areas where users can extend the functionality of Org. @menu @@ -16953,38 +17890,35 @@ Org. * Using the mapping API:: Mapping over all or selected entries @end menu -@node Hooks, Add-on packages, Hacking, Hacking +@node Hooks @section Hooks @cindex hooks -Org has a large number of hook variables that can be used to add -functionality. This appendix about hacking is going to illustrate the -use of some of them. A complete list of all hooks with documentation is -maintained by the Worg project and can be found at -@uref{http://orgmode.org/worg/org-configs/org-hooks.php}. +Org has a large number of hook variables for adding functionality. This +appendix illustrates using a few. A complete list of hooks with +documentation is maintained by the Worg project at +@uref{http://orgmode.org/worg/doc.html#hooks}. -@node Add-on packages, Adding hyperlink types, Hooks, Hacking +@node Add-on packages @section Add-on packages @cindex add-on packages -A large number of add-on packages have been written by various authors. +Various authors wrote a large number of add-on packages for Org. These packages are not part of Emacs, but they are distributed as contributed packages with the separate release available at @uref{http://orgmode.org}. See the @file{contrib/README} file in the source code directory for a list of -contributed files. You may also find some more information on the Worg page: +contributed files. Worg page with more information is at: @uref{http://orgmode.org/worg/org-contrib/}. -@node Adding hyperlink types, Adding export back-ends, Add-on packages, Hacking +@node Adding hyperlink types @section Adding hyperlink types @cindex hyperlinks, adding new types -Org has a large number of hyperlink types built-in -(@pxref{Hyperlinks}). If you would like to add new link types, Org -provides an interface for doing so. Let's look at an example file, -@file{org-man.el}, that will add support for creating links like -@samp{[[man:printf][The printf manpage]]} to show Unix manual pages inside -Emacs: +Org has many built-in hyperlink types (@pxref{Hyperlinks}), and an interface +for adding new link types. The example file, @file{org-man.el}, shows the +process of adding Org links to Unix man pages, which look like this: +@samp{[[man:printf][The printf manpage]]}: @lisp ;;; org-man.el - Support for links to manpages in Org @@ -17029,149 +17963,118 @@ PATH should be a topic that can be thrown at the man command." @end lisp @noindent -You would activate this new link type in @file{.emacs} with +To activate links to man pages in Org, enter this in the init file: @lisp (require 'org-man) @end lisp @noindent -Let's go through the file and see what it does. +A review of @file{org-man.el}: @enumerate @item -It does @code{(require 'org)} to make sure that @file{org.el} has been -loaded. +First, @code{(require 'org)} ensures @file{org.el} is loaded. @item -The next line calls @code{org-add-link-type} to define a new link type -with prefix @samp{man}. The call also contains the name of a function -that will be called to follow such a link. +The @code{org-add-link-type} defines a new link type with @samp{man} prefix. +The call contains the function to call that follows the link type. @item @vindex org-store-link-functions -The next line adds a function to @code{org-store-link-functions}, in -order to allow the command @kbd{C-c l} to record a useful link in a -buffer displaying a man page. +The next line adds a function to @code{org-store-link-functions} that records +a useful link with the command @kbd{C-c l} in a buffer displaying a man page. @end enumerate -The rest of the file defines the necessary variables and functions. -First there is a customization variable that determines which Emacs -command should be used to display man pages. There are two options, -@code{man} and @code{woman}. Then the function to follow a link is -defined. It gets the link path as an argument---in this case the link -path is just a topic for the manual command. The function calls the -value of @code{org-man-command} to display the man page. - -Finally the function @code{org-man-store-link} is defined. When you try -to store a link with @kbd{C-c l}, this function will be called to -try to make a link. The function must first decide if it is supposed to -create the link for this buffer type; we do this by checking the value -of the variable @code{major-mode}. If not, the function must exit and -return the value @code{nil}. If yes, the link is created by getting the -manual topic from the buffer name and prefixing it with the string -@samp{man:}. Then it must call the command @code{org-store-link-props} -and set the @code{:type} and @code{:link} properties. Optionally you -can also set the @code{:description} property to provide a default for -the link description when the link is later inserted into an Org -buffer with @kbd{C-c C-l}. - -When it makes sense for your new link type, you may also define a function -@code{org-PREFIX-complete-link} that implements special (e.g., completion) -support for inserting such a link with @kbd{C-c C-l}. Such a function should -not accept any arguments, and return the full link with prefix. - -@node Adding export back-ends, Context-sensitive commands, Adding hyperlink types, Hacking +The rest of the file defines necessary variables and functions. First is the +customization variable @code{org-man-command}. It has two options, +@code{man} and @code{woman}. Next is a function whose argument is the link +path, which for man pages is the topic of the man command. To follow the +link, the function calls the @code{org-man-command} to display the man page. + + +@kbd{C-c l} constructs and stores the link. + +@kbd{C-c l} calls the function @code{org-man-store-link}, which first checks +if the @code{major-mode} is appropriate. If check fails, the function +returns @code{nil}. Otherwise the function makes a link string by combining +the @samp{man:} prefix with the man topic. The function then calls +@code{org-store-link-props} with @code{:type} and @code{:link} properties. A +@code{:description} property is an optional string that is displayed when the +function inserts the link in the Org buffer. + +@kbd{C-c C-l} inserts the stored link. + +To define new link types, define a function that implements completion +support with @kbd{C-c C-l}. This function should not accept any arguments +but return the appropriate prefix and complete link string. + +@node Adding export back-ends @section Adding export back-ends @cindex Export, writing back-ends -Org 8.0 comes with a completely rewritten export engine which makes it easy -to write new export back-ends, either from scratch, or from deriving them -from existing ones. - -Your two entry points are respectively @code{org-export-define-backend} and -@code{org-export-define-derived-backend}. To grok these functions, you -should first have a look at @file{ox-latex.el} (for how to define a new -back-end from scratch) and @file{ox-beamer.el} (for how to derive a new -back-end from an existing one. - -When creating a new back-end from scratch, the basic idea is to set the name -of the back-end (as a symbol) and an an alist of elements and export -functions. On top of this, you will need to set additional keywords like -@code{:menu-entry} (to display the back-end in the export dispatcher), -@code{:export-block} (to specify what blocks should not be exported by this -back-end), and @code{:options-alist} (to let the user set export options that -are specific to this back-end.) - -Deriving a new back-end is similar, except that you need to set -@code{:translate-alist} to an alist of export functions that should be used -instead of the parent back-end functions. - -For a complete reference documentation, see +Org's export engine makes it easy for writing new back-ends. The framework +on which the engine was built makes it easy to derive new back-ends from +existing ones. + +The two main entry points to the export engine are: +@code{org-export-define-backend} and +@code{org-export-define-derived-backend}. To grok these functions, see +@file{ox-latex.el} for an example of defining a new back-end from scratch, +and @file{ox-beamer.el} for an example of deriving from an existing engine. + +For creating a new back-end from scratch, first set its name as a symbol in +an alist consisting of elements and export functions. To make the back-end +visible to the export dispatcher, set @code{:menu-entry} keyword. For export +options specific to this back-end, set the @code{:options-alist}. + +For creating a new back-end from an existing one, set @code{:translate-alist} +to an alist of export functions. This alist replaces the parent back-end +functions. + +For complete documentation, see @url{http://orgmode.org/worg/dev/org-export-reference.html, the Org Export Reference on Worg}. -@node Context-sensitive commands, Tables in arbitrary syntax, Adding export back-ends, Hacking +@node Context-sensitive commands @section Context-sensitive commands @cindex context-sensitive commands, hooks @cindex add-ons, context-sensitive commands @vindex org-ctrl-c-ctrl-c-hook -Org has several commands that act differently depending on context. The most -important example is the @kbd{C-c C-c} (@pxref{The very busy C-c C-c key}). -Also the @kbd{M-cursor} and @kbd{M-S-cursor} keys have this property. - -Add-ons can tap into this functionality by providing a function that detects -special context for that add-on and executes functionality appropriate for -the context. Here is an example from Dan Davison's @file{org-R.el} which -allows you to evaluate commands based on the @file{R} programming language -@footnote{@file{org-R.el} has been replaced by the Org mode functionality -described in @ref{Working With Source Code} and is now obsolete.}. For this -package, special contexts are lines that start with @code{#+R:} or -@code{#+RR:}. - -@lisp -(defun org-R-apply-maybe () - "Detect if this is context for org-R and execute R commands." - (if (save-excursion - (beginning-of-line 1) - (looking-at "#\\+RR?:")) - (progn (call-interactively 'org-R-apply) - t) ;; to signal that we took action - nil)) ;; to signal that we did not - -(add-hook 'org-ctrl-c-ctrl-c-hook 'org-R-apply-maybe) -@end lisp +Org has facilities for building context sensitive commands. Authors of Org +add-ons can tap into this functionality. -The function first checks if the cursor is in such a line. If that is the -case, @code{org-R-apply} is called and the function returns @code{t} to -signal that action was taken, and @kbd{C-c C-c} will stop looking for other -contexts. If the function finds it should do nothing locally, it returns -@code{nil} so that other, similar functions can have a try. +Some Org commands change depending on the context. The most important +example of this behavior is the @kbd{C-c C-c} (@pxref{The very busy C-c C-c +key}). Other examples are @kbd{M-cursor} and @kbd{M-S-cursor}. +These context sensitive commands work by providing a function that detects +special context for that add-on and executes functionality appropriate for +that context. -@node Tables in arbitrary syntax, Dynamic blocks, Context-sensitive commands, Hacking +@node Tables in arbitrary syntax @section Tables and lists in arbitrary syntax @cindex tables, in other modes @cindex lists, in other modes @cindex Orgtbl mode -Since Orgtbl mode can be used as a minor mode in arbitrary buffers, a -frequent feature request has been to make it work with native tables in -specific languages, for example @LaTeX{}. However, this is extremely -hard to do in a general way, would lead to a customization nightmare, -and would take away much of the simplicity of the Orgtbl mode table -editor. - -This appendix describes a different approach. We keep the Orgtbl mode -table in its native format (the @i{source table}), and use a custom -function to @i{translate} the table to the correct syntax, and to -@i{install} it in the right location (the @i{target table}). This puts -the burden of writing conversion functions on the user, but it allows -for a very flexible system. - -Bastien added the ability to do the same with lists, in Orgstruct mode. You -can use Org's facilities to edit and structure lists by turning -@code{orgstruct-mode} on, then locally exporting such lists in another format -(HTML, @LaTeX{} or Texinfo.) - +Because of Org's success in handling tables with Orgtbl, a frequently asked +feature is to Org's usability functions to other table formats native to +other modem's, such as @LaTeX{}. This would be hard to do in a general way +without complicated customization nightmares. Moreover, that would take Org +away from its simplicity roots that Orgtbl has proven. There is, however, an +alternate approach to accomplishing the same. + +This approach involves implementing a custom @emph{translate} function that +operates on a native Org @emph{source table} to produce a table in another +format. This strategy would keep the excellently working Orgtbl simple and +isolate complications, if any, confined to the translate function. To add +more alien table formats, we just add more translate functions. Also the +burden of developing custom translate functions for new table formats will be +in the hands of those who know those formats best. + +For an example of how this strategy works, see Orgstruct mode. In that mode, +Bastien added the ability to use Org's facilities to edit and re-structure +lists. He did by turning @code{orgstruct-mode} on, and then exporting the +list locally to another format, such as HTML, @LaTeX{} or Texinfo. @menu * Radio tables:: Sending and receiving radio tables @@ -17180,15 +18083,17 @@ can use Org's facilities to edit and structure lists by turning * Radio lists:: Sending and receiving lists @end menu -@node Radio tables, A @LaTeX{} example, Tables in arbitrary syntax, Tables in arbitrary syntax +@node Radio tables @subsection Radio tables @cindex radio tables -To define the location of the target table, you first need to create two -lines that are comments in the current mode, but contain magic words -@code{BEGIN/END RECEIVE ORGTBL} for Orgtbl mode to find. Orgtbl mode will -insert the translated table between these lines, replacing whatever was there -before. For example in C mode where comments are between @code{/* ... */}: +Radio tables are target locations for translated tables that are not near +their source. Org finds the target location and inserts the translated +table. + +The key to finding the target location are the magic words @code{BEGIN/END +RECEIVE ORGTBL}. They have to appear as comments in the current mode. If +the mode is C, then: @example /* BEGIN RECEIVE ORGTBL table_name */ @@ -17196,8 +18101,8 @@ before. For example in C mode where comments are between @code{/* ... */}: @end example @noindent -Just above the source table, we put a special line that tells -Orgtbl mode how to translate this table and where to install it. For +At the location of source, Org needs a special line to direct Orgtbl to +translate and to find the target for inserting the translated table. For example: @cindex #+ORGTBL @example @@ -17205,67 +18110,53 @@ example: @end example @noindent -@code{table_name} is the reference name for the table that is also used -in the receiver lines. @code{translation_function} is the Lisp function -that does the translation. Furthermore, the line can contain a list of -arguments (alternating key and value) at the end. The arguments will be -passed as a property list to the translation function for -interpretation. A few standard parameters are already recognized and -acted upon before the translation function is called: +@code{table_name} is the table's reference name, which is also used in the +receiver lines, and the @code{translation_function} is the Lisp function that +translates. This line, in addition, may also contain alternating key and +value arguments at the end. The translation function gets these values as a +property list. A few standard parameters are already recognized and acted +upon before the translation function is called: @table @code @item :skip N -Skip the first N lines of the table. Hlines do count as separate lines for -this parameter! +Skip the first N lines of the table. Hlines do count; include them if they +are to be skipped. @item :skipcols (n1 n2 ...) -List of columns that should be skipped. If the table has a column with -calculation marks, that column is automatically discarded as well. -Please note that the translator function sees the table @emph{after} the -removal of these columns, the function never knows that there have been -additional columns. - -@item :no-escape t -When non-@code{nil}, do not escape special characters @code{&%#_^} when exporting -the table. The default value is @code{nil}. +List of columns to be skipped. First Org automatically discards columns with +calculation marks and then sends the table to the translator function, which +then skips columns as specified in @samp{skipcols}. @end table @noindent -The one problem remaining is how to keep the source table in the buffer -without disturbing the normal workings of the file, for example during -compilation of a C file or processing of a @LaTeX{} file. There are a -number of different solutions: +To keep the source table intact in the buffer without being disturbed when +the source file is compiled or otherwise being worked on, use one of these +strategies: @itemize @bullet @item -The table could be placed in a block comment if that is supported by the -language. For example, in C mode you could wrap the table between -@samp{/*} and @samp{*/} lines. +Place the table in a block comment. For example, in C mode you could wrap +the table between @samp{/*} and @samp{*/} lines. @item -Sometimes it is possible to put the table after some kind of @i{END} -statement, for example @samp{\bye} in @TeX{} and @samp{\end@{document@}} -in @LaTeX{}. +Put the table after an @samp{END} statement. For example @samp{\bye} in +@TeX{} and @samp{\end@{document@}} in @LaTeX{}. @item -You can just comment the table line-by-line whenever you want to process -the file, and uncomment it whenever you need to edit the table. This -only sounds tedious---the command @kbd{M-x orgtbl-toggle-comment RET} -makes this comment-toggling very easy, in particular if you bind it to a -key. +Comment and uncomment each line of the table during edits. The @kbd{M-x +orgtbl-toggle-comment RET} command makes toggling easy. @end itemize -@node A @LaTeX{} example, Translator functions, Radio tables, Tables in arbitrary syntax +@node A @LaTeX{} example @subsection A @LaTeX{} example of radio tables @cindex @LaTeX{}, and Orgtbl mode -The best way to wrap the source table in @LaTeX{} is to use the -@code{comment} environment provided by @file{comment.sty}. It has to be -activated by placing @code{\usepackage@{comment@}} into the document -header. Orgtbl mode can insert a radio table skeleton@footnote{By -default this works only for @LaTeX{}, HTML, and Texinfo. Configure the -variable @code{orgtbl-radio-table-templates} to install templates for other -modes.} with the command @kbd{M-x orgtbl-insert-radio-table RET}. You will -be prompted for a table name, let's say we use @samp{salesfigures}. You -will then get the following template: +To wrap a source table in @LaTeX{}, use the @code{comment} environment +provided by @file{comment.sty}. To activate it, put +@code{\usepackage@{comment@}} in the document header. Orgtbl mode inserts a +radio table skeleton@footnote{By default this works only for @LaTeX{}, HTML, +and Texinfo. Configure the variable @code{orgtbl-radio-table-templates} to +install templates for other export formats.} with the command @kbd{M-x +orgtbl-insert-radio-table RET}, which prompts for a table name. For example, +if @samp{salesfigures} is the name, the template inserts: @cindex #+ORGTBL, SEND @example @@ -17279,17 +18170,17 @@ will then get the following template: @noindent @vindex @LaTeX{}-verbatim-environments -The @code{#+ORGTBL: SEND} line tells Orgtbl mode to use the function -@code{orgtbl-to-latex} to convert the table into @LaTeX{} and to put it -into the receiver location with name @code{salesfigures}. You may now -fill in the table---feel free to use the spreadsheet features@footnote{If -the @samp{#+TBLFM} line contains an odd number of dollar characters, -this may cause problems with font-lock in @LaTeX{} mode. As shown in the -example you can fix this by adding an extra line inside the -@code{comment} environment that is used to balance the dollar -expressions. If you are using AUC@TeX{} with the font-latex library, a -much better solution is to add the @code{comment} environment to the -variable @code{LaTeX-verbatim-environments}.}: +The line @code{#+ORGTBL: SEND} tells Orgtbl mode to use the function +@code{orgtbl-to-latex} to convert the table to @LaTeX{} format, then insert +the table at the target (receive) location named @code{salesfigures}. Now +the table is ready for data entry. It can even use spreadsheet +features@footnote{If the @samp{#+TBLFM} line contains an odd number of dollar +characters, this may cause problems with font-lock in @LaTeX{} mode. As +shown in the example you can fix this by adding an extra line inside the +@code{comment} environment that is used to balance the dollar expressions. +If you are using AUC@TeX{} with the font-latex library, a much better +solution is to add the @code{comment} environment to the variable +@code{LaTeX-verbatim-environments}.}: @example % BEGIN RECEIVE ORGTBL salesfigures @@ -17307,14 +18198,12 @@ variable @code{LaTeX-verbatim-environments}.}: @end example @noindent -When you are done, press @kbd{C-c C-c} in the table to get the converted -table inserted between the two marker lines. +After editing, @kbd{C-c C-c} inserts translated table at the target location, +between the two marker lines. -Now let's assume you want to make the table header by hand, because you -want to control how columns are aligned, etc. In this case we make sure -that the table translator skips the first 2 lines of the source -table, and tell the command to work as a @i{splice}, i.e., to not produce -header and footer commands of the target table: +For hand-made custom tables, note that the translator needs to skip the first +two lines of the source table. Also the command has to @emph{splice} out the +target table without the header and footer. @example \begin@{tabular@}@{lrrr@} @@ -17335,135 +18224,109 @@ Month & \multicolumn@{1@}@{c@}@{Days@} & Nr.\ sold & per day\\ @end example The @LaTeX{} translator function @code{orgtbl-to-latex} is already part of -Orgtbl mode. It uses a @code{tabular} environment to typeset the table -and marks horizontal lines with @code{\hline}. Furthermore, it -interprets the following parameters (see also @pxref{Translator functions}): +Orgtbl mode and uses @code{tabular} environment by default to typeset the +table and mark the horizontal lines with @code{\hline}. For additional +parameters to control output, @pxref{Translator functions}: @table @code @item :splice nil/t -When set to t, return only table body lines, don't wrap them into a -tabular environment. Default is @code{nil}. +When non-@code{nil}, returns only table body lines; not wrapped in tabular +environment. Default is @code{nil}. @item :fmt fmt -A format to be used to wrap each field, it should contain @code{%s} for the -original field value. For example, to wrap each field value in dollars, -you could use @code{:fmt "$%s$"}. This may also be a property list with +Format to warp each field. It should contain @code{%s} for the original +field value. For example, to wrap each field value in dollar symbol, you +could use @code{:fmt "$%s$"}. Format can also wrap a property list with column numbers and formats, for example @code{:fmt (2 "$%s$" 4 "%s\\%%")}. -A function of one argument can be used in place of the strings; the -function must return a formatted string. +In place of a string, a function of one argument can be used; the function +must return a formatted string. @item :efmt efmt -Use this format to print numbers with exponentials. The format should -have @code{%s} twice for inserting mantissa and exponent, for example -@code{"%s\\times10^@{%s@}"}. The default is @code{"%s\\,(%s)"}. This -may also be a property list with column numbers and formats, for example +Format numbers as exponentials. The spec should have @code{%s} twice for +inserting mantissa and exponent, for example @code{"%s\\times10^@{%s@}"}. +This may also be a property list with column numbers and formats, for example @code{:efmt (2 "$%s\\times10^@{%s@}$" 4 "$%s\\cdot10^@{%s@}$")}. After -@code{efmt} has been applied to a value, @code{fmt} will also be -applied. Similar to @code{fmt}, functions of two arguments can be -supplied instead of strings. +@code{efmt} has been applied to a value, @code{fmt} will also be applied. +Functions with two arguments can be supplied instead of strings. By default, +no special formatting is applied. @end table -@node Translator functions, Radio lists, A @LaTeX{} example, Tables in arbitrary syntax +@node Translator functions @subsection Translator functions @cindex HTML, and Orgtbl mode @cindex translator function -Orgtbl mode has several translator functions built-in: @code{orgtbl-to-csv} -(comma-separated values), @code{orgtbl-to-tsv} (TAB-separated values) -@code{orgtbl-to-latex}, @code{orgtbl-to-html}, and @code{orgtbl-to-texinfo}. -Except for @code{orgtbl-to-html}@footnote{The HTML translator uses the same -code that produces tables during HTML export.}, these all use a generic -translator, @code{orgtbl-to-generic}. For example, @code{orgtbl-to-latex} -itself is a very short function that computes the column definitions for the -@code{tabular} environment, defines a few field and line separators and then -hands processing over to the generic translator. Here is the entire code: - -@lisp -@group -(defun orgtbl-to-latex (table params) - "Convert the Orgtbl mode TABLE to LaTeX." - (let* ((alignment (mapconcat (lambda (x) (if x "r" "l")) - org-table-last-alignment "")) - (params2 - (list - :tstart (concat "\\begin@{tabular@}@{" alignment "@}") - :tend "\\end@{tabular@}" - :lstart "" :lend " \\\\" :sep " & " - :efmt "%s\\,(%s)" :hline "\\hline"))) - (orgtbl-to-generic table (org-combine-plists params2 params)))) -@end group -@end lisp +Orgtbl mode has built-in translator functions: @code{orgtbl-to-csv} +(comma-separated values), @code{orgtbl-to-tsv} (TAB-separated values), +@code{orgtbl-to-latex}, @code{orgtbl-to-html}, @code{orgtbl-to-texinfo}, +@code{orgtbl-to-unicode} and @code{orgtbl-to-orgtbl}. They use the generic +translator, @code{orgtbl-to-generic}, which delegates translations to various +export back-ends. -As you can see, the properties passed into the function (variable -@var{PARAMS}) are combined with the ones newly defined in the function -(variable @var{PARAMS2}). The ones passed into the function (i.e., the -ones set by the @samp{ORGTBL SEND} line) take precedence. So if you -would like to use the @LaTeX{} translator, but wanted the line endings to -be @samp{\\[2mm]} instead of the default @samp{\\}, you could just -overrule the default with +Properties passed to the function through the @samp{ORGTBL SEND} line take +precedence over properties defined inside the function. For example, this +overrides the default @LaTeX{} line endings, @samp{\\}, with @samp{\\[2mm]}: @example #+ORGTBL: SEND test orgtbl-to-latex :lend " \\\\[2mm]" @end example -For a new language, you can either write your own converter function in -analogy with the @LaTeX{} translator, or you can use the generic function -directly. For example, if you have a language where a table is started -with @samp{!BTBL!}, ended with @samp{!ETBL!}, and where table lines are -started with @samp{!BL!}, ended with @samp{!EL!}, and where the field -separator is a TAB, you could call the generic translator like this (on -a single line!): +For a new language translator, define a converter function. It can be a +generic function, such as shown in this example. It marks a beginning and +ending of a table with @samp{!BTBL!} and @samp{!ETBL!}; a beginning and +ending of lines with @samp{!BL!} and @samp{!EL!}; and uses a TAB for a field +separator: -@example -#+ORGTBL: SEND test orgtbl-to-generic :tstart "!BTBL!" :tend "!ETBL!" - :lstart "!BL! " :lend " !EL!" :sep "\t" -@end example +@lisp +(defun orgtbl-to-language (table params) + "Convert the orgtbl-mode TABLE to language." + (orgtbl-to-generic + table + (org-combine-plists + '(:tstart "!BTBL!" :tend "!ETBL!" :lstart "!BL!" :lend "!EL!" :sep "\t") + params))) +@end lisp @noindent -Please check the documentation string of the function -@code{orgtbl-to-generic} for a full list of parameters understood by -that function, and remember that you can pass each of them into +The documentation for the @code{orgtbl-to-generic} function shows a complete +list of parameters, each of which can be passed through to @code{orgtbl-to-latex}, @code{orgtbl-to-texinfo}, and any other function -using the generic function. - -Of course you can also write a completely new function doing complicated -things the generic translator cannot do. A translator function takes -two arguments. The first argument is the table, a list of lines, each -line either the symbol @code{hline} or a list of fields. The second -argument is the property list containing all parameters specified in the -@samp{#+ORGTBL: SEND} line. The function must return a single string -containing the formatted table. If you write a generally useful -translator, please post it on @email{emacs-orgmode@@gnu.org} so that -others can benefit from your work. - -@node Radio lists, , Translator functions, Tables in arbitrary syntax +using that generic function. + +For complicated translations the generic translator function could be +replaced by a custom translator function. Such a custom function must take +two arguments and return a single string containing the formatted table. The +first argument is the table whose lines are a list of fields or the symbol +@code{hline}. The second argument is the property list consisting of +parameters specified in the @samp{#+ORGTBL: SEND} line. Please share your +translator functions by posting them to the Org users mailing list, +@email{emacs-orgmode@@gnu.org}. + +@node Radio lists @subsection Radio lists @cindex radio lists @cindex org-list-insert-radio-list -Sending and receiving radio lists works exactly the same way as sending and -receiving radio tables (@pxref{Radio tables}). As for radio tables, you can -insert radio list templates in HTML, @LaTeX{} and Texinfo modes by calling -@code{org-list-insert-radio-list}. - -Here are the differences with radio tables: +Call the @code{org-list-insert-radio-list} function to insert a radio list +template in HTML, @LaTeX{}, and Texinfo mode documents. Sending and +receiving radio lists works is the same as for radio tables (@pxref{Radio +tables}) except for these differences: +@cindex #+ORGLST @itemize @minus @item Orgstruct mode must be active. @item -Use the @code{ORGLST} keyword instead of @code{ORGTBL}. +Use @code{ORGLST} keyword instead of @code{ORGTBL}. @item -The available translation functions for radio lists don't take -parameters. -@item -@kbd{C-c C-c} will work when pressed on the first item of the list. +@kbd{C-c C-c} works only on the first list item. @end itemize -Here is a @LaTeX{} example. Let's say that you have this in your -@LaTeX{} file: +Built-in translators functions are: @code{org-list-to-latex}, +@code{org-list-to-html} and @code{org-list-to-texinfo}. They use the +@code{org-list-to-generic} translator function. See its documentation for +parameters for accurate customizations of lists. Here is a @LaTeX{} example: -@cindex #+ORGLST @example % BEGIN RECEIVE ORGLST to-buy % END RECEIVE ORGLST to-buy @@ -17477,21 +18340,21 @@ Here is a @LaTeX{} example. Let's say that you have this in your \end@{comment@} @end example -Pressing @kbd{C-c C-c} on @code{a new house} and will insert the converted -@LaTeX{} list between the two marker lines. +@kbd{C-c C-c} on @samp{a new house} inserts the translated @LaTeX{} list +in-between the BEGIN and END marker lines. -@node Dynamic blocks, Special agenda views, Tables in arbitrary syntax, Hacking +@node Dynamic blocks @section Dynamic blocks @cindex dynamic blocks -Org documents can contain @emph{dynamic blocks}. These are -specially marked regions that are updated by some user-written function. -A good example for such a block is the clock table inserted by the -command @kbd{C-c C-x C-r} (@pxref{Clocking work time}). +Org supports @emph{dynamic blocks} in Org documents. They are inserted with +begin and end markers like any other @samp{src} code block, but the contents +are updated automatically by a user function. For example, @kbd{C-c C-x C-r} +inserts a dynamic table that updates the work time (@pxref{Clocking work +time}). -Dynamic blocks are enclosed by a BEGIN-END structure that assigns a name -to the block and can also specify parameters for the function producing -the content of the block. +Dynamic blocks can have names and function parameters. The syntax is similar +to @samp{src} code block specifications: @cindex #+BEGIN:dynamic block @example @@ -17500,7 +18363,7 @@ the content of the block. #+END: @end example -Dynamic blocks are updated with the following commands +These command update dynamic blocks: @table @kbd @orgcmd{C-c C-x C-u,org-dblock-update} @@ -17509,17 +18372,16 @@ Update dynamic block at point. Update all dynamic blocks in the current file. @end table -Updating a dynamic block means to remove all the text between BEGIN and -END, parse the BEGIN line for parameters and then call the specific -writer function for this block to insert the new content. If you want -to use the original content in the writer function, you can use the -extra parameter @code{:content}. +Before updating a dynamic block, Org removes content between the BEGIN and +END markers. Org then reads the parameters on the BEGIN line for passing to +the writer function. If the function expects to access the removed content, +then Org expects an extra parameter, @code{:content}, on the BEGIN line. -For a block with name @code{myblock}, the writer function is -@code{org-dblock-write:myblock} with as only parameter a property list -with the parameters given in the begin line. Here is a trivial example -of a block that keeps track of when the block update function was last -run: +To syntax for calling a writer function with a named block, @code{myblock} +is: @code{org-dblock-write:myblock}. Parameters come from the BEGIN line. + +The following is an example of a dynamic block and a block writer function +that updates the time when the function was last run: @example #+BEGIN: block-update-time :format "on %m/%d/%Y at %H:%M" @@ -17528,7 +18390,7 @@ run: @end example @noindent -The corresponding block writer function could look like this: +The dynamic block's writer function: @lisp (defun org-dblock-write:block-update-time (params) @@ -17537,47 +18399,40 @@ The corresponding block writer function could look like this: (format-time-string fmt)))) @end lisp -If you want to make sure that all dynamic blocks are always up-to-date, -you could add the function @code{org-update-all-dblocks} to a hook, for -example @code{before-save-hook}. @code{org-update-all-dblocks} is -written in a way such that it does nothing in buffers that are not in -@code{org-mode}. +To keep dynamic blocks up-to-date in an Org file, use the function, +@code{org-update-all-dblocks} in hook, such as @code{before-save-hook}. The +@code{org-update-all-dblocks} function does not run if the file is not in +Org mode. -You can narrow the current buffer to the current dynamic block (like any -other block) with @code{org-narrow-to-block}. +Dynamic blocks, like any other block, can be narrowed with +@code{org-narrow-to-block}. -@node Special agenda views, Speeding up your agendas, Dynamic blocks, Hacking +@node Special agenda views @section Special agenda views @cindex agenda views, user-defined @vindex org-agenda-skip-function @vindex org-agenda-skip-function-global -Org provides a special hook that can be used to narrow down the selection -made by these agenda views: @code{agenda}, @code{agenda*}@footnote{The -@code{agenda*} view is the same than @code{agenda} except that it only -considers @emph{appointments}, i.e., scheduled and deadline items that have a -time specification @code{[h]h:mm} in their time-stamps.}, @code{todo}, -@code{alltodo}, @code{tags}, @code{tags-todo}, @code{tags-tree}. You may -specify a function that is used at each match to verify if the match should -indeed be part of the agenda view, and if not, how much should be skipped. -You can specify a global condition that will be applied to all agenda views, -this condition would be stored in the variable -@code{org-agenda-skip-function-global}. More commonly, such a definition is -applied only to specific custom searches, using -@code{org-agenda-skip-function}. - -Let's say you want to produce a list of projects that contain a WAITING -tag anywhere in the project tree. Let's further assume that you have -marked all tree headings that define a project with the TODO keyword -PROJECT@. In this case you would run a TODO search for the keyword -PROJECT, but skip the match unless there is a WAITING tag anywhere in -the subtree belonging to the project line. - -To achieve this, you must write a function that searches the subtree for -the tag. If the tag is found, the function must return @code{nil} to -indicate that this match should not be skipped. If there is no such -tag, return the location of the end of the subtree, to indicate that -search should continue from there. +Org provides a special hook to further limit items in agenda views: +@code{agenda}, @code{agenda*}@footnote{The @code{agenda*} view is the same as +@code{agenda} except that it only considers @emph{appointments}, i.e., +scheduled and deadline items that have a time specification @samp{[h]h:mm} in +their time-stamps.}, @code{todo}, @code{alltodo}, @code{tags}, +@code{tags-todo}, @code{tags-tree}. Specify a custom function that tests +inclusion of every matched item in the view. This function can also +skip as much as is needed. + +For a global condition applicable to agenda views, use the +@code{org-agenda-skip-function-global} variable. Org uses a global condition +with @code{org-agenda-skip-function} for custom searching. + +This example defines a function for a custom view showing TODO items with +WAITING status. Manually this is a multi step search process, but with a +custom view, this can be automated as follows: + +The custom function searches the subtree for the WAITING tag and returns +@code{nil} on match. Otherwise it gives the location from where the search +continues. @lisp (defun my-skip-unless-waiting () @@ -17588,8 +18443,7 @@ search should continue from there. subtree-end))) ; tag not found, continue after end of subtree @end lisp -Now you may use this function in an agenda custom command, for example -like this: +To use this custom function in a custom agenda command: @lisp (org-add-agenda-custom-command @@ -17599,22 +18453,20 @@ like this: @end lisp @vindex org-agenda-overriding-header -Note that this also binds @code{org-agenda-overriding-header} to get a -meaningful header in the agenda view. +Note that this also binds @code{org-agenda-overriding-header} to a more +meaningful string suitable for the agenda view. @vindex org-odd-levels-only @vindex org-agenda-skip-function -A general way to create custom searches is to base them on a search for -entries with a certain level limit. If you want to study all entries with -your custom search function, simply do a search for -@samp{LEVEL>0}@footnote{Note that, when using @code{org-odd-levels-only}, a -level number corresponds to order in the hierarchy, not to the number of -stars.}, and then use @code{org-agenda-skip-function} to select the entries -you really want to have. - -You may also put a Lisp form into @code{org-agenda-skip-function}. In -particular, you may use the functions @code{org-agenda-skip-entry-if} -and @code{org-agenda-skip-subtree-if} in this form, for example: + +Search for entries with a limit set on levels for the custom search. This is +a general appraoch to creating custom searches in Org. To include all +levels, use @samp{LEVEL>0}@footnote{Note that, for +@code{org-odd-levels-only}, a level number corresponds to order in the +hierarchy, not to the number of stars.}. Then to selectively pick the +matched entries, use @code{org-agenda-skip-function}, which also accepts Lisp +forms, such as @code{org-agenda-skip-entry-if} and +@code{org-agenda-skip-subtree-if}. For example: @table @code @item (org-agenda-skip-entry-if 'scheduled) @@ -17640,8 +18492,8 @@ Skip current entry unless the regular expression matches. Same as above, but check and skip the entire subtree. @end table -Therefore we could also have written the search for WAITING projects -like this, even without defining a special function: +The following is an example of a search for @samp{WAITING} without the +special function: @lisp (org-add-agenda-custom-command @@ -17651,72 +18503,71 @@ like this, even without defining a special function: (org-agenda-overriding-header "Projects waiting for something: ")))) @end lisp -@node Speeding up your agendas, Extracting agenda information, Special agenda views, Hacking +@node Speeding up your agendas @section Speeding up your agendas @cindex agenda views, optimization -When your Org files grow in both number and size, agenda commands may start -to become slow. Below are some tips on how to speed up the agenda commands. +Some agenda commands slow down when the Org files grow in size or number. +Here are tips to speed up: @enumerate @item -Reduce the number of Org agenda files: this will reduce the slowness caused -by accessing a hard drive. +Reduce the number of Org agenda files to avoid slowdowns due to hard drive +accesses. @item -Reduce the number of DONE and archived headlines: this way the agenda does -not need to skip them. +Reduce the number of @samp{DONE} and archived headlines so agenda operations +that skip over these can finish faster. @item @vindex org-agenda-dim-blocked-tasks -Inhibit the dimming of blocked tasks: +Do not dim blocked tasks: @lisp (setq org-agenda-dim-blocked-tasks nil) @end lisp @item @vindex org-startup-folded @vindex org-agenda-inhibit-startup -Inhibit agenda files startup options: +Stop preparing agenda buffers on startup: @lisp (setq org-agenda-inhibit-startup nil) @end lisp @item @vindex org-agenda-show-inherited-tags @vindex org-agenda-use-tag-inheritance -Disable tag inheritance in agenda: +Disable tag inheritance for agendas: @lisp (setq org-agenda-use-tag-inheritance nil) @end lisp @end enumerate -You can set these options for specific agenda views only. See the docstrings -of these variables for details on why they affect the agenda generation, and -this @uref{http://orgmode.org/worg/agenda-optimization.html, dedicated Worg -page} for further explanations. +These options can be applied to selected agenda views. For more details +about generation of agenda views, see the docstrings for the relevant +variables, and this @uref{http://orgmode.org/worg/agenda-optimization.html, +dedicated Worg page} for agenda optimization. -@node Extracting agenda information, Using the property API, Speeding up your agendas, Hacking +@node Extracting agenda information @section Extracting agenda information @cindex agenda, pipe @cindex Scripts, for agenda processing @vindex org-agenda-custom-commands -Org provides commands to access agenda information for the command -line in Emacs batch mode. This extracted information can be sent -directly to a printer, or it can be read by a program that does further -processing of the data. The first of these commands is the function -@code{org-batch-agenda}, that produces an agenda view and sends it as -ASCII text to STDOUT@. The command takes a single string as parameter. -If the string has length 1, it is used as a key to one of the commands -you have configured in @code{org-agenda-custom-commands}, basically any -key you can use after @kbd{C-c a}. For example, to directly print the -current TODO list, you could use +Org provides commands to access agendas through Emacs batch mode. Through +this command-line interface, agendas are automated for further processing or +printing. + +@code{org-batch-agenda} creates an agenda view in ASCII and outputs to +STDOUT. This command takes one string parameter. When string length=1, Org +uses it as a key to @code{org-agenda-custom-commands}. These are the same +ones available through @kbd{C-c a}. + +This example command line directly prints the TODO list to the printer: @example emacs -batch -l ~/.emacs -eval '(org-batch-agenda "t")' | lpr @end example -If the parameter is a string with 2 or more characters, it is used as a -tags/TODO match string. For example, to print your local shopping list -(all items with the tag @samp{shop}, but excluding the tag -@samp{NewYork}), you could use +When the string parameter length is two or more characters, Org matches it +with tags/TODO strings. For example, this example command line prints items +tagged with @samp{shop}, but excludes items tagged with @samp{NewYork}: @example emacs -batch -l ~/.emacs \ @@ -17724,7 +18575,7 @@ emacs -batch -l ~/.emacs \ @end example @noindent -You may also modify parameters on the fly like this: +An example showing on-the-fly parameter modifications: @example emacs -batch -l ~/.emacs \ @@ -17736,14 +18587,11 @@ emacs -batch -l ~/.emacs \ @end example @noindent -which will produce a 30-day agenda, fully restricted to the Org file -@file{~/org/projects.org}, not even including the diary. +which will produce an agenda for the next 30 days from just the +@file{~/org/projects.org} file. -If you want to process the agenda data in more sophisticated ways, you -can use the command @code{org-batch-agenda-csv} to get a comma-separated -list of values for each agenda item. Each line in the output will -contain a number of fields separated by commas. The fields in a line -are: +For structured processing of agenda output, use @code{org-batch-agenda-csv} +with the following fields: @example category @r{The category of the item} @@ -17769,12 +18617,15 @@ priority-n @r{The computed numerical priority} @end example @noindent -Time and date will only be given if a timestamp (or deadline/scheduled) -led to the selection of the item. +If the selection of the agenda item was based on a timestamp, including those +items with @samp{DEADLINE} and @samp{SCHEDULED} keywords, then Org includes +date and time in the output. -A CSV list like this is very easy to use in a post-processing script. -For example, here is a Perl program that gets the TODO list from -Emacs/Org and prints all the items, preceded by a checkbox: +If the selection of the agenda item was based on a timestamp (or +deadline/scheduled), then Org includes date and time in the output. + +Here is an example of a post-processing script in Perl. It takes the CSV +output from Emacs and prints with a checkbox: @example #!/usr/bin/perl @@ -17795,13 +18646,12 @@ foreach $line (split(/\n/,$agenda)) @{ @} @end example -@node Using the property API, Using the mapping API, Extracting agenda information, Hacking +@node Using the property API @section Using the property API @cindex API, for properties @cindex properties, API -Here is a description of the functions that can be used to work with -properties. +Functions for working with properties. @defun org-entry-properties &optional pom which Get all properties of the entry at point-or-marker POM.@* @@ -17813,14 +18663,15 @@ POM may also be @code{nil}, in which case the current entry is used. If WHICH is @code{nil} or @code{all}, get all properties. If WHICH is @code{special} or @code{standard}, only get that subclass. @end defun + @vindex org-use-property-inheritance @findex org-insert-property-drawer @defun org-entry-get pom property &optional inherit -Get value of @code{PROPERTY} for entry at point-or-marker @code{POM}@. By default, -this only looks at properties defined locally in the entry. If @code{INHERIT} -is non-@code{nil} and the entry does not have the property, then also check -higher levels of the hierarchy. If @code{INHERIT} is the symbol -@code{selective}, use inheritance if and only if the setting of +Get value of @code{PROPERTY} for entry at point-or-marker @code{POM}@. By +default, this only looks at properties defined locally in the entry. If +@code{INHERIT} is non-@code{nil} and the entry does not have the property, +then also check higher levels of the hierarchy. If @code{INHERIT} is the +symbol @code{selective}, use inheritance if and only if the setting of @code{org-use-property-inheritance} selects @code{PROPERTY} for inheritance. @end defun @@ -17837,7 +18688,7 @@ Get all property keys in the current buffer. @end defun @defun org-insert-property-drawer -Insert a property drawer for the current entry. Also +Insert a property drawer for the current entry. @end defun @defun org-entry-put-multivalued-property pom property &rest values @@ -17875,41 +18726,37 @@ to be entered. The functions must return @code{nil} if they are not responsible for this property. @end defopt -@node Using the mapping API, , Using the property API, Hacking +@node Using the mapping API @section Using the mapping API @cindex API, for mapping @cindex mapping entries, API -Org has sophisticated mapping capabilities to find all entries satisfying -certain criteria. Internally, this functionality is used to produce agenda -views, but there is also an API that can be used to execute arbitrary -functions for each or selected entries. The main entry point for this API -is: +Org has sophisticated mapping capabilities for finding entries. Org uses +this functionality internally for generating agenda views. Org also exposes +an API for executing arbitrary functions for each selected entry. The API's +main entry point is: @defun org-map-entries func &optional match scope &rest skip -Call @code{FUNC} at each headline selected by @code{MATCH} in @code{SCOPE}. +Call @samp{FUNC} at each headline selected by @code{MATCH} in @code{SCOPE}. -@code{FUNC} is a function or a Lisp form. The function will be called -without arguments, with the cursor positioned at the beginning of the -headline. The return values of all calls to the function will be collected -and returned as a list. +@samp{FUNC} is a function or a Lisp form. With the cursor positioned at the +beginning of the headline, call the function without arguments. Org returns +an alist of return values of calls to the function. -The call to @code{FUNC} will be wrapped into a save-excursion form, so -@code{FUNC} does not need to preserve point. After evaluation, the cursor -will be moved to the end of the line (presumably of the headline of the -processed entry) and search continues from there. Under some circumstances, -this may not produce the wanted results. For example, if you have removed -(e.g., archived) the current (sub)tree it could mean that the next entry will -be skipped entirely. In such cases, you can specify the position from where -search should continue by making @code{FUNC} set the variable -@code{org-map-continue-from} to the desired buffer position. +To avoid preserving point, Org wraps the call to @code{FUNC} in +save-excursion form. After evaluation, Org moves the cursor to the end of +the line that was just processed. Search continues from that point forward. +This may not always work as expected under some conditions, such as if the +current sub-tree was removed by a previous archiving operation. In such rare +circumstances, Org skips the next entry entirely when it should not. To stop +Org from such skips, make @samp{FUNC} set the variable +@code{org-map-continue-from} to a specific buffer position. -@code{MATCH} is a tags/property/todo match as it is used in the agenda match -view. Only headlines that are matched by this query will be considered -during the iteration. When @code{MATCH} is @code{nil} or @code{t}, all -headlines will be visited by the iteration. +@samp{MATCH} is a tags/property/TODO match. Org iterates only matched +headlines. Org iterates over all headlines when @code{MATCH} is @code{nil} +or @code{t}. -@code{SCOPE} determines the scope of this command. It can be any of: +@samp{SCOPE} determines the scope of this command. It can be any of: @example nil @r{the current buffer, respecting the restriction if any} @@ -17925,8 +18772,8 @@ agenda-with-archives @r{if this is a list, all files in the list will be scanned} @end example @noindent -The remaining args are treated as settings for the skipping facilities of -the scanner. The following items can be given here: +The remaining args are treated as settings for the scanner's skipping +facilities. Valid args are: @vindex org-agenda-skip-function @example @@ -17940,10 +18787,9 @@ function or Lisp form @end example @end defun -The function given to that mapping routine can really do anything you like. -It can use the property API (@pxref{Using the property API}) to gather more -information about the entry, or in order to change metadata in the entry. -Here are a couple of functions that might be handy: +The mapping routine can call any arbitrary function, even functions that +change meta data or query the property API (@pxref{Using the property API}). +Here are some handy functions: @defun org-todo &optional arg Change the TODO state of the entry. See the docstring of the functions for @@ -17969,9 +18815,9 @@ Promote the current entry. Demote the current entry. @end defun -Here is a simple example that will turn all entries in the current file with -a tag @code{TOMORROW} into TODO entries with the keyword @code{UPCOMING}. -Entries in comment trees and in archive trees will be ignored. +This example turns all entries tagged with @code{TOMORROW} into TODO entries +with keyword @code{UPCOMING}. Org ignores entries in comment trees and +archive trees. @lisp (org-map-entries @@ -17986,105 +18832,103 @@ The following example counts the number of entries with TODO keyword (length (org-map-entries t "/+WAITING" 'agenda)) @end lisp -@node MobileOrg, History and Acknowledgments, Hacking, Top +@node MobileOrg @appendix MobileOrg @cindex iPhone @cindex MobileOrg -@i{MobileOrg} is the name of the mobile companion app for Org mode, currently -available for iOS and for Android. @i{MobileOrg} offers offline viewing and -capture support for an Org mode system rooted on a ``real'' computer. It -does also allow you to record changes to existing entries. The -@uref{https://github.com/MobileOrg/, iOS implementation} for the -@i{iPhone/iPod Touch/iPad} series of devices, was started by Richard Moreland -and is now in the hands Sean Escriva. Android users should check out -@uref{http://wiki.github.com/matburt/mobileorg-android/, MobileOrg Android} -by Matt Jones. The two implementations are not identical but offer similar -features. - -This appendix describes the support Org has for creating agenda views in a -format that can be displayed by @i{MobileOrg}, and for integrating notes -captured and changes made by @i{MobileOrg} into the main system. - -For changing tags and TODO states in MobileOrg, you should have set up the -customization variables @code{org-todo-keywords} and @code{org-tag-alist} to -cover all important tags and TODO keywords, even if individual files use only -part of these. MobileOrg will also offer you states and tags set up with -in-buffer settings, but it will understand the logistics of TODO state -@i{sets} (@pxref{Per-file keywords}) and @i{mutually exclusive} tags +MobileOrg is a companion mobile app that runs on iOS and Android devices. +MobileOrg enables offline-views and capture support for an Org mode system +that is rooted on a ``real'' computer. MobileOrg can record changes to +existing entries. + +The @uref{https://github.com/MobileOrg/, iOS implementation} for the +@emph{iPhone/iPod Touch/iPad} series of devices, was started by Richard +Moreland and is now in the hands Sean Escriva. Android users should check +out @uref{http://wiki.github.com/matburt/mobileorg-android/, MobileOrg +Android} by Matt Jones. Though the two implementations are not identical, +they offer similar features. + +This appendix describes Org's support for agenda view formats compatible with +MobileOrg. It also describes synchronizing changes, such as to notes, +between MobileOrg and the computer. + +To change tags and TODO states in MobileOrg, first customize the variables +@code{org-todo-keywords} and @code{org-tag-alist}. These should cover all +the important tags and TODO keywords, even if Org files use only some of +them. Though MobileOrg has in-buffer settings, it understands TODO states +@emph{sets} (@pxref{Per-file keywords}) and @emph{mutually exclusive} tags (@pxref{Setting tags}) only for those set in these variables. @menu -* Setting up the staging area:: Where to interact with the mobile device +* Setting up the staging area:: For the mobile device * Pushing to MobileOrg:: Uploading Org files and agendas * Pulling from MobileOrg:: Integrating captured and flagged items @end menu -@node Setting up the staging area, Pushing to MobileOrg, MobileOrg, MobileOrg +@node Setting up the staging area @section Setting up the staging area -MobileOrg needs to interact with Emacs through a directory on a server. If you -are using a public server, you should consider to encrypt the files that are -uploaded to the server. This can be done with Org mode 7.02 and with -@i{MobileOrg 1.5} (iPhone version), and you need an @file{openssl} -installation on your system. To turn on encryption, set a password in -@i{MobileOrg} and, on the Emacs side, configure the variable -@code{org-mobile-use-encryption}@footnote{If you can safely store the -password in your Emacs setup, you might also want to configure -@code{org-mobile-encryption-password}. Please read the docstring of that -variable. Note that encryption will apply only to the contents of the -@file{.org} files. The file names themselves will remain visible.}. - -The easiest way to create that directory is to use a free -@uref{http://dropbox.com,Dropbox.com} account@footnote{If you cannot use -Dropbox, or if your version of MobileOrg does not support it, you can use a -webdav server. For more information, check out the documentation of MobileOrg and also this +MobileOrg needs access to a file directory on a server to interact with +Emacs. With a public server, consider encrypting the files. MobileOrg +version 1.5 supports encryption for the iPhone. Org also requires +@file{openssl} installed on the local computer. To turn on encryption, set +the same password in MobileOrg and in Emacs. Set the password in the +variable @code{org-mobile-use-encryption}@footnote{If Emacs is configured for +safe storing of passwords, then configure the variable, +@code{org-mobile-encryption-password}; please read the docstring of that +variable.}. Note that even after MobileOrg encrypts the file contents, the +file names will remain visible on the file systems of the local computer, the +server, and the mobile device. + +For a server to host files, consider options like +@uref{http://dropbox.com,Dropbox.com} account@footnote{An alternative is to +use webdav server. MobileOrg documentation has details of webdav server +configuration. Additional help is at @uref{http://orgmode.org/worg/org-faq.html#mobileorg_webdav, FAQ entry}.}. -When MobileOrg first connects to your Dropbox, it will create a directory -@i{MobileOrg} inside the Dropbox. After the directory has been created, tell -Emacs about it: +On first connection, MobileOrg creates a directory @file{MobileOrg/} on +Dropbox. Pass its location to Emacs through an init file variable as +follows: @lisp (setq org-mobile-directory "~/Dropbox/MobileOrg") @end lisp -Org mode has commands to put files for @i{MobileOrg} into that directory, -and to read captured notes from there. +Org copies files to the above directory for MobileOrg. Org also uses the +same directory for sharing notes between Org and MobileOrg. -@node Pushing to MobileOrg, Pulling from MobileOrg, Setting up the staging area, MobileOrg +@node Pushing to MobileOrg @section Pushing to MobileOrg -This operation copies all files currently listed in @code{org-mobile-files} -to the directory @code{org-mobile-directory}. By default this list contains -all agenda files (as listed in @code{org-agenda-files}), but additional files -can be included by customizing @code{org-mobile-files}. File names will be -staged with paths relative to @code{org-directory}, so all files should be -inside this directory@footnote{Symbolic links in @code{org-directory} need to -have the same name than their targets.}. - -The push operation also creates a special Org file @file{agendas.org} with -all custom agenda view defined by the user@footnote{While creating the -agendas, Org mode will force ID properties on all referenced entries, so that -these entries can be uniquely identified if @i{MobileOrg} flags them for -further action. If you do not want to get these properties in so many -entries, you can set the variable @code{org-mobile-force-id-on-agenda-items} -to @code{nil}. Org mode will then rely on outline paths, in the hope that -these will be unique enough.}. - -Finally, Org writes the file @file{index.org}, containing links to all other -files. @i{MobileOrg} first reads this file from the server, and then -downloads all agendas and Org files listed in it. To speed up the download, -MobileOrg will only read files whose checksums@footnote{Checksums are stored -automatically in the file @file{checksums.dat}} have changed. - -@node Pulling from MobileOrg, , Pushing to MobileOrg, MobileOrg +Org pushes files listed in @code{org-mobile-files} to +@code{org-mobile-directory}. Files include agenda files (as listed in +@code{org-agenda-files}). Customize @code{org-mobile-files} to add other +files. File names will be staged with paths relative to +@code{org-directory}, so all files should be inside this +directory@footnote{Symbolic links in @code{org-directory} should have the +same name as their targets.}. + +Push creates a special Org file @file{agendas.org} with custom agenda views +defined by the user@footnote{While creating the agendas, Org mode will force +ID properties on all referenced entries, so that these entries can be +uniquely identified if MobileOrg flags them for further action. To avoid +setting properties configure the variable +@code{org-mobile-force-id-on-agenda-items} to @code{nil}. Org mode will then +rely on outline paths, assuming they are unique.}. + +Org writes the file @file{index.org}, containing links to other files. +MobileOrg reads this file first from the server to determine what other files +to download for agendas. For faster downloads, MobileOrg will read only +those files whose checksums@footnote{Checksums are stored automatically in +the file @file{checksums.dat}.} have changed. + +@node Pulling from MobileOrg @section Pulling from MobileOrg -When @i{MobileOrg} synchronizes with the server, it not only pulls the Org -files for viewing. It also appends captured entries and pointers to flagged -and changed entries to the file @file{mobileorg.org} on the server. Org has -a @emph{pull} operation that integrates this information into an inbox file -and operates on the pointers to flagged entries. Here is how it works: +When MobileOrg synchronizes with the server, it pulls the Org files for +viewing. It then appends to the file @file{mobileorg.org} on the server the +captured entries, pointers to flagged and changed entries. Org integrates +its data in an inbox file format. @enumerate @item @@ -18092,46 +18936,37 @@ Org moves all entries found in @file{mobileorg.org}@footnote{@file{mobileorg.org} will be empty after this operation.} and appends them to the file pointed to by the variable @code{org-mobile-inbox-for-pull}. Each captured entry and each editing event -will be a top-level entry in the inbox file. +is a top-level entry in the inbox file. @item -After moving the entries, Org will attempt to implement the changes made in -@i{MobileOrg}. Some changes are applied directly and without user -interaction. Examples are all changes to tags, TODO state, headline and body -text that can be cleanly applied. Entries that have been flagged for further -action will receive a tag @code{:FLAGGED:}, so that they can be easily found -again. When there is a problem finding an entry or applying the change, the -pointer entry will remain in the inbox and will be marked with an error -message. You need to later resolve these issues by hand. +After moving the entries, Org attempts changes to MobileOrg. Some changes +are applied directly and without user interaction. Examples include changes +to tags, TODO state, headline and body text. Entries for further action are +tagged as @code{:FLAGGED:}. Org marks entries with problems with an error +message in the inbox. They have to be resolved manually. @item -Org will then generate an agenda view with all flagged entries. The user -should then go through these entries and do whatever actions are necessary. -If a note has been stored while flagging an entry in @i{MobileOrg}, that note -will be displayed in the echo area when the cursor is on the corresponding -agenda line. +Org generates an agenda view for flagged entries for user intervention to +clean up. For notes stored in flagged entries, MobileOrg displays them in +the echo area when the cursor is on the corresponding agenda item. @table @kbd @kindex ? @item ? -Pressing @kbd{?} in that special agenda will display the full flagging note in -another window and also push it onto the kill ring. So you could use @kbd{? -z C-y C-c C-c} to store that flagging note as a normal note in the entry. -Pressing @kbd{?} twice in succession will offer to remove the -@code{:FLAGGED:} tag along with the recorded flagging note (which is stored -in a property). In this way you indicate that the intended processing for -this flagged entry is finished. +Pressing @kbd{?} displays the entire flagged note in another window. Org +also pushes it to the kill ring. To store flagged note as a normal note, use +@kbd{? z C-y C-c C-c}. Pressing @kbd{?} twice does these things: first it +removes the @code{:FLAGGED:} tag; second, it removes the flagged note from +the property drawer; third, it signals that manual editing of the flagged +entry is now finished. @end table @end enumerate @kindex C-c a ? -If you are not able to process all flagged entries directly, you can always -return to this agenda view@footnote{Note, however, that there is a subtle -difference. The view created automatically by @kbd{M-x org-mobile-pull RET} -is guaranteed to search all files that have been addressed by the last pull. -This might include a file that is not currently in your list of agenda files. -If you later use @kbd{C-c a ?} to regenerate the view, only the current -agenda files will be searched.} using @kbd{C-c a ?}. - -@node History and Acknowledgments, GNU Free Documentation License, MobileOrg, Top +@kbd{C-c a ?} returns to the agenda view to finish processing flagged +entries. Note that these entries may not be the most recent since MobileOrg +searches files that were last pulled. To get an updated agenda view with +changes since the last pull, pull again. + +@node History and acknowledgments @appendix History and acknowledgments @cindex acknowledgments @cindex history @@ -18143,17 +18978,17 @@ Org was born in 2003, out of frustration over the user interface of the Emacs Outline mode. I was trying to organize my notes and projects, and using Emacs seemed to be the natural way to go. However, having to remember eleven different commands with two or three keys per command, only to hide and show -parts of the outline tree, that seemed entirely unacceptable to me. Also, -when using outlines to take notes, I constantly wanted to restructure the -tree, organizing it parallel to my thoughts and plans. @emph{Visibility -cycling} and @emph{structure editing} were originally implemented in the -package @file{outline-magic.el}, but quickly moved to the more general -@file{org.el}. As this environment became comfortable for project planning, -the next step was adding @emph{TODO entries}, basic @emph{timestamps}, and -@emph{table support}. These areas highlighted the two main goals that Org -still has today: to be a new, outline-based, plain text mode with innovative -and intuitive editing features, and to incorporate project planning -functionality directly into a notes file. +parts of the outline tree, that seemed entirely unacceptable. Also, when +using outlines to take notes, I constantly wanted to restructure the tree, +organizing it paralleling my thoughts and plans. @emph{Visibility cycling} +and @emph{structure editing} were originally implemented in the package +@file{outline-magic.el}, but quickly moved to the more general @file{org.el}. +As this environment became comfortable for project planning, the next step +was adding @emph{TODO entries}, basic @emph{timestamps}, and @emph{table +support}. These areas highlighted the two main goals that Org still has +today: to be a new, outline-based, plain text mode with innovative and +intuitive editing features, and to incorporate project planning functionality +directly into a notes file. Since the first release, literally thousands of emails to me or to @email{emacs-orgmode@@gnu.org} have provided a constant stream of bug @@ -18169,15 +19004,17 @@ Before I get to this list, a few special mentions are in order: @table @i @item Bastien Guerry Bastien has written a large number of extensions to Org (most of them -integrated into the core by now), including the @LaTeX{} exporter and the plain -list parser. His support during the early days, when he basically acted as -co-maintainer, was central to the success of this project. Bastien also -invented Worg, helped establishing the Web presence of Org, and sponsored -hosting costs for the orgmode.org website. +integrated into the core by now), including the @LaTeX{} exporter and the +plain list parser. His support during the early days was central to the +success of this project. Bastien also invented Worg, helped establishing the +Web presence of Org, and sponsored hosting costs for the orgmode.org website. +Bastien stepped in as maintainer of Org between 2011 and 2013, at a time when +I desperately needed a break. @item Eric Schulte and Dan Davison Eric and Dan are jointly responsible for the Org-babel system, which turns Org into a multi-language environment for evaluating code and doing literate -programming and reproducible research. +programming and reproducible research. This has become one of Org's killer +features that define what Org is today. @item John Wiegley John has contributed a number of great ideas and patches directly to Org, including the attachment system (@file{org-attach.el}), integration with @@ -18198,9 +19035,8 @@ let me know what I am missing here! @section From Bastien -I (Bastien) have been maintaining Org since January 2011. This appendix -would not be complete without adding a few more acknowledgements and thanks -to Carsten's ones above. +I (Bastien) have been maintaining Org between 2011 and 2013. This appendix +would not be complete without adding a few more acknowledgments and thanks. I am first grateful to Carsten for his trust while handing me over the maintainership of Org. His unremitting support is what really helped me @@ -18218,13 +19054,13 @@ Eric is maintaining the Babel parts of Org. His reactivity here kept me away from worrying about possible bugs here and let me focus on other parts. @item Nicolas Goaziou -Nicolas is maintaining the consistency of the deepest parts of Org. His -work on @file{org-element.el} and @file{ox.el} has been outstanding, and -opened the doors for many new ideas and features. He rewrote many of the -old exporters to use the new export engine, and helped with documenting -this major change. More importantly (if that's possible), he has been more -than reliable during all the work done for Org 8.0, and always very -reactive on the mailing list. +Nicolas is maintaining the consistency of the deepest parts of Org. His work +on @file{org-element.el} and @file{ox.el} has been outstanding, and it opened +the doors for many new ideas and features. He rewrote many of the old +exporters to use the new export engine, and helped with documenting this +major change. More importantly (if that's possible), he has been more than +reliable during all the work done for Org 8.0, and always very reactive on +the mailing list. @item Achim Gratz Achim rewrote the building process of Org, turning some @emph{ad hoc} tools @@ -18280,13 +19116,14 @@ specified time. calculations and improved XEmacs compatibility, in particular by porting @file{nouline.el} to XEmacs. @item -@i{Sacha Chua} suggested copying some linking code from Planner. +@i{Sacha Chua} suggested copying some linking code from Planner, and helped +make Org pupular through her blog. @item @i{Toby S. Cubitt} contributed to the code for clock formats. @item -@i{Baoqiu Cui} contributed the DocBook exporter. It has been deleted from -Org 8.0: you can now export to Texinfo and export the @file{.texi} file to -DocBook using @code{makeinfo}. +@i{Baoqiu Cui} contributed the first DocBook exporter. In Org 8.0, we go a +different route: you can now export to Texinfo and export the @file{.texi} +file to DocBook using @code{makeinfo}. @item @i{Eddward DeVilla} proposed and tested checkbox statistics. He also came up with the idea of properties, and that there should be an API for @@ -18383,7 +19220,7 @@ basis. @i{Stefan Monnier} provided a patch to keep the Emacs-Lisp compiler happy. @item -@i{Richard Moreland} wrote @i{MobileOrg} for the iPhone. +@i{Richard Moreland} wrote MobileOrg for the iPhone. @item @i{Rick Moynihan} proposed allowing multiple TODO sequences in a file and being able to quickly restrict the agenda to a subtree. @@ -18501,35 +19338,37 @@ work on a tty. @item @i{Piotr Zielinski} wrote @file{org-mouse.el}, proposed agenda blocks and contributed various ideas and code snippets. +@item +@i{Marco Wahl} wrote @file{org-eww.el}. @end itemize -@node GNU Free Documentation License, Main Index, History and Acknowledgments, Top +@node GNU Free Documentation License @appendix GNU Free Documentation License @include doclicense.texi -@node Main Index, Key Index, GNU Free Documentation License, Top +@node Main Index @unnumbered Concept index @printindex cp -@node Key Index, Command and Function Index, Main Index, Top +@node Key Index @unnumbered Key index @printindex ky -@node Command and Function Index, Variable Index, Key Index, Top +@node Command and Function Index @unnumbered Command and function index @printindex fn -@node Variable Index, , Command and Function Index, Top +@node Variable Index @unnumbered Variable index This is not a complete index of variables and faces, only the ones that are -mentioned in the manual. For a more complete list, use @kbd{M-x -org-customize @key{RET}} and then click yourself through the tree. +mentioned in the manual. For a complete list, use @kbd{M-x org-customize +@key{RET}}. @printindex vr diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 38df7b2bd8..fb50175316 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -1,17 +1,1368 @@ -ORG NEWS -- history of user-visible changes. -*- mode: org; coding: utf-8 -*- +ORG NEWS -- history of user-visible changes. -*- org -*- #+LINK: doc http://orgmode.org/worg/doc.html#%s -#+LINK: git http://orgmode.org/w/?p=org-mode.git;a=commit;h=%s +#+LINK: git http://orgmode.org/cgit.cgi/org-mode.git/commit/?id=%s Copyright (C) 2012-2017 Free Software Foundation, Inc. See the end of the file for license conditions. -Please send Org bug reports to emacs-orgmode@gnu.org. +Please send Org bug reports to mailto:emacs-orgmode@gnu.org. -* Version 8.2.3 +* Version 9.0 ** Incompatible changes +*** Emacs 23 support has been dropped + +From now on, Org expects at least Emacs 24.3, although Emacs 24.4 or +above is suggested. + +*** XEmacs support has been dropped + +Incomplete compatibility layer with XEmacs has been removed. If you +want to take over maintainance of this compatibility, please contact +our mailing list. + +*** New syntax for export blocks + +Export blocks are explicitly marked as such at the syntax level to +disambiguate their parsing from special blocks. The new syntax is + +#+BEGIN_SRC org +,#+BEGIN_EXPORT backend +... +,#+END_EXPORT +#+END_SRC + +instead of + +#+BEGIN_SRC org +,#+BEGIN_backend +... +,#+END_backend +#+END_SRC + +As a consequence, =INCLUDE= keywords syntax is modified, e.g., + +#+BEGIN_SRC org +,#+INCLUDE: "file.org" HTML +#+END_SRC + +becomes + +#+BEGIN_SRC org +,#+INCLUDE: "file.org" export html +#+END_SRC + +The following function repairs export blocks and =INCLUDE= keywords +using previous syntax: + +#+BEGIN_SRC emacs-lisp +(defun org-repair-export-blocks () + "Repair export blocks and INCLUDE keywords in current buffer." + (interactive) + (when (eq major-mode 'org-mode) + (let ((case-fold-search t) + (back-end-re (regexp-opt + '("HTML" "ASCII" "LATEX" "ODT" "MARKDOWN" "MD" "ORG" + "MAN" "BEAMER" "TEXINFO" "GROFF" "KOMA-LETTER") + t))) + (org-with-wide-buffer + (goto-char (point-min)) + (let ((block-re (concat "^[ \t]*#\\+BEGIN_" back-end-re))) + (save-excursion + (while (re-search-forward block-re nil t) + (let ((element (save-match-data (org-element-at-point)))) + (when (eq (org-element-type element) 'special-block) + (save-excursion + (goto-char (org-element-property :end element)) + (save-match-data (search-backward "_")) + (forward-char) + (insert "EXPORT") + (delete-region (point) (line-end-position))) + (replace-match "EXPORT \\1" nil nil nil 1)))))) + (let ((include-re + (format "^[ \t]*#\\+INCLUDE: .*?%s[ \t]*$" back-end-re))) + (while (re-search-forward include-re nil t) + (let ((element (save-match-data (org-element-at-point)))) + (when (and (eq (org-element-type element) 'keyword) + (string= (org-element-property :key element) "INCLUDE")) + (replace-match "EXPORT \\1" nil nil nil 1))))))))) +#+END_SRC + +Moreover, ~:export-block~ keyword used in ~org-export-define-backend~ and +~org-export-define-derived-backend~ is no longer used and needs to be +removed. + +*** Footnotes + +**** [1]-like constructs are not valid footnotes + +Using =[1]= as a footnote was already discouraged in the manual, since +it introduced too many false-positives in many Org documents. These +constructs are now unsupported. + +If you used =[N]= in some of your documents, consider turning them into +=[fn:N]=. + +**** /Org Footnote/ library doesn't handle non-Org buffers + +Commands for footnotes in an Org document no longer try to do +something in non-Org ones. If you need to have footnotes there, +consider using the =footnote.el= library, shipped with Emacs. + +In particular, ~org-footnote-tag-for-non-org-mode-files~ no longer +exists. + +*** ~org-file-apps~ no longer accepts S-expressions as commands + +The variable now accepts functions of two arguments instead of plain +S-expressions. Replacing a S-expresion with an appropriate function +is straightforward. For example + +: ("pdf" . (foo)) + +becomes + +: ("pdf" . (lambda (file link) (foo))) + +*** The ~{{{modification-time}}}~ macro can get time via =vc= + +The modification time will be determined via =vc.el= if the second +argument is non-nil. See the manual for details. + +*** Preparation and completion functions in publishing projects change signature + +Preparation and completion functions are now called with an argument, +which is the project property list. It used to be dynamically scoped +through the ~project-plist~ variable. + +*** Old Babel header properties are no longer supported + +Using header arguments as property names is no longer possible. As +such, the following + +#+BEGIN_EXAMPLE +,* Headline +:PROPERTIES: +:exports: code +:var: a=1 b=2 +:var+: c=3 +:END: +#+END_EXAMPLE + +should be written instead + +#+BEGIN_EXAMPLE +,* Headline +:PROPERTIES: +:header-args: :exports code +:header-args: :var a=1 b=2 +:header-args+: :var c=3 +:END: +#+END_EXAMPLE + +Please note that, however, old properties were defined at the source +block definition. Current ones are defined where the block is called. + +** New features + +*** ~org-eww~ has been moved into core +*** New org-protocol key=value syntax + +Org-protocol can now handle query-style parameters such as: + +#+begin_example +org-protocol://store-link?url=http:%2F%2Flocalhost%2Findex.html&title=The%20title +org-protocol://capture?template=x&title=Hello&body=World&url=http:%2F%2Fexample.com +#+end_example + +Old-style links such as +: org-protocol://store-link:/http:%2F%2Flocalhost%2Findex.html/The%20title +continue to be supported. + +If you have defined your own handler functions for +~org-protocol-protocol-alist~, change them to accept either a property +list (for new-style links) or a string (for old-style links). Use +~org-protocol-parse-parameters~ to convert old-style links into property +lists. + +*** New Org linter library + +~org-lint~ can check syntax and report common issues in Org documents. + +*** New option ~date-tree-last~ for ~org-agenda-insert-diary-strategy~ + +When ~org-agenda-insert-diary-strategy~ is set to ~date-tree-last~, diary +entries are added to last in the date tree. + +*** New ~vbar~ entity + +~\vbar~ or ~\vbar{}~ will be exported unconditionnally as a =|=, +unlike to existing ~\vert~, which is expanded as ~|~ when using +a HTML derived export back-end. + +*** Export + +**** New =#+latex_compiler= keyword to set LaTeX compiler. + +PDFLaTeX, XeLaTeX, and LuaLaTeX are supported. See the manual for +details. + +**** New option ~org-export-with-broken-links~ + +This option tells the export process how to behave when encountering +a broken internal link. See its docstring for more information. + +**** Attributes support in custom language environments for LaTeX export + +Custom language environments for LaTeX export can now define the +string to be inserted during export, using attributes to indicate the +position of the elements. See variable ~org-latex-custom-lang-environments~ +for more details. + +**** New Texinfo ~options~ attribute on special blocks + +Using ~:options~ as a Texinfo attribute, it is possible to add +information to custom environments. See manual for details. + +**** New HTML ~id~ attributes on special, example and quote blocks + +If the block has a =#+NAME:= attribute assigned, then the HTML element +will have an ~id~ attribute with that name in the HTML export. This +enables one to create links to these elements in other places, e.g., +~text~. + +**** Listings with captions are now numbered in HTML export + +The class associated to the numbering is "listing-number". If you +don't want these blocks to be numbered, as it was the case until now, +You may want to add ~.listing-number { display: none; }~ to the CSS +used. + +**** Line Numbering in SRC/EXAMPLE blocks support arbitrary start number + +The ~-n~ option to ~SRC~ and ~EXAMPLE~ blocks can now take a numeric +argument to specify the staring line number for the source or example +block. The ~+n~ option can now take a numeric argument that will be +added to the last line number from the previous block as the starting +point for the SRC/EXAMPLE block. + +#+BEGIN_SRC org +,#+BEGIN_SRC emacs-lisp -n 20 +;; this will export with line number 20 +(message "This is line 21") +,#+END_SRC +,#+BEGIN_SRC emacs-lisp +n 10 +;; This will be listed as line 31 +(message "This is line 32") +,#+END_SRC +#+END_SRC + +**** Allow toggling center for images in LaTeX export + +With the global variable ~org-latex-images-centered~ or the local +attribute ~:center~ it is now possible to center an image in LaTeX +export. + +**** Default CSS class ~org-svg~ for SVG images in HTML export + +SVG images exported in HTML are now by default assigned a CSS class +~org-svg~ if no CSS class is specified with the ~:class~ attribute. By +default, the CSS styling of class ~org-svg~ specifies an image width of +90\thinsp{}% of the container the image. + +**** Markdown footnote export customization + +Variables ~org-md-footnotes-section~ and ~org-md-footnote-format~ +introduced for =ox-md.el=. Both new variables define template strings +which can be used to customize the format of the exported footnotes +section and individual footnotes, respectively. + +*** Babel + +**** Blocks with coderefs labels can now be evaluated + +The labels are removed prior to evaluating the block. + +**** Support for Lua language +**** Support for SLY in Lisp blocks + +See ~org-babel-lisp-eval-fn~ to activate it. + +**** Support for Stan language + +New ob-stan.el library. + +Evaluating a Stan block can produce two different results. + +1. Dump the source code contents to a file. + + This file can then be used as a variable in other blocks, which + allows interfaces like RStan to use the model. + +2. Compile the contents to a model file. + + This provides access to the CmdStan interface. To use this, set + ~org-babel-stan-cmdstan-directory~ and provide a ~:file~ argument + that does not end in ".stan". + +For more information and usage examples, visit +http://orgmode.org/worg/org-contrib/babel/languages/ob-doc-stan.html + +**** Support for Oracle databases via ~sqlplus~ + +=ob-sql= library supports running SQL blocks against an Oracle +database using ~sqlplus~. Use with properties like this (all +mandatory): + +#+BEGIN_EXAMPLE +:engine oracle +:dbhost +:dbport <1521> +:dbuser +:database +:dbpassword +#+END_EXAMPLE + +**** Improved support to Microsoft SQL Server via ~sqlcmd~ + +=ob-sql= library removes support to the ~msosql~ engine which uses the +deprecated ~osql~ command line tool, and replaces it with ~mssql~ +engine which uses the ~sqlcmd~ command line tool. Use with properties +like this: + +#+BEGIN_EXAMPLE +:engine mssql +:dbhost +:dbuser +:dbpassword +:database +#+END_EXAMPLE + +If you want to use the *trusted connection* feature, omit *both* the +=dbuser= and =dbpassword= properties and add =cmdline -E= to the properties. + +If your Emacs is running in a Cygwin environment, the =ob-sql= library +can pass the converted path to the =sqlcmd= tool. + +**** Improved support of header arguments for postgresql + +The postgresql engine in a sql code block supports now ~:dbport~ nd +~:dbpassword~ as header arguments. + +**** Support for additional plantuml output formats + +The support for output formats of [[http://plantuml.com/][plantuml]] has been extended to now +include: + +All Diagrams: +- png :: +- svg :: +- eps :: +- pdf :: +- vdx :: +- txt :: ASCII art +- utxt :: ASCII art using unicode characters + +Class Diagrams: +- xmi :: +- html :: + +State Diagrams: +- scxml :: + +The output formats are determined by the file extension specified +using the :file property, e.g.: + +#+begin_src plantuml :file diagram.png +@startuml +Alice -> Bob: Authentication Request +Bob --> Alice: Authentication Response + +Alice -> Bob: Another authentication Request +Alice <-- Bob: another authentication Response +@enduml +#+end_src + +Please note that *pdf* *does not work out of the box* and needs additional +setup in addition to plantuml. See [[http://plantuml.com/pdf.html]] for +details and setup information. + +*** Rewrite of radio lists + +Radio lists, i.e, Org plain lists in foreign buffers, have been +rewritten to be on par with Radio tables. You can use a large set of +parameters to control how a given list should be rendered. See manual +for details. + +*** org-bbdb-anniversaries-future + +Used like ~org-bbdb-anniversaries~, it provides a few days warning for +upcoming anniversaries (default: 7 days). + +*** Clear non-repeated SCHEDULED upon repeating a task + +If the task is repeated, and therefore done at least one, scheduling +information is no longer relevant. It is therefore removed. + +See [[git:481719fbd5751aaa9c672b762cb43aea8ee986b0][commit message]] for more information. + +*** Support for ISO week trees + +ISO week trees are an alternative date tree format that orders entries +by ISO week and not by month. + +For example: + +: * 2015 +: ** 2015-W35 +: ** 2015-W36 +: *** 2015-08-31 Monday + +They are supported in org-capture via ~file+weektree~ and +~file+weektree+prompt~ target specifications. + +*** Accept ~:indent~ parameter when capturing column view + +When defining a "columnview" dynamic block, it is now possible to add +an :indent parameter, much like the one in the clock table. + +On the other hand, stars no longer appear in an ITEM field. + +*** Columns view + +**** ~org-columns~ accepts a prefix argument + +When called with a prefix argument, ~org-columns~ apply to the whole +buffer unconditionally. + +**** New variable : ~org-agenda-view-columns-initially~ + +The variable used to be a ~defvar~, it is now a ~defcustom~. + +**** Allow custom summaries + +It is now possible to add new summary types, or override those +provided by Org by customizing ~org-columns-summary-types~, which see. + +**** Allow multiple summaries for any property + +Columns can now summarize the same property using different summary +types. + +*** Preview LaTeX snippets in buffers not visiting files +*** New option ~org-attach-commit~ + +When non-nil, commit attachments with git, assuming the document is in +a git repository. + +*** Allow conditional case-fold searches in ~org-occur~ + +When set to ~smart~, the new variable ~org-occur-case-fold-search~ allows +to mimic =isearch.el=: if the regexp searched contains any upper case +character (or character class), the search is case sensitive. +Otherwise, it is case insensitive. + +*** More robust repeated =ox-latex= footnote handling + +Repeated footnotes are now numbered by referring to a label in the +first footnote. + +*** The ~org-block~ face is inherited by ~src-blocks~ + +This works also when =org-src-fontify-natively= is non-nil. It is also +possible to specify per-languages faces. See =org-src-block-faces= and +the manual for details. + +*** Links are now customizable + +Links can now have custom colors, tooltips, keymaps, display behavior, +etc. Links are now centralized in ~org-link-parameters~. + +** New functions + +*** ~org-next-line-empty-p~ + +It replaces the deprecated ~next~ argument to ~org-previous-line-empty-p~. + +*** ~org-show-children~ + +It is a faster implementation of ~outline-show-children~. + +** Removed functions + +*** ~org-agenda-filter-by-tag-refine~ has been removed. + +Use ~org-agenda-filter-by-tag~ instead. + +*** ~org-agenda-todayp~ is deprecated. + +Use ~org-agenda-today-p~ instead. + +*** ~org-babel-get-header~ is removed. + +Use ~org-babel--get-vars~ or ~assq~ instead, as applicable. + +*** ~org-babel-trim~ is deprecated. + +Use ~org-trim~ instead. + +*** ~org-element-remove-indentation~ is deprecated. + +Use ~org-remove-indentation~ instead. + +*** ~org-image-file-name-regexp~ is deprecated + +Use ~image-file-name-regexp~ instead. +The never-used-in-core ~extensions~ argument has been dropped. + +*** ~org-list-parse-list~ is deprecated + +Use ~org-list-to-lisp~ instead. + +*** ~org-on-heading-p~ is deprecated + +A comment to this effect was in the source code since 7.8.03, but +now a byte-compiler warning will be generated as well. + +*** ~org-table-p~ is deprecated + +Use ~org-at-table-p~ instead. + +*** ~org-table-recognize-table.el~ is deprecated + +It was not called by any org code since 2010. + +*** Various reimplementations of cl-lib functions are deprecated + +The affected functions are: +- ~org-count~ +- ~org-remove-if~ +- ~org-remove-if-not~ +- ~org-reduce~ +- ~org-every~ +- ~org-some~ + +Additionally, ~org-sublist~ is deprecated in favor of ~cl-subseq~. Note +the differences in indexing conventions: ~org-sublist~ is 1-based and +end-inclusive; ~cl-subseq~ is 0-based and end-exclusive. + +** Removed options + +*** Remove all options related to ~ido~ or ~iswitchb~ + +This includes ~org-completion-use-iswitchb~ and ~org-completion-use-ido~. +Instead Org uses regular functions, e.g., ~completion-read~ so as to +let those libraries operate. + +*** Remove ~org-list-empty-line-terminates-plain-lists~ + +Two consecutive blank lines always terminate all levels of current +plain list. + +*** ~fixltx2e~ is removed from ~org-latex-default-packages-alist~ + +fixltx2e is obsolete, see LaTeX News 22. + +** Miscellaneous +*** Add Icelandic smart quotes +*** Allow multiple receiver locations in radio tables and lists +*** Allow angular links within link descriptions + +It is now allowed to write, e.g., +~[[http:orgmode.org][]]~ as an equivalent to +~[[http:orgmode.org][file:unicorn.png]]~. The advantage of the former +is that spaces are allowed within the path. + +*** Beamer export back-ends uses ~org-latex-prefer-user-labels~ +*** ~:preparation-function~ called earlier during publishing + +Functions in this list are called before any file is associated to the +current projet. Thus, they can be used to generate to be published +Org files. + +*** Function ~org-remove-indentation~ changes. + +The new algorithm doesn't remove TAB characters not used for +indentation. + +*** Secure placeholders in capture templates + +Placeholders in capture templates are no longer expanded recursively. +However, ~%(...)~ constructs are expanded very late, so you can fill +the contents of the S-exp with the replacement text of non-interactive +placeholders. As before, interactive ones are still expanded as the +very last step, so the previous statement doesn't apply to them. + +Note that only ~%(...)~ placeholders initially present in the +template, or introduced using a file placeholder, i.e., ~%[...]~ are +expanded. This prevents evaluating potentially malicious code when +another placeholder, e.g., ~%i~ expands to a S-exp. + +*** Links stored by ~org-gnus-store-link~ in nnir groups + +Since gnus nnir groups are temporary, ~org-gnus-store-link~ now refers +to the article's original group. + +*** ~org-babel-check-confirm-evaluate~ is now a function instead of a macro + +The calling convention has changed. + +*** HTML export table row customization changes + +Variable ~org-html-table-row-tags~ has been split into +~org-html-table-row-open-tag~ and ~org-html-table-row-close-tag~. +Both new variables can be either a string or a function which will be +called with 6 parameters. + +*** =ITEM= special property returns headline without stars +*** Rename ~org-insert-columns-dblock~ into ~org-columns-insert-dblock~ + +The previous name is, for the time being, kept as an obsolete alias. + +*** ~org-trim~ can preserve leading indentation. + +When setting a new optional argument to a non-nil value, ~org-trim~ +preserves leading indentation while removing blank lines at the +beginning of the string. The behavior is identical for white space at +the end of the string. + +*** Function ~org-info-export~ changes. + +HTML links created from certain info links now point to =gnu.org= URL's rather +than just to local files. For example info links such as =info:emacs#List +Buffers= used to be converted to HTML links like this: + +: emacs#List Buffers + +where local file =emacs.html= is referenced. +For most folks this file does not exist. +Thus the new behavior is to generate this HTML link instead: + +: emacs#List Buffers + +All emacs related info links are similarly translated plus few other +=gnu.org= manuals. + +*** Repeaters with a ~++~ interval and a time can be shifted to later today + +Previously, if a recurring task had a timestamp of +~<2016-01-01 Fri 20:00 ++1d>~ and was completed on =2016-01-02= at +=08:00=, the task would skip =2016-01-02= and would be rescheduled for +=2016-01-03=. Timestamps with ~++~ cookies and a specific time will +now shift to the first possible future occurrence, even if the +occurrence is later the same day the task is completed. (Timestamps +already in the future are still shifted one time further into the +future.) + +*** ~org-mobile-action-alist~ is now a defconst + +It used to be a defcustom, with a warning that it shouldn't be +modified anyway. + +*** ~file+emacs~ and ~file+sys~ link types are deprecated + +They are still supported in Org 9.0 but will eventually be removed in +a later release. Use ~file~ link type along with universal arguments +to force opening it in either Emacs or with system application. + +*** New defcustom ~org-babel-J-command~ stores the j command +*** New defalias ~org-babel-execute:j~ + +Allows J source blocks be indicated by letter j. Previously the +indication letter was solely J. + +*** ~org-open-line~ ignores tables at the very beginning of the buffer + +When ~org-special-ctrl-o~ is non-nil, it is impractical to create +a blank line above a table at the beginning of the document. Now, as +a special case, ~org-open-line~ behaves normally in this situation. + +*** ~org-babel-hash-show-time~ is now customizable + +The experimental variable used to be more or less confidential, as +a ~defvar~. + +*** New ~:format~ property to parsed links + +It defines the format of the original link. Possible values are: +~plain~, ~bracket~ and ~angle~. + +* Version 8.3 + +** Incompatible changes + +*** Properties drawers syntax changes + +Properties drawers are now required to be located right after a +headline and its planning line, when applicable. + +It will break some documents as TODO states changes were sometimes +logged before the property drawer. + +The following function will repair them: + +#+BEGIN_SRC emacs-lisp +(defun org-repair-property-drawers () + "Fix properties drawers in current buffer. +Ignore non Org buffers." + (when (eq major-mode 'org-mode) + (org-with-wide-buffer + (goto-char (point-min)) + (let ((case-fold-search t) + (inline-re (and (featurep 'org-inlinetask) + (concat (org-inlinetask-outline-regexp) + "END[ \t]*$")))) + (org-map-entries + (lambda () + (unless (and inline-re (org-looking-at-p inline-re)) + (save-excursion + (let ((end (save-excursion (outline-next-heading) (point)))) + (forward-line) + (when (org-looking-at-p org-planning-line-re) (forward-line)) + (when (and (< (point) end) + (not (org-looking-at-p org-property-drawer-re)) + (save-excursion + (and (re-search-forward org-property-drawer-re end t) + (eq (org-element-type + (save-match-data (org-element-at-point))) + 'drawer)))) + (insert (delete-and-extract-region + (match-beginning 0) + (min (1+ (match-end 0)) end))) + (unless (bolp) (insert "\n")))))))))))) +#+END_SRC + +*** Using "COMMENT" is now equivalent to commenting with "#" + +If you used "COMMENT" in headlines to prevent a subtree from being +exported, you can still do it but all information within the subtree +is now commented out, i.e. no #+OPTIONS line will be parsed or taken +into account when exporting. + +If you want to exclude a headline from export while using its contents +for setting options, use =:noexport:= (see =org-export-exclude-tags=.) + +*** =#+CATEGORY= keywords no longer apply partially to document + +It was possible to use several such keywords and have them apply to +the text below until the next one, but strongly deprecated since Org +5.14 (2008). + +=#+CATEGORY= keywords are now global to the document. You can use node +properties to set category for a subtree, e.g., + +#+BEGIN_SRC org +,* Headline + :PROPERTIES: + :CATEGORY: some category + :END: +#+END_SRC + +*** New variable to control visibility when revealing a location + +~org-show-following-heading~, ~org-show-siblings~, ~org-show-entry-below~ +and ~org-show-hierarchy-above~ no longer exist. Instead, visibility is +controlled through a single variable: ~org-show-context-detail~, which +see. + +*** Replace disputed keys again when reading a date + +~org-replace-disputed-keys~ has been ignored when reading date since +version 8.1, but the former behavior is restored again. + +Keybinding for reading date can be customized with a new variable +~org-read-date-minibuffer-local-map~. + +*** No default title is provided when =TITLE= keyword is missing + +Skipping =TITLE= keyword no longer provides the current file name, or +buffer name, as the title. Instead, simply ignore the title. + +*** Default bindings of =C-c C-n= and =C-c C-p= changed + +The key sequences =C-c C-n= and =C-c C-p= are now bound to +~org-next-visible-heading~ and ~org-previous-visible-heading~ +respectively, rather than the =outline-mode= versions of these +functions. The Org version of these functions skips over inline tasks +(and even-level headlines when ~org-odd-levels-only~ is set). + +*** ~org-element-context~ no longer return objects in keywords + +~org-element-context~ used to return objects on some keywords, i.e., +=TITLE=, =DATE= and =AUTHOR=. It now returns only the keyword. + +*** ~org-timer-default-timer~ type changed from number to string + +If you have, in your configuration, something like =(setq +org-timer-default-timer 10)= replace it with =(setq +org-timer-default-timer "10")=. + +*** Functions signature changes + +The following functions require an additional argument. See their +docstring for more information. + +- ~org-export-collect-footnote-definitions~ +- ~org-html-format-headline-function~ +- ~org-html-format-inlinetask-function~ +- ~org-latex-format-headline-function~ +- ~org-latex-format-inlinetask-function~ +- ~org-link-search~ + +** New features + +*** Default lexical evaluation of emacs-lisp src blocks + +Emacs-lisp src blocks in babel are now evaluated using lexical +scoping. There is a new header to control this behavior. + +The default results in an eval with lexical scoping. +:lexical yes + +This turns lexical scoping off in the eval (the former behavior). +:lexical no + +This uses the lexical environment with x=42 in the eval. +:lexical '((x . 42)) + +*** Behavior of ~org-return~ changed + +If point is before or after the headline title, insert a new line +without changing the headline. + +*** Hierarchies of tags + +The functionality of nesting tags in hierarchies is added to org-mode. +This is the generalization of what was previously called "Tag groups" +in the manual. That term is now changed to "Tag hierarchy". + +The following in-buffer definition: + +#+BEGIN_SRC org + ,#+TAGS: [ Group : SubOne SubTwo ] + ,#+TAGS: [ SubOne : SubOne1 SubOne2 ] + ,#+TAGS: [ SubTwo : SubTwo1 SubTwo2 ] +#+END_SRC + +Should be seen as the following tree of tags: + +- Group + - SubOne + - SubOne1 + - SubOne2 + - SubTwo + - SubTwo1 + - SubTwo2 + +Searching for "Group" should return all tags defined above. Filtering +on SubOne filters also it's sub-tags. Etc. + +There is no limit on the depth for the tag hierarchy. + +*** Additional syntax for non-unique grouptags + +Additional syntax is defined for grouptags if the tags in the group +don't have to be distinct on a heading. + +Grouptags had to previously be defined with { }. This syntax is +already used for exclusive tags and Grouptags need their own, +non-exclusive syntax. This behaviour is achieved with [ ]. Note: { } +can still be used also for Grouptags but then only one of the given +tags can be used on the headline at the same time. Example: + +[ group : sub1 sub2 ] + +#+BEGIN_SRC org +,* Test :sub1:sub2: +#+END_SRC + +This is a more general case than the already existing syntax for +grouptags; { }. + +*** Define regular expression patterns as tags + +Tags can be defined as grouptags with regular expressions as +"sub-tags". + +The regular expressions in the group must be marked up within { }. +Example use: + +: #+TAGS: [ Project : {P@.+} ] + +Searching for the tag Project will now list all tags also including +regular expression matches for P@.+. This is good for example for +projects tagged with a common identifier, i.e. P@2014_OrgTags. + +*** Filtering in the agenda on grouptags (Tag hierarchies) + +Filtering in the agenda on grouptags filters all of the related tags. +Except if a filter is applied with a (double) prefix-argument. + +Filtering in the agenda on subcategories does not filter the "above" +levels anymore. + +If a grouptag contains a regular expression the regular expression +is also used as a filter. + +*** Minor refactoring of ~org-agenda-filter-by-tag~ + +Now uses the argument ARG and optional argument exclude instead of +strip and narrow. ARG because the argument has multiple purposes and +makes more sense than strip now. The term "narrowing" is changed to +exclude. + +The main purpose is for the function to make more logical sense when +filtering on tags now when tags can be structured in hierarchies. + +*** Babel: support for sed scripts + +Thanks to Bjarte Johansen for this feature. + +*** Babel: support for Processing language + +New ob-processing.el library. + +This library implements necessary functions for implementing editing +of Processing code blocks, viewing the resulting sketches in an +external viewer, and HTML export of the sketches. + +Check the documentation for more details. + +Thanks to Jarmo Hurri for this feature. + +*** New behaviour for ~org-toggle-latex-fragment~ + +The new behaviour is the following: + +- With a double prefix argument or with a single prefix argument when + point is before the first headline, toggle overlays in the whole + buffer; + +- With a single prefix argument, toggle overlays in the current + subtree; + +- On latex code, toggle overlay at point; + +- Otherwise, toggle overlays in the current section. + +*** Additional markup with =#+INCLUDE= keyword + +The content of the included file can now be optionally marked up, for +instance as HTML. See the documentation for details. + +*** File links with =#+INCLUDE= keyword + +Objects can be extracted via =#+INCLUDE= using file links. It is +possible to include only the contents of the object. See manual for +more information. + +*** Drawers do not need anymore to be referenced in =#+DRAWERS= + +One can use a drawer without listing it in the =#+DRAWERS= keyword, +which is now obsolete. As a consequence, this change also deprecates +~org-drawers~ variable. + +*** ~org-edit-special~ can edit export blocks + +Using C-c ' on an export block now opens a sub-editing buffer. Major +mode in that buffer is determined by export backend name (e.g., +"latex" \to "latex-mode"). You can define exceptions to this rule by +configuring ~org-src-lang-modes~, which see. + +*** Additional =:hline= processing to ob-shell + +If the argument =:hlines yes= is present in a babel call, an optional +argument =:hlines-string= can be used to define a string to use as a +representation for the lisp symbol ='hline= in the shell program. The +default is =hline=. + +*** Markdown export supports switches in source blocks + +For example, it is now possible to number lines using the =-n= switch in +a source block. + +*** New option in ASCII export + +Plain lists can have an extra margin by setting ~org-ascii-list-margin~ +variable to an appopriate integer. + +*** New blocks in ASCII export + +ASCII export now supports =#+BEGIN_JUSTIFYRIGHT= and =#+BEGIN_JUSTIFYLEFT= +blocks. See documentation for details. + +*** More back-end specific publishing options + +The number of publishing options specific to each back-end has been +increased. See manual for details. + +*** Export inline source blocks + +Inline source code was used to be removed upon exporting. They are +now handled as standard code blocks, i.e., the source code can appear +in the output, depending on the parameters. + +*** Extend ~org-export-first-sibling-p~ and ~org-export-last-sibling-p~ + +These functions now support any element or object, not only headlines. + +*** New function: ~org-export-table-row-in-header-p~ + +*** New function: ~org-export-get-reference~ + +*** New function: ~org-element-lineage~ + +This function deprecates ~org-export-get-genealogy~. It also provides +more features. See docstring for details. + +*** New function: ~org-element-copy~ + +*** New filter: ~org-export-filter-body-functions~ + +Functions in this filter are applied on the body of the exported +document, befor wrapping it within the template. + +*** New :environment parameter when exporting example blocks to LaTeX + +: #+ATTR_LATEX: :environment myverbatim +: #+BEGIN_EXAMPLE +: This sentence is false. +: #+END_EXAMPLE + +will be exported using =@samp(myverbatim)= instead of =@samp(verbatim)=. + +*** Various improvements on radio tables + +Radio tables feature now relies on Org's export framework ("ox.el"). +~:no-escape~ parameter no longer exists, but additional global +parameters are now supported: ~:raw~, ~:backend~. Moreover, there are new +parameters specific to some pre-defined translators, e.g., +~:environment~ and ~:booktabs~ for ~orgtbl-to-latex~. See translators +docstrings (including ~orgtbl-to-generic~) for details. + +*** Non-floating minted listings in Latex export + +It is not possible to specify =#+attr_latex: :float nil= in conjunction +with source blocks exported by the minted package. + +*** Field formulas can now create columns as needed + +Previously, evaluating formulas that referenced out-of-bounds columns +would throw an error. A new variable ~org-table-formula-create-columns~ +was added to adjust this behavior. It is now possible to silently add +new columns, to do so with a warning or to explicitly ask the user +each time. + +*** ASCII plot + +Ability to plot values in a column through ASCII-art bars. See manual +for details. + +*** New hook: ~org-archive-hook~ + +This hook is called after successfully archiving a subtree, with point +on the original subtree, not yet deleted. + +*** New option: ~org-attach-archive-delete~ + +When non-nil, attachments from archived subtrees are removed. + +*** New option: ~org-latex-caption-above~ + +This variable generalizes ~org-latex-table-caption-above~, which is now +deprecated. In addition to tables, it applies to source blocks, +special blocks and images. See docstring for more information. + +*** New option: ~org-latex-prefer-user-labels~ + +See the docstring for more information. + +*** Export unnumbered headlines + +Headlines, for which the property ~UNNUMBERED~ is non-nil, are now +exported without section numbers irrespective of their levels. The +property is inherited by children. + +*** Tables can be sorted with an arbitrary function + +It is now possible to specify a function, both programatically, +through a new optional argument, and interactively with ~f~ or ~F~ keys, +to sort a table. + +*** Table of contents can be local to a section + +The ~TOC~ keywords now accepts an optional ~local~ parameter. See manual +for details. + +*** Countdown timers can now be paused + +~org-timer-pause-time~ now pauses and restarts both relative and +countdown timers. + +*** New option ~only-window~ for ~org-agenda-window-setup~ + +When ~org-agenda-window-setup~ is set to ~only-window~, the agenda is +displayed as the sole window of the current frame. + +*** ~{{{date}}}~ macro supports optional formatting argument + +It is now possible to supply and optional formatting argument to +~{{{date}}}~. See manual for details. + +*** ~{{{property}}}~ macro supports optional search argument + +It is now possible to supply an optional search option to +~{{{property}}}~ in order to retrieve remote properties optional. See +manual for details. + +*** New option ~org-export-with-title~ + +It is possible to suppress the title insertion with ~#+OPTIONS: +title:nil~ or globally using the variable ~org-export-with-title~. + +*** New entities family: "\_ " + +"\_ " are used to insert up to 20 contiguous spaces in various +back-ends. In particular, this family can be used to introduce +leading spaces within table cells. + +*** New MathJax configuration options + +Org uses the MathJax CDN by default. See the manual and the docstring +of ~org-html-mathjax-options~ for details. + +*** New behaviour in `org-export-options-alist' + +When defining a back-end, it is now possible to specify to give +`parse' behaviour on a keyword. It is equivalent to call +`org-element-parse-secondary-string' on the value. + +However, parsed =KEYWORD= is automatically associated to an +=:EXPORT_KEYWORD:= property, which can be used to override the keyword +value during a subtree export. Moreover, macros are expanded in such +keywords and properties. + +*** Viewport support in html export + +Viewport for mobile-optimized website is now automatically inserted +when exporting to html. See ~org-html-viewport~ for details. + +*** New ~#+SUBTITLE~ export keyword + +Org can typeset a subtitle in some export backends. See the manual +for details. + +*** Remotely edit a footnote definition + +Calling ~org-edit-footnote-reference~ (C-c ') on a footnote reference +allows to edit its definition, as long as it is not anonymous, in a +dedicated buffer. It works even if buffer is currently narrowed. + +*** New function ~org-delete-indentation~ bound to ~M-^~ + +Work as ~delete-indentation~ unless at heading, in which case text is +added to headline text. + +*** Support for images in Texinfo export + +~Texinfo~ back-end now handles images. See the manual for details. + +*** Support for captions in Texinfo export + +Tables and source blocks can now have captions. Additionally, lists +of tables and lists of listings can be inserted in the document with +=#+TOC= keyword. + +*** Countdown timer support hh:mm:ss format + +In addition to setting countdown timers in minutes, they can also be +set using the hh:mm:ss format. + +*** Extend ~org-clone-subtree-with-time-shift~ + +~org-clone-subtree-with-time-shift~ now accepts 0 as an argument for the +number of clones, which removes the repeater from the original subtree +and creates one shifted, repeating clone. + +*** New time block for clock tables: ~untilnow~ + +It encompasses all past closed clocks. + +*** Support for the ~polyglossia~ LaTeX package + +See the docstring of ~org-latex-classes~ and +~org-latex-guess-polyglossia-language~ for details. + +*** None-floating tables, graphics and blocks can have captions + +*** `org-insert-heading' can be forced to insert top-level headline + +** Removed functions + +*** Removed function ~org-translate-time~ + +Use ~org-timestamp-translate~ instead. + +*** Removed function ~org-beamer-insert-options-template~ + +This function inserted a Beamer specific template at point or in +current subtree. Use ~org-export-insert-default-template~ instead, as +it provides more features and covers all export back-ends. It is also +accessible from the export dispatcher. + +*** Removed function ~org-timer-cancel-timer~ + +~org-timer-stop~ now stops both relative and countdown timers. + +*** Removed function ~org-export-solidify-link-text~ + +This function, being non-bijective, introduced bug in internal +references. Use ~org-export-get-reference~ instead. + +*** Removed function ~org-end-of-meta-data-and-drawers~ + +The function is superseded by ~org-end-of-meta-data~, called with an +optional argument. + +*** Removed functions ~org-table-colgroup-line-p~, ~org-table-cookie-line-p~ + +These functions were left-over from pre 8.0 era. They are not correct +anymore. Since they are not needed, they have no replacement. + +** Removed options + +*** ~org-list-empty-line-terminates-plain-lists~ is deprecated + +It will be kept in code base until next release, for backward +compatibility. + +If you need to separate consecutive lists with blank lines, always use +two of them, as if this option was nil (default value). + +*** ~org-export-with-creator~ is a boolean + +Special ~comment~ value is no longer allowed. It is possible to use a +body filter to add comments about the creator at the end of the +document instead. + +*** Removed option =org-html-use-unicode-chars= + +Setting this to non-nil was problematic as it converted characters +everywhere in the buffer, possibly corrupting URLs. + +*** Removed option =org-babel-sh-command= + +This undocumented option defaulted to the value of =shell-file-name= at +the time of loading =ob-shell=. The new behaviour is to use the value +of =shell-file-name= directly when the shell langage is =shell=. To chose +a different shell, either customize =shell-file-name= or bind this +variable locally. + +*** Removed option =org-babel-sh-var-quote-fmt= + +This undocumented option was supposed to provide different quoting +styles when changing the shell type. Changing the shell type can now +be done directly from the source block and the quoting style has to be +compatible across all shells, so a customization doesn't make sense +anymore. The chosen hard coded quoting style conforms to POSIX. + +*** Removed option ~org-insert-labeled-timestamps-at-point~ + +Setting this option to anything else that the default value (nil) +would create invalid planning info. This dangerous option is now +removed. + +*** Removed option ~org-koma-letter-use-title~ + +Use org-export-with-title instead. See also below. + +*** Removed option ~org-entities-ascii-explanatory~ + +This variable has no effect since Org 8.0. + +*** Removed option ~org-table-error-on-row-ref-crossing-hline~ + +This variable has no effect since August 2009. + +*** Removed MathML-related options from ~org-html-mathjax-options~ + +MathJax automatically chooses the best display technology based on the +end-users browser. You may force initial usage of MathML via +~org-html-mathjax-template~ or by setting the ~path~ property of +~org-html-mathjax-options~. + +*** Removed comment-related filters + +~org-export-filter-comment-functions~ and +~org-export-filter-comment-block-functions~ variables do not exist +anymore. + +** Miscellaneous + +*** Strip all meta data from ITEM special property + +ITEM special property does not contain TODO, priority or tags anymore. + +*** File names in links accept are now compatible with URI syntax + +Absolute file names can now start with =///= in addition to =/=. E.g., +=[[file:///home/me/unicorn.jpg]]=. + +*** Footnotes in included files are now local to the file + +As a consequence, it is possible to include multiple Org files with +footnotes in a master document without being concerned about footnote +labels colliding. + +*** Mailto links now use regular URI syntax + +This change deprecates old Org syntax for mailto links: +=mailto:user@domain::Subject=. + +*** =QUOTE= keywords do not exist anymore + +=QUOTE= keywords have been deprecated since Org 8.2. + +*** Select tests to perform with the build system + +The build system has been enhanced to allow test selection with a +regular expression by defining =BTEST_RE= during the test invocation. +This is especially useful during bisection to find just when a +particular test failure was introduced. + +*** Exact heading search for external links ignore spaces and cookies + +Exact heading search for links now ignore spaces and cookies. This is +the case for links of the form ~file:projects.org::*task title~, as well +as links of the form ~file:projects.org::some words~ when +~org-link-search-must-match-exact-headline~ is not nil. + +*** ~org-latex-hyperref-template~, ~org-latex-title-command~ formatting + +New formatting keys are supported. See the respective docstrings. +Note, ~org-latex-hyperref-template~ has a new default value. + +*** ~float, wasysym, marvosym~ are removed from ~org-latex-default-packages-alist~ + +If you require any of these package add them to your preamble via +~org-latex-packages-alist~. Org also uses default LaTeX ~\tolerance~ now. + +*** When exporting, throw an error on unresolved id/fuzzy links and code refs + +This helps spotting wrong links. + +* Version 8.2 + +** Incompatible changes +*** =ob-sh.el= renamed to =ob-shell= +This may require two changes in user config. + +1. In =org-babel-do-load-languages=, change =(sh . t)= to =(shell . t)=. +2. Edit =local.mk= files to change the value of =BTEST_OB_LANGUAGES= + to remove "sh" and include "shell". + *** Combine org-mac-message.el and org-mac-link-grabber into org-mac-link.el Please remove calls to =(require 'org-mac-message)= and =(require @@ -171,6 +1522,18 @@ then inline code snippets will be wrapped into the formatting string. - =org-screenshot.el= by Max Mikhanosha :: an utility to handle screenshots easily from Org, using the external tool [[http://freecode.com/projects/scrot][scrot]]. +** Miscellaneous + +*** "QUOTE" keywords in headlines are deprecated + +"QUOTE" keywords are an undocumented feature in Org. When a headline +starts with the keyword "QUOTE", its contents are parsed as +a ~quote-section~ and treated as an example block. You can achieve +the same with example blocks. + +This feature is deprecated and will be removed in the next Org +release. + * Version 8.0.1 ** Installation @@ -835,14 +2198,14 @@ See [[http://orgmode.org/org.html#Lookup-functions][the manual]] for details. These new startup keywords are now available: -| Startup keyword | Option | -|----------------------------------+---------------------------------------------| +| Startup keyword | Option | +|--------------------------------+-------------------------------------------| | =#+STARTUP: logdrawer= | =(setq org-log-into-drawer t)= | | =#+STARTUP: nologdrawer= | =(setq org-log-into-drawer nil)= | -|----------------------------------+---------------------------------------------| +|--------------------------------+-------------------------------------------| | =#+STARTUP: logstatesreversed= | =(setq org-log-states-order-reversed t)= | | =#+STARTUP: nologstatesreversed= | =(setq org-log-states-order-reversed nil)= | -|----------------------------------+---------------------------------------------| +|--------------------------------+-------------------------------------------| | =#+STARTUP: latexpreview= | =(setq org-startup-with-latex-preview t)= | | =#+STARTUP: nolatexpreview= | =(setq org-startup-with-latex-preview nil)= | @@ -952,7 +2315,7 @@ instead of requiring each Babel library one by one. - New option [[doc:org-gnus-no-server][org-gnus-no-server]] to start Gnus with =gnus-no-server= - Org is now distributed with =htmlize.el= version 1.43 - ~org-drill.el~ has been updated to version 2.3.7 -- ~org-mac-iCal.el~ now supports OS X versions up to 10.8 +- ~org-mac-iCal.el~ now supports MacOSX version up to 10.8 - Various improvements to ~org-contacts.el~ and =orgpan.el= ** Outside Org @@ -1021,6 +2384,13 @@ consistent with using the `:' key in agenda view. You can now use `=' for [[doc::org-columns][org-columns]]. ** =org-float= is now obsolete, use =diary-float= instead +** No GPL manual anymore + +There used to be a GPL version of the Org manual, but this is not the +case anymore, the Free Software Foundation does not permit this. + +The GNU FDL license is now included in the manual directly. + ** Enhanced compatibility with Emacs 22 and XEmacs Thanks to Achim for his work on enhancing Org's compatibility with @@ -1046,8 +2416,8 @@ See http://orgmode.org/elpa/ ** Overview of the new keybindings - | Keybinding | Speedy | Command | - |-----------------+--------+-----------------------------| + | Keybinding | Speedy | Command | + |---------------+--------+-----------------------------| | =C-c C-x C-z= | | [[doc::org-clock-resolve][org-clock-resolve]] | | =C-c C-x C-q= | | [[doc::org-clock-cancel][org-clock-cancel]] | | =C-c C-x C-x= | | [[doc::org-clock-in-last][org-clock-in-last]] | @@ -1055,12 +2425,12 @@ See http://orgmode.org/elpa/ | =*= | | [[doc::org-agenda-bulk-mark-all][org-agenda-bulk-mark-all]] | | =C-c C-M-l= | | [[doc::org-insert-all-links][org-insert-all-links]] | | =C-c C-x C-M-v= | | [[doc::org-redisplay-inline-images][org-redisplay-inline-images]] | - | =C-c C-x E= | =E= | [[doc::org-inc-effort][org-inc-effort]] | - | | =#= | [[doc::org-toggle-comment][org-toggle-comment]] | - | | =:= | [[doc::org-columns][org-columns]] | - | | =W= | Set =APPT_WARNTIME= | + | =C-c C-x E= | =E= | [[doc::org-inc-effort][org-inc-effort]] | + | | =#= | [[doc::org-toggle-comment][org-toggle-comment]] | + | | =:= | [[doc::org-columns][org-columns]] | + | | =W= | Set =APPT_WARNTIME= | | =k= | | [[doc::org-agenda-capture][org-agenda-capture]] | - | C-c , | , | [[doc::org-priority][org-priority]] | + | C-c , | , | [[doc::org-priority][org-priority]] | ** New package and Babel language @@ -1225,7 +2595,7 @@ See http://orgmode.org/elpa/ **** New =todo-unblocked= and =nottodo-unblocked= skip conditions - See the [[http://orgmode.org/w/?p%3Dorg-mode.git%3Ba%3Dcommit%3Bh%3Df426da][git commit]] for more explanations. + See the [[http://orgmode.org/cgit.cgi/org-mode.git/commit/?id=f426da][git commit]] for more explanations. **** Allow category filtering in the agenda @@ -1542,7 +2912,7 @@ See http://orgmode.org/elpa/ Thanks to Carsten for implementing this. **** ODT: Add support for ODT export in org-bbdb.el -**** ODT: Add support for indented tables (see [[http://orgmode.org/w/?p%3Dorg-mode.git%3Ba%3Dcommit%3Bh%3De9fd33][this commit]] for details) +**** ODT: Add support for indented tables (see [[http://orgmode.org/cgit.cgi/org-mode.git/commit/?id=e9fd33][this commit]] for details) **** ODT: Improve the conversion from ODT to other formats **** ASCII: Swap the level-1/level-2 characters to underline the headlines **** Support for Chinese, simplified Chinese, Russian, Ukrainian and Japanese diff --git a/etc/org/OrgOdtStyles.xml b/etc/org/OrgOdtStyles.xml index f41d9840cb..1a8edee99b 100644 --- a/etc/org/OrgOdtStyles.xml +++ b/etc/org/OrgOdtStyles.xml @@ -109,33 +109,53 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/etc/org/README b/etc/org/README index 68905add81..d04f434962 100644 --- a/etc/org/README +++ b/etc/org/README @@ -1,7 +1,7 @@ The files OrgOdtContentTemplate.xml and OrgOdtStyles.xml have the following copyright information: -Copyright (C) 2010-2017 Free Software Foundation, Inc. +Copyright (C) 2010-2014 Free Software Foundation, Inc. These files are part of GNU Emacs. diff --git a/etc/org/library-of-babel.org b/etc/org/library-of-babel.org new file mode 100644 index 0000000000..0098e72639 --- /dev/null +++ b/etc/org/library-of-babel.org @@ -0,0 +1,584 @@ +#+title: The Library of Babel +#+author: Org-mode People +#+STARTUP: hideblocks + +* Introduction + +The Library of Babel is an extensible collection of ready-made and +easily-shortcut-callable source-code blocks for handling common tasks. +Org-babel comes pre-populated with the source-code blocks located in +this file. It is possible to add source-code blocks from any org-mode +file to the library by calling =(org-babel-lob-ingest +"path/to/file.org")=. + +This file is included in worg mainly less for viewing through the web +interface, and more for contribution through the worg git repository. +If you have code snippets that you think others may find useful please +add them to this file and [[file:~/src/worg/worg-git.org::contribute-to-worg][contribute them]] to worg. + +The raw Org-mode text of this file can be downloaded at +[[repofile:contrib/babel/library-of-babel.org][library-of-babel.org]] + +* Simple + +A collection of simple utility functions: + +#+name: echo +#+begin_src emacs-lisp :var input="echo'd" + input +#+end_src + +* File I/O + +** Reading and writing files + +Read the contents of the file at =file=. The =:results vector= and +=:results scalar= header arguments can be used to read the contents of +file as either a table or a string. + +#+name: read +#+begin_src emacs-lisp :var file="" :var format="" + (if (string= format "csv") + (with-temp-buffer + (org-table-import (expand-file-name file) nil) + (org-table-to-lisp)) + (with-temp-buffer + (insert-file-contents (expand-file-name file)) + (buffer-string))) +#+end_src + +Write =data= to a file at =file=. If =data= is a list, then write it +as a table in traditional Org-mode table syntax. + +#+name: write +#+begin_src emacs-lisp :var data="" :var file="" :var ext='() + (flet ((echo (r) (if (stringp r) r (format "%S" r)))) + (with-temp-file file + (case (and (listp data) + (or ext (intern (file-name-extension file)))) + ('tsv (insert (orgtbl-to-tsv data '(:fmt echo)))) + ('csv (insert (orgtbl-to-csv data '(:fmt echo)))) + (t (org-babel-insert-result data))))) + nil +#+end_src + +** Remote files + +*** json + +Read local or remote file in [[http://www.json.org/][json]] format into emacs-lisp objects. + +#+name: json +#+begin_src emacs-lisp :var file='() :var url='() + (require 'json) + (cond + (file + (with-temp-filebuffer file + (goto-char (point-min)) + (json-read))) + (url + (require 'w3m) + (with-temp-buffer + (w3m-retrieve url) + (goto-char (point-min)) + (json-read)))) +#+end_src + +*** Google docs + +The following code blocks make use of the [[http://code.google.com/p/googlecl/][googlecl]] Google command line +tool. This tool provides functionality for accessing Google services +from the command line, and the following code blocks use /googlecl/ +for reading from and writing to Google docs with Org-mode code blocks. + +**** Read a document from Google docs + +The =google= command seems to be throwing "Moved Temporarily" errors +when trying to download textual documents, but this is working fine +for spreadsheets. + +#+name: gdoc-read +#+begin_src emacs-lisp :var title="example" :var format="csv" + (let* ((file (concat title "." format)) + (cmd (format "google docs get --format %S --title %S" format title))) + (message cmd) (message (shell-command-to-string cmd)) + (prog1 (if (string= format "csv") + (with-temp-buffer + (org-table-import (shell-quote-argument file) '(4)) + (org-table-to-lisp)) + (with-temp-buffer + (insert-file-contents (shell-quote-argument file)) + (buffer-string))) + (delete-file file))) +#+end_src + +For example, a line like the following can be used to read the +contents of a spreadsheet named =num-cells= into a table. +: #+call: gdoc-read(title="num-cells"") + +A line like the following can be used to read the contents of a +document as a string. + +: #+call: gdoc-read(title="loremi", :format "txt") + +**** Write a document to a Google docs + +Write =data= to a google document named =title=. If =data= is tabular +it will be saved to a spreadsheet, otherwise it will be saved as a +normal document. + +#+name: gdoc-write +#+begin_src emacs-lisp :var title="babel-upload" :var data=fibs(n=10) :results silent + (let* ((format (if (listp data) "csv" "txt")) + (tmp-file (make-temp-file "org-babel-google-doc" nil (concat "." format))) + (cmd (format "google docs upload --title %S %S" title tmp-file))) + (with-temp-file tmp-file + (insert + (if (listp data) + (orgtbl-to-csv + data '(:fmt (lambda (el) (if (stringp el) el (format "%S" el))))) + (if (stringp data) data (format "%S" data))))) + (message cmd) + (prog1 (shell-command-to-string cmd) (delete-file tmp-file))) +#+end_src + +example usage +: #+name: fibs +: #+begin_src emacs-lisp :var n=8 +: (flet ((fib (m) (if (< m 2) 1 (+ (fib (- m 1)) (fib (- m 2)))))) +: (mapcar (lambda (el) (list el (fib el))) (number-sequence 0 (- n 1)))) +: #+end_src +: +: #+call: gdoc-write(title="fibs", data=fibs(n=10)) + +* Plotting code + +** R + +Plot column 2 (y axis) against column 1 (x axis). Columns 3 and +beyond, if present, are ignored. + +#+name: R-plot +#+begin_src R :var data=R-plot-example-data +plot(data) +#+end_src + +#+tblname: R-plot-example-data +| 1 | 2 | +| 2 | 4 | +| 3 | 9 | +| 4 | 16 | +| 5 | 25 | + +#+call: R-plot(data=R-plot-example-data) + +#+resname: R-plot(data=R-plot-example-data) +: nil + +** Gnuplot + +* Org reference + +** Headline references + +#+name: headline +#+begin_src emacs-lisp :var headline=top :var file='() + (save-excursion + (when file (get-file-buffer file)) + (org-open-link-from-string (org-make-link-string headline)) + (save-restriction + (org-narrow-to-subtree) + (buffer-string))) +#+end_src + +#+call: headline(headline="headline references") + +* Tables + +** LaTeX Table Export + +*** booktabs + +This source block can be used to wrap a table in the latex =booktabs= +environment. The source block adds a =toprule= and =bottomrule= (so +don't use =hline= at the top or bottom of the table). The =hline= +after the header is replaced with a =midrule=. + +Note that this function bypasses the Org-mode LaTeX exporter and calls +=orgtbl-to-generic= to create the output table. This means that the +entries in the table are not translated from Org-mode to LaTeX. + +It takes the following arguments -- all but the first two are +optional. + +| arg | description | +|-------+--------------------------------------------| +| table | a reference to the table | +| align | alignment string | +| env | optional environment, default to "tabular" | +| width | optional width specification string | + +#+name: booktabs +#+begin_src emacs-lisp :var table='((:head) hline (:body)) :var align='() :var env="tabular" :var width='() :noweb yes :results latex + (flet ((to-tab (tab) + (orgtbl-to-generic + (mapcar (lambda (lis) + (if (listp lis) + (mapcar (lambda (el) + (if (stringp el) + el + (format "%S" el))) lis) + lis)) tab) + (list :lend " \\\\" :sep " & " :hline "\\hline")))) + (org-fill-template + " + \\begin{%env}%width%align + \\toprule + %table + \\bottomrule + \\end{%env}\n" + (list + (cons "env" (or env "table")) + (cons "width" (if width (format "{%s}" width) "")) + (cons "align" (if align (format "{%s}" align) "")) + (cons "table" + ;; only use \midrule if it looks like there are column headers + (if (equal 'hline (second table)) + (concat (to-tab (list (first table))) + "\n\\midrule\n" + (to-tab (cddr table))) + (to-tab table)))))) +#+end_src + +*** longtable + +This block can be used to wrap a table in the latex =longtable= +environment, it takes the following arguments -- all but the first two +are optional. + +| arg | description | +|-----------+-------------------------------------------------------------| +| table | a reference to the table | +| align | optional alignment string | +| width | optional width specification string | +| hline | the string to use as hline separator, defaults to "\\hline" | +| head | optional "head" string | +| firsthead | optional "firsthead" string | +| foot | optional "foot" string | +| lastfoot | optional "lastfoot" string | + +#+name: longtable +#+begin_src emacs-lisp :var table='((:table)) :var align='() :var width='() :var hline="\\hline" :var firsthead='() :var head='() :var foot='() :var lastfoot='() :noweb yes :results latex + (org-fill-template + " + \\begin{longtable}%width%align + %firsthead + %head + %foot + %lastfoot + + %table + \\end{longtable}\n" + (list + (cons "width" (if width (format "{%s}" width) "")) + (cons "align" (if align (format "{%s}" align) "")) + (cons "firsthead" (if firsthead (concat firsthead "\n\\endfirsthead\n") "")) + (cons "head" (if head (concat head "\n\\endhead\n") "")) + (cons "foot" (if foot (concat foot "\n\\endfoot\n") "")) + (cons "lastfoot" (if lastfoot (concat lastfoot "\n\\endlastfoot\n") "")) + (cons "table" (orgtbl-to-generic + (mapcar (lambda (lis) + (if (listp lis) + (mapcar (lambda (el) + (if (stringp el) + el + (format "%S" el))) lis) + lis)) table) + (list :lend " \\\\" :sep " & " :hline hline))))) +#+end_src + +*** booktabs-notes + +This source block builds on [[booktabs]]. It accepts two additional +arguments, both of which are optional. + +#+tblname: arguments +| arg | description | +|--------+------------------------------------------------------| +| notes | an org-mode table with footnotes | +| lspace | if non-nil, insert =addlinespace= after =bottomrule= | + +An example footnote to the =arguments= table specifies the column +span. Note the use of LaTeX, rather than Org-mode, markup. + +#+tblname: arguments-notes +| \multicolumn{2}{l}{This is a footnote to the \emph{arguments} table.} | + +#+name: booktabs-notes +#+begin_src emacs-lisp :var table='((:head) hline (:body)) :var notes='() :var align='() :var env="tabular" :var width='() :var lspace='() :noweb yes :results latex + (flet ((to-tab (tab) + (orgtbl-to-generic + (mapcar (lambda (lis) + (if (listp lis) + (mapcar (lambda (el) + (if (stringp el) + el + (format "%S" el))) lis) + lis)) tab) + (list :lend " \\\\" :sep " & " :hline "\\hline")))) + (org-fill-template + " + \\begin{%env}%width%align + \\toprule + %table + \\bottomrule%spacer + %notes + \\end{%env}\n" + (list + (cons "env" (or env "table")) + (cons "width" (if width (format "{%s}" width) "")) + (cons "align" (if align (format "{%s}" align) "")) + (cons "spacer" (if lspace "\\addlinespace" "")) + (cons "table" + ;; only use \midrule if it looks like there are column headers + (if (equal 'hline (second table)) + (concat (to-tab (list (first table))) + "\n\\midrule\n" + (to-tab (cddr table))) + (to-tab table))) + (cons "notes" (if notes (to-tab notes) "")) + ))) +#+end_src + +** Elegant lisp for transposing a matrix + +#+tblname: transpose-example +| 1 | 2 | 3 | +| 4 | 5 | 6 | + +#+name: transpose +#+begin_src emacs-lisp :var table=transpose-example + (apply #'mapcar* #'list table) +#+end_src + +#+resname: +| 1 | 4 | +| 2 | 5 | +| 3 | 6 | + +** Convert every element of a table to a string + +#+tblname: hetero-table +| 1 | 2 | 3 | +| a | b | c | + +#+name: all-to-string +#+begin_src emacs-lisp :var tbl='() + (defun all-to-string (tbl) + (if (listp tbl) + (mapcar #'all-to-string tbl) + (if (stringp tbl) + tbl + (format "%s" tbl)))) + (all-to-string tbl) +#+end_src + +#+begin_src emacs-lisp :var tbl=hetero-table + (mapcar (lambda (row) (mapcar (lambda (cell) (stringp cell)) row)) tbl) +#+end_src + +#+name: +| nil | nil | nil | +| t | t | t | + +#+begin_src emacs-lisp :var tbl=all-to-string(hetero-table) + (mapcar (lambda (row) (mapcar (lambda (cell) (stringp cell)) row)) tbl) +#+end_src + +#+name: +| t | t | t | +| t | t | t | + +* Misc + +** File-specific Version Control logging + :PROPERTIES: + :AUTHOR: Luke Crook + :END: + +This function will attempt to retrieve the entire commit log for the +file associated with the current buffer and insert this log into the +export. The function uses the Emacs VC commands to interface to the +local version control system, but has only been tested to work with +Git. 'limit' is currently unsupported. + +#+name: vc-log +#+headers: :var limit=-1 +#+headers: :var buf=(buffer-name (current-buffer)) +#+begin_src emacs-lisp + ;; Most of this code is copied from vc.el vc-print-log + (require 'vc) + (when (vc-find-backend-function + (vc-backend (buffer-file-name (get-buffer buf))) 'print-log) + (let ((limit -1) + (vc-fileset nil) + (backend nil) + (files nil)) + (with-current-buffer (get-buffer buf) + (setq vc-fileset (vc-deduce-fileset t)) ; FIXME: Why t? --Stef + (setq backend (car vc-fileset)) + (setq files (cadr vc-fileset))) + (with-temp-buffer + (let ((status (vc-call-backend + backend 'print-log files (current-buffer)))) + (when (and (processp status) ; Make sure status is a process + (= 0 (process-exit-status status))) ; which has not terminated + (while (not (eq 'exit (process-status status))) + (sit-for 1 t))) + (buffer-string))))) +#+end_src + +** Trivial python code blocks + +#+name: python-identity +#+begin_src python :var a=1 +a +#+end_src + +#+name: python-add +#+begin_src python :var a=1 :var b=2 +a + b +#+end_src + +** Arithmetic + +#+name: lob-add +#+begin_src emacs-lisp :var a=0 :var b=0 + (+ a b) +#+end_src + +#+name: lob-minus +#+begin_src emacs-lisp :var a=0 :var b=0 + (- a b) +#+end_src + +#+name: lob-times +#+begin_src emacs-lisp :var a=0 :var b=0 + (* a b) +#+end_src + +#+name: lob-div +#+begin_src emacs-lisp :var a=0 :var b=0 + (/ a b) +#+end_src + +* GANTT Charts + +The =elispgantt= source block was sent to the mailing list by Eric +Fraga. It was modified slightly by Tom Dye. + +#+name: elispgantt +#+begin_src emacs-lisp :var table=gantttest + (let ((dates "") + (entries (nthcdr 2 table)) + (milestones "") + (nmilestones 0) + (ntasks 0) + (projecttime 0) + (tasks "") + (xlength 1)) + (message "Initial: %s\n" table) + (message "Entries: %s\n" entries) + (while entries + (let ((entry (first entries))) + (if (listp entry) + (let ((id (first entry)) + (type (nth 1 entry)) + (label (nth 2 entry)) + (task (nth 3 entry)) + (dependencies (nth 4 entry)) + (start (nth 5 entry)) + (duration (nth 6 entry)) + (end (nth 7 entry)) + (alignment (nth 8 entry))) + (if (> start projecttime) (setq projecttime start)) + (if (string= type "task") + (let ((end (+ start duration)) + (textposition (+ start (/ duration 2))) + (flush "")) + (if (string= alignment "left") + (progn + (setq textposition start) + (setq flush "[left]")) + (if (string= alignment "right") + (progn + (setq textposition end) + (setq flush "[right]")))) + (setq tasks + (format "%s \\gantttask{%s}{%s}{%d}{%d}{%d}{%s}\n" + tasks label task start end textposition flush)) + (setq ntasks (+ 1 ntasks)) + (if (> end projecttime) + (setq projecttime end))) + (if (string= type "milestone") + (progn + (setq milestones + (format + "%s \\ganttmilestone{$\\begin{array}{c}\\mbox{%s}\\\\ \\mbox{%s}\\end{array}$}{%d}\n" + milestones label task start)) + (setq nmilestones (+ 1 nmilestones))) + (if (string= type "date") + (setq dates (format "%s \\ganttdateline{%s}{%d}\n" + dates label start)) + (message "Ignoring entry with type %s\n" type))))) + (message "Ignoring non-list entry %s\n" entry)) ; end if list entry + (setq entries (cdr entries)))) ; end while entries left + (format "\\pgfdeclarelayer{background} + \\pgfdeclarelayer{foreground} + \\pgfsetlayers{background,foreground} + \\renewcommand{\\ganttprojecttime}{%d} + \\renewcommand{\\ganttntasks}{%d} + \\noindent + \\begin{tikzpicture}[y=-0.75cm,x=0.75\\textwidth] + \\begin{pgfonlayer}{background} + \\draw[very thin, red!10!white] (0,1+\\ganttntasks) grid [ystep=0.75cm,xstep=1/\\ganttprojecttime] (1,0); + \\draw[\\ganttdatelinecolour] (0,0) -- (1,0); + \\draw[\\ganttdatelinecolour] (0,1+\\ganttntasks) -- (1,1+\\ganttntasks); + \\end{pgfonlayer} + %s + %s + %s + \\end{tikzpicture}" projecttime ntasks tasks milestones dates)) +#+end_src + +* Available languages + :PROPERTIES: + :AUTHOR: Bastien + :END: + +** From Org's core + +| Language | Identifier | Language | Identifier | +|------------+------------+----------------+------------| +| Asymptote | asymptote | Awk | awk | +| Emacs Calc | calc | C | C | +| C++ | C++ | Clojure | clojure | +| CSS | css | ditaa | ditaa | +| Graphviz | dot | Emacs Lisp | emacs-lisp | +| gnuplot | gnuplot | Haskell | haskell | +| Javascript | js | LaTeX | latex | +| Ledger | ledger | Lisp | lisp | +| Lilypond | lilypond | MATLAB | matlab | +| Mscgen | mscgen | Objective Caml | ocaml | +| Octave | octave | Org-mode | org | +| | | Perl | perl | +| Plantuml | plantuml | Python | python | +| R | R | Ruby | ruby | +| Sass | sass | Scheme | scheme | +| GNU Screen | screen | shell | sh | +| SQL | sql | SQLite | sqlite | + +** From Org's contrib/babel/langs + +- ob-oz.el, by Torsten Anders and Eric Schulte +- ob-fomus.el, by Torsten Anders diff --git a/etc/refcards/orgcard.tex b/etc/refcards/orgcard.tex index b12ae7be59..9ab6b4aef1 100644 --- a/etc/refcards/orgcard.tex +++ b/etc/refcards/orgcard.tex @@ -1,7 +1,7 @@ % Reference Card for Org Mode -\def\orgversionnumber{8.2} -\def\versionyear{2014} % latest update -\input emacsver.tex +\def\orgversionnumber{9.0.9} +\def\versionyear{2017} % latest update +\def\year{2017} % latest copyright year %**start of header \newcount\columnsperpage @@ -80,9 +80,6 @@ \centerline{Released under the terms of the GNU General Public License} \centerline{version 3 or later.} -\centerline{For more Emacs documentation, and the \TeX{} source for this card, see} -\centerline{the Emacs distribution, or {\tt http://www.gnu.org/software/emacs}} - \endgroup} % make \bye not \outer so that the \def\bye in the \else clause below @@ -312,10 +309,11 @@ \section{Structure Editing} \key{turn item/line into headline}{C-c *} \key{promote/demote heading}{M-LEFT/RIGHT} \metax{promote/demote current subtree}{M-S-LEFT/RIGHT} -\metax{move subtree/list item up/down}{M-S-UP/DOWN} +\metax{move subtree/list item up/down}{M-UP/DOWN} +\metax{move the line at point up/down}{M-S-UP/DOWN} \metax{sort subtree/region/plain-list}{C-c \^{}} \metax{clone a subtree}{C-c C-x c} -\metax{copy visible text}{C-c C-x v} +\metax{copy visible parts of the region}{C-c C-x v} \metax{kill/copy subtree}{C-c C-x C-w/M-w} \metax{yank subtree}{C-c C-x C-y or C-y} \metax{narrow buffer to subtree / widen}{C-x n s/w} @@ -333,7 +331,6 @@ \section{Filtering and Sparse Trees} \key{construct a sparse tree by various criteria}{C-c /} \key{view TODO's in sparse tree}{C-c / t/T} \key{global TODO list in agenda mode}{C-c a t \noteone} -\key{time sorted view of current org file}{C-c a L} \section{Tables} @@ -375,7 +372,6 @@ \section{Tables} \metax{cut/copy/paste rectangular region}{C-c C-x C-w/M-w/C-y} %\key{copy rectangular region}{C-c C-x M-w} %\key{paste rectangular region}{C-c C-x C-y} -\key{fill paragraph across selected cells}{C-c C-q} {\bf Miscellaneous} @@ -574,7 +570,6 @@ \section{Agenda Views} \key{match tags, TODO kwds, properties}{C-c a m \noteone} \key{match only in TODO entries}{C-c a M \noteone} \key{find stuck projects}{C-c a \# \noteone} -\key{show timeline of current org file}{C-c a L \noteone} \key{configure custom commands}{C-c a C \noteone} %\key{configure stuck projects}{C-c a ! \noteone} \key{agenda for date at cursor}{C-c C-o} @@ -661,8 +656,11 @@ \section{Exporting and Publishing} \key{export/publish dispatcher}{C-c C-e} -\key{export visible part only}{C-c C-e v} -\key{insert template of export options}{C-c C-e t} +\key{toggle asynchronous export}{C-c C-e C-a} +\key{toggle body/visible only export}{C-c C-e C-b/v} +\key{toggle subtree export}{C-c C-e C-s} +\key{insert template of export options}{C-c C-e \#} + \key{toggle fixed width for entry or region}{C-c :} \key{toggle pretty display of scripts, entities}{C-c C-x {\tt\char`\\}} @@ -690,6 +688,5 @@ \section{Notes} \bye % Local variables: -% compile-command: "tex refcard" +% compile-command: "pdftex orgcard" % End: - diff --git a/etc/schema/od-manifest-schema-v1.2-os.rnc b/etc/schema/od-manifest-schema-v1.2-os.rnc new file mode 100644 index 0000000000..87f84d1ea8 --- /dev/null +++ b/etc/schema/od-manifest-schema-v1.2-os.rnc @@ -0,0 +1,88 @@ +# Open Document Format for Office Applications (OpenDocument) Version 1.2 +# OASIS Standard, 29 September 2011 +# Manifest Relax-NG Schema +# Source: http://docs.oasis-open.org/office/v1.2/os/ +# Copyright (c) OASIS Open 2002-2011, 2013. All Rights Reserved. +# +# All capitalized terms in the following text have the meanings assigned to them +# in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The +# full Policy may be found at the OASIS website. +# +# This document and translations of it may be copied and furnished to others, and +# derivative works that comment on or otherwise explain it or assist in its +# implementation may be prepared, copied, published, and distributed, in whole or +# in part, without restriction of any kind, provided that the above copyright +# notice and this section are included on all such copies and derivative works. +# However, this document itself may not be modified in any way, including by +# removing the copyright notice or references to OASIS, except as needed for the +# purpose of developing any document or deliverable produced by an OASIS +# Technical Committee (in which case the rules applicable to copyrights, as set +# forth in the OASIS IPR Policy, must be followed) or as required to translate it +# into languages other than English. +# +# The limited permissions granted above are perpetual and will not be revoked by +# OASIS or its successors or assigns. +# +# This document and the information contained herein is provided on an "AS IS" +# basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +# LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT +# INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR +# FITNESS FOR A PARTICULAR PURPOSE. + +namespace manifest = + "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" + +start = manifest +manifest = element manifest:manifest { manifest-attlist, file-entry+ } +manifest-attlist = attribute manifest:version { "1.2" } +file-entry = + element manifest:file-entry { file-entry-attlist, encryption-data? } +file-entry-attlist = + attribute manifest:full-path { \string } + & attribute manifest:size { nonNegativeInteger }? + & attribute manifest:media-type { \string } + & attribute manifest:preferred-view-mode { + "edit" | "presentation-slide-show" | "read-only" | namespacedToken + }? + & attribute manifest:version { \string }? +encryption-data = + element manifest:encryption-data { + encryption-data-attlist, + algorithm, + start-key-generation?, + key-derivation + } +encryption-data-attlist = + attribute manifest:checksum-type { "SHA1/1K" | anyURI } + & attribute manifest:checksum { base64Binary } +algorithm = + element manifest:algorithm { algorithm-attlist, anyElements } +algorithm-attlist = + attribute manifest:algorithm-name { "Blowfish CFB" | anyURI } + & attribute manifest:initialisation-vector { base64Binary } +anyAttListOrElements = + attribute * { text }*, + anyElements +anyElements = + element * { + mixed { anyAttListOrElements } + }* +key-derivation = + element manifest:key-derivation { key-derivation-attlist, empty } +key-derivation-attlist = + attribute manifest:key-derivation-name { "PBKDF2" | anyURI } + & attribute manifest:salt { base64Binary } + & attribute manifest:iteration-count { nonNegativeInteger } + & attribute manifest:key-size { nonNegativeInteger }? +start-key-generation = + element manifest:start-key-generation { + start-key-generation-attlist, empty + } +start-key-generation-attlist = + attribute manifest:start-key-generation-name { "SHA1" | anyURI } + & attribute manifest:key-size { nonNegativeInteger }? +base64Binary = xsd:base64Binary +namespacedToken = xsd:QName { pattern = "[^:]+:[^:]+" } +nonNegativeInteger = xsd:nonNegativeInteger +\string = xsd:string +anyURI = xsd:anyURI diff --git a/etc/schema/od-schema-v1.2-os.rnc b/etc/schema/od-schema-v1.2-os.rnc new file mode 100644 index 0000000000..8d679d62e4 --- /dev/null +++ b/etc/schema/od-schema-v1.2-os.rnc @@ -0,0 +1,6280 @@ +# Open Document Format for Office Applications (OpenDocument) Version 1.2 +# OASIS Standard, 29 September 2011 +# Relax-NG Schema +# Source: http://docs.oasis-open.org/office/v1.2/os/ +# Copyright (c) OASIS Open 2002-2011, 2013. All Rights Reserved. +# +# All capitalized terms in the following text have the meanings assigned to them +# in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The +# full Policy may be found at the OASIS website. +# +# This document and translations of it may be copied and furnished to others, and +# derivative works that comment on or otherwise explain it or assist in its +# implementation may be prepared, copied, published, and distributed, in whole or +# in part, without restriction of any kind, provided that the above copyright +# notice and this section are included on all such copies and derivative works. +# However, this document itself may not be modified in any way, including by +# removing the copyright notice or references to OASIS, except as needed for the +# purpose of developing any document or deliverable produced by an OASIS +# Technical Committee (in which case the rules applicable to copyrights, as set +# forth in the OASIS IPR Policy, must be followed) or as required to translate it +# into languages other than English. +# +# The limited permissions granted above are perpetual and will not be revoked by +# OASIS or its successors or assigns. +# +# This document and the information contained herein is provided on an "AS IS" +# basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +# LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT +# INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR +# FITNESS FOR A PARTICULAR PURPOSE. + +namespace anim = "urn:oasis:names:tc:opendocument:xmlns:animation:1.0" +namespace chart = "urn:oasis:names:tc:opendocument:xmlns:chart:1.0" +namespace config = "urn:oasis:names:tc:opendocument:xmlns:config:1.0" +namespace db = "urn:oasis:names:tc:opendocument:xmlns:database:1.0" +namespace dc = "http://purl.org/dc/elements/1.1/" +namespace dr3d = "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" +namespace draw = "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" +namespace fo = + "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" +namespace form = "urn:oasis:names:tc:opendocument:xmlns:form:1.0" +namespace grddl = "http://www.w3.org/2003/g/data-view#" +namespace math = "http://www.w3.org/1998/Math/MathML" +namespace meta = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0" +namespace number = "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" +namespace office = "urn:oasis:names:tc:opendocument:xmlns:office:1.0" +namespace presentation = + "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" +namespace script = "urn:oasis:names:tc:opendocument:xmlns:script:1.0" +namespace smil = + "urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" +namespace style = "urn:oasis:names:tc:opendocument:xmlns:style:1.0" +namespace svg = + "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" +namespace table = "urn:oasis:names:tc:opendocument:xmlns:table:1.0" +namespace text = "urn:oasis:names:tc:opendocument:xmlns:text:1.0" +namespace xforms = "http://www.w3.org/2002/xforms" +namespace xhtml = "http://www.w3.org/1999/xhtml" +namespace xlink = "http://www.w3.org/1999/xlink" + +office-process-content = attribute office:process-content { boolean }? +start = + office-document + | office-document-content + | office-document-styles + | office-document-meta + | office-document-settings +office-document = + element office:document { + office-document-attrs, + office-document-common-attrs, + office-meta, + office-settings, + office-scripts, + office-font-face-decls, + office-styles, + office-automatic-styles, + office-master-styles, + office-body + } +office-document-content = + element office:document-content { + office-document-common-attrs, + office-scripts, + office-font-face-decls, + office-automatic-styles, + office-body + } +office-document-styles = + element office:document-styles { + office-document-common-attrs, + office-font-face-decls, + office-styles, + office-automatic-styles, + office-master-styles + } +office-document-meta = + element office:document-meta { + office-document-common-attrs, office-meta + } +office-document-settings = + element office:document-settings { + office-document-common-attrs, office-settings + } +office-document-common-attrs = + attribute office:version { "1.2" } + & attribute grddl:transformation { + list { anyIRI* } + }? +office-document-attrs = attribute office:mimetype { \string } +office-meta = element office:meta { office-meta-content-strict }? +office-meta-content-strict = office-meta-data* +office-body = element office:body { office-body-content } +office-body-content = + element office:text { + office-text-attlist, + office-text-content-prelude, + office-text-content-main, + office-text-content-epilogue + } + | element office:drawing { + office-drawing-attlist, + office-drawing-content-prelude, + office-drawing-content-main, + office-drawing-content-epilogue + } + | element office:presentation { + office-presentation-attlist, + office-presentation-content-prelude, + office-presentation-content-main, + office-presentation-content-epilogue + } + | element office:spreadsheet { + office-spreadsheet-attlist, + office-spreadsheet-content-prelude, + office-spreadsheet-content-main, + office-spreadsheet-content-epilogue + } + | element office:chart { + office-chart-attlist, + office-chart-content-prelude, + office-chart-content-main, + office-chart-content-epilogue + } + | element office:image { + office-image-attlist, + office-image-content-prelude, + office-image-content-main, + office-image-content-epilogue + } + | office-database +office-text-content-prelude = + office-forms, text-tracked-changes, text-decls, table-decls +office-text-content-main = + text-content* + | (text-page-sequence, (shape)*) +text-content = + text-h + | text-p + | text-list + | text-numbered-paragraph + | table-table + | text-section + | text-soft-page-break + | text-table-of-content + | text-illustration-index + | text-table-index + | text-object-index + | text-user-index + | text-alphabetical-index + | text-bibliography + | shape + | change-marks +office-text-content-epilogue = table-functions +office-text-attlist = + attribute text:global { boolean }? + & attribute text:use-soft-page-breaks { boolean }? +office-drawing-attlist = empty +office-drawing-content-prelude = text-decls, table-decls +office-drawing-content-main = draw-page* +office-drawing-content-epilogue = table-functions +office-presentation-attlist = empty +office-presentation-content-prelude = + text-decls, table-decls, presentation-decls +office-presentation-content-main = draw-page* +office-presentation-content-epilogue = + presentation-settings, table-functions +office-spreadsheet-content-prelude = + table-tracked-changes?, text-decls, table-decls +table-decls = + table-calculation-settings?, + table-content-validations?, + table-label-ranges? +office-spreadsheet-content-main = table-table* +office-spreadsheet-content-epilogue = table-functions +table-functions = + table-named-expressions?, + table-database-ranges?, + table-data-pilot-tables?, + table-consolidation?, + table-dde-links? +office-chart-attlist = empty +office-chart-content-prelude = text-decls, table-decls +office-chart-content-main = chart-chart +office-chart-content-epilogue = table-functions +office-image-attlist = empty +office-image-content-prelude = empty +office-image-content-main = draw-frame +office-image-content-epilogue = empty +office-settings = element office:settings { config-config-item-set+ }? +config-config-item-set = + element config:config-item-set { + config-config-item-set-attlist, config-items + } +config-items = + (config-config-item + | config-config-item-set + | config-config-item-map-named + | config-config-item-map-indexed)+ +config-config-item-set-attlist = attribute config:name { \string } +config-config-item = + element config:config-item { config-config-item-attlist, text } +config-config-item-attlist = + attribute config:name { \string } + & attribute config:type { + "boolean" + | "short" + | "int" + | "long" + | "double" + | "string" + | "datetime" + | "base64Binary" + } +config-config-item-map-indexed = + element config:config-item-map-indexed { + config-config-item-map-indexed-attlist, + config-config-item-map-entry+ + } +config-config-item-map-indexed-attlist = + attribute config:name { \string } +config-config-item-map-entry = + element config:config-item-map-entry { + config-config-item-map-entry-attlist, config-items + } +config-config-item-map-entry-attlist = + attribute config:name { \string }? +config-config-item-map-named = + element config:config-item-map-named { + config-config-item-map-named-attlist, config-config-item-map-entry+ + } +config-config-item-map-named-attlist = attribute config:name { \string } +office-scripts = + element office:scripts { office-script*, office-event-listeners? }? +office-script = + element office:script { + office-script-attlist, + mixed { anyElements } + } +office-script-attlist = attribute script:language { \string } +office-font-face-decls = + element office:font-face-decls { style-font-face* }? +office-styles = + element office:styles { + styles + & style-default-style* + & style-default-page-layout? + & text-outline-style? + & text-notes-configuration* + & text-bibliography-configuration? + & text-linenumbering-configuration? + & draw-gradient* + & svg-linearGradient* + & svg-radialGradient* + & draw-hatch* + & draw-fill-image* + & draw-marker* + & draw-stroke-dash* + & draw-opacity* + & style-presentation-page-layout* + & table-table-template* + }? +office-automatic-styles = + element office:automatic-styles { styles & style-page-layout* }? +office-master-styles = + element office:master-styles { + style-master-page* & style-handout-master? & draw-layer-set? + }? +styles = + style-style* + & text-list-style* + & number-number-style* + & number-currency-style* + & number-percentage-style* + & number-date-style* + & number-time-style* + & number-boolean-style* + & number-text-style* +office-meta-data = + element meta:generator { \string } + | element dc:title { \string } + | element dc:description { \string } + | element dc:subject { \string } + | element meta:keyword { \string } + | element meta:initial-creator { \string } + | dc-creator + | element meta:printed-by { \string } + | element meta:creation-date { dateTime } + | dc-date + | element meta:print-date { dateTime } + | element meta:template { + attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:actuate { "onRequest" }?, + attribute xlink:title { \string }?, + attribute meta:date { dateTime }? + } + | element meta:auto-reload { + (attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:show { "replace" }?, + attribute xlink:actuate { "onLoad" }?)?, + attribute meta:delay { duration }? + } + | element meta:hyperlink-behaviour { + attribute office:target-frame-name { targetFrameName }?, + attribute xlink:show { "new" | "replace" }? + } + | element dc:language { language } + | element meta:editing-cycles { nonNegativeInteger } + | element meta:editing-duration { duration } + | element meta:document-statistic { + attribute meta:page-count { nonNegativeInteger }?, + attribute meta:table-count { nonNegativeInteger }?, + attribute meta:draw-count { nonNegativeInteger }?, + attribute meta:image-count { nonNegativeInteger }?, + attribute meta:ole-object-count { nonNegativeInteger }?, + attribute meta:object-count { nonNegativeInteger }?, + attribute meta:paragraph-count { nonNegativeInteger }?, + attribute meta:word-count { nonNegativeInteger }?, + attribute meta:character-count { nonNegativeInteger }?, + attribute meta:frame-count { nonNegativeInteger }?, + attribute meta:sentence-count { nonNegativeInteger }?, + attribute meta:syllable-count { nonNegativeInteger }?, + attribute meta:non-whitespace-character-count { + nonNegativeInteger + }?, + attribute meta:row-count { nonNegativeInteger }?, + attribute meta:cell-count { nonNegativeInteger }? + } + | element meta:user-defined { + attribute meta:name { \string }, + ((attribute meta:value-type { "float" }, + double) + | (attribute meta:value-type { "date" }, + dateOrDateTime) + | (attribute meta:value-type { "time" }, + duration) + | (attribute meta:value-type { "boolean" }, + boolean) + | (attribute meta:value-type { "string" }, + \string) + | text) + } +dc-creator = element dc:creator { \string } +dc-date = element dc:date { dateTime } +text-h = + element text:h { + heading-attrs, + paragraph-attrs, + text-number?, + paragraph-content-or-hyperlink* + } +heading-attrs = + attribute text:outline-level { positiveInteger } + & attribute text:restart-numbering { boolean }? + & attribute text:start-value { nonNegativeInteger }? + & attribute text:is-list-header { boolean }? +text-number = element text:number { \string } +text-p = + element text:p { paragraph-attrs, paragraph-content-or-hyperlink* } +paragraph-attrs = + attribute text:style-name { styleNameRef }? + & attribute text:class-names { styleNameRefs }? + & attribute text:cond-style-name { styleNameRef }? + & (xml-id, + attribute text:id { NCName }?)? + & common-in-content-meta-attlist? +text-page-sequence = element text:page-sequence { text-page+ } +text-page = element text:page { text-page-attlist, empty } +text-page-attlist = attribute text:master-page-name { styleNameRef } +text-list = + element text:list { + text-list-attr, text-list-header?, text-list-item* + } +text-list-attr = + attribute text:style-name { styleNameRef }? + & attribute text:continue-numbering { boolean }? + & attribute text:continue-list { IDREF }? + & xml-id? +text-list-item = + element text:list-item { text-list-item-attr, text-list-item-content } +text-list-item-content = + text-number?, (text-p | text-h | text-list | text-soft-page-break)* +text-list-item-attr = + attribute text:start-value { nonNegativeInteger }? + & attribute text:style-override { styleNameRef }? + & xml-id? +text-list-header = + element text:list-header { + text-list-header-attr, text-list-item-content + } +text-list-header-attr = xml-id? +text-numbered-paragraph = + element text:numbered-paragraph { + text-numbered-paragraph-attr, text-number?, (text-p | text-h) + } +text-numbered-paragraph-attr = + attribute text:list-id { NCName } + & attribute text:level { positiveInteger }? + & (attribute text:style-name { styleNameRef }, + attribute text:continue-numbering { boolean }, + attribute text:start-value { nonNegativeInteger })? + & xml-id? +text-section = + element text:section { + text-section-attlist, + (text-section-source | text-section-source-dde | empty), + text-content* + } +text-section-attlist = + common-section-attlist + & (attribute text:display { "true" | "none" } + | (attribute text:display { "condition" }, + attribute text:condition { \string }) + | empty) +common-section-attlist = + attribute text:style-name { styleNameRef }? + & attribute text:name { \string } + & attribute text:protected { boolean }? + & attribute text:protection-key { \string }? + & attribute text:protection-key-digest-algorithm { anyIRI }? + & xml-id? +text-section-source = + element text:section-source { text-section-source-attr } +text-section-source-attr = + (attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:show { "embed" }?)? + & attribute text:section-name { \string }? + & attribute text:filter-name { \string }? +text-section-source-dde = office-dde-source +text-tracked-changes = + element text:tracked-changes { + text-tracked-changes-attr, text-changed-region* + }? +text-tracked-changes-attr = attribute text:track-changes { boolean }? +text-changed-region = + element text:changed-region { + text-changed-region-attr, text-changed-region-content + } +text-changed-region-attr = + xml-id, + attribute text:id { NCName }? +text-changed-region-content = + element text:insertion { office-change-info } + | element text:deletion { office-change-info, text-content* } + | element text:format-change { office-change-info } +change-marks = + element text:change { change-mark-attr } + | element text:change-start { change-mark-attr } + | element text:change-end { change-mark-attr } +change-mark-attr = attribute text:change-id { IDREF } +text-soft-page-break = element text:soft-page-break { empty } +text-decls = + element text:variable-decls { text-variable-decl* }?, + element text:sequence-decls { text-sequence-decl* }?, + element text:user-field-decls { text-user-field-decl* }?, + element text:dde-connection-decls { text-dde-connection-decl* }?, + text-alphabetical-index-auto-mark-file? +paragraph-content-or-hyperlink = paragraph-content | text-a +paragraph-content = + text + | element text:s { + attribute text:c { nonNegativeInteger }? + } + | element text:tab { text-tab-attr } + | element text:line-break { empty } + | text-soft-page-break + | element text:span { + attribute text:style-name { styleNameRef }?, + attribute text:class-names { styleNameRefs }?, + paragraph-content-or-hyperlink* + } + | element text:meta { + text-meta-attlist, paragraph-content-or-hyperlink* + } + | (text-bookmark | text-bookmark-start | text-bookmark-end) + | element text:reference-mark { + attribute text:name { \string } + } + | (element text:reference-mark-start { + attribute text:name { \string } + } + | element text:reference-mark-end { + attribute text:name { \string } + }) + | element text:note { + text-note-class, + attribute text:id { \string }?, + element text:note-citation { + attribute text:label { \string }?, + text + }, + element text:note-body { text-content* } + } + | element text:ruby { + attribute text:style-name { styleNameRef }?, + element text:ruby-base { paragraph-content-or-hyperlink* }, + element text:ruby-text { + attribute text:style-name { styleNameRef }?, + text + } + } + | (office-annotation | office-annotation-end) + | change-marks + | shape + | element text:date { text-date-attlist, text } + | element text:time { text-time-attlist, text } + | element text:page-number { text-page-number-attlist, text } + | element text:page-continuation { + text-page-continuation-attlist, text + } + | element text:sender-firstname { common-field-fixed-attlist, text } + | element text:sender-lastname { common-field-fixed-attlist, text } + | element text:sender-initials { common-field-fixed-attlist, text } + | element text:sender-title { common-field-fixed-attlist, text } + | element text:sender-position { common-field-fixed-attlist, text } + | element text:sender-email { common-field-fixed-attlist, text } + | element text:sender-phone-private { + common-field-fixed-attlist, text + } + | element text:sender-fax { common-field-fixed-attlist, text } + | element text:sender-company { common-field-fixed-attlist, text } + | element text:sender-phone-work { common-field-fixed-attlist, text } + | element text:sender-street { common-field-fixed-attlist, text } + | element text:sender-city { common-field-fixed-attlist, text } + | element text:sender-postal-code { common-field-fixed-attlist, text } + | element text:sender-country { common-field-fixed-attlist, text } + | element text:sender-state-or-province { + common-field-fixed-attlist, text + } + | element text:author-name { common-field-fixed-attlist, text } + | element text:author-initials { common-field-fixed-attlist, text } + | element text:chapter { text-chapter-attlist, text } + | element text:file-name { text-file-name-attlist, text } + | element text:template-name { text-template-name-attlist, text } + | element text:sheet-name { text } + | element text:variable-set { + (common-field-name-attlist + & common-field-formula-attlist + & common-value-and-type-attlist + & common-field-display-value-none-attlist + & common-field-data-style-name-attlist), + text + } + | element text:variable-get { + (common-field-name-attlist + & common-field-display-value-formula-attlist + & common-field-data-style-name-attlist), + text + } + | element text:variable-input { + (common-field-name-attlist + & common-field-description-attlist + & common-value-type-attlist + & common-field-display-value-none-attlist + & common-field-data-style-name-attlist), + text + } + | element text:user-field-get { + (common-field-name-attlist + & common-field-display-value-formula-none-attlist + & common-field-data-style-name-attlist), + text + } + | element text:user-field-input { + (common-field-name-attlist + & common-field-description-attlist + & common-field-data-style-name-attlist), + text + } + | element text:sequence { + (common-field-name-attlist + & common-field-formula-attlist + & common-field-num-format-attlist + & text-sequence-ref-name), + text + } + | element text:expression { + (common-field-formula-attlist + & common-value-and-type-attlist? + & common-field-display-value-formula-attlist + & common-field-data-style-name-attlist), + text + } + | element text:text-input { common-field-description-attlist, text } + | element text:initial-creator { common-field-fixed-attlist, text } + | element text:creation-date { + (common-field-fixed-attlist + & common-field-data-style-name-attlist + & attribute text:date-value { dateOrDateTime }?), + text + } + | element text:creation-time { + (common-field-fixed-attlist + & common-field-data-style-name-attlist + & attribute text:time-value { timeOrDateTime }?), + text + } + | element text:description { common-field-fixed-attlist, text } + | element text:user-defined { + (common-field-fixed-attlist + & attribute text:name { \string } + & common-field-data-style-name-attlist + & attribute office:value { double }? + & attribute office:date-value { dateOrDateTime }? + & attribute office:time-value { duration }? + & attribute office:boolean-value { boolean }? + & attribute office:string-value { \string }?), + text + } + | element text:print-time { + (common-field-fixed-attlist + & common-field-data-style-name-attlist + & attribute text:time-value { time }?), + text + } + | element text:print-date { + (common-field-fixed-attlist + & common-field-data-style-name-attlist + & attribute text:date-value { date }?), + text + } + | element text:printed-by { common-field-fixed-attlist, text } + | element text:title { common-field-fixed-attlist, text } + | element text:subject { common-field-fixed-attlist, text } + | element text:keywords { common-field-fixed-attlist, text } + | element text:editing-cycles { common-field-fixed-attlist, text } + | element text:editing-duration { + (common-field-fixed-attlist + & common-field-data-style-name-attlist + & attribute text:duration { duration }?), + text + } + | element text:modification-time { + (common-field-fixed-attlist + & common-field-data-style-name-attlist + & attribute text:time-value { time }?), + text + } + | element text:modification-date { + (common-field-fixed-attlist + & common-field-data-style-name-attlist + & attribute text:date-value { date }?), + text + } + | element text:creator { common-field-fixed-attlist, text } + | element text:page-count + | text:paragraph-count + | text:word-count + | text:character-count + | text:table-count + | text:image-count + | text:object-count { + common-field-num-format-attlist, text + } + | element text:database-display { + text-database-display-attlist, text + } + | element text:database-next { text-database-next-attlist } + | element text:database-row-select { + text-database-row-select-attlist + } + | element text:database-row-number { + (common-field-database-table + & common-field-num-format-attlist + & attribute text:value { nonNegativeInteger }?), + text + } + | element text:database-name { common-field-database-table, text } + | element text:page-variable-set { + text-set-page-variable-attlist, text + } + | element text:page-variable-get { + text-get-page-variable-attlist, text + } + | element text:placeholder { text-placeholder-attlist, text } + | element text:conditional-text { + text-conditional-text-attlist, text + } + | element text:hidden-text { text-hidden-text-attlist, text } + | element text:reference-ref | text:bookmark-ref { + text-common-ref-content & text-bookmark-ref-content + } + | element text:note-ref { + text-common-ref-content & text-note-ref-content + } + | element text:sequence-ref { + text-common-ref-content & text-sequence-ref-content + } + | element text:script { + ((attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }) + | text) + & attribute script:language { \string }? + } + | element text:execute-macro { + attribute text:name { \string }?, + office-event-listeners?, + text + } + | element text:hidden-paragraph { + text-hidden-paragraph-attlist, text + } + | element text:dde-connection { + attribute text:connection-name { \string }, + text + } + | element text:measure { + attribute text:kind { "value" | "unit" | "gap" }, + text + } + | element text:table-formula { + (common-field-formula-attlist + & common-field-display-value-formula-attlist + & common-field-data-style-name-attlist), + text + } + | element text:meta-field { + text-meta-field-attlist, paragraph-content-or-hyperlink* + } + | element text:toc-mark-start { text-toc-mark-start-attrs } + | element text:toc-mark-end { text-id } + | element text:toc-mark { + attribute text:string-value { \string }, + text-outline-level + } + | element text:user-index-mark-start { + text-id, text-outline-level, text-index-name + } + | element text:user-index-mark-end { text-id } + | element text:user-index-mark { + attribute text:string-value { \string }, + text-outline-level, + text-index-name + } + | element text:alphabetical-index-mark-start { + text-id, text-alphabetical-index-mark-attrs + } + | element text:alphabetical-index-mark-end { text-id } + | element text:alphabetical-index-mark { + attribute text:string-value { \string }, + text-alphabetical-index-mark-attrs + } + | element text:bibliography-mark { + attribute text:bibliography-type { text-bibliography-types }, + attribute text:identifier + | text:address + | text:annote + | text:author + | text:booktitle + | text:chapter + | text:edition + | text:editor + | text:howpublished + | text:institution + | text:journal + | text:month + | text:note + | text:number + | text:organizations + | text:pages + | text:publisher + | text:school + | text:series + | text:title + | text:report-type + | text:volume + | text:year + | text:url + | text:custom1 + | text:custom2 + | text:custom3 + | text:custom4 + | text:custom5 + | text:isbn + | text:issn { \string }*, + text + } + | element presentation:header { empty } + | element presentation:footer { empty } + | element presentation:date-time { empty } +text-tab-attr = attribute text:tab-ref { nonNegativeInteger }? +text-a = + element text:a { + text-a-attlist, office-event-listeners?, paragraph-content* + } +text-a-attlist = + attribute office:name { \string }? + & attribute office:title { \string }? + & attribute xlink:type { "simple" } + & attribute xlink:href { anyIRI } + & attribute xlink:actuate { "onRequest" }? + & attribute office:target-frame-name { targetFrameName }? + & attribute xlink:show { "new" | "replace" }? + & attribute text:style-name { styleNameRef }? + & attribute text:visited-style-name { styleNameRef }? +text-meta-attlist = common-in-content-meta-attlist? & xml-id? +text-bookmark = element text:bookmark { text-bookmark-attlist, empty } +text-bookmark-start = + element text:bookmark-start { text-bookmark-start-attlist, empty } +text-bookmark-end = + element text:bookmark-end { text-bookmark-end-attlist, empty } +text-bookmark-attlist = + attribute text:name { \string } + & xml-id? +text-bookmark-start-attlist = + attribute text:name { \string } + & xml-id? + & common-in-content-meta-attlist? +text-bookmark-end-attlist = attribute text:name { \string } +text-note-class = attribute text:note-class { "footnote" | "endnote" } +text-date-attlist = + (common-field-fixed-attlist & common-field-data-style-name-attlist) + & attribute text:date-value { dateOrDateTime }? + & attribute text:date-adjust { duration }? +text-time-attlist = + (common-field-fixed-attlist & common-field-data-style-name-attlist) + & attribute text:time-value { timeOrDateTime }? + & attribute text:time-adjust { duration }? +text-page-number-attlist = + (common-field-num-format-attlist & common-field-fixed-attlist) + & attribute text:page-adjust { integer }? + & attribute text:select-page { "previous" | "current" | "next" }? +text-page-continuation-attlist = + attribute text:select-page { "previous" | "next" } + & attribute text:string-value { \string }? +text-chapter-attlist = + attribute text:display { + "name" + | "number" + | "number-and-name" + | "plain-number-and-name" + | "plain-number" + } + & attribute text:outline-level { nonNegativeInteger } +text-file-name-attlist = + attribute text:display { + "full" | "path" | "name" | "name-and-extension" + }? + & common-field-fixed-attlist +text-template-name-attlist = + attribute text:display { + "full" | "path" | "name" | "name-and-extension" | "area" | "title" + }? +text-variable-decl = + element text:variable-decl { + common-field-name-attlist, common-value-type-attlist + } +text-user-field-decl = + element text:user-field-decl { + common-field-name-attlist, + common-field-formula-attlist?, + common-value-and-type-attlist + } +text-sequence-decl = + element text:sequence-decl { text-sequence-decl-attlist } +text-sequence-decl-attlist = + common-field-name-attlist + & attribute text:display-outline-level { nonNegativeInteger } + & attribute text:separation-character { character }? +text-sequence-ref-name = attribute text:ref-name { \string }? +common-field-database-table = + common-field-database-table-attlist, common-field-database-name +common-field-database-name = + attribute text:database-name { \string }? + | form-connection-resource +common-field-database-table-attlist = + attribute text:table-name { \string } + & attribute text:table-type { "table" | "query" | "command" }? +text-database-display-attlist = + common-field-database-table + & common-field-data-style-name-attlist + & attribute text:column-name { \string } +text-database-next-attlist = + common-field-database-table + & attribute text:condition { \string }? +text-database-row-select-attlist = + common-field-database-table + & attribute text:condition { \string }? + & attribute text:row-number { nonNegativeInteger }? +text-set-page-variable-attlist = + attribute text:active { boolean }? + & attribute text:page-adjust { integer }? +text-get-page-variable-attlist = common-field-num-format-attlist +text-placeholder-attlist = + attribute text:placeholder-type { + "text" | "table" | "text-box" | "image" | "object" + } + & common-field-description-attlist +text-conditional-text-attlist = + attribute text:condition { \string } + & attribute text:string-value-if-true { \string } + & attribute text:string-value-if-false { \string } + & attribute text:current-value { boolean }? +text-hidden-text-attlist = + attribute text:condition { \string } + & attribute text:string-value { \string } + & attribute text:is-hidden { boolean }? +text-common-ref-content = + text + & attribute text:ref-name { \string }? +text-bookmark-ref-content = + attribute text:reference-format { + common-ref-format-values + | "number-no-superior" + | "number-all-superior" + | "number" + }? +text-note-ref-content = + attribute text:reference-format { common-ref-format-values }? + & text-note-class +text-sequence-ref-content = + attribute text:reference-format { + common-ref-format-values + | "category-and-value" + | "caption" + | "value" + }? +common-ref-format-values = "page" | "chapter" | "direction" | "text" +text-hidden-paragraph-attlist = + attribute text:condition { \string } + & attribute text:is-hidden { boolean }? +text-meta-field-attlist = xml-id & common-field-data-style-name-attlist +common-value-type-attlist = attribute office:value-type { valueType } +common-value-and-type-attlist = + (attribute office:value-type { "float" }, + attribute office:value { double }) + | (attribute office:value-type { "percentage" }, + attribute office:value { double }) + | (attribute office:value-type { "currency" }, + attribute office:value { double }, + attribute office:currency { \string }?) + | (attribute office:value-type { "date" }, + attribute office:date-value { dateOrDateTime }) + | (attribute office:value-type { "time" }, + attribute office:time-value { duration }) + | (attribute office:value-type { "boolean" }, + attribute office:boolean-value { boolean }) + | (attribute office:value-type { "string" }, + attribute office:string-value { \string }?) +common-field-fixed-attlist = attribute text:fixed { boolean }? +common-field-name-attlist = attribute text:name { variableName } +common-field-description-attlist = + attribute text:description { \string }? +common-field-display-value-none-attlist = + attribute text:display { "value" | "none" }? +common-field-display-value-formula-none-attlist = + attribute text:display { "value" | "formula" | "none" }? +common-field-display-value-formula-attlist = + attribute text:display { "value" | "formula" }? +common-field-formula-attlist = attribute text:formula { \string }? +common-field-data-style-name-attlist = + attribute style:data-style-name { styleNameRef }? +common-field-num-format-attlist = common-num-format-attlist? +text-toc-mark-start-attrs = text-id, text-outline-level +text-outline-level = attribute text:outline-level { positiveInteger }? +text-id = attribute text:id { \string } +text-index-name = attribute text:index-name { \string } +text-alphabetical-index-mark-attrs = + attribute text:key1 { \string }? + & attribute text:key2 { \string }? + & attribute text:string-value-phonetic { \string }? + & attribute text:key1-phonetic { \string }? + & attribute text:key2-phonetic { \string }? + & attribute text:main-entry { boolean }? +text-bibliography-types = + "article" + | "book" + | "booklet" + | "conference" + | "custom1" + | "custom2" + | "custom3" + | "custom4" + | "custom5" + | "email" + | "inbook" + | "incollection" + | "inproceedings" + | "journal" + | "manual" + | "mastersthesis" + | "misc" + | "phdthesis" + | "proceedings" + | "techreport" + | "unpublished" + | "www" +text-index-body = element text:index-body { index-content-main* } +index-content-main = text-content | text-index-title +text-index-title = + element text:index-title { + common-section-attlist, index-content-main* + } +text-table-of-content = + element text:table-of-content { + common-section-attlist, + text-table-of-content-source, + text-index-body + } +text-table-of-content-source = + element text:table-of-content-source { + text-table-of-content-source-attlist, + text-index-title-template?, + text-table-of-content-entry-template*, + text-index-source-styles* + } +text-table-of-content-source-attlist = + attribute text:outline-level { positiveInteger }? + & attribute text:use-outline-level { boolean }? + & attribute text:use-index-marks { boolean }? + & attribute text:use-index-source-styles { boolean }? + & attribute text:index-scope { "document" | "chapter" }? + & attribute text:relative-tab-stop-position { boolean }? +text-table-of-content-entry-template = + element text:table-of-content-entry-template { + text-table-of-content-entry-template-attlist, + text-table-of-content-children* + } +text-table-of-content-children = + text-index-entry-chapter + | text-index-entry-page-number + | text-index-entry-text + | text-index-entry-span + | text-index-entry-tab-stop + | text-index-entry-link-start + | text-index-entry-link-end +text-table-of-content-entry-template-attlist = + attribute text:outline-level { positiveInteger } + & attribute text:style-name { styleNameRef } +text-illustration-index = + element text:illustration-index { + common-section-attlist, + text-illustration-index-source, + text-index-body + } +text-illustration-index-source = + element text:illustration-index-source { + text-illustration-index-source-attrs, + text-index-title-template?, + text-illustration-index-entry-template? + } +text-illustration-index-source-attrs = + text-index-scope-attr + & text-relative-tab-stop-position-attr + & attribute text:use-caption { boolean }? + & attribute text:caption-sequence-name { \string }? + & attribute text:caption-sequence-format { + "text" | "category-and-value" | "caption" + }? +text-index-scope-attr = + attribute text:index-scope { "document" | "chapter" }? +text-relative-tab-stop-position-attr = + attribute text:relative-tab-stop-position { boolean }? +text-illustration-index-entry-template = + element text:illustration-index-entry-template { + text-illustration-index-entry-content + } +text-illustration-index-entry-content = + text-illustration-index-entry-template-attrs, + (text-index-entry-chapter + | text-index-entry-page-number + | text-index-entry-text + | text-index-entry-span + | text-index-entry-tab-stop)* +text-illustration-index-entry-template-attrs = + attribute text:style-name { styleNameRef } +text-table-index = + element text:table-index { + common-section-attlist, text-table-index-source, text-index-body + } +text-table-index-source = + element text:table-index-source { + text-illustration-index-source-attrs, + text-index-title-template?, + text-table-index-entry-template? + } +text-table-index-entry-template = + element text:table-index-entry-template { + text-illustration-index-entry-content + } +text-object-index = + element text:object-index { + common-section-attlist, text-object-index-source, text-index-body + } +text-object-index-source = + element text:object-index-source { + text-object-index-source-attrs, + text-index-title-template?, + text-object-index-entry-template? + } +text-object-index-source-attrs = + text-index-scope-attr + & text-relative-tab-stop-position-attr + & attribute text:use-spreadsheet-objects { boolean }? + & attribute text:use-math-objects { boolean }? + & attribute text:use-draw-objects { boolean }? + & attribute text:use-chart-objects { boolean }? + & attribute text:use-other-objects { boolean }? +text-object-index-entry-template = + element text:object-index-entry-template { + text-illustration-index-entry-content + } +text-user-index = + element text:user-index { + common-section-attlist, text-user-index-source, text-index-body + } +text-user-index-source = + element text:user-index-source { + text-user-index-source-attr, + text-index-title-template?, + text-user-index-entry-template*, + text-index-source-styles* + } +text-user-index-source-attr = + text-index-scope-attr + & text-relative-tab-stop-position-attr + & attribute text:use-index-marks { boolean }? + & attribute text:use-index-source-styles { boolean }? + & attribute text:use-graphics { boolean }? + & attribute text:use-tables { boolean }? + & attribute text:use-floating-frames { boolean }? + & attribute text:use-objects { boolean }? + & attribute text:copy-outline-levels { boolean }? + & attribute text:index-name { \string } +text-user-index-entry-template = + element text:user-index-entry-template { + text-user-index-entry-template-attrs, + (text-index-entry-chapter + | text-index-entry-page-number + | text-index-entry-text + | text-index-entry-span + | text-index-entry-tab-stop)* + } +text-user-index-entry-template-attrs = + attribute text:outline-level { positiveInteger } + & attribute text:style-name { styleNameRef } +text-alphabetical-index = + element text:alphabetical-index { + common-section-attlist, + text-alphabetical-index-source, + text-index-body + } +text-alphabetical-index-source = + element text:alphabetical-index-source { + text-alphabetical-index-source-attrs, + text-index-title-template?, + text-alphabetical-index-entry-template* + } +text-alphabetical-index-source-attrs = + text-index-scope-attr + & text-relative-tab-stop-position-attr + & attribute text:ignore-case { boolean }? + & attribute text:main-entry-style-name { styleNameRef }? + & attribute text:alphabetical-separators { boolean }? + & attribute text:combine-entries { boolean }? + & attribute text:combine-entries-with-dash { boolean }? + & attribute text:combine-entries-with-pp { boolean }? + & attribute text:use-keys-as-entries { boolean }? + & attribute text:capitalize-entries { boolean }? + & attribute text:comma-separated { boolean }? + & attribute fo:language { languageCode }? + & attribute fo:country { countryCode }? + & attribute fo:script { scriptCode }? + & attribute style:rfc-language-tag { language }? + & attribute text:sort-algorithm { \string }? +text-alphabetical-index-auto-mark-file = + element text:alphabetical-index-auto-mark-file { + attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI } + } +text-alphabetical-index-entry-template = + element text:alphabetical-index-entry-template { + text-alphabetical-index-entry-template-attrs, + (text-index-entry-chapter + | text-index-entry-page-number + | text-index-entry-text + | text-index-entry-span + | text-index-entry-tab-stop)* + } +text-alphabetical-index-entry-template-attrs = + attribute text:outline-level { "1" | "2" | "3" | "separator" } + & attribute text:style-name { styleNameRef } +text-bibliography = + element text:bibliography { + common-section-attlist, text-bibliography-source, text-index-body + } +text-bibliography-source = + element text:bibliography-source { + text-index-title-template?, text-bibliography-entry-template* + } +text-bibliography-entry-template = + element text:bibliography-entry-template { + text-bibliography-entry-template-attrs, + (text-index-entry-span + | text-index-entry-tab-stop + | text-index-entry-bibliography)* + } +text-bibliography-entry-template-attrs = + attribute text:bibliography-type { text-bibliography-types } + & attribute text:style-name { styleNameRef } +text-index-source-styles = + element text:index-source-styles { + attribute text:outline-level { positiveInteger }, + text-index-source-style* + } +text-index-source-style = + element text:index-source-style { + attribute text:style-name { styleName }, + empty + } +text-index-title-template = + element text:index-title-template { + attribute text:style-name { styleNameRef }?, + text + } +text-index-entry-chapter = + element text:index-entry-chapter { + attribute text:style-name { styleNameRef }?, + text-index-entry-chapter-attrs + } +text-index-entry-chapter-attrs = + attribute text:display { + "name" + | "number" + | "number-and-name" + | "plain-number" + | "plain-number-and-name" + }? + & attribute text:outline-level { positiveInteger }? +text-index-entry-text = + element text:index-entry-text { + attribute text:style-name { styleNameRef }? + } +text-index-entry-page-number = + element text:index-entry-page-number { + attribute text:style-name { styleNameRef }? + } +text-index-entry-span = + element text:index-entry-span { + attribute text:style-name { styleNameRef }?, + text + } +text-index-entry-bibliography = + element text:index-entry-bibliography { + text-index-entry-bibliography-attrs + } +text-index-entry-bibliography-attrs = + attribute text:style-name { styleNameRef }? + & attribute text:bibliography-data-field { + "address" + | "annote" + | "author" + | "bibliography-type" + | "booktitle" + | "chapter" + | "custom1" + | "custom2" + | "custom3" + | "custom4" + | "custom5" + | "edition" + | "editor" + | "howpublished" + | "identifier" + | "institution" + | "isbn" + | "issn" + | "journal" + | "month" + | "note" + | "number" + | "organizations" + | "pages" + | "publisher" + | "report-type" + | "school" + | "series" + | "title" + | "url" + | "volume" + | "year" + } +text-index-entry-tab-stop = + element text:index-entry-tab-stop { + attribute text:style-name { styleNameRef }?, + text-index-entry-tab-stop-attrs + } +text-index-entry-tab-stop-attrs = + attribute style:leader-char { character }? + & (attribute style:type { "right" } + | (attribute style:type { "left" }, + attribute style:position { length })) +text-index-entry-link-start = + element text:index-entry-link-start { + attribute text:style-name { styleNameRef }? + } +text-index-entry-link-end = + element text:index-entry-link-end { + attribute text:style-name { styleNameRef }? + } +table-table = + element table:table { + table-table-attlist, + table-title?, + table-desc?, + table-table-source?, + office-dde-source?, + table-scenario?, + office-forms?, + table-shapes?, + table-columns-and-groups, + table-rows-and-groups, + table-named-expressions? + } +table-columns-and-groups = + (table-table-column-group | table-columns-no-group)+ +table-columns-no-group = + (table-columns, (table-table-header-columns, table-columns?)?) + | (table-table-header-columns, table-columns?) +table-columns = table-table-columns | table-table-column+ +table-rows-and-groups = (table-table-row-group | table-rows-no-group)+ +table-rows-no-group = + (table-rows, (table-table-header-rows, table-rows?)?) + | (table-table-header-rows, table-rows?) +table-rows = + table-table-rows | (text-soft-page-break?, table-table-row)+ +table-table-attlist = + attribute table:name { \string }? + & attribute table:style-name { styleNameRef }? + & attribute table:template-name { \string }? + & attribute table:use-first-row-styles { boolean }? + & attribute table:use-last-row-styles { boolean }? + & attribute table:use-first-column-styles { boolean }? + & attribute table:use-last-column-styles { boolean }? + & attribute table:use-banding-rows-styles { boolean }? + & attribute table:use-banding-columns-styles { boolean }? + & attribute table:protected { boolean }? + & attribute table:protection-key { \string }? + & attribute table:protection-key-digest-algorithm { anyIRI }? + & attribute table:print { boolean }? + & attribute table:print-ranges { cellRangeAddressList }? + & xml-id? + & attribute table:is-sub-table { boolean }? +table-title = element table:title { text } +table-desc = element table:desc { text } +table-table-row = + element table:table-row { + table-table-row-attlist, + (table-table-cell | table-covered-table-cell)+ + } +table-table-row-attlist = + attribute table:number-rows-repeated { positiveInteger }? + & attribute table:style-name { styleNameRef }? + & attribute table:default-cell-style-name { styleNameRef }? + & attribute table:visibility { table-visibility-value }? + & xml-id? +table-visibility-value = "visible" | "collapse" | "filter" +table-table-cell = + element table:table-cell { + table-table-cell-attlist, + table-table-cell-attlist-extra, + table-table-cell-content + } +table-covered-table-cell = + element table:covered-table-cell { + table-table-cell-attlist, table-table-cell-content + } +table-table-cell-content = + table-cell-range-source?, + office-annotation?, + table-detective?, + text-content* +table-table-cell-attlist = + attribute table:number-columns-repeated { positiveInteger }? + & attribute table:style-name { styleNameRef }? + & attribute table:content-validation-name { \string }? + & attribute table:formula { \string }? + & common-value-and-type-attlist? + & attribute table:protect { boolean }? + & attribute table:protected { boolean }? + & xml-id? + & common-in-content-meta-attlist? +table-table-cell-attlist-extra = + attribute table:number-columns-spanned { positiveInteger }? + & attribute table:number-rows-spanned { positiveInteger }? + & attribute table:number-matrix-columns-spanned { positiveInteger }? + & attribute table:number-matrix-rows-spanned { positiveInteger }? +table-table-column = + element table:table-column { table-table-column-attlist, empty } +table-table-column-attlist = + attribute table:number-columns-repeated { positiveInteger }? + & attribute table:style-name { styleNameRef }? + & attribute table:visibility { table-visibility-value }? + & attribute table:default-cell-style-name { styleNameRef }? + & xml-id? +table-table-header-columns = + element table:table-header-columns { table-table-column+ } +table-table-columns = + element table:table-columns { table-table-column+ } +table-table-column-group = + element table:table-column-group { + table-table-column-group-attlist, table-columns-and-groups + } +table-table-column-group-attlist = attribute table:display { boolean }? +table-table-header-rows = + element table:table-header-rows { + (text-soft-page-break?, table-table-row)+ + } +table-table-rows = + element table:table-rows { (text-soft-page-break?, table-table-row)+ } +table-table-row-group = + element table:table-row-group { + table-table-row-group-attlist, table-rows-and-groups + } +table-table-row-group-attlist = attribute table:display { boolean }? +cellAddress = + xsd:string { + pattern = "($?([^\. ']+|'([^']|'')+'))?\.$?[A-Z]+$?[0-9]+" + } +cellRangeAddress = + xsd:string { + pattern = + "($?([^\. ']+|'([^']|'')+'))?\.$?[A-Z]+$?[0-9]+(:($?([^\. ']+|'([^']|'')+'))?\.$?[A-Z]+$?[0-9]+)?" + } + | xsd:string { + pattern = + "($?([^\. ']+|'([^']|'')+'))?\.$?[0-9]+:($?([^\. ']+|'([^']|'')+'))?\.$?[0-9]+" + } + | xsd:string { + pattern = + "($?([^\. ']+|'([^']|'')+'))?\.$?[A-Z]+:($?([^\. ']+|'([^']|'')+'))?\.$?[A-Z]+" + } +cellRangeAddressList = + xsd:string + >> dc:description [ + 'Value is a space separated list of "cellRangeAddress" patterns' + ] +table-table-source = + element table:table-source { + table-table-source-attlist, table-linked-source-attlist, empty + } +table-table-source-attlist = + attribute table:mode { "copy-all" | "copy-results-only" }? + & attribute table:table-name { \string }? +table-linked-source-attlist = + attribute xlink:type { "simple" } + & attribute xlink:href { anyIRI } + & attribute xlink:actuate { "onRequest" }? + & attribute table:filter-name { \string }? + & attribute table:filter-options { \string }? + & attribute table:refresh-delay { duration }? +table-scenario = + element table:scenario { table-scenario-attlist, empty } +table-scenario-attlist = + attribute table:scenario-ranges { cellRangeAddressList } + & attribute table:is-active { boolean } + & attribute table:display-border { boolean }? + & attribute table:border-color { color }? + & attribute table:copy-back { boolean }? + & attribute table:copy-styles { boolean }? + & attribute table:copy-formulas { boolean }? + & attribute table:comment { \string }? + & attribute table:protected { boolean }? +table-shapes = element table:shapes { shape+ } +table-cell-range-source = + element table:cell-range-source { + table-table-cell-range-source-attlist, + table-linked-source-attlist, + empty + } +table-table-cell-range-source-attlist = + attribute table:name { \string } + & attribute table:last-column-spanned { positiveInteger } + & attribute table:last-row-spanned { positiveInteger } +table-detective = + element table:detective { table-highlighted-range*, table-operation* } +table-operation = + element table:operation { table-operation-attlist, empty } +table-operation-attlist = + attribute table:name { + "trace-dependents" + | "remove-dependents" + | "trace-precedents" + | "remove-precedents" + | "trace-errors" + } + & attribute table:index { nonNegativeInteger } +table-highlighted-range = + element table:highlighted-range { + (table-highlighted-range-attlist + | table-highlighted-range-attlist-invalid), + empty + } +table-highlighted-range-attlist = + attribute table:cell-range-address { cellRangeAddress }? + & attribute table:direction { + "from-another-table" | "to-another-table" | "from-same-table" + } + & attribute table:contains-error { boolean }? +table-highlighted-range-attlist-invalid = + attribute table:marked-invalid { boolean } +office-spreadsheet-attlist = + attribute table:structure-protected { boolean }?, + attribute table:protection-key { \string }?, + attribute table:protection-key-digest-algorithm { anyIRI }? +table-calculation-settings = + element table:calculation-settings { + table-calculation-setting-attlist, + table-null-date?, + table-iteration? + } +table-calculation-setting-attlist = + attribute table:case-sensitive { boolean }? + & attribute table:precision-as-shown { boolean }? + & attribute table:search-criteria-must-apply-to-whole-cell { + boolean + }? + & attribute table:automatic-find-labels { boolean }? + & attribute table:use-regular-expressions { boolean }? + & attribute table:use-wildcards { boolean }? + & attribute table:null-year { positiveInteger }? +table-null-date = + element table:null-date { + attribute table:value-type { "date" }?, + attribute table:date-value { date }?, + empty + } +table-iteration = + element table:iteration { + attribute table:status { "enable" | "disable" }?, + attribute table:steps { positiveInteger }?, + attribute table:maximum-difference { double }?, + empty + } +table-content-validations = + element table:content-validations { table-content-validation+ } +table-content-validation = + element table:content-validation { + table-validation-attlist, + table-help-message?, + (table-error-message | (table-error-macro, office-event-listeners))? + } +table-validation-attlist = + attribute table:name { \string } + & attribute table:condition { \string }? + & attribute table:base-cell-address { cellAddress }? + & attribute table:allow-empty-cell { boolean }? + & attribute table:display-list { + "none" | "unsorted" | "sort-ascending" + }? +table-help-message = + element table:help-message { + attribute table:title { \string }?, + attribute table:display { boolean }?, + text-p* + } +table-error-message = + element table:error-message { + attribute table:title { \string }?, + attribute table:display { boolean }?, + attribute table:message-type { + "stop" | "warning" | "information" + }?, + text-p* + } +table-error-macro = + element table:error-macro { + attribute table:execute { boolean }? + } +table-label-ranges = element table:label-ranges { table-label-range* } +table-label-range = + element table:label-range { table-label-range-attlist, empty } +table-label-range-attlist = + attribute table:label-cell-range-address { cellRangeAddress } + & attribute table:data-cell-range-address { cellRangeAddress } + & attribute table:orientation { "column" | "row" } +table-named-expressions = + element table:named-expressions { + (table-named-range | table-named-expression)* + } +table-named-range = + element table:named-range { table-named-range-attlist, empty } +table-named-range-attlist = + attribute table:name { \string }, + attribute table:cell-range-address { cellRangeAddress }, + attribute table:base-cell-address { cellAddress }?, + attribute table:range-usable-as { + "none" + | list { + ("print-range" | "filter" | "repeat-row" | "repeat-column")+ + } + }? +table-named-expression = + element table:named-expression { + table-named-expression-attlist, empty + } +table-named-expression-attlist = + attribute table:name { \string }, + attribute table:expression { \string }, + attribute table:base-cell-address { cellAddress }? +table-database-ranges = + element table:database-ranges { table-database-range* } +table-database-range = + element table:database-range { + table-database-range-attlist, + (table-database-source-sql + | table-database-source-table + | table-database-source-query)?, + table-filter?, + table-sort?, + table-subtotal-rules? + } +table-database-range-attlist = + attribute table:name { \string }? + & attribute table:is-selection { boolean }? + & attribute table:on-update-keep-styles { boolean }? + & attribute table:on-update-keep-size { boolean }? + & attribute table:has-persistent-data { boolean }? + & attribute table:orientation { "column" | "row" }? + & attribute table:contains-header { boolean }? + & attribute table:display-filter-buttons { boolean }? + & attribute table:target-range-address { cellRangeAddress } + & attribute table:refresh-delay { boolean }? +table-database-source-sql = + element table:database-source-sql { + table-database-source-sql-attlist, empty + } +table-database-source-sql-attlist = + attribute table:database-name { \string } + & attribute table:sql-statement { \string } + & attribute table:parse-sql-statement { boolean }? +table-database-source-query = + element table:database-source-table { + table-database-source-table-attlist, empty + } +table-database-source-table-attlist = + attribute table:database-name { \string } + & attribute table:database-table-name { \string } +table-database-source-table = + element table:database-source-query { + table-database-source-query-attlist, empty + } +table-database-source-query-attlist = + attribute table:database-name { \string } + & attribute table:query-name { \string } +table-sort = element table:sort { table-sort-attlist, table-sort-by+ } +table-sort-attlist = + attribute table:bind-styles-to-content { boolean }? + & attribute table:target-range-address { cellRangeAddress }? + & attribute table:case-sensitive { boolean }? + & attribute table:language { languageCode }? + & attribute table:country { countryCode }? + & attribute table:script { scriptCode }? + & attribute table:rfc-language-tag { language }? + & attribute table:algorithm { \string }? + & attribute table:embedded-number-behavior { + "alpha-numeric" | "integer" | "double" + }? +table-sort-by = element table:sort-by { table-sort-by-attlist, empty } +table-sort-by-attlist = + attribute table:field-number { nonNegativeInteger } + & attribute table:data-type { + "text" | "number" | "automatic" | \string + }? + & attribute table:order { "ascending" | "descending" }? +table-subtotal-rules = + element table:subtotal-rules { + table-subtotal-rules-attlist, + table-sort-groups?, + table-subtotal-rule* + } +table-subtotal-rules-attlist = + attribute table:bind-styles-to-content { boolean }? + & attribute table:case-sensitive { boolean }? + & attribute table:page-breaks-on-group-change { boolean }? +table-sort-groups = + element table:sort-groups { table-sort-groups-attlist, empty } +table-sort-groups-attlist = + attribute table:data-type { + "text" | "number" | "automatic" | \string + }? + & attribute table:order { "ascending" | "descending" }? +table-subtotal-rule = + element table:subtotal-rule { + table-subtotal-rule-attlist, table-subtotal-field* + } +table-subtotal-rule-attlist = + attribute table:group-by-field-number { nonNegativeInteger } +table-subtotal-field = + element table:subtotal-field { table-subtotal-field-attlist, empty } +table-subtotal-field-attlist = + attribute table:field-number { nonNegativeInteger } + & attribute table:function { + "average" + | "count" + | "countnums" + | "max" + | "min" + | "product" + | "stdev" + | "stdevp" + | "sum" + | "var" + | "varp" + | \string + } +table-filter = + element table:filter { + table-filter-attlist, + (table-filter-condition | table-filter-and | table-filter-or) + } +table-filter-attlist = + attribute table:target-range-address { cellRangeAddress }? + & attribute table:condition-source { "self" | "cell-range" }? + & attribute table:condition-source-range-address { cellRangeAddress }? + & attribute table:display-duplicates { boolean }? +table-filter-and = + element table:filter-and { + (table-filter-or | table-filter-condition)+ + } +table-filter-or = + element table:filter-or { + (table-filter-and | table-filter-condition)+ + } +table-filter-condition = + element table:filter-condition { + table-filter-condition-attlist, table-filter-set-item* + } +table-filter-condition-attlist = + attribute table:field-number { nonNegativeInteger } + & attribute table:value { \string | double } + & attribute table:operator { \string } + & attribute table:case-sensitive { \string }? + & attribute table:data-type { "text" | "number" }? +table-filter-set-item = + element table:filter-set-item { + attribute table:value { \string }, + empty + } +table-data-pilot-tables = + element table:data-pilot-tables { table-data-pilot-table* } +table-data-pilot-table = + element table:data-pilot-table { + table-data-pilot-table-attlist, + (table-database-source-sql + | table-database-source-table + | table-database-source-query + | table-source-service + | table-source-cell-range)?, + table-data-pilot-field+ + } +table-data-pilot-table-attlist = + attribute table:name { \string } + & attribute table:application-data { \string }? + & attribute table:grand-total { "none" | "row" | "column" | "both" }? + & attribute table:ignore-empty-rows { boolean }? + & attribute table:identify-categories { boolean }? + & attribute table:target-range-address { cellRangeAddress } + & attribute table:buttons { cellRangeAddressList }? + & attribute table:show-filter-button { boolean }? + & attribute table:drill-down-on-double-click { boolean }? +table-source-cell-range = + element table:source-cell-range { + table-source-cell-range-attlist, table-filter? + } +table-source-cell-range-attlist = + attribute table:cell-range-address { cellRangeAddress } +table-source-service = + element table:source-service { table-source-service-attlist, empty } +table-source-service-attlist = + attribute table:name { \string } + & attribute table:source-name { \string } + & attribute table:object-name { \string } + & attribute table:user-name { \string }? + & attribute table:password { \string }? +table-data-pilot-field = + element table:data-pilot-field { + table-data-pilot-field-attlist, + table-data-pilot-level?, + table-data-pilot-field-reference?, + table-data-pilot-groups? + } +table-data-pilot-field-attlist = + attribute table:source-field-name { \string } + & (attribute table:orientation { + "row" | "column" | "data" | "hidden" + } + | (attribute table:orientation { "page" }, + attribute table:selected-page { \string })) + & attribute table:is-data-layout-field { \string }? + & attribute table:function { + "auto" + | "average" + | "count" + | "countnums" + | "max" + | "min" + | "product" + | "stdev" + | "stdevp" + | "sum" + | "var" + | "varp" + | \string + }? + & attribute table:used-hierarchy { integer }? +table-data-pilot-level = + element table:data-pilot-level { + table-data-pilot-level-attlist, + table-data-pilot-subtotals?, + table-data-pilot-members?, + table-data-pilot-display-info?, + table-data-pilot-sort-info?, + table-data-pilot-layout-info? + } +table-data-pilot-level-attlist = attribute table:show-empty { boolean }? +table-data-pilot-subtotals = + element table:data-pilot-subtotals { table-data-pilot-subtotal* } +table-data-pilot-subtotal = + element table:data-pilot-subtotal { + table-data-pilot-subtotal-attlist, empty + } +table-data-pilot-subtotal-attlist = + attribute table:function { + "auto" + | "average" + | "count" + | "countnums" + | "max" + | "min" + | "product" + | "stdev" + | "stdevp" + | "sum" + | "var" + | "varp" + | \string + } +table-data-pilot-members = + element table:data-pilot-members { table-data-pilot-member* } +table-data-pilot-member = + element table:data-pilot-member { + table-data-pilot-member-attlist, empty + } +table-data-pilot-member-attlist = + attribute table:name { \string } + & attribute table:display { boolean }? + & attribute table:show-details { boolean }? +table-data-pilot-display-info = + element table:data-pilot-display-info { + table-data-pilot-display-info-attlist, empty + } +table-data-pilot-display-info-attlist = + attribute table:enabled { boolean } + & attribute table:data-field { \string } + & attribute table:member-count { nonNegativeInteger } + & attribute table:display-member-mode { "from-top" | "from-bottom" } +table-data-pilot-sort-info = + element table:data-pilot-sort-info { + table-data-pilot-sort-info-attlist, empty + } +table-data-pilot-sort-info-attlist = + ((attribute table:sort-mode { "data" }, + attribute table:data-field { \string }) + | attribute table:sort-mode { "none" | "manual" | "name" }) + & attribute table:order { "ascending" | "descending" } +table-data-pilot-layout-info = + element table:data-pilot-layout-info { + table-data-pilot-layout-info-attlist, empty + } +table-data-pilot-layout-info-attlist = + attribute table:layout-mode { + "tabular-layout" + | "outline-subtotals-top" + | "outline-subtotals-bottom" + } + & attribute table:add-empty-lines { boolean } +table-data-pilot-field-reference = + element table:data-pilot-field-reference { + table-data-pilot-field-reference-attlist + } +table-data-pilot-field-reference-attlist = + attribute table:field-name { \string } + & ((attribute table:member-type { "named" }, + attribute table:member-name { \string }) + | attribute table:member-type { "previous" | "next" }) + & attribute table:type { + "none" + | "member-difference" + | "member-percentage" + | "member-percentage-difference" + | "running-total" + | "row-percentage" + | "column-percentage" + | "total-percentage" + | "index" + } +table-data-pilot-groups = + element table:data-pilot-groups { + table-data-pilot-groups-attlist, table-data-pilot-group+ + } +table-data-pilot-groups-attlist = + attribute table:source-field-name { \string } + & (attribute table:date-start { dateOrDateTime | "auto" } + | attribute table:start { double | "auto" }) + & (attribute table:date-end { dateOrDateTime | "auto" } + | attribute table:end { double | "auto" }) + & attribute table:step { double } + & attribute table:grouped-by { + "seconds" + | "minutes" + | "hours" + | "days" + | "months" + | "quarters" + | "years" + } +table-data-pilot-group = + element table:data-pilot-group { + table-data-pilot-group-attlist, table-data-pilot-group-member+ + } +table-data-pilot-group-attlist = attribute table:name { \string } +table-data-pilot-group-member = + element table:data-pilot-group-member { + table-data-pilot-group-member-attlist + } +table-data-pilot-group-member-attlist = attribute table:name { \string } +table-consolidation = + element table:consolidation { table-consolidation-attlist, empty } +table-consolidation-attlist = + attribute table:function { + "average" + | "count" + | "countnums" + | "max" + | "min" + | "product" + | "stdev" + | "stdevp" + | "sum" + | "var" + | "varp" + | \string + } + & attribute table:source-cell-range-addresses { cellRangeAddressList } + & attribute table:target-cell-address { cellAddress } + & attribute table:use-labels { "none" | "row" | "column" | "both" }? + & attribute table:link-to-source-data { boolean }? +table-dde-links = element table:dde-links { table-dde-link+ } +table-tracked-changes = + element table:tracked-changes { + table-tracked-changes-attlist, + (table-cell-content-change + | table-insertion + | table-deletion + | table-movement)* + } +table-tracked-changes-attlist = + attribute table:track-changes { boolean }? +table-insertion = + element table:insertion { + table-insertion-attlist, + common-table-change-attlist, + office-change-info, + table-dependencies?, + table-deletions? + } +table-insertion-attlist = + attribute table:type { "row" | "column" | "table" } + & attribute table:position { integer } + & attribute table:count { positiveInteger }? + & attribute table:table { integer }? +table-dependencies = element table:dependencies { table-dependency+ } +table-dependency = + element table:dependency { + attribute table:id { \string }, + empty + } +table-deletions = + element table:deletions { + (table-cell-content-deletion | table-change-deletion)+ + } +table-cell-content-deletion = + element table:cell-content-deletion { + attribute table:id { \string }?, + table-cell-address?, + table-change-track-table-cell? + } +table-change-deletion = + element table:change-deletion { + attribute table:id { \string }?, + empty + } +table-deletion = + element table:deletion { + table-deletion-attlist, + common-table-change-attlist, + office-change-info, + table-dependencies?, + table-deletions?, + table-cut-offs? + } +table-deletion-attlist = + attribute table:type { "row" | "column" | "table" } + & attribute table:position { integer } + & attribute table:table { integer }? + & attribute table:multi-deletion-spanned { integer }? +table-cut-offs = + element table:cut-offs { + table-movement-cut-off+ + | (table-insertion-cut-off, table-movement-cut-off*) + } +table-insertion-cut-off = + element table:insertion-cut-off { + table-insertion-cut-off-attlist, empty + } +table-insertion-cut-off-attlist = + attribute table:id { \string } + & attribute table:position { integer } +table-movement-cut-off = + element table:movement-cut-off { + table-movement-cut-off-attlist, empty + } +table-movement-cut-off-attlist = + attribute table:position { integer } + | (attribute table:start-position { integer }, + attribute table:end-position { integer }) +table-movement = + element table:movement { + common-table-change-attlist, + table-source-range-address, + table-target-range-address, + office-change-info, + table-dependencies?, + table-deletions? + } +table-source-range-address = + element table:source-range-address { + common-table-range-attlist, empty + } +table-target-range-address = + element table:target-range-address { + common-table-range-attlist, empty + } +common-table-range-attlist = + common-table-cell-address-attlist + | common-table-cell-range-address-attlist +common-table-cell-address-attlist = + attribute table:column { integer }, + attribute table:row { integer }, + attribute table:table { integer } +common-table-cell-range-address-attlist = + attribute table:start-column { integer }, + attribute table:start-row { integer }, + attribute table:start-table { integer }, + attribute table:end-column { integer }, + attribute table:end-row { integer }, + attribute table:end-table { integer } +table-change-track-table-cell = + element table:change-track-table-cell { + table-change-track-table-cell-attlist, text-p* + } +table-change-track-table-cell-attlist = + attribute table:cell-address { cellAddress }? + & attribute table:matrix-covered { boolean }? + & attribute table:formula { \string }? + & attribute table:number-matrix-columns-spanned { positiveInteger }? + & attribute table:number-matrix-rows-spanned { positiveInteger }? + & common-value-and-type-attlist? +table-cell-content-change = + element table:cell-content-change { + common-table-change-attlist, + table-cell-address, + office-change-info, + table-dependencies?, + table-deletions?, + table-previous + } +table-cell-address = + element table:cell-address { + common-table-cell-address-attlist, empty + } +table-previous = + element table:previous { + attribute table:id { \string }?, + table-change-track-table-cell + } +common-table-change-attlist = + attribute table:id { \string } + & attribute table:acceptance-state { + "accepted" | "rejected" | "pending" + }? + & attribute table:rejecting-change-id { \string }? +style-handout-master = + element style:handout-master { + common-presentation-header-footer-attlist, + style-handout-master-attlist, + shape* + } +style-handout-master-attlist = + attribute presentation:presentation-page-layout-name { styleNameRef }? + & attribute style:page-layout-name { styleNameRef } + & attribute draw:style-name { styleNameRef }? +draw-layer-set = element draw:layer-set { draw-layer* } +draw-layer = + element draw:layer { draw-layer-attlist, svg-title?, svg-desc? } +draw-layer-attlist = + attribute draw:name { \string } + & attribute draw:protected { boolean }? + & attribute draw:display { "always" | "screen" | "printer" | "none" }? +draw-page = + element draw:page { + common-presentation-header-footer-attlist, + draw-page-attlist, + svg-title?, + svg-desc?, + draw-layer-set?, + office-forms?, + shape*, + (presentation-animations | animation-element)?, + presentation-notes? + } +draw-page-attlist = + attribute draw:name { \string }? + & attribute draw:style-name { styleNameRef }? + & attribute draw:master-page-name { styleNameRef } + & attribute presentation:presentation-page-layout-name { + styleNameRef + }? + & (xml-id, + attribute draw:id { NCName }?)? + & attribute draw:nav-order { IDREFS }? +common-presentation-header-footer-attlist = + attribute presentation:use-header-name { \string }? + & attribute presentation:use-footer-name { \string }? + & attribute presentation:use-date-time-name { \string }? +shape = shape-instance | draw-a +shape-instance = + draw-rect + | draw-line + | draw-polyline + | draw-polygon + | draw-regular-polygon + | draw-path + | draw-circle + | draw-ellipse + | draw-g + | draw-page-thumbnail + | draw-frame + | draw-measure + | draw-caption + | draw-connector + | draw-control + | dr3d-scene + | draw-custom-shape +draw-rect = + element draw:rect { + draw-rect-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +draw-rect-attlist = + attribute draw:corner-radius { nonNegativeLength }? + | (attribute svg:rx { nonNegativeLength }?, + attribute svg:ry { nonNegativeLength }?) +draw-line = + element draw:line { + draw-line-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +draw-line-attlist = + attribute svg:x1 { coordinate } + & attribute svg:y1 { coordinate } + & attribute svg:x2 { coordinate } + & attribute svg:y2 { coordinate } +draw-polyline = + element draw:polyline { + common-draw-points-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-viewbox-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +common-draw-points-attlist = attribute draw:points { points } +draw-polygon = + element draw:polygon { + common-draw-points-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-viewbox-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +draw-regular-polygon = + element draw:regular-polygon { + draw-regular-polygon-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +draw-regular-polygon-attlist = + (attribute draw:concave { "false" } + | (attribute draw:concave { "true" }, + draw-regular-polygon-sharpness-attlist)) + & attribute draw:corners { positiveInteger } +draw-regular-polygon-sharpness-attlist = + attribute draw:sharpness { percent } +draw-path = + element draw:path { + common-draw-path-data-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-viewbox-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +common-draw-path-data-attlist = attribute svg:d { pathData } +draw-circle = + element draw:circle { + ((draw-circle-attlist, common-draw-circle-ellipse-pos-attlist) + | (common-draw-position-attlist, common-draw-size-attlist)), + common-draw-circle-ellipse-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +common-draw-circle-ellipse-pos-attlist = + attribute svg:cx { coordinate }, + attribute svg:cy { coordinate } +draw-circle-attlist = attribute svg:r { length } +common-draw-circle-ellipse-attlist = + attribute draw:kind { "full" | "section" | "cut" | "arc" }? + & attribute draw:start-angle { angle }? + & attribute draw:end-angle { angle }? +draw-ellipse = + element draw:ellipse { + ((draw-ellipse-attlist, common-draw-circle-ellipse-pos-attlist) + | (common-draw-position-attlist, common-draw-size-attlist)), + common-draw-circle-ellipse-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +draw-ellipse-attlist = + attribute svg:rx { length }, + attribute svg:ry { length } +draw-connector = + element draw:connector { + draw-connector-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + common-draw-viewbox-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +draw-connector-attlist = + attribute draw:type { "standard" | "lines" | "line" | "curve" }? + & (attribute svg:x1 { coordinate }, + attribute svg:y1 { coordinate })? + & attribute draw:start-shape { IDREF }? + & attribute draw:start-glue-point { nonNegativeInteger }? + & (attribute svg:x2 { coordinate }, + attribute svg:y2 { coordinate })? + & attribute draw:end-shape { IDREF }? + & attribute draw:end-glue-point { nonNegativeInteger }? + & attribute draw:line-skew { + list { length, (length, length?)? } + }? + & attribute svg:d { pathData }? +draw-caption = + element draw:caption { + draw-caption-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +draw-caption-attlist = + (attribute draw:caption-point-x { coordinate }, + attribute draw:caption-point-y { coordinate })? + & attribute draw:corner-radius { nonNegativeLength }? +draw-measure = + element draw:measure { + draw-measure-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text + } +draw-measure-attlist = + attribute svg:x1 { coordinate } + & attribute svg:y1 { coordinate } + & attribute svg:x2 { coordinate } + & attribute svg:y2 { coordinate } +draw-control = + element draw:control { + draw-control-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + draw-glue-point* + } +draw-control-attlist = attribute draw:control { IDREF } +draw-page-thumbnail = + element draw:page-thumbnail { + draw-page-thumbnail-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + presentation-shape-attlist, + common-draw-shape-with-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc? + } +draw-page-thumbnail-attlist = + attribute draw:page-number { positiveInteger }? +draw-g = + element draw:g { + draw-g-attlist, + common-draw-z-index-attlist, + common-draw-name-attlist, + common-draw-id-attlist, + common-draw-style-name-attlist, + common-text-spreadsheet-shape-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + shape* + } +draw-g-attlist = attribute svg:y { coordinate }? +common-draw-name-attlist = attribute draw:name { \string }? +common-draw-caption-id-attlist = attribute draw:caption-id { IDREF }? +common-draw-position-attlist = + attribute svg:x { coordinate }?, + attribute svg:y { coordinate }? +common-draw-size-attlist = + attribute svg:width { length }?, + attribute svg:height { length }? +common-draw-transform-attlist = attribute draw:transform { \string }? +common-draw-viewbox-attlist = + attribute svg:viewBox { + list { integer, integer, integer, integer } + } +common-draw-style-name-attlist = + (attribute draw:style-name { styleNameRef }?, + attribute draw:class-names { styleNameRefs }?) + | (attribute presentation:style-name { styleNameRef }?, + attribute presentation:class-names { styleNameRefs }?) +common-draw-text-style-name-attlist = + attribute draw:text-style-name { styleNameRef }? +common-draw-layer-name-attlist = attribute draw:layer { \string }? +common-draw-id-attlist = + (xml-id, + attribute draw:id { NCName }?)? +common-draw-z-index-attlist = + attribute draw:z-index { nonNegativeInteger }? +common-text-spreadsheet-shape-attlist = + attribute table:end-cell-address { cellAddress }? + & attribute table:end-x { coordinate }? + & attribute table:end-y { coordinate }? + & attribute table:table-background { boolean }? + & common-text-anchor-attlist +common-text-anchor-attlist = + attribute text:anchor-type { + "page" | "frame" | "paragraph" | "char" | "as-char" + }? + & attribute text:anchor-page-number { positiveInteger }? +draw-text = (text-p | text-list)* +common-draw-shape-with-styles-attlist = + common-draw-z-index-attlist, + common-draw-id-attlist, + common-draw-layer-name-attlist, + common-draw-style-name-attlist, + common-draw-transform-attlist, + common-draw-name-attlist, + common-text-spreadsheet-shape-attlist +common-draw-shape-with-text-and-styles-attlist = + common-draw-shape-with-styles-attlist, + common-draw-text-style-name-attlist +draw-glue-point = + element draw:glue-point { draw-glue-point-attlist, empty } +draw-glue-point-attlist = + attribute draw:id { nonNegativeInteger } + & attribute svg:x { distance | percent } + & attribute svg:y { distance | percent } + & attribute draw:align { + "top-left" + | "top" + | "top-right" + | "left" + | "center" + | "right" + | "bottom-left" + | "bottom-right" + }? + & attribute draw:escape-direction { + "auto" + | "left" + | "right" + | "up" + | "down" + | "horizontal" + | "vertical" + } +svg-title = element svg:title { text } +svg-desc = element svg:desc { text } +draw-frame = + element draw:frame { + common-draw-shape-with-text-and-styles-attlist, + common-draw-position-attlist, + common-draw-rel-size-attlist, + common-draw-caption-id-attlist, + presentation-shape-attlist, + draw-frame-attlist, + (draw-text-box + | draw-image + | draw-object + | draw-object-ole + | draw-applet + | draw-floating-frame + | draw-plugin + | table-table)*, + office-event-listeners?, + draw-glue-point*, + draw-image-map?, + svg-title?, + svg-desc?, + (draw-contour-polygon | draw-contour-path)? + } +common-draw-rel-size-attlist = + common-draw-size-attlist, + attribute style:rel-width { percent | "scale" | "scale-min" }?, + attribute style:rel-height { percent | "scale" | "scale-min" }? +draw-frame-attlist = attribute draw:copy-of { \string }? +draw-text-box = + element draw:text-box { draw-text-box-attlist, text-content* } +draw-text-box-attlist = + attribute draw:chain-next-name { \string }? + & attribute draw:corner-radius { nonNegativeLength }? + & attribute fo:min-height { length | percent }? + & attribute fo:min-width { length | percent }? + & attribute fo:max-height { length | percent }? + & attribute fo:max-width { length | percent }? + & (xml-id, + attribute text:id { NCName }?)? +draw-image = + element draw:image { + draw-image-attlist, + (common-draw-data-attlist | office-binary-data), + draw-text + } +common-draw-data-attlist = + attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:show { "embed" }?, + attribute xlink:actuate { "onLoad" }? +office-binary-data = element office:binary-data { base64Binary } +draw-image-attlist = + attribute draw:filter-name { \string }? + & xml-id? +draw-object = + element draw:object { + draw-object-attlist, + (common-draw-data-attlist | office-document | math-math) + } +draw-object-ole = + element draw:object-ole { + draw-object-ole-attlist, + (common-draw-data-attlist | office-binary-data) + } +draw-object-attlist = + attribute draw:notify-on-update-of-ranges { + cellRangeAddressList | \string + }? + & xml-id? +draw-object-ole-attlist = + attribute draw:class-id { \string }? + & xml-id? +draw-applet = + element draw:applet { + draw-applet-attlist, common-draw-data-attlist?, draw-param* + } +draw-applet-attlist = + attribute draw:code { \string }? + & attribute draw:object { \string }? + & attribute draw:archive { \string }? + & attribute draw:may-script { boolean }? + & xml-id? +draw-plugin = + element draw:plugin { + draw-plugin-attlist, common-draw-data-attlist, draw-param* + } +draw-plugin-attlist = + attribute draw:mime-type { \string }? + & xml-id? +draw-param = element draw:param { draw-param-attlist, empty } +draw-param-attlist = + attribute draw:name { \string }? + & attribute draw:value { \string }? +draw-floating-frame = + element draw:floating-frame { + draw-floating-frame-attlist, common-draw-data-attlist + } +draw-floating-frame-attlist = + attribute draw:frame-name { \string }? + & xml-id? +draw-contour-polygon = + element draw:contour-polygon { + common-contour-attlist, + common-draw-size-attlist, + common-draw-viewbox-attlist, + common-draw-points-attlist, + empty + } +draw-contour-path = + element draw:contour-path { + common-contour-attlist, + common-draw-size-attlist, + common-draw-viewbox-attlist, + common-draw-path-data-attlist, + empty + } +common-contour-attlist = attribute draw:recreate-on-edit { boolean } +draw-a = element draw:a { draw-a-attlist, shape-instance } +draw-a-attlist = + attribute xlink:type { "simple" } + & attribute xlink:href { anyIRI } + & attribute xlink:actuate { "onRequest" }? + & attribute office:target-frame-name { targetFrameName }? + & attribute xlink:show { "new" | "replace" }? + & attribute office:name { \string }? + & attribute office:title { \string }? + & attribute office:server-map { boolean }? + & xml-id? +draw-image-map = + element draw:image-map { + (draw-area-rectangle | draw-area-circle | draw-area-polygon)* + } +draw-area-rectangle = + element draw:area-rectangle { + common-draw-area-attlist, + attribute svg:x { coordinate }, + attribute svg:y { coordinate }, + attribute svg:width { length }, + attribute svg:height { length }, + svg-title?, + svg-desc?, + office-event-listeners? + } +draw-area-circle = + element draw:area-circle { + common-draw-area-attlist, + attribute svg:cx { coordinate }, + attribute svg:cy { coordinate }, + attribute svg:r { length }, + svg-title?, + svg-desc?, + office-event-listeners? + } +draw-area-polygon = + element draw:area-polygon { + common-draw-area-attlist, + attribute svg:x { coordinate }, + attribute svg:y { coordinate }, + attribute svg:width { length }, + attribute svg:height { length }, + common-draw-viewbox-attlist, + common-draw-points-attlist, + svg-title?, + svg-desc?, + office-event-listeners? + } +common-draw-area-attlist = + (attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute office:target-frame-name { targetFrameName }?, + attribute xlink:show { "new" | "replace" }?)? + & attribute office:name { \string }? + & attribute draw:nohref { "nohref" }? +dr3d-scene = + element dr3d:scene { + dr3d-scene-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-style-name-attlist, + common-draw-z-index-attlist, + common-draw-id-attlist, + common-draw-layer-name-attlist, + common-text-spreadsheet-shape-attlist, + common-dr3d-transform-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + dr3d-light*, + shapes3d*, + draw-glue-point* + } +shapes3d = + dr3d-scene | dr3d-extrude | dr3d-sphere | dr3d-rotate | dr3d-cube +dr3d-scene-attlist = + attribute dr3d:vrp { vector3D }? + & attribute dr3d:vpn { vector3D }? + & attribute dr3d:vup { vector3D }? + & attribute dr3d:projection { "parallel" | "perspective" }? + & attribute dr3d:distance { length }? + & attribute dr3d:focal-length { length }? + & attribute dr3d:shadow-slant { angle }? + & attribute dr3d:shade-mode { + "flat" | "phong" | "gouraud" | "draft" + }? + & attribute dr3d:ambient-color { color }? + & attribute dr3d:lighting-mode { boolean }? +common-dr3d-transform-attlist = attribute dr3d:transform { \string }? +dr3d-light = element dr3d:light { dr3d-light-attlist, empty } +dr3d-light-attlist = + attribute dr3d:diffuse-color { color }? + & attribute dr3d:direction { vector3D } + & attribute dr3d:enabled { boolean }? + & attribute dr3d:specular { boolean }? +dr3d-cube = + element dr3d:cube { + dr3d-cube-attlist, + common-draw-z-index-attlist, + common-draw-id-attlist, + common-draw-layer-name-attlist, + common-draw-style-name-attlist, + common-dr3d-transform-attlist, + empty + } +dr3d-cube-attlist = + attribute dr3d:min-edge { vector3D }?, + attribute dr3d:max-edge { vector3D }? +dr3d-sphere = + element dr3d:sphere { + dr3d-sphere-attlist, + common-draw-z-index-attlist, + common-draw-id-attlist, + common-draw-layer-name-attlist, + common-draw-style-name-attlist, + common-dr3d-transform-attlist, + empty + } +dr3d-sphere-attlist = + attribute dr3d:center { vector3D }? + & attribute dr3d:size { vector3D }? +dr3d-extrude = + element dr3d:extrude { + common-draw-path-data-attlist, + common-draw-viewbox-attlist, + common-draw-id-attlist, + common-draw-z-index-attlist, + common-draw-layer-name-attlist, + common-draw-style-name-attlist, + common-dr3d-transform-attlist, + empty + } +dr3d-rotate = + element dr3d:rotate { + common-draw-viewbox-attlist, + common-draw-path-data-attlist, + common-draw-z-index-attlist, + common-draw-id-attlist, + common-draw-layer-name-attlist, + common-draw-style-name-attlist, + common-dr3d-transform-attlist, + empty + } +draw-custom-shape = + element draw:custom-shape { + draw-custom-shape-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-shape-with-text-and-styles-attlist, + common-draw-caption-id-attlist, + svg-title?, + svg-desc?, + office-event-listeners?, + draw-glue-point*, + draw-text, + draw-enhanced-geometry? + } +draw-custom-shape-attlist = + attribute draw:engine { namespacedToken }? + & attribute draw:data { \string }? +draw-enhanced-geometry = + element draw:enhanced-geometry { + draw-enhanced-geometry-attlist, draw-equation*, draw-handle* + } +draw-enhanced-geometry-attlist = + attribute draw:type { custom-shape-type }? + & attribute svg:viewBox { + list { integer, integer, integer, integer } + }? + & attribute draw:mirror-vertical { boolean }? + & attribute draw:mirror-horizontal { boolean }? + & attribute draw:text-rotate-angle { angle }? + & attribute draw:extrusion-allowed { boolean }? + & attribute draw:text-path-allowed { boolean }? + & attribute draw:concentric-gradient-fill-allowed { boolean }? + & attribute draw:extrusion { boolean }? + & attribute draw:extrusion-brightness { zeroToHundredPercent }? + & attribute draw:extrusion-depth { + list { length, double } + }? + & attribute draw:extrusion-diffusion { percent }? + & attribute draw:extrusion-number-of-line-segments { integer }? + & attribute draw:extrusion-light-face { boolean }? + & attribute draw:extrusion-first-light-harsh { boolean }? + & attribute draw:extrusion-second-light-harsh { boolean }? + & attribute draw:extrusion-first-light-level { zeroToHundredPercent }? + & attribute draw:extrusion-second-light-level { + zeroToHundredPercent + }? + & attribute draw:extrusion-first-light-direction { vector3D }? + & attribute draw:extrusion-second-light-direction { vector3D }? + & attribute draw:extrusion-metal { boolean }? + & attribute dr3d:shade-mode { + "flat" | "phong" | "gouraud" | "draft" + }? + & attribute draw:extrusion-rotation-angle { + list { angle, angle } + }? + & attribute draw:extrusion-rotation-center { vector3D }? + & attribute draw:extrusion-shininess { zeroToHundredPercent }? + & attribute draw:extrusion-skew { + list { double, angle } + }? + & attribute draw:extrusion-specularity { zeroToHundredPercent }? + & attribute dr3d:projection { "parallel" | "perspective" }? + & attribute draw:extrusion-viewpoint { point3D }? + & attribute draw:extrusion-origin { + list { extrusionOrigin, extrusionOrigin } + }? + & attribute draw:extrusion-color { boolean }? + & attribute draw:enhanced-path { \string }? + & attribute draw:path-stretchpoint-x { double }? + & attribute draw:path-stretchpoint-y { double }? + & attribute draw:text-areas { \string }? + & attribute draw:glue-points { \string }? + & attribute draw:glue-point-type { + "none" | "segments" | "rectangle" + }? + & attribute draw:glue-point-leaving-directions { \string }? + & attribute draw:text-path { boolean }? + & attribute draw:text-path-mode { "normal" | "path" | "shape" }? + & attribute draw:text-path-scale { "path" | "shape" }? + & attribute draw:text-path-same-letter-heights { boolean }? + & attribute draw:modifiers { \string }? +custom-shape-type = "non-primitive" | \string +point3D = + xsd:string { + pattern = + "\([ ]*-?([0-9]+(\.[0-9]*)?|\.[0-9]+)((cm)|(mm)|(in)|(pt)|(pc))([ ]+-?([0-9]+(\.[0-9]*)?|\.[0-9]+)((cm)|(mm)|(in)|(pt)|(pc))){2}[ ]*\)" + } +extrusionOrigin = + xsd:double { minInclusive = "-0.5" maxInclusive = "0.5" } +draw-equation = element draw:equation { draw-equation-attlist, empty } +draw-equation-attlist = + attribute draw:name { \string }? + & attribute draw:formula { \string }? +draw-handle = element draw:handle { draw-handle-attlist, empty } +draw-handle-attlist = + attribute draw:handle-mirror-vertical { boolean }? + & attribute draw:handle-mirror-horizontal { boolean }? + & attribute draw:handle-switched { boolean }? + & attribute draw:handle-position { \string } + & attribute draw:handle-range-x-minimum { \string }? + & attribute draw:handle-range-x-maximum { \string }? + & attribute draw:handle-range-y-minimum { \string }? + & attribute draw:handle-range-y-maximum { \string }? + & attribute draw:handle-polar { \string }? + & attribute draw:handle-radius-range-minimum { \string }? + & attribute draw:handle-radius-range-maximum { \string }? +presentation-shape-attlist = + attribute presentation:class { presentation-classes }? + & attribute presentation:placeholder { boolean }? + & attribute presentation:user-transformed { boolean }? +presentation-classes = + "title" + | "outline" + | "subtitle" + | "text" + | "graphic" + | "object" + | "chart" + | "table" + | "orgchart" + | "page" + | "notes" + | "handout" + | "header" + | "footer" + | "date-time" + | "page-number" +presentation-animations = + element presentation:animations { + (presentation-animation-elements | presentation-animation-group)* + } +presentation-animation-elements = + presentation-show-shape + | presentation-show-text + | presentation-hide-shape + | presentation-hide-text + | presentation-dim + | presentation-play +presentation-sound = + element presentation:sound { + presentation-sound-attlist, + attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:actuate { "onRequest" }?, + attribute xlink:show { "new" | "replace" }?, + empty + } +presentation-sound-attlist = + attribute presentation:play-full { boolean }? + & xml-id? +presentation-show-shape = + element presentation:show-shape { + common-presentation-effect-attlist, presentation-sound? + } +common-presentation-effect-attlist = + attribute draw:shape-id { IDREF } + & attribute presentation:effect { presentationEffects }? + & attribute presentation:direction { presentationEffectDirections }? + & attribute presentation:speed { presentationSpeeds }? + & attribute presentation:delay { duration }? + & attribute presentation:start-scale { percent }? + & attribute presentation:path-id { \string }? +presentationEffects = + "none" + | "fade" + | "move" + | "stripes" + | "open" + | "close" + | "dissolve" + | "wavyline" + | "random" + | "lines" + | "laser" + | "appear" + | "hide" + | "move-short" + | "checkerboard" + | "rotate" + | "stretch" +presentationEffectDirections = + "none" + | "from-left" + | "from-top" + | "from-right" + | "from-bottom" + | "from-center" + | "from-upper-left" + | "from-upper-right" + | "from-lower-left" + | "from-lower-right" + | "to-left" + | "to-top" + | "to-right" + | "to-bottom" + | "to-upper-left" + | "to-upper-right" + | "to-lower-right" + | "to-lower-left" + | "path" + | "spiral-inward-left" + | "spiral-inward-right" + | "spiral-outward-left" + | "spiral-outward-right" + | "vertical" + | "horizontal" + | "to-center" + | "clockwise" + | "counter-clockwise" +presentationSpeeds = "slow" | "medium" | "fast" +presentation-show-text = + element presentation:show-text { + common-presentation-effect-attlist, presentation-sound? + } +presentation-hide-shape = + element presentation:hide-shape { + common-presentation-effect-attlist, presentation-sound? + } +presentation-hide-text = + element presentation:hide-text { + common-presentation-effect-attlist, presentation-sound? + } +presentation-dim = + element presentation:dim { + presentation-dim-attlist, presentation-sound? + } +presentation-dim-attlist = + attribute draw:shape-id { IDREF } + & attribute draw:color { color } +presentation-play = + element presentation:play { presentation-play-attlist, empty } +presentation-play-attlist = + attribute draw:shape-id { IDREF }, + attribute presentation:speed { presentationSpeeds }? +presentation-animation-group = + element presentation:animation-group { + presentation-animation-elements* + } +common-anim-attlist = + attribute presentation:node-type { + "default" + | "on-click" + | "with-previous" + | "after-previous" + | "timing-root" + | "main-sequence" + | "interactive-sequence" + }? + & attribute presentation:preset-id { \string }? + & attribute presentation:preset-sub-type { \string }? + & attribute presentation:preset-class { + "custom" + | "entrance" + | "exit" + | "emphasis" + | "motion-path" + | "ole-action" + | "media-call" + }? + & attribute presentation:master-element { IDREF }? + & attribute presentation:group-id { \string }? + & (xml-id, + attribute anim:id { NCName }?)? +presentation-event-listener = + element presentation:event-listener { + presentation-event-listener-attlist, presentation-sound? + } +presentation-event-listener-attlist = + attribute script:event-name { \string } + & attribute presentation:action { + "none" + | "previous-page" + | "next-page" + | "first-page" + | "last-page" + | "hide" + | "stop" + | "execute" + | "show" + | "verb" + | "fade-out" + | "sound" + | "last-visited-page" + } + & attribute presentation:effect { presentationEffects }? + & attribute presentation:direction { presentationEffectDirections }? + & attribute presentation:speed { presentationSpeeds }? + & attribute presentation:start-scale { percent }? + & (attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:show { "embed" }?, + attribute xlink:actuate { "onRequest" }?)? + & attribute presentation:verb { nonNegativeInteger }? +presentation-decls = presentation-decl* +presentation-decl = + element presentation:header-decl { + presentation-header-decl-attlist, text + } + | element presentation:footer-decl { + presentation-footer-decl-attlist, text + } + | element presentation:date-time-decl { + presentation-date-time-decl-attlist, text + } +presentation-header-decl-attlist = + attribute presentation:name { \string } +presentation-footer-decl-attlist = + attribute presentation:name { \string } +presentation-date-time-decl-attlist = + attribute presentation:name { \string } + & attribute presentation:source { "fixed" | "current-date" } + & attribute style:data-style-name { styleNameRef }? +presentation-settings = + element presentation:settings { + presentation-settings-attlist, presentation-show* + }? +presentation-settings-attlist = + attribute presentation:start-page { \string }? + & attribute presentation:show { \string }? + & attribute presentation:full-screen { boolean }? + & attribute presentation:endless { boolean }? + & attribute presentation:pause { duration }? + & attribute presentation:show-logo { boolean }? + & attribute presentation:force-manual { boolean }? + & attribute presentation:mouse-visible { boolean }? + & attribute presentation:mouse-as-pen { boolean }? + & attribute presentation:start-with-navigator { boolean }? + & attribute presentation:animations { "enabled" | "disabled" }? + & attribute presentation:transition-on-click { + "enabled" | "disabled" + }? + & attribute presentation:stay-on-top { boolean }? + & attribute presentation:show-end-of-presentation-slide { boolean }? +presentation-show = + element presentation:show { presentation-show-attlist, empty } +presentation-show-attlist = + attribute presentation:name { \string } + & attribute presentation:pages { \string } +chart-chart = + element chart:chart { + chart-chart-attlist, + chart-title?, + chart-subtitle?, + chart-footer?, + chart-legend?, + chart-plot-area, + table-table? + } +chart-chart-attlist = + attribute chart:class { namespacedToken } + & common-draw-size-attlist + & attribute chart:column-mapping { \string }? + & attribute chart:row-mapping { \string }? + & attribute chart:style-name { styleNameRef }? + & (attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI })? + & xml-id? +chart-title = element chart:title { chart-title-attlist, text-p? } +chart-title-attlist = + attribute table:cell-range { cellRangeAddressList }? + & common-draw-position-attlist + & attribute chart:style-name { styleNameRef }? +chart-subtitle = element chart:subtitle { chart-title-attlist, text-p? } +chart-footer = element chart:footer { chart-title-attlist, text-p? } +chart-legend = element chart:legend { chart-legend-attlist, text-p? } +chart-legend-attlist = + ((attribute chart:legend-position { + "start" | "end" | "top" | "bottom" + }, + attribute chart:legend-align { "start" | "center" | "end" }?) + | attribute chart:legend-position { + "top-start" | "bottom-start" | "top-end" | "bottom-end" + } + | empty) + & common-draw-position-attlist + & (attribute style:legend-expansion { "wide" | "high" | "balanced" } + | (attribute style:legend-expansion { "custom" }, + attribute style:legend-expansion-aspect-ratio { double }) + | empty) + & attribute chart:style-name { styleNameRef }? +chart-plot-area = + element chart:plot-area { + chart-plot-area-attlist, + dr3d-light*, + chart-axis*, + chart-series*, + chart-stock-gain-marker?, + chart-stock-loss-marker?, + chart-stock-range-line?, + chart-wall?, + chart-floor? + } +chart-plot-area-attlist = + common-draw-position-attlist + & common-draw-size-attlist + & attribute chart:style-name { styleNameRef }? + & attribute table:cell-range-address { cellRangeAddressList }? + & attribute chart:data-source-has-labels { + "none" | "row" | "column" | "both" + }? + & dr3d-scene-attlist + & common-dr3d-transform-attlist + & xml-id? +chart-wall = element chart:wall { chart-wall-attlist, empty } +chart-wall-attlist = + attribute svg:width { length }? + & attribute chart:style-name { styleNameRef }? +chart-floor = element chart:floor { chart-floor-attlist, empty } +chart-floor-attlist = + attribute svg:width { length }? + & attribute chart:style-name { styleNameRef }? +chart-axis = + element chart:axis { + chart-axis-attlist, chart-title?, chart-categories?, chart-grid* + } +chart-axis-attlist = + attribute chart:dimension { chart-dimension } + & attribute chart:name { \string }? + & attribute chart:style-name { styleNameRef }? +chart-dimension = "x" | "y" | "z" +chart-categories = + element chart:categories { + attribute table:cell-range-address { cellRangeAddressList }? + } +chart-grid = element chart:grid { chart-grid-attlist } +chart-grid-attlist = + attribute chart:class { "major" | "minor" }? + & attribute chart:style-name { styleNameRef }? +chart-series = + element chart:series { + chart-series-attlist, + chart-domain*, + chart-mean-value?, + chart-regression-curve*, + chart-error-indicator*, + chart-data-point*, + chart-data-label? + } +chart-series-attlist = + attribute chart:values-cell-range-address { cellRangeAddressList }? + & attribute chart:label-cell-address { cellRangeAddressList }? + & attribute chart:class { namespacedToken }? + & attribute chart:attached-axis { \string }? + & attribute chart:style-name { styleNameRef }? + & xml-id? +chart-domain = + element chart:domain { + attribute table:cell-range-address { cellRangeAddressList }? + } +chart-data-point = + element chart:data-point { + chart-data-point-attlist, chart-data-label? + } +chart-data-point-attlist = + attribute chart:repeated { positiveInteger }? + & attribute chart:style-name { styleNameRef }? + & xml-id? +chart-data-label = + element chart:data-label { chart-data-label-attlist, text-p? } +chart-data-label-attlist = + common-draw-position-attlist + & attribute chart:style-name { styleNameRef }? +chart-mean-value = + element chart:mean-value { chart-mean-value-attlist, empty } +chart-mean-value-attlist = attribute chart:style-name { styleNameRef }? +chart-error-indicator = + element chart:error-indicator { chart-error-indicator-attlist, empty } +chart-error-indicator-attlist = + attribute chart:style-name { styleNameRef }? + & attribute chart:dimension { chart-dimension } +chart-regression-curve = + element chart:regression-curve { + chart-regression-curve-attlist, chart-equation? + } +chart-regression-curve-attlist = + attribute chart:style-name { styleNameRef }? +chart-equation = + element chart:equation { chart-equation-attlist, text-p? } +chart-equation-attlist = + attribute chart:automatic-content { boolean }? + & attribute chart:display-r-square { boolean }? + & attribute chart:display-equation { boolean }? + & common-draw-position-attlist + & attribute chart:style-name { styleNameRef }? +chart-stock-gain-marker = + element chart:stock-gain-marker { common-stock-marker-attlist } +chart-stock-loss-marker = + element chart:stock-loss-marker { common-stock-marker-attlist } +chart-stock-range-line = + element chart:stock-range-line { common-stock-marker-attlist } +common-stock-marker-attlist = + attribute chart:style-name { styleNameRef }? +office-database = + element office:database { + db-data-source, + db-forms?, + db-reports?, + db-queries?, + db-table-presentations?, + db-schema-definition? + } +db-data-source = + element db:data-source { + db-data-source-attlist, + db-connection-data, + db-driver-settings?, + db-application-connection-settings? + } +db-data-source-attlist = empty +db-connection-data = + element db:connection-data { + db-connection-data-attlist, + (db-database-description | db-connection-resource), + db-login? + } +db-connection-data-attlist = empty +db-database-description = + element db:database-description { + db-database-description-attlist, + (db-file-based-database | db-server-database) + } +db-database-description-attlist = empty +db-file-based-database = + element db:file-based-database { db-file-based-database-attlist } +db-file-based-database-attlist = + attribute xlink:type { "simple" } + & attribute xlink:href { anyIRI } + & attribute db:media-type { \string } + & attribute db:extension { \string }? +db-server-database = + element db:server-database { db-server-database-attlist, empty } +db-server-database-attlist = + attribute db:type { namespacedToken } + & (db-host-and-port | db-local-socket-name) + & attribute db:database-name { \string }? +db-host-and-port = + attribute db:hostname { \string }, + attribute db:port { positiveInteger }? +db-local-socket-name = attribute db:local-socket { \string }? +db-connection-resource = + element db:connection-resource { + db-connection-resource-attlist, empty + } +db-connection-resource-attlist = + attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:show { "none" }?, + attribute xlink:actuate { "onRequest" }? +db-login = element db:login { db-login-attlist, empty } +db-login-attlist = + (attribute db:user-name { \string } + | attribute db:use-system-user { boolean })? + & attribute db:is-password-required { boolean }? + & attribute db:login-timeout { positiveInteger }? +db-driver-settings = + element db:driver-settings { + db-driver-settings-attlist, + db-auto-increment?, + db-delimiter?, + db-character-set?, + db-table-settings? + } +db-driver-settings-attlist = + db-show-deleted + & attribute db:system-driver-settings { \string }? + & attribute db:base-dn { \string }? + & db-is-first-row-header-line + & attribute db:parameter-name-substitution { boolean }? +db-show-deleted = attribute db:show-deleted { boolean }? +db-is-first-row-header-line = + attribute db:is-first-row-header-line { boolean }? +db-auto-increment = + element db:auto-increment { db-auto-increment-attlist, empty } +db-auto-increment-attlist = + attribute db:additional-column-statement { \string }? + & attribute db:row-retrieving-statement { \string }? +db-delimiter = element db:delimiter { db-delimiter-attlist, empty } +db-delimiter-attlist = + attribute db:field { \string }? + & attribute db:string { \string }? + & attribute db:decimal { \string }? + & attribute db:thousand { \string }? +db-character-set = + element db:character-set { db-character-set-attlist, empty } +db-character-set-attlist = attribute db:encoding { textEncoding }? +db-table-settings = element db:table-settings { db-table-setting* } +db-table-setting = + element db:table-setting { + db-table-setting-attlist, db-delimiter?, db-character-set?, empty + } +db-table-setting-attlist = db-is-first-row-header-line, db-show-deleted +db-application-connection-settings = + element db:application-connection-settings { + db-application-connection-settings-attlist, + db-table-filter?, + db-table-type-filter?, + db-data-source-settings? + } +db-application-connection-settings-attlist = + attribute db:is-table-name-length-limited { boolean }? + & attribute db:enable-sql92-check { boolean }? + & attribute db:append-table-alias-name { boolean }? + & attribute db:ignore-driver-privileges { boolean }? + & attribute db:boolean-comparison-mode { + "equal-integer" + | "is-boolean" + | "equal-boolean" + | "equal-use-only-zero" + }? + & attribute db:use-catalog { boolean }? + & attribute db:max-row-count { integer }? + & attribute db:suppress-version-columns { boolean }? +db-table-filter = + element db:table-filter { + db-table-filter-attlist, + db-table-include-filter?, + db-table-exclude-filter? + } +db-table-filter-attlist = empty +db-table-include-filter = + element db:table-include-filter { + db-table-include-filter-attlist, db-table-filter-pattern+ + } +db-table-include-filter-attlist = empty +db-table-exclude-filter = + element db:table-exclude-filter { + db-table-exclude-filter-attlist, db-table-filter-pattern+ + } +db-table-exclude-filter-attlist = empty +db-table-filter-pattern = + element db:table-filter-pattern { + db-table-filter-pattern-attlist, \string + } +db-table-filter-pattern-attlist = empty +db-table-type-filter = + element db:table-type-filter { + db-table-type-filter-attlist, db-table-type* + } +db-table-type-filter-attlist = empty +db-table-type = element db:table-type { db-table-type-attlist, \string } +db-table-type-attlist = empty +db-data-source-settings = + element db:data-source-settings { + db-data-source-settings-attlist, db-data-source-setting+ + } +db-data-source-settings-attlist = empty +db-data-source-setting = + element db:data-source-setting { + db-data-source-setting-attlist, db-data-source-setting-value+ + } +db-data-source-setting-attlist = + attribute db:data-source-setting-is-list { boolean }? + & attribute db:data-source-setting-name { \string } + & attribute db:data-source-setting-type { + db-data-source-setting-types + } +db-data-source-setting-types = + "boolean" | "short" | "int" | "long" | "double" | "string" +db-data-source-setting-value = + element db:data-source-setting-value { + db-data-source-setting-value-attlist, \string + } +db-data-source-setting-value-attlist = empty +db-forms = + element db:forms { + db-forms-attlist, (db-component | db-component-collection)* + } +db-forms-attlist = empty +db-reports = + element db:reports { + db-reports-attlist, (db-component | db-component-collection)* + } +db-reports-attlist = empty +db-component-collection = + element db:component-collection { + db-component-collection-attlist, + common-db-object-name, + common-db-object-title, + common-db-object-description, + (db-component | db-component-collection)* + } +db-component-collection-attlist = empty +db-component = + element db:component { + db-component-attlist, + common-db-object-name, + common-db-object-title, + common-db-object-description, + (office-document | math-math)? + } +db-component-attlist = + (attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:show { "none" }?, + attribute xlink:actuate { "onRequest" }?)? + & attribute db:as-template { boolean }? +db-queries = + element db:queries { + db-queries-attlist, (db-query | db-query-collection)* + } +db-queries-attlist = empty +db-query-collection = + element db:query-collection { + db-query-collection-attlist, + common-db-object-name, + common-db-object-title, + common-db-object-description, + (db-query | db-query-collection)* + } +db-query-collection-attlist = empty +db-query = + element db:query { + db-query-attlist, + common-db-object-name, + common-db-object-title, + common-db-object-description, + common-db-table-style-name, + db-order-statement?, + db-filter-statement?, + db-columns?, + db-update-table? + } +db-query-attlist = + attribute db:command { \string } + & attribute db:escape-processing { boolean }? +db-order-statement = + element db:order-statement { db-command, db-apply-command, empty } +db-filter-statement = + element db:filter-statement { db-command, db-apply-command, empty } +db-update-table = + element db:update-table { common-db-table-name-attlist } +db-table-presentations = + element db:table-representations { + db-table-presentations-attlist, db-table-presentation* + } +db-table-presentations-attlist = empty +db-table-presentation = + element db:table-representation { + db-table-presentation-attlist, + common-db-table-name-attlist, + common-db-object-title, + common-db-object-description, + common-db-table-style-name, + db-order-statement?, + db-filter-statement?, + db-columns? + } +db-table-presentation-attlist = empty +db-columns = element db:columns { db-columns-attlist, db-column+ } +db-columns-attlist = empty +db-column = + element db:column { + db-column-attlist, + common-db-object-name, + common-db-object-title, + common-db-object-description, + common-db-default-value + } +db-column-attlist = + attribute db:visible { boolean }? + & attribute db:style-name { styleNameRef }? + & attribute db:default-cell-style-name { styleNameRef }? +db-command = attribute db:command { \string } +db-apply-command = attribute db:apply-command { boolean }? +common-db-table-name-attlist = + attribute db:name { \string } + & attribute db:catalog-name { \string }? + & attribute db:schema-name { \string }? +common-db-object-name = attribute db:name { \string } +common-db-object-title = attribute db:title { \string }? +common-db-object-description = attribute db:description { \string }? +common-db-table-style-name = + attribute db:style-name { styleNameRef }? + & attribute db:default-row-style-name { styleNameRef }? +common-db-default-value = common-value-and-type-attlist? +db-schema-definition = + element db:schema-definition { + db-schema-definition-attlist, db-table-definitions + } +db-schema-definition-attlist = empty +db-table-definitions = + element db:table-definitions { + db-table-definitions-attlist, db-table-definition* + } +db-table-definitions-attlist = empty +db-table-definition = + element db:table-definition { + common-db-table-name-attlist, + db-table-definition-attlist, + db-column-definitions, + db-keys?, + db-indices? + } +db-table-definition-attlist = attribute db:type { \string }? +db-column-definitions = + element db:column-definitions { + db-column-definitions-attlist, db-column-definition+ + } +db-column-definitions-attlist = empty +db-column-definition = + element db:column-definition { + db-column-definition-attlist, common-db-default-value + } +db-column-definition-attlist = + attribute db:name { \string } + & attribute db:data-type { db-data-types }? + & attribute db:type-name { \string }? + & attribute db:precision { positiveInteger }? + & attribute db:scale { positiveInteger }? + & attribute db:is-nullable { "no-nulls" | "nullable" }? + & attribute db:is-empty-allowed { boolean }? + & attribute db:is-autoincrement { boolean }? +db-data-types = + "bit" + | "boolean" + | "tinyint" + | "smallint" + | "integer" + | "bigint" + | "float" + | "real" + | "double" + | "numeric" + | "decimal" + | "char" + | "varchar" + | "longvarchar" + | "date" + | "time" + | "timestmp" + | "binary" + | "varbinary" + | "longvarbinary" + | "sqlnull" + | "other" + | "object" + | "distinct" + | "struct" + | "array" + | "blob" + | "clob" + | "ref" +db-keys = element db:keys { db-keys-attlist, db-key+ } +db-keys-attlist = empty +db-key = element db:key { db-key-attlist, db-key-columns+ } +db-key-attlist = + attribute db:name { \string }? + & attribute db:type { "primary" | "unique" | "foreign" } + & attribute db:referenced-table-name { \string }? + & attribute db:update-rule { + "cascade" | "restrict" | "set-null" | "no-action" | "set-default" + }? + & attribute db:delete-rule { + "cascade" | "restrict" | "set-null" | "no-action" | "set-default" + }? +db-key-columns = + element db:key-columns { db-key-columns-attlist, db-key-column+ } +db-key-columns-attlist = empty +db-key-column = element db:key-column { db-key-column-attlist, empty } +db-key-column-attlist = + attribute db:name { \string }? + & attribute db:related-column-name { \string }? +db-indices = element db:indices { db-indices-attlist, db-index+ } +db-indices-attlist = empty +db-index = element db:index { db-index-attlist, db-index-columns+ } +db-index-attlist = + attribute db:name { \string } + & attribute db:catalog-name { \string }? + & attribute db:is-unique { boolean }? + & attribute db:is-clustered { boolean }? +db-index-columns = element db:index-columns { db-index-column+ } +db-index-column = + element db:index-column { db-index-column-attlist, empty } +db-index-column-attlist = + attribute db:name { \string } + & attribute db:is-ascending { boolean }? +office-forms = + element office:forms { + office-forms-attlist, (form-form | xforms-model)* + }? +office-forms-attlist = + attribute form:automatic-focus { boolean }? + & attribute form:apply-design-mode { boolean }? +form-form = + element form:form { + common-form-control-attlist, + form-form-attlist, + form-properties?, + office-event-listeners?, + (controls | form-form)*, + form-connection-resource? + } +form-form-attlist = + (attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:actuate { "onRequest" }?)? + & attribute office:target-frame { targetFrameName }? + & attribute form:method { "get" | "post" | \string }? + & attribute form:enctype { \string }? + & attribute form:allow-deletes { boolean }? + & attribute form:allow-inserts { boolean }? + & attribute form:allow-updates { boolean }? + & attribute form:apply-filter { boolean }? + & attribute form:command-type { "table" | "query" | "command" }? + & attribute form:command { \string }? + & attribute form:datasource { anyIRI | \string }? + & attribute form:master-fields { \string }? + & attribute form:detail-fields { \string }? + & attribute form:escape-processing { boolean }? + & attribute form:filter { \string }? + & attribute form:ignore-result { boolean }? + & attribute form:navigation-mode { navigation }? + & attribute form:order { \string }? + & attribute form:tab-cycle { tab-cycles }? +navigation = "none" | "current" | "parent" +tab-cycles = "records" | "current" | "page" +form-connection-resource = + element form:connection-resource { + attribute xlink:href { anyIRI }, + empty + } +xforms-model = element xforms:model { anyAttListOrElements } +column-controls = + element form:text { form-text-attlist, common-form-control-content } + | element form:textarea { + form-textarea-attlist, common-form-control-content, text-p* + } + | element form:formatted-text { + form-formatted-text-attlist, common-form-control-content + } + | element form:number { + form-number-attlist, + common-numeric-control-attlist, + common-form-control-content, + common-linked-cell, + common-spin-button, + common-repeat, + common-delay-for-repeat + } + | element form:date { + form-date-attlist, + common-numeric-control-attlist, + common-form-control-content, + common-linked-cell, + common-spin-button, + common-repeat, + common-delay-for-repeat + } + | element form:time { + form-time-attlist, + common-numeric-control-attlist, + common-form-control-content, + common-linked-cell, + common-spin-button, + common-repeat, + common-delay-for-repeat + } + | element form:combobox { + form-combobox-attlist, common-form-control-content, form-item* + } + | element form:listbox { + form-listbox-attlist, common-form-control-content, form-option* + } + | element form:checkbox { + form-checkbox-attlist, common-form-control-content + } +controls = + column-controls + | element form:password { + form-password-attlist, common-form-control-content + } + | element form:file { form-file-attlist, common-form-control-content } + | element form:fixed-text { + form-fixed-text-attlist, common-form-control-content + } + | element form:button { + form-button-attlist, common-form-control-content + } + | element form:image { + form-image-attlist, common-form-control-content + } + | element form:radio { + form-radio-attlist, common-form-control-content + } + | element form:frame { + form-frame-attlist, common-form-control-content + } + | element form:image-frame { + form-image-frame-attlist, common-form-control-content + } + | element form:hidden { + form-hidden-attlist, common-form-control-content + } + | element form:grid { + form-grid-attlist, common-form-control-content, form-column* + } + | element form:value-range { + form-value-range-attlist, common-form-control-content + } + | element form:generic-control { + form-generic-control-attlist, common-form-control-content + } +form-text-attlist = + form-control-attlist, + common-current-value-attlist, + common-disabled-attlist, + common-maxlength-attlist, + common-printable-attlist, + common-readonly-attlist, + common-tab-attlist, + common-title-attlist, + common-value-attlist, + common-convert-empty-attlist, + common-data-field-attlist, + common-linked-cell +form-control-attlist = + common-form-control-attlist, + common-control-id-attlist, + xforms-bind-attlist +common-form-control-content = form-properties?, office-event-listeners? +form-textarea-attlist = + form-control-attlist, + common-current-value-attlist, + common-disabled-attlist, + common-maxlength-attlist, + common-printable-attlist, + common-readonly-attlist, + common-tab-attlist, + common-title-attlist, + common-value-attlist, + common-convert-empty-attlist, + common-data-field-attlist, + common-linked-cell +form-password-attlist = + form-control-attlist + & common-disabled-attlist + & common-maxlength-attlist + & common-printable-attlist + & common-tab-attlist + & common-title-attlist + & common-value-attlist + & common-convert-empty-attlist + & common-linked-cell + & attribute form:echo-char { character }? +form-file-attlist = + form-control-attlist, + common-current-value-attlist, + common-disabled-attlist, + common-maxlength-attlist, + common-printable-attlist, + common-readonly-attlist, + common-tab-attlist, + common-title-attlist, + common-value-attlist, + common-linked-cell +form-formatted-text-attlist = + form-control-attlist + & common-current-value-attlist + & common-disabled-attlist + & common-maxlength-attlist + & common-printable-attlist + & common-readonly-attlist + & common-tab-attlist + & common-title-attlist + & common-value-attlist + & common-convert-empty-attlist + & common-data-field-attlist + & common-linked-cell + & common-spin-button + & common-repeat + & common-delay-for-repeat + & attribute form:max-value { \string }? + & attribute form:min-value { \string }? + & attribute form:validation { boolean }? +common-numeric-control-attlist = + form-control-attlist, + common-disabled-attlist, + common-maxlength-attlist, + common-printable-attlist, + common-readonly-attlist, + common-tab-attlist, + common-title-attlist, + common-convert-empty-attlist, + common-data-field-attlist +form-number-attlist = + attribute form:value { double }? + & attribute form:current-value { double }? + & attribute form:min-value { double }? + & attribute form:max-value { double }? +form-date-attlist = + attribute form:value { date }? + & attribute form:current-value { date }? + & attribute form:min-value { date }? + & attribute form:max-value { date }? +form-time-attlist = + attribute form:value { time }? + & attribute form:current-value { time }? + & attribute form:min-value { time }? + & attribute form:max-value { time }? +form-fixed-text-attlist = + form-control-attlist + & for + & common-disabled-attlist + & label + & common-printable-attlist + & common-title-attlist + & attribute form:multi-line { boolean }? +form-combobox-attlist = + form-control-attlist + & common-current-value-attlist + & common-disabled-attlist + & dropdown + & common-maxlength-attlist + & common-printable-attlist + & common-readonly-attlist + & size + & common-tab-attlist + & common-title-attlist + & common-value-attlist + & common-convert-empty-attlist + & common-data-field-attlist + & list-source + & list-source-type + & common-linked-cell + & common-source-cell-range + & attribute form:auto-complete { boolean }? +form-item = element form:item { form-item-attlist, text } +form-item-attlist = label +form-listbox-attlist = + form-control-attlist + & common-disabled-attlist + & dropdown + & common-printable-attlist + & size + & common-tab-attlist + & common-title-attlist + & bound-column + & common-data-field-attlist + & list-source + & list-source-type + & common-linked-cell + & list-linkage-type + & common-source-cell-range + & attribute form:multiple { boolean }? + & attribute form:xforms-list-source { \string }? +list-linkage-type = + attribute form:list-linkage-type { + "selection" | "selection-indices" + }? +form-option = element form:option { form-option-attlist, text } +form-option-attlist = + current-selected, selected, label, common-value-attlist +form-button-attlist = + form-control-attlist + & button-type + & common-disabled-attlist + & label + & image-data + & common-printable-attlist + & common-tab-attlist + & target-frame + & target-location + & common-title-attlist + & common-value-attlist + & common-form-relative-image-position-attlist + & common-repeat + & common-delay-for-repeat + & attribute form:default-button { boolean }? + & attribute form:toggle { boolean }? + & attribute form:focus-on-click { boolean }? + & attribute form:xforms-submission { \string }? +form-image-attlist = + form-control-attlist, + button-type, + common-disabled-attlist, + image-data, + common-printable-attlist, + common-tab-attlist, + target-frame, + target-location, + common-title-attlist, + common-value-attlist +form-checkbox-attlist = + form-control-attlist + & common-disabled-attlist + & label + & common-printable-attlist + & common-tab-attlist + & common-title-attlist + & common-value-attlist + & common-data-field-attlist + & common-form-visual-effect-attlist + & common-form-relative-image-position-attlist + & common-linked-cell + & attribute form:current-state { states }? + & attribute form:is-tristate { boolean }? + & attribute form:state { states }? +states = "unchecked" | "checked" | "unknown" +form-radio-attlist = + form-control-attlist, + current-selected, + common-disabled-attlist, + label, + common-printable-attlist, + selected, + common-tab-attlist, + common-title-attlist, + common-value-attlist, + common-data-field-attlist, + common-form-visual-effect-attlist, + common-form-relative-image-position-attlist, + common-linked-cell +form-frame-attlist = + form-control-attlist, + common-disabled-attlist, + for, + label, + common-printable-attlist, + common-title-attlist +form-image-frame-attlist = + form-control-attlist, + common-disabled-attlist, + image-data, + common-printable-attlist, + common-readonly-attlist, + common-title-attlist, + common-data-field-attlist +form-hidden-attlist = form-control-attlist, common-value-attlist +form-grid-attlist = + form-control-attlist, + common-disabled-attlist, + common-printable-attlist, + common-tab-attlist, + common-title-attlist +form-column = + element form:column { form-column-attlist, column-controls+ } +form-column-attlist = + common-form-control-attlist, label, text-style-name +text-style-name = attribute form:text-style-name { styleNameRef }? +form-value-range-attlist = + form-control-attlist + & common-disabled-attlist + & common-printable-attlist + & common-tab-attlist + & common-title-attlist + & common-value-attlist + & common-linked-cell + & common-repeat + & common-delay-for-repeat + & attribute form:max-value { integer }? + & attribute form:min-value { integer }? + & attribute form:step-size { positiveInteger }? + & attribute form:page-step-size { positiveInteger }? + & attribute form:orientation { "horizontal" | "vertical" }? +form-generic-control-attlist = form-control-attlist +common-form-control-attlist = + attribute form:name { \string }? + & attribute form:control-implementation { namespacedToken }? +xforms-bind-attlist = attribute xforms:bind { \string }? +types = "submit" | "reset" | "push" | "url" +button-type = attribute form:button-type { types }? +common-control-id-attlist = + xml-id, + attribute form:id { NCName }? +current-selected = attribute form:current-selected { boolean }? +common-value-attlist = attribute form:value { \string }? +common-current-value-attlist = attribute form:current-value { \string }? +common-disabled-attlist = attribute form:disabled { boolean }? +dropdown = attribute form:dropdown { boolean }? +for = attribute form:for { \string }? +image-data = attribute form:image-data { anyIRI }? +label = attribute form:label { \string }? +common-maxlength-attlist = + attribute form:max-length { nonNegativeInteger }? +common-printable-attlist = attribute form:printable { boolean }? +common-readonly-attlist = attribute form:readonly { boolean }? +selected = attribute form:selected { boolean }? +size = attribute form:size { nonNegativeInteger }? +common-tab-attlist = + attribute form:tab-index { nonNegativeInteger }? + & attribute form:tab-stop { boolean }? +target-frame = attribute office:target-frame { targetFrameName }? +target-location = attribute xlink:href { anyIRI }? +common-title-attlist = attribute form:title { \string }? +common-form-visual-effect-attlist = + attribute form:visual-effect { "flat" | "3d" }? +common-form-relative-image-position-attlist = + attribute form:image-position { "center" }? + | (attribute form:image-position { + "start" | "end" | "top" | "bottom" + }, + attribute form:image-align { "start" | "center" | "end" }?) +bound-column = attribute form:bound-column { \string }? +common-convert-empty-attlist = + attribute form:convert-empty-to-null { boolean }? +common-data-field-attlist = attribute form:data-field { \string }? +list-source = attribute form:list-source { \string }? +list-source-type = + attribute form:list-source-type { + "table" + | "query" + | "sql" + | "sql-pass-through" + | "value-list" + | "table-fields" + }? +common-linked-cell = + attribute form:linked-cell { cellAddress | \string }? +common-source-cell-range = + attribute form:source-cell-range { cellRangeAddress | \string }? +common-spin-button = attribute form:spin-button { boolean }? +common-repeat = attribute form:repeat { boolean }? +common-delay-for-repeat = attribute form:delay-for-repeat { duration }? +form-properties = element form:properties { form-property+ } +form-property = + element form:property { + form-property-name, form-property-value-and-type-attlist + } + | element form:list-property { + form-property-name, form-property-type-and-value-list + } +form-property-name = attribute form:property-name { \string } +form-property-value-and-type-attlist = + common-value-and-type-attlist + | attribute office:value-type { "void" } +form-property-type-and-value-list = + (attribute office:value-type { "float" }, + element form:list-value { + attribute office:value { double } + }*) + | (attribute office:value-type { "percentage" }, + element form:list-value { + attribute office:value { double } + }*) + | (attribute office:value-type { "currency" }, + element form:list-value { + attribute office:value { double }, + attribute office:currency { \string }? + }*) + | (attribute office:value-type { "date" }, + element form:list-value { + attribute office:date-value { dateOrDateTime } + }*) + | (attribute office:value-type { "time" }, + element form:list-value { + attribute office:time-value { duration } + }*) + | (attribute office:value-type { "boolean" }, + element form:list-value { + attribute office:boolean-value { boolean } + }*) + | (attribute office:value-type { "string" }, + element form:list-value { + attribute office:string-value { \string } + }*) + | attribute office:value-type { "void" } +office-annotation = + element office:annotation { + office-annotation-attlist, + draw-caption-attlist, + common-draw-position-attlist, + common-draw-size-attlist, + common-draw-shape-with-text-and-styles-attlist, + dc-creator?, + dc-date?, + meta-date-string?, + (text-p | text-list)* + } +office-annotation-end = + element office:annotation-end { office-annotation-end-attlist } +office-annotation-attlist = + attribute office:display { boolean }? + & common-office-annotation-name-attlist? +office-annotation-end-attlist = common-office-annotation-name-attlist +common-office-annotation-name-attlist = + attribute office:name { \string } +meta-date-string = element meta:date-string { \string } +common-num-format-prefix-suffix-attlist = + attribute style:num-prefix { \string }?, + attribute style:num-suffix { \string }? +common-num-format-attlist = + attribute style:num-format { "1" | "i" | "I" | \string | empty } + | (attribute style:num-format { "a" | "A" }, + style-num-letter-sync-attlist) + | empty +style-num-letter-sync-attlist = + attribute style:num-letter-sync { boolean }? +office-change-info = + element office:change-info { dc-creator, dc-date, text-p* } +office-event-listeners = + element office:event-listeners { + (script-event-listener | presentation-event-listener)* + } +script-event-listener = + element script:event-listener { script-event-listener-attlist, empty } +script-event-listener-attlist = + attribute script:event-name { \string } + & attribute script:language { \string } + & (attribute script:macro-name { \string } + | (attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:actuate { "onRequest" }?)) +math-math = element math:math { mathMarkup } +[ + dc:description [ + "To avoid inclusion of the complete MathML schema, anything is allowed within a math:math top-level element" + ] +] +mathMarkup = + (attribute * { text } + | text + | element * { mathMarkup })* +text-dde-connection-decl = + element text:dde-connection-decl { + text-dde-connection-decl-attlist, common-dde-connection-decl-attlist + } +text-dde-connection-decl-attlist = attribute office:name { \string } +common-dde-connection-decl-attlist = + attribute office:dde-application { \string } + & attribute office:dde-topic { \string } + & attribute office:dde-item { \string } + & attribute office:automatic-update { boolean }? +table-dde-link = + element table:dde-link { office-dde-source, table-table } +office-dde-source = + element office:dde-source { + office-dde-source-attlist, common-dde-connection-decl-attlist + } +office-dde-source-attlist = + attribute office:name { \string }? + & attribute office:conversion-mode { + "into-default-style-data-style" + | "into-english-number" + | "keep-text" + }? +animation-element = + element anim:animate { + common-anim-target-attlist, + common-anim-named-target-attlist, + common-anim-values-attlist, + common-anim-spline-mode-attlist, + common-spline-anim-value-attlist, + common-timing-attlist, + common-anim-add-accum-attlist + } + | element anim:set { + common-anim-target-attlist, + common-anim-named-target-attlist, + common-anim-set-values-attlist, + common-timing-attlist, + common-anim-add-accum-attlist + } + | element anim:animateMotion { + anim-animate-motion-attlist, + common-anim-target-attlist, + common-anim-named-target-attlist, + common-anim-add-accum-attlist, + common-anim-values-attlist, + common-timing-attlist, + common-spline-anim-value-attlist + } + | element anim:animateColor { + common-anim-target-attlist, + common-anim-named-target-attlist, + common-anim-add-accum-attlist, + common-anim-values-attlist, + common-anim-spline-mode-attlist, + common-spline-anim-value-attlist, + anim-animate-color-attlist, + common-timing-attlist + } + | element anim:animateTransform { + common-anim-target-attlist, + common-anim-named-target-attlist, + common-anim-add-accum-attlist, + common-anim-values-attlist, + anim-animate-transform-attlist, + common-timing-attlist + } + | element anim:transitionFilter { + common-anim-target-attlist, + common-anim-add-accum-attlist, + common-anim-values-attlist, + common-anim-spline-mode-attlist, + anim-transition-filter-attlist, + common-timing-attlist + } + | element anim:par { + common-anim-attlist, + common-timing-attlist, + common-endsync-timing-attlist, + animation-element* + } + | element anim:seq { + common-anim-attlist, + common-endsync-timing-attlist, + common-timing-attlist, + animation-element* + } + | element anim:iterate { + common-anim-attlist, + anim-iterate-attlist, + common-timing-attlist, + common-endsync-timing-attlist, + animation-element* + } + | element anim:audio { + common-anim-attlist, + anim-audio-attlist, + common-basic-timing-attlist + } + | element anim:command { + common-anim-attlist, + anim-command-attlist, + common-begin-end-timing-attlist, + common-anim-target-attlist, + element anim:param { + attribute anim:name { \string }, + attribute anim:value { \string } + }* + } +anim-animate-motion-attlist = + attribute svg:path { pathData }? + & attribute svg:origin { \string }? + & attribute smil:calcMode { + "discrete" | "linear" | "paced" | "spline" + }? +anim-animate-color-attlist = + attribute anim:color-interpolation { "rgb" | "hsl" }? + & attribute anim:color-interpolation-direction { + "clockwise" | "counter-clockwise" + }? +anim-animate-transform-attlist = + attribute svg:type { + "translate" | "scale" | "rotate" | "skewX" | "skewY" + } +anim-transition-filter-attlist = + attribute smil:type { \string } + & attribute smil:subtype { \string }? + & attribute smil:direction { "forward" | "reverse" }? + & attribute smil:fadeColor { color }? + & attribute smil:mode { "in" | "out" }? +common-anim-target-attlist = + attribute smil:targetElement { IDREF }? + & attribute anim:sub-item { \string }? +common-anim-named-target-attlist = + attribute smil:attributeName { \string } +common-anim-values-attlist = + attribute smil:values { \string }? + & attribute anim:formula { \string }? + & common-anim-set-values-attlist + & attribute smil:from { \string }? + & attribute smil:by { \string }? +common-anim-spline-mode-attlist = + attribute smil:calcMode { + "discrete" | "linear" | "paced" | "spline" + }? +common-spline-anim-value-attlist = + attribute smil:keyTimes { \string }? + & attribute smil:keySplines { \string }? +common-anim-add-accum-attlist = + attribute smil:accumulate { "none" | "sum" }? + & attribute smil:additive { "replace" | "sum" }? +common-anim-set-values-attlist = attribute smil:to { \string }? +common-begin-end-timing-attlist = + attribute smil:begin { \string }? + & attribute smil:end { \string }? +common-dur-timing-attlist = attribute smil:dur { \string }? +common-endsync-timing-attlist = + attribute smil:endsync { "first" | "last" | "all" | "media" | IDREF }? +common-repeat-timing-attlist = + attribute smil:repeatDur { \string }?, + attribute smil:repeatCount { nonNegativeDecimal | "indefinite" }? +nonNegativeDecimal = xsd:decimal { minInclusive = "0.0" } +common-fill-timing-attlist = + attribute smil:fill { + "remove" | "freeze" | "hold" | "auto" | "default" | "transition" + }? +common-fill-default-attlist = + attribute smil:fillDefault { + "remove" | "freeze" | "hold" | "transition" | "auto" | "inherit" + }? +common-restart-timing-attlist = + attribute smil:restart { + "never" | "always" | "whenNotActive" | "default" + }? +common-restart-default-attlist = + attribute smil:restartDefault { + "never" | "always" | "whenNotActive" | "inherit" + }? +common-time-manip-attlist = + attribute smil:accelerate { zeroToOneDecimal }? + & attribute smil:decelerate { zeroToOneDecimal }? + & attribute smil:autoReverse { boolean }? +zeroToOneDecimal = xsd:decimal { minInclusive = "0" maxInclusive = "1" } +common-basic-timing-attlist = + common-begin-end-timing-attlist, + common-dur-timing-attlist, + common-repeat-timing-attlist, + common-restart-timing-attlist, + common-restart-default-attlist, + common-fill-timing-attlist, + common-fill-default-attlist +common-timing-attlist = + common-basic-timing-attlist, common-time-manip-attlist +anim-iterate-attlist = + common-anim-target-attlist + & attribute anim:iterate-type { \string }? + & attribute anim:iterate-interval { duration }? +anim-audio-attlist = + attribute xlink:href { anyIRI }? + & attribute anim:audio-level { double }? +anim-command-attlist = attribute anim:command { \string } +style-style = + element style:style { + style-style-attlist, style-style-content, style-map* + } +common-in-content-meta-attlist = + attribute xhtml:about { URIorSafeCURIE }, + attribute xhtml:property { CURIEs }, + common-meta-literal-attlist +common-meta-literal-attlist = + attribute xhtml:datatype { CURIE }?, + attribute xhtml:content { \string }? +xml-id = attribute xml:id { ID } +style-style-attlist = + attribute style:name { styleName } + & attribute style:display-name { \string }? + & attribute style:parent-style-name { styleNameRef }? + & attribute style:next-style-name { styleNameRef }? + & attribute style:list-level { positiveInteger | empty }? + & attribute style:list-style-name { styleName | empty }? + & attribute style:master-page-name { styleNameRef }? + & attribute style:auto-update { boolean }? + & attribute style:data-style-name { styleNameRef }? + & attribute style:percentage-data-style-name { styleNameRef }? + & attribute style:class { \string }? + & attribute style:default-outline-level { positiveInteger | empty }? +style-map = element style:map { style-map-attlist, empty } +style-map-attlist = + attribute style:condition { \string } + & attribute style:apply-style-name { styleNameRef } + & attribute style:base-cell-address { cellAddress }? +style-default-style = + element style:default-style { style-style-content } +style-page-layout = + element style:page-layout { + style-page-layout-attlist, style-page-layout-content + } +style-page-layout-content = + style-page-layout-properties?, + style-header-style?, + style-footer-style? +style-page-layout-attlist = + attribute style:name { styleName } + & attribute style:page-usage { + "all" | "left" | "right" | "mirrored" + }? +style-header-style = + element style:header-style { style-header-footer-properties? } +style-footer-style = + element style:footer-style { style-header-footer-properties? } +style-default-page-layout = + element style:default-page-layout { style-page-layout-content } +style-master-page = + element style:master-page { + style-master-page-attlist, + (style-header, style-header-left?)?, + (style-footer, style-footer-left?)?, + draw-layer-set?, + office-forms?, + shape*, + animation-element?, + presentation-notes? + } +style-master-page-attlist = + attribute style:name { styleName } + & attribute style:display-name { \string }? + & attribute style:page-layout-name { styleNameRef } + & attribute draw:style-name { styleNameRef }? + & attribute style:next-style-name { styleNameRef }? +style-header = + element style:header { + common-style-header-footer-attlist, header-footer-content + } +style-footer = + element style:footer { + common-style-header-footer-attlist, header-footer-content + } +style-header-left = + element style:header-left { + common-style-header-footer-attlist, header-footer-content + } +style-footer-left = + element style:footer-left { + common-style-header-footer-attlist, header-footer-content + } +header-footer-content = + (text-tracked-changes, + text-decls, + (text-h + | text-p + | text-list + | table-table + | text-section + | text-table-of-content + | text-illustration-index + | text-table-index + | text-object-index + | text-user-index + | text-alphabetical-index + | text-bibliography + | text-index-title + | change-marks)*) + | (style-region-left?, style-region-center?, style-region-right?) +common-style-header-footer-attlist = + attribute style:display { boolean }? +style-region-left = element style:region-left { region-content } +style-region-center = element style:region-center { region-content } +style-region-right = element style:region-right { region-content } +region-content = text-p* +presentation-notes = + element presentation:notes { + common-presentation-header-footer-attlist, + presentation-notes-attlist, + office-forms, + shape* + } +presentation-notes-attlist = + attribute style:page-layout-name { styleNameRef }? + & attribute draw:style-name { styleNameRef }? +table-table-template = + element table:table-template { + table-table-template-attlist, + table-first-row?, + table-last-row?, + table-first-column?, + table-last-column?, + table-body, + table-even-rows?, + table-odd-rows?, + table-even-columns?, + table-odd-columns?, + table-background? + } +table-table-template-attlist = + attribute table:name { \string } + & attribute table:first-row-start-column { rowOrCol } + & attribute table:first-row-end-column { rowOrCol } + & attribute table:last-row-start-column { rowOrCol } + & attribute table:last-row-end-column { rowOrCol } +rowOrCol = "row" | "column" +table-first-row = + element table:first-row { common-table-template-attlist, empty } +table-last-row = + element table:last-row { common-table-template-attlist, empty } +table-first-column = + element table:first-column { common-table-template-attlist, empty } +table-last-column = + element table:last-column { common-table-template-attlist, empty } +table-body = element table:body { common-table-template-attlist, empty } +table-even-rows = + element table:even-rows { common-table-template-attlist, empty } +table-odd-rows = + element table:odd-rows { common-table-template-attlist, empty } +table-even-columns = + element table:even-columns { common-table-template-attlist, empty } +table-odd-columns = + element table:odd-columns { common-table-template-attlist, empty } +common-table-template-attlist = + attribute table:style-name { styleNameRef }, + attribute table:paragraph-style-name { styleNameRef }? +table-background = + element table:background { table-background-attlist, empty } +table-background-attlist = attribute table:style-name { styleNameRef } +style-font-face = + element style:font-face { + style-font-face-attlist, svg-font-face-src?, svg-definition-src? + } +style-font-face-attlist = + attribute svg:font-family { \string }? + & attribute svg:font-style { fontStyle }? + & attribute svg:font-variant { fontVariant }? + & attribute svg:font-weight { fontWeight }? + & attribute svg:font-stretch { + "normal" + | "ultra-condensed" + | "extra-condensed" + | "condensed" + | "semi-condensed" + | "semi-expanded" + | "expanded" + | "extra-expanded" + | "ultra-expanded" + }? + & attribute svg:font-size { positiveLength }? + & attribute svg:unicode-range { \string }? + & attribute svg:units-per-em { integer }? + & attribute svg:panose-1 { \string }? + & attribute svg:stemv { integer }? + & attribute svg:stemh { integer }? + & attribute svg:slope { integer }? + & attribute svg:cap-height { integer }? + & attribute svg:x-height { integer }? + & attribute svg:accent-height { integer }? + & attribute svg:ascent { integer }? + & attribute svg:descent { integer }? + & attribute svg:widths { \string }? + & attribute svg:bbox { \string }? + & attribute svg:ideographic { integer }? + & attribute svg:alphabetic { integer }? + & attribute svg:mathematical { integer }? + & attribute svg:hanging { integer }? + & attribute svg:v-ideographic { integer }? + & attribute svg:v-alphabetic { integer }? + & attribute svg:v-mathematical { integer }? + & attribute svg:v-hanging { integer }? + & attribute svg:underline-position { integer }? + & attribute svg:underline-thickness { integer }? + & attribute svg:strikethrough-position { integer }? + & attribute svg:strikethrough-thickness { integer }? + & attribute svg:overline-position { integer }? + & attribute svg:overline-thickness { integer }? + & attribute style:name { \string } + & attribute style:font-adornments { \string }? + & attribute style:font-family-generic { fontFamilyGeneric }? + & attribute style:font-pitch { fontPitch }? + & attribute style:font-charset { textEncoding }? +svg-font-face-src = + element svg:font-face-src { + (svg-font-face-uri | svg-font-face-name)+ + } +svg-font-face-uri = + element svg:font-face-uri { + common-svg-font-face-xlink-attlist, svg-font-face-format* + } +svg-font-face-format = + element svg:font-face-format { + attribute svg:string { \string }?, + empty + } +svg-font-face-name = + element svg:font-face-name { + attribute svg:name { \string }?, + empty + } +svg-definition-src = + element svg:definition-src { + common-svg-font-face-xlink-attlist, empty + } +common-svg-font-face-xlink-attlist = + attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:actuate { "onRequest" }? +number-number-style = + element number:number-style { + common-data-style-attlist, + style-text-properties?, + number-text?, + (any-number, number-text?)?, + style-map* + } +any-number = number-number | number-scientific-number | number-fraction +number-number = + element number:number { + number-number-attlist, + common-decimal-places-attlist, + common-number-attlist, + number-embedded-text* + } +number-number-attlist = + attribute number:decimal-replacement { \string }? + & attribute number:display-factor { double }? +number-embedded-text = + element number:embedded-text { number-embedded-text-attlist, text } +number-embedded-text-attlist = attribute number:position { integer } +number-scientific-number = + element number:scientific-number { + number-scientific-number-attlist, + common-decimal-places-attlist, + common-number-attlist, + empty + } +number-scientific-number-attlist = + attribute number:min-exponent-digits { integer }? +number-fraction = + element number:fraction { + number-fraction-attlist, common-number-attlist, empty + } +number-fraction-attlist = + attribute number:min-numerator-digits { integer }? + & attribute number:min-denominator-digits { integer }? + & attribute number:denominator-value { integer }? +number-currency-style = + element number:currency-style { + common-data-style-attlist, + common-auto-reorder-attlist, + style-text-properties?, + number-text?, + ((number-and-text, currency-symbol-and-text?) + | (currency-symbol-and-text, number-and-text?))?, + style-map* + } +currency-symbol-and-text = number-currency-symbol, number-text? +number-and-text = number-number, number-text? +number-currency-symbol = + element number:currency-symbol { + number-currency-symbol-attlist, text + } +number-currency-symbol-attlist = + attribute number:language { languageCode }?, + attribute number:country { countryCode }?, + attribute number:script { scriptCode }?, + attribute number:rfc-language-tag { language }? +number-percentage-style = + element number:percentage-style { + common-data-style-attlist, + style-text-properties?, + number-text?, + number-and-text?, + style-map* + } +number-date-style = + element number:date-style { + common-data-style-attlist, + common-auto-reorder-attlist, + common-format-source-attlist, + style-text-properties?, + number-text?, + (any-date, number-text?)+, + style-map* + } +any-date = + number-day + | number-month + | number-year + | number-era + | number-day-of-week + | number-week-of-year + | number-quarter + | number-hours + | number-am-pm + | number-minutes + | number-seconds +number-day = + element number:day { + number-day-attlist, common-calendar-attlist, empty + } +number-day-attlist = attribute number:style { "short" | "long" }? +number-month = + element number:month { + number-month-attlist, common-calendar-attlist, empty + } +number-month-attlist = + attribute number:textual { boolean }? + & attribute number:possessive-form { boolean }? + & attribute number:style { "short" | "long" }? +number-year = + element number:year { + number-year-attlist, common-calendar-attlist, empty + } +number-year-attlist = attribute number:style { "short" | "long" }? +number-era = + element number:era { + number-era-attlist, common-calendar-attlist, empty + } +number-era-attlist = attribute number:style { "short" | "long" }? +number-day-of-week = + element number:day-of-week { + number-day-of-week-attlist, common-calendar-attlist, empty + } +number-day-of-week-attlist = + attribute number:style { "short" | "long" }? +number-week-of-year = + element number:week-of-year { common-calendar-attlist, empty } +number-quarter = + element number:quarter { + number-quarter-attlist, common-calendar-attlist, empty + } +number-quarter-attlist = attribute number:style { "short" | "long" }? +number-time-style = + element number:time-style { + number-time-style-attlist, + common-data-style-attlist, + common-format-source-attlist, + style-text-properties?, + number-text?, + (any-time, number-text?)+, + style-map* + } +any-time = number-hours | number-am-pm | number-minutes | number-seconds +number-time-style-attlist = + attribute number:truncate-on-overflow { boolean }? +number-hours = element number:hours { number-hours-attlist, empty } +number-hours-attlist = attribute number:style { "short" | "long" }? +number-minutes = + element number:minutes { number-minutes-attlist, empty } +number-minutes-attlist = attribute number:style { "short" | "long" }? +number-seconds = + element number:seconds { number-seconds-attlist, empty } +number-seconds-attlist = + attribute number:style { "short" | "long" }? + & attribute number:decimal-places { integer }? +number-am-pm = element number:am-pm { empty } +number-boolean-style = + element number:boolean-style { + common-data-style-attlist, + style-text-properties?, + number-text?, + (number-boolean, number-text?)?, + style-map* + } +number-boolean = element number:boolean { empty } +number-text-style = + element number:text-style { + common-data-style-attlist, + style-text-properties?, + number-text?, + (number-text-content, number-text?)*, + style-map* + } +number-text = element number:text { text } +number-text-content = element number:text-content { empty } +common-data-style-attlist = + attribute style:name { styleName } + & attribute style:display-name { \string }? + & attribute number:language { languageCode }? + & attribute number:country { countryCode }? + & attribute number:script { scriptCode }? + & attribute number:rfc-language-tag { language }? + & attribute number:title { \string }? + & attribute style:volatile { boolean }? + & attribute number:transliteration-format { \string }? + & attribute number:transliteration-language { countryCode }? + & attribute number:transliteration-country { countryCode }? + & attribute number:transliteration-style { + "short" | "medium" | "long" + }? +common-auto-reorder-attlist = + attribute number:automatic-order { boolean }? +common-format-source-attlist = + attribute number:format-source { "fixed" | "language" }? +common-decimal-places-attlist = + attribute number:decimal-places { integer }? +common-number-attlist = + attribute number:min-integer-digits { integer }? + & attribute number:grouping { boolean }? +common-calendar-attlist = + attribute number:calendar { + "gregorian" + | "gengou" + | "ROC" + | "hanja_yoil" + | "hanja" + | "hijri" + | "jewish" + | "buddhist" + | \string + }? +style-style-content = + (attribute style:family { "text" }, + style-text-properties?) + | (attribute style:family { "paragraph" }, + style-paragraph-properties?, + style-text-properties?) + | (attribute style:family { "section" }, + style-section-properties?) + | (attribute style:family { "ruby" }, + style-ruby-properties?) + | (attribute style:family { "table" }, + style-table-properties?) + | (attribute style:family { "table-column" }, + style-table-column-properties?) + | (attribute style:family { "table-row" }, + style-table-row-properties?) + | (attribute style:family { "table-cell" }, + style-table-cell-properties?, + style-paragraph-properties?, + style-text-properties?) + | (attribute style:family { "graphic" | "presentation" }, + style-graphic-properties?, + style-paragraph-properties?, + style-text-properties?) + | (attribute style:family { "drawing-page" }, + style-drawing-page-properties?) + | (attribute style:family { "chart" }, + style-chart-properties?, + style-graphic-properties?, + style-paragraph-properties?, + style-text-properties?) +text-linenumbering-configuration = + element text:linenumbering-configuration { + text-linenumbering-configuration-attlist, + text-linenumbering-separator? + } +text-linenumbering-configuration-attlist = + attribute text:number-lines { boolean }? + & common-num-format-attlist? + & attribute text:style-name { styleNameRef }? + & attribute text:increment { nonNegativeInteger }? + & attribute text:number-position { + "left" | "right" | "inner" | "outer" + }? + & attribute text:offset { nonNegativeLength }? + & attribute text:count-empty-lines { boolean }? + & attribute text:count-in-text-boxes { boolean }? + & attribute text:restart-on-page { boolean }? +text-linenumbering-separator = + element text:linenumbering-separator { + attribute text:increment { nonNegativeInteger }?, + text + } +text-notes-configuration = + element text:notes-configuration { text-notes-configuration-content } +text-notes-configuration-content = + text-note-class + & attribute text:citation-style-name { styleNameRef }? + & attribute text:citation-body-style-name { styleNameRef }? + & attribute text:default-style-name { styleNameRef }? + & attribute text:master-page-name { styleNameRef }? + & attribute text:start-value { nonNegativeInteger }? + & common-num-format-prefix-suffix-attlist + & common-num-format-attlist? + & attribute text:start-numbering-at { + "document" | "chapter" | "page" + }? + & attribute text:footnotes-position { + "text" | "page" | "section" | "document" + }? + & element text:note-continuation-notice-forward { text }? + & element text:note-continuation-notice-backward { text }? +text-bibliography-configuration = + element text:bibliography-configuration { + text-bibliography-configuration-attlist, text-sort-key* + } +text-bibliography-configuration-attlist = + attribute text:prefix { \string }? + & attribute text:suffix { \string }? + & attribute text:numbered-entries { boolean }? + & attribute text:sort-by-position { boolean }? + & attribute fo:language { languageCode }? + & attribute fo:country { countryCode }? + & attribute fo:script { scriptCode }? + & attribute style:rfc-language-tag { language }? + & attribute text:sort-algorithm { \string }? +text-sort-key = element text:sort-key { text-sort-key-attlist, empty } +text-sort-key-attlist = + attribute text:key { + "address" + | "annote" + | "author" + | "bibliography-type" + | "booktitle" + | "chapter" + | "custom1" + | "custom2" + | "custom3" + | "custom4" + | "custom5" + | "edition" + | "editor" + | "howpublished" + | "identifier" + | "institution" + | "isbn" + | "issn" + | "journal" + | "month" + | "note" + | "number" + | "organizations" + | "pages" + | "publisher" + | "report-type" + | "school" + | "series" + | "title" + | "url" + | "volume" + | "year" + }, + attribute text:sort-ascending { boolean }? +text-list-style = + element text:list-style { + text-list-style-attr, text-list-style-content* + } +text-list-style-attr = + attribute style:name { styleName } + & attribute style:display-name { \string }? + & attribute text:consecutive-numbering { boolean }? +text-list-style-content = + element text:list-level-style-number { + text-list-level-style-attr, + text-list-level-style-number-attr, + style-list-level-properties?, + style-text-properties? + } + | element text:list-level-style-bullet { + text-list-level-style-attr, + text-list-level-style-bullet-attr, + style-list-level-properties?, + style-text-properties? + } + | element text:list-level-style-image { + text-list-level-style-attr, + text-list-level-style-image-attr, + style-list-level-properties? + } +text-list-level-style-number-attr = + attribute text:style-name { styleNameRef }? + & common-num-format-attlist + & common-num-format-prefix-suffix-attlist + & attribute text:display-levels { positiveInteger }? + & attribute text:start-value { positiveInteger }? +text-list-level-style-bullet-attr = + attribute text:style-name { styleNameRef }? + & attribute text:bullet-char { character } + & common-num-format-prefix-suffix-attlist + & attribute text:bullet-relative-size { percent }? +text-list-level-style-image-attr = + common-draw-data-attlist | office-binary-data +text-list-level-style-attr = attribute text:level { positiveInteger } +text-outline-style = + element text:outline-style { + text-outline-style-attr, text-outline-level-style+ + } +text-outline-style-attr = attribute style:name { styleName } +text-outline-level-style = + element text:outline-level-style { + text-outline-level-style-attlist, + style-list-level-properties?, + style-text-properties? + } +text-outline-level-style-attlist = + attribute text:level { positiveInteger } + & attribute text:style-name { styleNameRef }? + & common-num-format-attlist + & common-num-format-prefix-suffix-attlist + & attribute text:display-levels { positiveInteger }? + & attribute text:start-value { positiveInteger }? +style-graphic-properties = + element style:graphic-properties { + style-graphic-properties-content-strict + } +style-graphic-properties-content-strict = + style-graphic-properties-attlist, + style-graphic-fill-properties-attlist, + style-graphic-properties-elements +style-drawing-page-properties = + element style:drawing-page-properties { + style-drawing-page-properties-content-strict + } +style-drawing-page-properties-content-strict = + style-graphic-fill-properties-attlist, + style-drawing-page-properties-attlist, + style-drawing-page-properties-elements +draw-gradient = + element draw:gradient { + common-draw-gradient-attlist, draw-gradient-attlist, empty + } +common-draw-gradient-attlist = + attribute draw:name { styleName }? + & attribute draw:display-name { \string }? + & attribute draw:style { gradient-style } + & attribute draw:cx { percent }? + & attribute draw:cy { percent }? + & attribute draw:angle { angle }? + & attribute draw:border { percent }? +gradient-style = + "linear" | "axial" | "radial" | "ellipsoid" | "square" | "rectangular" +draw-gradient-attlist = + attribute draw:start-color { color }? + & attribute draw:end-color { color }? + & attribute draw:start-intensity { zeroToHundredPercent }? + & attribute draw:end-intensity { zeroToHundredPercent }? +svg-linearGradient = + element svg:linearGradient { + common-svg-gradient-attlist, + attribute svg:x1 { coordinate | percent }?, + attribute svg:y1 { coordinate | percent }?, + attribute svg:x2 { coordinate | percent }?, + attribute svg:y2 { coordinate | percent }?, + svg-stop* + } +svg-radialGradient = + element svg:radialGradient { + common-svg-gradient-attlist, + attribute svg:cx { coordinate | percent }?, + attribute svg:cy { coordinate | percent }?, + attribute svg:r { coordinate | percent }?, + attribute svg:fx { coordinate | percent }?, + attribute svg:fy { coordinate | percent }?, + svg-stop* + } +svg-stop = + element svg:stop { + attribute svg:offset { double | percent }, + attribute svg:stop-color { color }?, + attribute svg:stop-opacity { double }? + } +common-svg-gradient-attlist = + attribute svg:gradientUnits { "objectBoundingBox" }? + & attribute svg:gradientTransform { \string }? + & attribute svg:spreadMethod { "pad" | "reflect" | "repeat" }? + & attribute draw:name { styleName } + & attribute draw:display-name { \string }? +draw-hatch = element draw:hatch { draw-hatch-attlist, empty } +draw-hatch-attlist = + attribute draw:name { styleName } + & attribute draw:display-name { \string }? + & attribute draw:style { "single" | "double" | "triple" } + & attribute draw:color { color }? + & attribute draw:distance { length }? + & attribute draw:rotation { angle }? +draw-fill-image = + element draw:fill-image { + draw-fill-image-attlist, + attribute xlink:type { "simple" }, + attribute xlink:href { anyIRI }, + attribute xlink:show { "embed" }?, + attribute xlink:actuate { "onLoad" }?, + empty + } +draw-fill-image-attlist = + attribute draw:name { styleName } + & attribute draw:display-name { \string }? + & attribute svg:width { length }? + & attribute svg:height { length }? +draw-opacity = + element draw:opacity { + common-draw-gradient-attlist, draw-opacity-attlist, empty + } +draw-opacity-attlist = + attribute draw:start { zeroToHundredPercent }?, + attribute draw:end { zeroToHundredPercent }? +draw-marker = + element draw:marker { + draw-marker-attlist, + common-draw-viewbox-attlist, + common-draw-path-data-attlist, + empty + } +draw-marker-attlist = + attribute draw:name { styleName } + & attribute draw:display-name { \string }? +draw-stroke-dash = + element draw:stroke-dash { draw-stroke-dash-attlist, empty } +draw-stroke-dash-attlist = + attribute draw:name { styleName } + & attribute draw:display-name { \string }? + & attribute draw:style { "rect" | "round" }? + & attribute draw:dots1 { integer }? + & attribute draw:dots1-length { length | percent }? + & attribute draw:dots2 { integer }? + & attribute draw:dots2-length { length | percent }? + & attribute draw:distance { length | percent }? +style-presentation-page-layout = + element style:presentation-page-layout { + attribute style:name { styleName }, + attribute style:display-name { \string }?, + presentation-placeholder* + } +presentation-placeholder = + element presentation:placeholder { + attribute presentation:object { presentation-classes }, + attribute svg:x { coordinate | percent }, + attribute svg:y { coordinate | percent }, + attribute svg:width { length | percent }, + attribute svg:height { length | percent }, + empty + } +style-page-layout-properties = + element style:page-layout-properties { + style-page-layout-properties-content-strict + } +style-page-layout-properties-content-strict = + style-page-layout-properties-attlist, + style-page-layout-properties-elements +style-page-layout-properties-attlist = + attribute fo:page-width { length }? + & attribute fo:page-height { length }? + & common-num-format-attlist? + & common-num-format-prefix-suffix-attlist + & attribute style:paper-tray-name { "default" | \string }? + & attribute style:print-orientation { "portrait" | "landscape" }? + & common-horizontal-margin-attlist + & common-vertical-margin-attlist + & common-margin-attlist + & common-border-attlist + & common-border-line-width-attlist + & common-padding-attlist + & common-shadow-attlist + & common-background-color-attlist + & attribute style:register-truth-ref-style-name { styleNameRef }? + & attribute style:print { + list { + ("headers" + | "grid" + | "annotations" + | "objects" + | "charts" + | "drawings" + | "formulas" + | "zero-values")* + } + }? + & attribute style:print-page-order { "ttb" | "ltr" }? + & attribute style:first-page-number { positiveInteger | "continue" }? + & attribute style:scale-to { percent }? + & attribute style:scale-to-pages { positiveInteger }? + & attribute style:table-centering { + "horizontal" | "vertical" | "both" | "none" + }? + & attribute style:footnote-max-height { length }? + & common-writing-mode-attlist + & attribute style:layout-grid-mode { "none" | "line" | "both" }? + & attribute style:layout-grid-standard-mode { boolean }? + & attribute style:layout-grid-base-height { length }? + & attribute style:layout-grid-ruby-height { length }? + & attribute style:layout-grid-lines { positiveInteger }? + & attribute style:layout-grid-base-width { length }? + & attribute style:layout-grid-color { color }? + & attribute style:layout-grid-ruby-below { boolean }? + & attribute style:layout-grid-print { boolean }? + & attribute style:layout-grid-display { boolean }? + & attribute style:layout-grid-snap-to { boolean }? +style-page-layout-properties-elements = + style-background-image & style-columns & style-footnote-sep +style-footnote-sep = + element style:footnote-sep { style-footnote-sep-attlist, empty }? +style-footnote-sep-attlist = + attribute style:width { length }?, + attribute style:rel-width { percent }?, + attribute style:color { color }?, + attribute style:line-style { lineStyle }?, + attribute style:adjustment { "left" | "center" | "right" }?, + attribute style:distance-before-sep { length }?, + attribute style:distance-after-sep { length }? +style-header-footer-properties = + element style:header-footer-properties { + style-header-footer-properties-content-strict + } +style-header-footer-properties-content-strict = + style-header-footer-properties-attlist, + style-header-footer-properties-elements +style-header-footer-properties-attlist = + attribute svg:height { length }? + & attribute fo:min-height { length }? + & common-horizontal-margin-attlist + & common-vertical-margin-attlist + & common-margin-attlist + & common-border-attlist + & common-border-line-width-attlist + & common-padding-attlist + & common-background-color-attlist + & common-shadow-attlist + & attribute style:dynamic-spacing { boolean }? +style-header-footer-properties-elements = style-background-image +style-text-properties = + element style:text-properties { style-text-properties-content-strict } +style-text-properties-content-strict = + style-text-properties-attlist, style-text-properties-elements +style-text-properties-elements = empty +style-text-properties-attlist = + attribute fo:font-variant { fontVariant }? + & attribute fo:text-transform { + "none" | "lowercase" | "uppercase" | "capitalize" + }? + & attribute fo:color { color }? + & attribute style:use-window-font-color { boolean }? + & attribute style:text-outline { boolean }? + & attribute style:text-line-through-type { lineType }? + & attribute style:text-line-through-style { lineStyle }? + & attribute style:text-line-through-width { lineWidth }? + & attribute style:text-line-through-color { "font-color" | color }? + & attribute style:text-line-through-text { \string }? + & attribute style:text-line-through-text-style { styleNameRef }? + & attribute style:text-position { + list { (percent | "super" | "sub"), percent? } + }? + & attribute style:font-name { \string }? + & attribute style:font-name-asian { \string }? + & attribute style:font-name-complex { \string }? + & attribute fo:font-family { \string }? + & attribute style:font-family-asian { \string }? + & attribute style:font-family-complex { \string }? + & attribute style:font-family-generic { fontFamilyGeneric }? + & attribute style:font-family-generic-asian { fontFamilyGeneric }? + & attribute style:font-family-generic-complex { fontFamilyGeneric }? + & attribute style:font-style-name { \string }? + & attribute style:font-style-name-asian { \string }? + & attribute style:font-style-name-complex { \string }? + & attribute style:font-pitch { fontPitch }? + & attribute style:font-pitch-asian { fontPitch }? + & attribute style:font-pitch-complex { fontPitch }? + & attribute style:font-charset { textEncoding }? + & attribute style:font-charset-asian { textEncoding }? + & attribute style:font-charset-complex { textEncoding }? + & attribute fo:font-size { positiveLength | percent }? + & attribute style:font-size-asian { positiveLength | percent }? + & attribute style:font-size-complex { positiveLength | percent }? + & attribute style:font-size-rel { length }? + & attribute style:font-size-rel-asian { length }? + & attribute style:font-size-rel-complex { length }? + & attribute style:script-type { + "latin" | "asian" | "complex" | "ignore" + }? + & attribute fo:letter-spacing { length | "normal" }? + & attribute fo:language { languageCode }? + & attribute style:language-asian { languageCode }? + & attribute style:language-complex { languageCode }? + & attribute fo:country { countryCode }? + & attribute style:country-asian { countryCode }? + & attribute style:country-complex { countryCode }? + & attribute fo:script { scriptCode }? + & attribute style:script-asian { scriptCode }? + & attribute style:script-complex { scriptCode }? + & attribute style:rfc-language-tag { language }? + & attribute style:rfc-language-tag-asian { language }? + & attribute style:rfc-language-tag-complex { language }? + & attribute fo:font-style { fontStyle }? + & attribute style:font-style-asian { fontStyle }? + & attribute style:font-style-complex { fontStyle }? + & attribute style:font-relief { "none" | "embossed" | "engraved" }? + & attribute fo:text-shadow { shadowType }? + & attribute style:text-underline-type { lineType }? + & attribute style:text-underline-style { lineStyle }? + & attribute style:text-underline-width { lineWidth }? + & attribute style:text-underline-color { "font-color" | color }? + & attribute style:text-overline-type { lineType }? + & attribute style:text-overline-style { lineStyle }? + & attribute style:text-overline-width { lineWidth }? + & attribute style:text-overline-color { "font-color" | color }? + & attribute style:text-overline-mode { lineMode }? + & attribute fo:font-weight { fontWeight }? + & attribute style:font-weight-asian { fontWeight }? + & attribute style:font-weight-complex { fontWeight }? + & attribute style:text-underline-mode { lineMode }? + & attribute style:text-line-through-mode { lineMode }? + & attribute style:letter-kerning { boolean }? + & attribute style:text-blinking { boolean }? + & common-background-color-attlist + & attribute style:text-combine { "none" | "letters" | "lines" }? + & attribute style:text-combine-start-char { character }? + & attribute style:text-combine-end-char { character }? + & attribute style:text-emphasize { + "none" + | list { + ("none" | "accent" | "dot" | "circle" | "disc"), + ("above" | "below") + } + }? + & attribute style:text-scale { percent }? + & attribute style:text-rotation-angle { angle }? + & attribute style:text-rotation-scale { "fixed" | "line-height" }? + & attribute fo:hyphenate { boolean }? + & attribute fo:hyphenation-remain-char-count { positiveInteger }? + & attribute fo:hyphenation-push-char-count { positiveInteger }? + & (attribute text:display { "true" } + | attribute text:display { "none" } + | (attribute text:display { "condition" }, + attribute text:condition { "none" }) + | empty) +fontVariant = "normal" | "small-caps" +fontFamilyGeneric = + "roman" | "swiss" | "modern" | "decorative" | "script" | "system" +fontPitch = "fixed" | "variable" +textEncoding = xsd:string { pattern = "[A-Za-z][A-Za-z0-9._\-]*" } +fontStyle = "normal" | "italic" | "oblique" +shadowType = "none" | \string +lineType = "none" | "single" | "double" +lineStyle = + "none" + | "solid" + | "dotted" + | "dash" + | "long-dash" + | "dot-dash" + | "dot-dot-dash" + | "wave" +lineWidth = + "auto" + | "normal" + | "bold" + | "thin" + | "medium" + | "thick" + | positiveInteger + | percent + | positiveLength +fontWeight = + "normal" + | "bold" + | "100" + | "200" + | "300" + | "400" + | "500" + | "600" + | "700" + | "800" + | "900" +lineMode = "continuous" | "skip-white-space" +style-paragraph-properties = + element style:paragraph-properties { + style-paragraph-properties-content-strict + } +style-paragraph-properties-content-strict = + style-paragraph-properties-attlist, + style-paragraph-properties-elements +style-paragraph-properties-attlist = + attribute fo:line-height { "normal" | nonNegativeLength | percent }? + & attribute style:line-height-at-least { nonNegativeLength }? + & attribute style:line-spacing { length }? + & attribute style:font-independent-line-spacing { boolean }? + & common-text-align + & attribute fo:text-align-last { "start" | "center" | "justify" }? + & attribute style:justify-single-word { boolean }? + & attribute fo:keep-together { "auto" | "always" }? + & attribute fo:widows { nonNegativeInteger }? + & attribute fo:orphans { nonNegativeInteger }? + & attribute style:tab-stop-distance { nonNegativeLength }? + & attribute fo:hyphenation-keep { "auto" | "page" }? + & attribute fo:hyphenation-ladder-count { + "no-limit" | positiveInteger + }? + & attribute style:register-true { boolean }? + & common-horizontal-margin-attlist + & attribute fo:text-indent { length | percent }? + & attribute style:auto-text-indent { boolean }? + & common-vertical-margin-attlist + & common-margin-attlist + & common-break-attlist + & common-background-color-attlist + & common-border-attlist + & common-border-line-width-attlist + & attribute style:join-border { boolean }? + & common-padding-attlist + & common-shadow-attlist + & common-keep-with-next-attlist + & attribute text:number-lines { boolean }? + & attribute text:line-number { nonNegativeInteger }? + & attribute style:text-autospace { "none" | "ideograph-alpha" }? + & attribute style:punctuation-wrap { "simple" | "hanging" }? + & attribute style:line-break { "normal" | "strict" }? + & attribute style:vertical-align { + "top" | "middle" | "bottom" | "auto" | "baseline" + }? + & common-writing-mode-attlist + & attribute style:writing-mode-automatic { boolean }? + & attribute style:snap-to-layout-grid { boolean }? + & common-page-number-attlist + & common-background-transparency-attlist +common-text-align = + attribute fo:text-align { + "start" | "end" | "left" | "right" | "center" | "justify" + }? +style-paragraph-properties-elements = + style-tab-stops & style-drop-cap & style-background-image +style-tab-stops = element style:tab-stops { style-tab-stop* }? +style-tab-stop = + element style:tab-stop { style-tab-stop-attlist, empty } +style-tab-stop-attlist = + attribute style:position { length } + & (attribute style:type { "left" | "center" | "right" }? + | (attribute style:type { "char" }, + style-tab-stop-char-attlist)) + & attribute style:leader-type { lineType }? + & attribute style:leader-style { lineStyle }? + & attribute style:leader-width { lineWidth }? + & attribute style:leader-color { "font-color" | color }? + & attribute style:leader-text { character }? + & attribute style:leader-text-style { styleNameRef }? +style-tab-stop-char-attlist = attribute style:char { character } +style-drop-cap = + element style:drop-cap { style-drop-cap-attlist, empty }? +style-drop-cap-attlist = + attribute style:length { "word" | positiveInteger }? + & attribute style:lines { positiveInteger }? + & attribute style:distance { length }? + & attribute style:style-name { styleNameRef }? +common-horizontal-margin-attlist = + attribute fo:margin-left { length | percent }?, + attribute fo:margin-right { length | percent }? +common-vertical-margin-attlist = + attribute fo:margin-top { nonNegativeLength | percent }?, + attribute fo:margin-bottom { nonNegativeLength | percent }? +common-margin-attlist = + attribute fo:margin { nonNegativeLength | percent }? +common-break-attlist = + attribute fo:break-before { "auto" | "column" | "page" }?, + attribute fo:break-after { "auto" | "column" | "page" }? +common-background-color-attlist = + attribute fo:background-color { "transparent" | color }? +style-background-image = + element style:background-image { + style-background-image-attlist, + (common-draw-data-attlist | office-binary-data | empty) + }? +style-background-image-attlist = + attribute style:repeat { "no-repeat" | "repeat" | "stretch" }? + & attribute style:position { + "left" + | "center" + | "right" + | "top" + | "bottom" + | list { horiBackPos, vertBackPos } + | list { vertBackPos, horiBackPos } + }? + & attribute style:filter-name { \string }? + & attribute draw:opacity { zeroToHundredPercent }? +horiBackPos = "left" | "center" | "right" +vertBackPos = "top" | "center" | "bottom" +common-border-attlist = + attribute fo:border { \string }?, + attribute fo:border-top { \string }?, + attribute fo:border-bottom { \string }?, + attribute fo:border-left { \string }?, + attribute fo:border-right { \string }? +common-border-line-width-attlist = + attribute style:border-line-width { borderWidths }?, + attribute style:border-line-width-top { borderWidths }?, + attribute style:border-line-width-bottom { borderWidths }?, + attribute style:border-line-width-left { borderWidths }?, + attribute style:border-line-width-right { borderWidths }? +borderWidths = list { positiveLength, positiveLength, positiveLength } +common-padding-attlist = + attribute fo:padding { nonNegativeLength }?, + attribute fo:padding-top { nonNegativeLength }?, + attribute fo:padding-bottom { nonNegativeLength }?, + attribute fo:padding-left { nonNegativeLength }?, + attribute fo:padding-right { nonNegativeLength }? +common-shadow-attlist = attribute style:shadow { shadowType }? +common-keep-with-next-attlist = + attribute fo:keep-with-next { "auto" | "always" }? +common-writing-mode-attlist = + attribute style:writing-mode { + "lr-tb" | "rl-tb" | "tb-rl" | "tb-lr" | "lr" | "rl" | "tb" | "page" + }? +common-page-number-attlist = + attribute style:page-number { positiveInteger | "auto" }? +common-background-transparency-attlist = + attribute style:background-transparency { zeroToHundredPercent }? +style-ruby-properties = + element style:ruby-properties { style-ruby-properties-content-strict } +style-ruby-properties-content-strict = + style-ruby-properties-attlist, style-ruby-properties-elements +style-ruby-properties-elements = empty +style-ruby-properties-attlist = + attribute style:ruby-position { "above" | "below" }? + & attribute style:ruby-align { + "left" + | "center" + | "right" + | "distribute-letter" + | "distribute-space" + }? +style-section-properties = + element style:section-properties { + style-section-properties-content-strict + } +style-section-properties-content-strict = + style-section-properties-attlist, style-section-properties-elements +style-section-properties-attlist = + common-background-color-attlist + & common-horizontal-margin-attlist + & attribute style:protect { boolean }? + & common-editable-attlist + & attribute text:dont-balance-text-columns { boolean }? + & common-writing-mode-attlist +style-section-properties-elements = + style-background-image & style-columns & text-notes-configuration* +style-columns = + element style:columns { + style-columns-attlist, style-column-sep?, style-column* + }? +style-columns-attlist = + attribute fo:column-count { positiveInteger } + & attribute fo:column-gap { length }? +style-column = element style:column { style-column-attlist } +style-column-attlist = + attribute style:rel-width { relativeLength } + & attribute fo:start-indent { length }? + & attribute fo:end-indent { length }? + & attribute fo:space-before { length }? + & attribute fo:space-after { length }? +style-column-sep = element style:column-sep { style-column-sep-attlist } +style-column-sep-attlist = + attribute style:style { + "none" | "solid" | "dotted" | "dashed" | "dot-dashed" + }? + & attribute style:width { length } + & attribute style:height { zeroToHundredPercent }? + & attribute style:vertical-align { "top" | "middle" | "bottom" }? + & attribute style:color { color }? +style-table-properties = + element style:table-properties { + style-table-properties-content-strict + } +style-table-properties-content-strict = + style-table-properties-attlist, style-table-properties-elements +style-table-properties-attlist = + attribute style:width { positiveLength }? + & attribute style:rel-width { percent }? + & attribute table:align { "left" | "center" | "right" | "margins" }? + & common-horizontal-margin-attlist + & common-vertical-margin-attlist + & common-margin-attlist + & common-page-number-attlist + & common-break-attlist + & common-background-color-attlist + & common-shadow-attlist + & common-keep-with-next-attlist + & attribute style:may-break-between-rows { boolean }? + & attribute table:border-model { "collapsing" | "separating" }? + & common-writing-mode-attlist + & attribute table:display { boolean }? +style-table-properties-elements = style-background-image +style-table-column-properties = + element style:table-column-properties { + style-table-column-properties-content-strict + } +style-table-column-properties-content-strict = + style-table-column-properties-attlist, + style-table-column-properties-elements +style-table-column-properties-elements = empty +style-table-column-properties-attlist = + attribute style:column-width { positiveLength }? + & attribute style:rel-column-width { relativeLength }? + & attribute style:use-optimal-column-width { boolean }? + & common-break-attlist +style-table-row-properties = + element style:table-row-properties { + style-table-row-properties-content-strict + } +style-table-row-properties-content-strict = + style-table-row-properties-attlist, + style-table-row-properties-elements +style-table-row-properties-attlist = + attribute style:row-height { positiveLength }? + & attribute style:min-row-height { nonNegativeLength }? + & attribute style:use-optimal-row-height { boolean }? + & common-background-color-attlist + & common-break-attlist + & attribute fo:keep-together { "auto" | "always" }? +style-table-row-properties-elements = style-background-image +style-table-cell-properties = + element style:table-cell-properties { + style-table-cell-properties-content-strict + } +style-table-cell-properties-content-strict = + style-table-cell-properties-attlist, + style-table-cell-properties-elements +style-table-cell-properties-attlist = + attribute style:vertical-align { + "top" | "middle" | "bottom" | "automatic" + }? + & attribute style:text-align-source { "fix" | "value-type" }? + & common-style-direction-attlist + & attribute style:glyph-orientation-vertical { + "auto" | "0" | "0deg" | "0rad" | "0grad" + }? + & common-writing-mode-attlist + & common-shadow-attlist + & common-background-color-attlist + & common-border-attlist + & attribute style:diagonal-tl-br { \string }? + & attribute style:diagonal-tl-br-widths { borderWidths }? + & attribute style:diagonal-bl-tr { \string }? + & attribute style:diagonal-bl-tr-widths { borderWidths }? + & common-border-line-width-attlist + & common-padding-attlist + & attribute fo:wrap-option { "no-wrap" | "wrap" }? + & common-rotation-angle-attlist + & attribute style:rotation-align { + "none" | "bottom" | "top" | "center" + }? + & attribute style:cell-protect { + "none" + | "hidden-and-protected" + | list { ("protected" | "formula-hidden")+ } + }? + & attribute style:print-content { boolean }? + & attribute style:decimal-places { nonNegativeInteger }? + & attribute style:repeat-content { boolean }? + & attribute style:shrink-to-fit { boolean }? +common-style-direction-attlist = + attribute style:direction { "ltr" | "ttb" }? +style-table-cell-properties-elements = style-background-image +common-rotation-angle-attlist = + attribute style:rotation-angle { angle }? +style-list-level-properties = + element style:list-level-properties { + style-list-level-properties-content-strict + } +style-list-level-properties-content-strict = + style-list-level-properties-attlist, + style-list-level-properties-elements +style-list-level-properties-attlist = + common-text-align + & attribute text:space-before { length }? + & attribute text:min-label-width { nonNegativeLength }? + & attribute text:min-label-distance { nonNegativeLength }? + & attribute style:font-name { \string }? + & attribute fo:width { positiveLength }? + & attribute fo:height { positiveLength }? + & common-vertical-rel-attlist + & common-vertical-pos-attlist + & attribute text:list-level-position-and-space-mode { + "label-width-and-position" | "label-alignment" + }? +style-list-level-properties-elements = style-list-level-label-alignment +style-list-level-label-alignment = + element style:list-level-label-alignment { + style-list-level-label-alignment-attlist, empty + }? +style-list-level-label-alignment-attlist = + attribute text:label-followed-by { "listtab" | "space" | "nothing" } + & attribute text:list-tab-stop-position { length }? + & attribute fo:text-indent { length }? + & attribute fo:margin-left { length }? +style-graphic-properties-attlist = + attribute draw:stroke { "none" | "dash" | "solid" }? + & attribute draw:stroke-dash { styleNameRef }? + & attribute draw:stroke-dash-names { styleNameRefs }? + & attribute svg:stroke-width { length }? + & attribute svg:stroke-color { color }? + & attribute draw:marker-start { styleNameRef }? + & attribute draw:marker-end { styleNameRef }? + & attribute draw:marker-start-width { length }? + & attribute draw:marker-end-width { length }? + & attribute draw:marker-start-center { boolean }? + & attribute draw:marker-end-center { boolean }? + & attribute svg:stroke-opacity { + xsd:double { minInclusive = "0" maxInclusive = "1" } + | zeroToHundredPercent + }? + & attribute draw:stroke-linejoin { + "miter" | "round" | "bevel" | "middle" | "none" + }? + & attribute svg:stroke-linecap { "butt" | "square" | "round" }? + & attribute draw:symbol-color { color }? + & attribute text:animation { + "none" | "scroll" | "alternate" | "slide" + }? + & attribute text:animation-direction { + "left" | "right" | "up" | "down" + }? + & attribute text:animation-start-inside { boolean }? + & attribute text:animation-stop-inside { boolean }? + & attribute text:animation-repeat { nonNegativeInteger }? + & attribute text:animation-delay { duration }? + & attribute text:animation-steps { length }? + & attribute draw:auto-grow-width { boolean }? + & attribute draw:auto-grow-height { boolean }? + & attribute draw:fit-to-size { boolean }? + & attribute draw:fit-to-contour { boolean }? + & attribute draw:textarea-vertical-align { + "top" | "middle" | "bottom" | "justify" + }? + & attribute draw:textarea-horizontal-align { + "left" | "center" | "right" | "justify" + }? + & attribute fo:wrap-option { "no-wrap" | "wrap" }? + & attribute style:shrink-to-fit { boolean }? + & attribute draw:color-mode { + "greyscale" | "mono" | "watermark" | "standard" + }? + & attribute draw:color-inversion { boolean }? + & attribute draw:luminance { zeroToHundredPercent }? + & attribute draw:contrast { percent }? + & attribute draw:gamma { percent }? + & attribute draw:red { signedZeroToHundredPercent }? + & attribute draw:green { signedZeroToHundredPercent }? + & attribute draw:blue { signedZeroToHundredPercent }? + & attribute draw:image-opacity { zeroToHundredPercent }? + & attribute draw:shadow { "visible" | "hidden" }? + & attribute draw:shadow-offset-x { length }? + & attribute draw:shadow-offset-y { length }? + & attribute draw:shadow-color { color }? + & attribute draw:shadow-opacity { zeroToHundredPercent }? + & attribute draw:start-line-spacing-horizontal { distance }? + & attribute draw:start-line-spacing-vertical { distance }? + & attribute draw:end-line-spacing-horizontal { distance }? + & attribute draw:end-line-spacing-vertical { distance }? + & attribute draw:line-distance { distance }? + & attribute draw:guide-overhang { length }? + & attribute draw:guide-distance { distance }? + & attribute draw:start-guide { length }? + & attribute draw:end-guide { length }? + & attribute draw:placing { "below" | "above" }? + & attribute draw:parallel { boolean }? + & attribute draw:measure-align { + "automatic" | "left-outside" | "inside" | "right-outside" + }? + & attribute draw:measure-vertical-align { + "automatic" | "above" | "below" | "center" + }? + & attribute draw:unit { + "automatic" + | "mm" + | "cm" + | "m" + | "km" + | "pt" + | "pc" + | "inch" + | "ft" + | "mi" + }? + & attribute draw:show-unit { boolean }? + & attribute draw:decimal-places { nonNegativeInteger }? + & attribute draw:caption-type { + "straight-line" | "angled-line" | "angled-connector-line" + }? + & attribute draw:caption-angle-type { "fixed" | "free" }? + & attribute draw:caption-angle { angle }? + & attribute draw:caption-gap { distance }? + & attribute draw:caption-escape-direction { + "horizontal" | "vertical" | "auto" + }? + & attribute draw:caption-escape { length | percent }? + & attribute draw:caption-line-length { length }? + & attribute draw:caption-fit-line-length { boolean }? + & attribute dr3d:horizontal-segments { nonNegativeInteger }? + & attribute dr3d:vertical-segments { nonNegativeInteger }? + & attribute dr3d:edge-rounding { percent }? + & attribute dr3d:edge-rounding-mode { "correct" | "attractive" }? + & attribute dr3d:back-scale { percent }? + & attribute dr3d:depth { length }? + & attribute dr3d:backface-culling { "enabled" | "disabled" }? + & attribute dr3d:end-angle { angle }? + & attribute dr3d:close-front { boolean }? + & attribute dr3d:close-back { boolean }? + & attribute dr3d:lighting-mode { "standard" | "double-sided" }? + & attribute dr3d:normals-kind { "object" | "flat" | "sphere" }? + & attribute dr3d:normals-direction { "normal" | "inverse" }? + & attribute dr3d:texture-generation-mode-x { + "object" | "parallel" | "sphere" + }? + & attribute dr3d:texture-generation-mode-y { + "object" | "parallel" | "sphere" + }? + & attribute dr3d:texture-kind { "luminance" | "intensity" | "color" }? + & attribute dr3d:texture-filter { "enabled" | "disabled" }? + & attribute dr3d:texture-mode { "replace" | "modulate" | "blend" }? + & attribute dr3d:ambient-color { color }? + & attribute dr3d:emissive-color { color }? + & attribute dr3d:specular-color { color }? + & attribute dr3d:diffuse-color { color }? + & attribute dr3d:shininess { percent }? + & attribute dr3d:shadow { "visible" | "hidden" }? + & common-draw-rel-size-attlist + & attribute fo:min-width { length | percent }? + & attribute fo:min-height { length | percent }? + & attribute fo:max-height { length | percent }? + & attribute fo:max-width { length | percent }? + & common-horizontal-margin-attlist + & common-vertical-margin-attlist + & common-margin-attlist + & attribute style:print-content { boolean }? + & attribute style:protect { + "none" + | list { ("content" | "position" | "size")+ } + }? + & attribute style:horizontal-pos { + "left" + | "center" + | "right" + | "from-left" + | "inside" + | "outside" + | "from-inside" + }? + & attribute svg:x { coordinate }? + & attribute style:horizontal-rel { + "page" + | "page-content" + | "page-start-margin" + | "page-end-margin" + | "frame" + | "frame-content" + | "frame-start-margin" + | "frame-end-margin" + | "paragraph" + | "paragraph-content" + | "paragraph-start-margin" + | "paragraph-end-margin" + | "char" + }? + & common-vertical-pos-attlist + & common-vertical-rel-attlist + & common-text-anchor-attlist + & common-border-attlist + & common-border-line-width-attlist + & common-padding-attlist + & common-shadow-attlist + & common-background-color-attlist + & common-background-transparency-attlist + & common-editable-attlist + & attribute style:wrap { + "none" + | "left" + | "right" + | "parallel" + | "dynamic" + | "run-through" + | "biggest" + }? + & attribute style:wrap-dynamic-threshold { nonNegativeLength }? + & attribute style:number-wrapped-paragraphs { + "no-limit" | positiveInteger + }? + & attribute style:wrap-contour { boolean }? + & attribute style:wrap-contour-mode { "full" | "outside" }? + & attribute style:run-through { "foreground" | "background" }? + & attribute style:flow-with-text { boolean }? + & attribute style:overflow-behavior { + "clip" | "auto-create-new-frame" + }? + & attribute style:mirror { + "none" + | "vertical" + | horizontal-mirror + | list { "vertical", horizontal-mirror } + | list { horizontal-mirror, "vertical" } + }? + & attribute fo:clip { "auto" | clipShape }? + & attribute draw:wrap-influence-on-position { + "iterative" | "once-concurrent" | "once-successive" + }? + & common-writing-mode-attlist + & attribute draw:frame-display-scrollbar { boolean }? + & attribute draw:frame-display-border { boolean }? + & attribute draw:frame-margin-horizontal { nonNegativePixelLength }? + & attribute draw:frame-margin-vertical { nonNegativePixelLength }? + & attribute draw:visible-area-left { nonNegativeLength }? + & attribute draw:visible-area-top { nonNegativeLength }? + & attribute draw:visible-area-width { positiveLength }? + & attribute draw:visible-area-height { positiveLength }? + & attribute draw:draw-aspect { + "content" | "thumbnail" | "icon" | "print-view" + }? + & attribute draw:ole-draw-aspect { nonNegativeInteger }? +style-graphic-fill-properties-attlist = + attribute draw:fill { + "none" | "solid" | "bitmap" | "gradient" | "hatch" + }? + & attribute draw:fill-color { color }? + & attribute draw:secondary-fill-color { color }? + & attribute draw:fill-gradient-name { styleNameRef }? + & attribute draw:gradient-step-count { nonNegativeInteger }? + & attribute draw:fill-hatch-name { styleNameRef }? + & attribute draw:fill-hatch-solid { boolean }? + & attribute draw:fill-image-name { styleNameRef }? + & attribute style:repeat { "no-repeat" | "repeat" | "stretch" }? + & attribute draw:fill-image-width { length | percent }? + & attribute draw:fill-image-height { length | percent }? + & attribute draw:fill-image-ref-point-x { percent }? + & attribute draw:fill-image-ref-point-y { percent }? + & attribute draw:fill-image-ref-point { + "top-left" + | "top" + | "top-right" + | "left" + | "center" + | "right" + | "bottom-left" + | "bottom" + | "bottom-right" + }? + & attribute draw:tile-repeat-offset { + list { zeroToHundredPercent, ("horizontal" | "vertical") } + }? + & attribute draw:opacity { zeroToHundredPercent }? + & attribute draw:opacity-name { styleNameRef }? + & attribute svg:fill-rule { "nonzero" | "evenodd" }? +style-graphic-properties-elements = + text-list-style? & style-background-image & style-columns +common-vertical-pos-attlist = + attribute style:vertical-pos { + "top" | "middle" | "bottom" | "from-top" | "below" + }?, + attribute svg:y { coordinate }? +common-vertical-rel-attlist = + attribute style:vertical-rel { + "page" + | "page-content" + | "frame" + | "frame-content" + | "paragraph" + | "paragraph-content" + | "char" + | "line" + | "baseline" + | "text" + }? +common-editable-attlist = attribute style:editable { boolean }? +horizontal-mirror = + "horizontal" | "horizontal-on-odd" | "horizontal-on-even" +clipShape = + xsd:string { + pattern = + "rect\([ ]*((-?([0-9]+(\.[0-9]*)?|\.[0-9]+)((cm)|(mm)|(in)|(pt)|(pc)))|(auto))([ ]*,[ ]*((-?([0-9]+(\.[0-9]*)?|\.[0-9]+)((cm)|(mm)|(in)|(pt)|(pc))))|(auto)){3}[ ]*\)" + } +nonNegativePixelLength = + xsd:string { pattern = "([0-9]+(\.[0-9]*)?|\.[0-9]+)(px)" } +style-chart-properties = + element style:chart-properties { + style-chart-properties-content-strict + } +style-chart-properties-content-strict = + style-chart-properties-attlist, style-chart-properties-elements +style-chart-properties-elements = empty +style-chart-properties-attlist = + attribute chart:scale-text { boolean }? + & attribute chart:three-dimensional { boolean }? + & attribute chart:deep { boolean }? + & attribute chart:right-angled-axes { boolean }? + & (attribute chart:symbol-type { "none" } + | attribute chart:symbol-type { "automatic" } + | (attribute chart:symbol-type { "named-symbol" }, + attribute chart:symbol-name { + "square" + | "diamond" + | "arrow-down" + | "arrow-up" + | "arrow-right" + | "arrow-left" + | "bow-tie" + | "hourglass" + | "circle" + | "star" + | "x" + | "plus" + | "asterisk" + | "horizontal-bar" + | "vertical-bar" + }) + | (attribute chart:symbol-type { "image" }, + element chart:symbol-image { + attribute xlink:href { anyIRI } + }) + | empty) + & attribute chart:symbol-width { nonNegativeLength }? + & attribute chart:symbol-height { nonNegativeLength }? + & attribute chart:sort-by-x-values { boolean }? + & attribute chart:vertical { boolean }? + & attribute chart:connect-bars { boolean }? + & attribute chart:gap-width { integer }? + & attribute chart:overlap { integer }? + & attribute chart:group-bars-per-axis { boolean }? + & attribute chart:japanese-candle-stick { boolean }? + & attribute chart:interpolation { + "none" | "cubic-spline" | "b-spline" + }? + & attribute chart:spline-order { positiveInteger }? + & attribute chart:spline-resolution { positiveInteger }? + & attribute chart:pie-offset { nonNegativeInteger }? + & attribute chart:angle-offset { angle }? + & attribute chart:hole-size { percent }? + & attribute chart:lines { boolean }? + & attribute chart:solid-type { + "cuboid" | "cylinder" | "cone" | "pyramid" + }? + & attribute chart:stacked { boolean }? + & attribute chart:percentage { boolean }? + & attribute chart:treat-empty-cells { + "use-zero" | "leave-gap" | "ignore" + }? + & attribute chart:link-data-style-to-source { boolean }? + & attribute chart:logarithmic { boolean }? + & attribute chart:maximum { double }? + & attribute chart:minimum { double }? + & attribute chart:origin { double }? + & attribute chart:interval-major { double }? + & attribute chart:interval-minor-divisor { positiveInteger }? + & attribute chart:tick-marks-major-inner { boolean }? + & attribute chart:tick-marks-major-outer { boolean }? + & attribute chart:tick-marks-minor-inner { boolean }? + & attribute chart:tick-marks-minor-outer { boolean }? + & attribute chart:reverse-direction { boolean }? + & attribute chart:display-label { boolean }? + & attribute chart:text-overlap { boolean }? + & attribute text:line-break { boolean }? + & attribute chart:label-arrangement { + "side-by-side" | "stagger-even" | "stagger-odd" + }? + & common-style-direction-attlist + & common-rotation-angle-attlist + & attribute chart:data-label-number { + "none" | "value" | "percentage" | "value-and-percentage" + }? + & attribute chart:data-label-text { boolean }? + & attribute chart:data-label-symbol { boolean }? + & element chart:label-separator { text-p }? + & attribute chart:label-position { labelPositions }? + & attribute chart:label-position-negative { labelPositions }? + & attribute chart:visible { boolean }? + & attribute chart:auto-position { boolean }? + & attribute chart:auto-size { boolean }? + & attribute chart:mean-value { boolean }? + & attribute chart:error-category { + "none" + | "variance" + | "standard-deviation" + | "percentage" + | "error-margin" + | "constant" + | "standard-error" + | "cell-range" + }? + & attribute chart:error-percentage { double }? + & attribute chart:error-margin { double }? + & attribute chart:error-lower-limit { double }? + & attribute chart:error-upper-limit { double }? + & attribute chart:error-upper-indicator { boolean }? + & attribute chart:error-lower-indicator { boolean }? + & attribute chart:error-lower-range { cellRangeAddressList }? + & attribute chart:error-upper-range { cellRangeAddressList }? + & attribute chart:series-source { "columns" | "rows" }? + & attribute chart:regression-type { + "none" | "linear" | "logarithmic" | "exponential" | "power" + }? + & attribute chart:axis-position { "start" | "end" | double }? + & attribute chart:axis-label-position { + "near-axis" + | "near-axis-other-side" + | "outside-start" + | "outside-end" + }? + & attribute chart:tick-mark-position { + "at-labels" | "at-axis" | "at-labels-and-axis" + }? + & attribute chart:include-hidden-cells { boolean }? +labelPositions = + "avoid-overlap" + | "center" + | "top" + | "top-right" + | "right" + | "bottom-right" + | "bottom" + | "bottom-left" + | "left" + | "top-left" + | "inside" + | "outside" + | "near-origin" +style-drawing-page-properties-attlist = + attribute presentation:transition-type { + "manual" | "automatic" | "semi-automatic" + }? + & attribute presentation:transition-style { + "none" + | "fade-from-left" + | "fade-from-top" + | "fade-from-right" + | "fade-from-bottom" + | "fade-from-upperleft" + | "fade-from-upperright" + | "fade-from-lowerleft" + | "fade-from-lowerright" + | "move-from-left" + | "move-from-top" + | "move-from-right" + | "move-from-bottom" + | "move-from-upperleft" + | "move-from-upperright" + | "move-from-lowerleft" + | "move-from-lowerright" + | "uncover-to-left" + | "uncover-to-top" + | "uncover-to-right" + | "uncover-to-bottom" + | "uncover-to-upperleft" + | "uncover-to-upperright" + | "uncover-to-lowerleft" + | "uncover-to-lowerright" + | "fade-to-center" + | "fade-from-center" + | "vertical-stripes" + | "horizontal-stripes" + | "clockwise" + | "counterclockwise" + | "open-vertical" + | "open-horizontal" + | "close-vertical" + | "close-horizontal" + | "wavyline-from-left" + | "wavyline-from-top" + | "wavyline-from-right" + | "wavyline-from-bottom" + | "spiralin-left" + | "spiralin-right" + | "spiralout-left" + | "spiralout-right" + | "roll-from-top" + | "roll-from-left" + | "roll-from-right" + | "roll-from-bottom" + | "stretch-from-left" + | "stretch-from-top" + | "stretch-from-right" + | "stretch-from-bottom" + | "vertical-lines" + | "horizontal-lines" + | "dissolve" + | "random" + | "vertical-checkerboard" + | "horizontal-checkerboard" + | "interlocking-horizontal-left" + | "interlocking-horizontal-right" + | "interlocking-vertical-top" + | "interlocking-vertical-bottom" + | "fly-away" + | "open" + | "close" + | "melt" + }? + & attribute presentation:transition-speed { presentationSpeeds }? + & attribute smil:type { \string }? + & attribute smil:subtype { \string }? + & attribute smil:direction { "forward" | "reverse" }? + & attribute smil:fadeColor { color }? + & attribute presentation:duration { duration }? + & attribute presentation:visibility { "visible" | "hidden" }? + & attribute draw:background-size { "full" | "border" }? + & attribute presentation:background-objects-visible { boolean }? + & attribute presentation:background-visible { boolean }? + & attribute presentation:display-header { boolean }? + & attribute presentation:display-footer { boolean }? + & attribute presentation:display-page-number { boolean }? + & attribute presentation:display-date-time { boolean }? +style-drawing-page-properties-elements = presentation-sound? +\string = xsd:string +date = xsd:date +time = xsd:time +dateTime = xsd:dateTime +duration = xsd:duration +integer = xsd:integer +nonNegativeInteger = xsd:nonNegativeInteger +positiveInteger = xsd:positiveInteger +double = xsd:double +anyURI = xsd:anyURI +base64Binary = xsd:base64Binary +ID = xsd:ID +IDREF = xsd:IDREF +IDREFS = xsd:IDREFS +NCName = xsd:NCName +boolean = "true" | "false" +dateOrDateTime = xsd:date | xsd:dateTime +timeOrDateTime = xsd:time | xsd:dateTime +language = xsd:language +countryCode = xsd:token { pattern = "[A-Za-z0-9]{1,8}" } +languageCode = xsd:token { pattern = "[A-Za-z]{1,8}" } +scriptCode = xsd:token { pattern = "[A-Za-z0-9]{1,8}" } +character = xsd:string { length = "1" } +length = + xsd:string { + pattern = + "-?([0-9]+(\.[0-9]*)?|\.[0-9]+)((cm)|(mm)|(in)|(pt)|(pc)|(px))" + } +nonNegativeLength = + xsd:string { + pattern = + "([0-9]+(\.[0-9]*)?|\.[0-9]+)((cm)|(mm)|(in)|(pt)|(pc)|(px))" + } +positiveLength = + xsd:string { + pattern = + "([0-9]*[1-9][0-9]*(\.[0-9]*)?|0+\.[0-9]*[1-9][0-9]*|\.[0-9]*[1-9][0-9]*)((cm)|(mm)|(in)|(pt)|(pc)|(px))" + } +percent = xsd:string { pattern = "-?([0-9]+(\.[0-9]*)?|\.[0-9]+)%" } +zeroToHundredPercent = + xsd:string { + pattern = "([0-9]?[0-9](\.[0-9]*)?|100(\.0*)?|\.[0-9]+)%" + } +signedZeroToHundredPercent = + xsd:string { + pattern = "-?([0-9]?[0-9](\.[0-9]*)?|100(\.0*)?|\.[0-9]+)%" + } +relativeLength = xsd:string { pattern = "[0-9]+\*" } +coordinate = length +distance = length +color = xsd:string { pattern = "#[0-9a-fA-F]{6}" } +angle = xsd:string +CURIE = + xsd:string { pattern = "(([\i-[:]][\c-[:]]*)?:)?.+" minLength = "1" } +CURIEs = list { CURIE+ } +SafeCURIE = + xsd:string { + pattern = "\[(([\i-[:]][\c-[:]]*)?:)?.+\]" + minLength = "3" + } +URIorSafeCURIE = anyURI | SafeCURIE +styleName = xsd:NCName +styleNameRef = xsd:NCName | empty +styleNameRefs = list { xsd:NCName* } +variableName = xsd:string +targetFrameName = "_self" | "_blank" | "_parent" | "_top" | \string +valueType = + "float" + | "time" + | "date" + | "percentage" + | "currency" + | "boolean" + | "string" +points = + xsd:string { pattern = "-?[0-9]+,-?[0-9]+([ ]+-?[0-9]+,-?[0-9]+)*" } +pathData = xsd:string +vector3D = + xsd:string { + pattern = + "\([ ]*-?([0-9]+(\.[0-9]*)?|\.[0-9]+)([ ]+-?([0-9]+(\.[0-9]*)?|\.[0-9]+)){2}[ ]*\)" + } +namespacedToken = xsd:QName { pattern = "[^:]+:[^:]+" } +anyIRI = + xsd:anyURI + >> dc:description [ + "An IRI-reference as defined in [RFC3987]. See ODF 1.2 Part 1 section 18.3." + ] +anyAttListOrElements = + attribute * { text }*, + anyElements +anyElements = + element * { + mixed { anyAttListOrElements } + }* diff --git a/lisp/org/ob-C.el b/lisp/org/ob-C.el index 77cfd53785..2bdda68d58 100644 --- a/lisp/org/ob-C.el +++ b/lisp/org/ob-C.el @@ -1,8 +1,9 @@ -;;; ob-C.el --- org-babel functions for C and similar languages +;;; ob-C.el --- Babel Functions for C and Similar Languages -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. ;; Author: Eric Schulte +;; Thierry Banel ;; Keywords: literate programming, reproducible research ;; Homepage: http://orgmode.org @@ -23,37 +24,57 @@ ;;; Commentary: -;; Org-Babel support for evaluating C code. +;; Org-Babel support for evaluating C, C++, D code. ;; ;; very limited implementation: ;; - currently only support :results output ;; - not much in the way of error feedback ;;; Code: -(eval-when-compile - (require 'cl)) -(require 'ob) + (require 'cc-mode) +(require 'ob) -(declare-function org-entry-get "org" - (pom property &optional inherit literal-nil)) +(declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) +(declare-function org-remove-indentation "org" (code &optional n)) +(declare-function org-trim "org" (s &optional keep-lead)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("C++" . "cpp")) +(add-to-list 'org-babel-tangle-lang-exts '("D" . "d")) (defvar org-babel-default-header-args:C '()) -(defvar org-babel-C-compiler "gcc" - "Command used to compile a C source code file into an -executable.") - -(defvar org-babel-C++-compiler "g++" - "Command used to compile a C++ source code file into an -executable.") +(defcustom org-babel-C-compiler "gcc" + "Command used to compile a C source code file into an executable. +May be either a command in the path, like gcc +or an absolute path name, like /usr/local/bin/gcc +parameter may be used, like gcc -v" + :group 'org-babel + :version "24.3" + :type 'string) + +(defcustom org-babel-C++-compiler "g++" + "Command used to compile a C++ source code file into an executable. +May be either a command in the path, like g++ +or an absolute path name, like /usr/local/bin/g++ +parameter may be used, like g++ -v" + :group 'org-babel + :version "24.3" + :type 'string) + +(defcustom org-babel-D-compiler "rdmd" + "Command used to compile and execute a D source code file. +May be either a command in the path, like rdmd +or an absolute path name, like /usr/local/bin/rdmd +parameter may be used, like rdmd --chatty" + :group 'org-babel + :version "24.3" + :type 'string) (defvar org-babel-c-variant nil - "Internal variable used to hold which type of C (e.g. C or C++) + "Internal variable used to hold which type of C (e.g. C or C++ or D) is currently being evaluated.") (defun org-babel-execute:cpp (body params) @@ -61,88 +82,189 @@ is currently being evaluated.") This function calls `org-babel-execute:C++'." (org-babel-execute:C++ body params)) +(defun org-babel-expand-body:cpp (body params) + "Expand a block of C++ code with org-babel according to its +header arguments." + (org-babel-expand-body:C++ body params)) + (defun org-babel-execute:C++ (body params) "Execute a block of C++ code with org-babel. This function is called by `org-babel-execute-src-block'." (let ((org-babel-c-variant 'cpp)) (org-babel-C-execute body params))) (defun org-babel-expand-body:C++ (body params) - "Expand a block of C++ code with org-babel according to it's -header arguments (calls `org-babel-C-expand')." - (let ((org-babel-c-variant 'cpp)) (org-babel-C-expand body params))) + "Expand a block of C++ code with org-babel according to its +header arguments." + (let ((org-babel-c-variant 'cpp)) (org-babel-C-expand-C++ body params))) + +(defun org-babel-execute:D (body params) + "Execute a block of D code with org-babel. +This function is called by `org-babel-execute-src-block'." + (let ((org-babel-c-variant 'd)) (org-babel-C-execute body params))) + +(defun org-babel-expand-body:D (body params) + "Expand a block of D code with org-babel according to its +header arguments." + (let ((org-babel-c-variant 'd)) (org-babel-C-expand-D body params))) (defun org-babel-execute:C (body params) "Execute a block of C code with org-babel. This function is called by `org-babel-execute-src-block'." (let ((org-babel-c-variant 'c)) (org-babel-C-execute body params))) -(defun org-babel-expand-body:c (body params) - "Expand a block of C code with org-babel according to it's -header arguments (calls `org-babel-C-expand')." - (let ((org-babel-c-variant 'c)) (org-babel-C-expand body params))) +(defun org-babel-expand-body:C (body params) + "Expand a block of C code with org-babel according to its +header arguments." + (let ((org-babel-c-variant 'c)) (org-babel-C-expand-C body params))) (defun org-babel-C-execute (body params) "This function should only be called by `org-babel-execute:C' -or `org-babel-execute:C++'." +or `org-babel-execute:C++' or `org-babel-execute:D'." (let* ((tmp-src-file (org-babel-temp-file "C-src-" - (cond - ((equal org-babel-c-variant 'c) ".c") - ((equal org-babel-c-variant 'cpp) ".cpp")))) - (tmp-bin-file (org-babel-temp-file "C-bin-" org-babel-exeext)) - (cmdline (cdr (assoc :cmdline params))) - (flags (cdr (assoc :flags params))) - (full-body (org-babel-C-expand body params)) - (compile - (progn - (with-temp-file tmp-src-file (insert full-body)) - (org-babel-eval - (format "%s -o %s %s %s" - (cond - ((equal org-babel-c-variant 'c) org-babel-C-compiler) - ((equal org-babel-c-variant 'cpp) org-babel-C++-compiler)) - (org-babel-process-file-name tmp-bin-file) - (mapconcat 'identity - (if (listp flags) flags (list flags)) " ") - (org-babel-process-file-name tmp-src-file)) "")))) + (pcase org-babel-c-variant + (`c ".c") (`cpp ".cpp") (`d ".d")))) + (tmp-bin-file ;not used for D + (org-babel-process-file-name + (org-babel-temp-file "C-bin-" org-babel-exeext))) + (cmdline (cdr (assq :cmdline params))) + (cmdline (if cmdline (concat " " cmdline) "")) + (flags (cdr (assq :flags params))) + (flags (mapconcat 'identity + (if (listp flags) flags (list flags)) " ")) + (libs (org-babel-read + (or (cdr (assq :libs params)) + (org-entry-get nil "libs" t)) + nil)) + (libs (mapconcat #'identity + (if (listp libs) libs (list libs)) + " ")) + (full-body + (pcase org-babel-c-variant + (`c (org-babel-C-expand-C body params)) + (`cpp (org-babel-C-expand-C++ body params)) + (`d (org-babel-C-expand-D body params))))) + (with-temp-file tmp-src-file (insert full-body)) + (pcase org-babel-c-variant + ((or `c `cpp) + (org-babel-eval + (format "%s -o %s %s %s %s" + (pcase org-babel-c-variant + (`c org-babel-C-compiler) + (`cpp org-babel-C++-compiler)) + tmp-bin-file + flags + (org-babel-process-file-name tmp-src-file) + libs) + "")) + (`d nil)) ;; no separate compilation for D (let ((results - (org-babel-trim - (org-babel-eval - (concat tmp-bin-file (if cmdline (concat " " cmdline) "")) "")))) - (org-babel-reassemble-table - (org-babel-result-cond (cdr (assoc :result-params params)) - (org-babel-read results) - (let ((tmp-file (org-babel-temp-file "c-"))) - (with-temp-file tmp-file (insert results)) - (org-babel-import-elisp-from-file tmp-file))) - (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) - (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params))))) - )) - -(defun org-babel-C-expand (body params) + (org-babel-eval + (pcase org-babel-c-variant + ((or `c `cpp) + (concat tmp-bin-file cmdline)) + (`d + (format "%s %s %s %s" + org-babel-D-compiler + flags + (org-babel-process-file-name tmp-src-file) + cmdline))) + ""))) + (when results + (setq results (org-trim (org-remove-indentation results))) + (org-babel-reassemble-table + (org-babel-result-cond (cdr (assq :result-params params)) + (org-babel-read results t) + (let ((tmp-file (org-babel-temp-file "c-"))) + (with-temp-file tmp-file (insert results)) + (org-babel-import-elisp-from-file tmp-file))) + (org-babel-pick-name + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) + (org-babel-pick-name + (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))) + ))) + +(defun org-babel-C-expand-C++ (body params) "Expand a block of C or C++ code with org-babel according to -it's header arguments." - (let ((vars (mapcar #'cdr (org-babel-get-header params :var))) - (main-p (not (string= (cdr (assoc :main params)) "no"))) - (includes (or (cdr (assoc :includes params)) - (org-babel-read (org-entry-get nil "includes" t)))) - (defines (org-babel-read - (or (cdr (assoc :defines params)) - (org-babel-read (org-entry-get nil "defines" t)))))) +its header arguments." + (org-babel-C-expand-C body params)) + +(defun org-babel-C-expand-C (body params) + "Expand a block of C or C++ code with org-babel according to +its header arguments." + (let ((vars (org-babel--get-vars params)) + (colnames (cdr (assq :colname-names params))) + (main-p (not (string= (cdr (assq :main params)) "no"))) + (includes (org-babel-read + (or (cdr (assq :includes params)) + (org-entry-get nil "includes" t)) + nil)) + (defines (org-babel-read + (or (cdr (assq :defines params)) + (org-entry-get nil "defines" t)) + nil))) + (when (stringp includes) + (setq includes (split-string includes))) + (when (stringp defines) + (let ((y nil) + (result (list t))) + (dolist (x (split-string defines)) + (if (null y) + (setq y x) + (nconc result (list (concat y " " x))) + (setq y nil))) + (setq defines (cdr result)))) (mapconcat 'identity (list ;; includes (mapconcat (lambda (inc) (format "#include %s" inc)) - (if (listp includes) includes (list includes)) "\n") + includes "\n") ;; defines (mapconcat (lambda (inc) (format "#define %s" inc)) (if (listp defines) defines (list defines)) "\n") ;; variables (mapconcat 'org-babel-C-var-to-C vars "\n") + ;; table sizes + (mapconcat 'org-babel-C-table-sizes-to-C vars "\n") + ;; tables headers utility + (when colnames + (org-babel-C-utility-header-to-C)) + ;; tables headers + (mapconcat 'org-babel-C-header-to-C colnames "\n") + ;; body + (if main-p + (org-babel-C-ensure-main-wrap body) + body) "\n") "\n"))) + +(defun org-babel-C-expand-D (body params) + "Expand a block of D code with org-babel according to +its header arguments." + (let ((vars (org-babel--get-vars params)) + (colnames (cdr (assq :colname-names params))) + (main-p (not (string= (cdr (assq :main params)) "no"))) + (imports (or (cdr (assq :imports params)) + (org-babel-read (org-entry-get nil "imports" t))))) + (when (stringp imports) + (setq imports (split-string imports))) + (setq imports (append imports '("std.stdio" "std.conv"))) + (mapconcat 'identity + (list + "module mmm;" + ;; imports + (mapconcat + (lambda (inc) (format "import %s;" inc)) + imports "\n") + ;; variables + (mapconcat 'org-babel-C-var-to-C vars "\n") + ;; table sizes + (mapconcat 'org-babel-C-table-sizes-to-C vars "\n") + ;; tables headers utility + (when colnames + (org-babel-C-utility-header-to-C)) + ;; tables headers + (mapconcat 'org-babel-C-header-to-C colnames "\n") ;; body (if main-p (org-babel-C-ensure-main-wrap body) @@ -154,12 +276,12 @@ it's header arguments." body (format "int main() {\n%s\nreturn 0;\n}\n" body))) -(defun org-babel-prep-session:C (session params) +(defun org-babel-prep-session:C (_session _params) "This function does nothing as C is a compiled language with no support for sessions" (error "C is a compiled language -- no support for sessions")) -(defun org-babel-load-session:C (session body params) +(defun org-babel-load-session:C (_session _body _params) "This function does nothing as C is a compiled language with no support for sessions" (error "C is a compiled language -- no support for sessions")) @@ -177,58 +299,79 @@ support for sessions" "Determine the type of VAL. Return a list (TYPE-NAME FORMAT). TYPE-NAME should be the name of the type. FORMAT can be either a format string or a function which is called with VAL." + (let* ((basetype (org-babel-C-val-to-base-type val)) + (type + (pcase basetype + (`integerp '("int" "%d")) + (`floatp '("double" "%f")) + (`stringp + (list + (if (eq org-babel-c-variant 'd) "string" "const char*") + "\"%s\"")) + (_ (error "unknown type %S" basetype))))) + (cond + ((integerp val) type) ;; an integer declared in the #+begin_src line + ((floatp val) type) ;; a numeric declared in the #+begin_src line + ((and (listp val) (listp (car val))) ;; a table + `(,(car type) + (lambda (val) + (cons + (format "[%d][%d]" (length val) (length (car val))) + (concat + (if (eq org-babel-c-variant 'd) "[\n" "{\n") + (mapconcat + (lambda (v) + (concat + (if (eq org-babel-c-variant 'd) " [" " {") + (mapconcat (lambda (w) (format ,(cadr type) w)) v ",") + (if (eq org-babel-c-variant 'd) "]" "}"))) + val + ",\n") + (if (eq org-babel-c-variant 'd) "\n]" "\n}")))))) + ((or (listp val) (vectorp val)) ;; a list declared in the #+begin_src line + `(,(car type) + (lambda (val) + (cons + (format "[%d]" (length val)) + (concat + (if (eq org-babel-c-variant 'd) "[" "{") + (mapconcat (lambda (v) (format ,(cadr type) v)) val ",") + (if (eq org-babel-c-variant 'd) "]" "}")))))) + (t ;; treat unknown types as string + type)))) + +(defun org-babel-C-val-to-base-type (val) + "Determine the base type of VAL which may be +`integerp' if all base values are integers +`floatp' if all base values are either floating points or integers +`stringp' otherwise." (cond - ((integerp val) '("int" "%d")) - ((floatp val) '("double" "%f")) + ((integerp val) 'integerp) + ((floatp val) 'floatp) ((or (listp val) (vectorp val)) - (lexical-let ((type (org-babel-C-val-to-C-list-type val))) - (list (car type) - (lambda (val) - (cons - (format "[%d]%s" - (length val) - (car (org-babel-C-format-val type (elt val 0)))) - (concat "{ " - (mapconcat (lambda (v) - (cdr (org-babel-C-format-val type v))) - val - ", ") - " }")))))) - (t ;; treat unknown types as string - '("char" (lambda (val) - (let ((s (format "%s" val))) ;; convert to string for unknown types - (cons (format "[%d]" (1+ (length s))) - (concat "\"" s "\"")))))))) - -(defun org-babel-C-val-to-C-list-type (val) - "Determine the C array type of a VAL." - (let (type) - (mapc - #'(lambda (i) - (let* ((tmp-type (org-babel-C-val-to-C-type i)) - (type-name (car type)) - (tmp-type-name (car tmp-type))) - (when (and type (not (string= type-name tmp-type-name))) - (if (and (member type-name '("int" "double" "int32_t")) - (member tmp-type-name '("int" "double" "int32_t"))) - (setq tmp-type '("double" "" "%f")) - (error "Only homogeneous lists are supported by C. You can not mix %s and %s" - type-name - tmp-type-name))) - (setq type tmp-type))) - val) - type)) + (let ((type nil)) + (mapc (lambda (v) + (pcase (org-babel-C-val-to-base-type v) + (`stringp (setq type 'stringp)) + (`floatp + (if (or (not type) (eq type 'integerp)) + (setq type 'floatp))) + (`integerp + (unless type (setq type 'integerp))))) + val) + type)) + (t 'stringp))) (defun org-babel-C-var-to-C (pair) "Convert an elisp val into a string of C code specifying a var of the same value." ;; TODO list support (let ((var (car pair)) - (val (cdr pair))) + (val (cdr pair))) (when (symbolp val) (setq val (symbol-name val)) (when (= (length val) 1) - (setq val (string-to-char val)))) + (setq val (string-to-char val)))) (let* ((type-data (org-babel-C-val-to-C-type val)) (type (car type-data)) (formated (org-babel-C-format-val type-data val)) @@ -240,6 +383,66 @@ of the same value." suffix data)))) +(defun org-babel-C-table-sizes-to-C (pair) + "Create constants of table dimensions, if PAIR is a table." + (when (listp (cdr pair)) + (cond + ((listp (cadr pair)) ;; a table + (concat + (format "const int %s_rows = %d;" (car pair) (length (cdr pair))) + "\n" + (format "const int %s_cols = %d;" (car pair) (length (cadr pair))))) + (t ;; a list declared in the #+begin_src line + (format "const int %s_cols = %d;" (car pair) (length (cdr pair))))))) + +(defun org-babel-C-utility-header-to-C () + "Generate a utility function to convert a column name +into a column number." + (pcase org-babel-c-variant + ((or `c `cpp) + "int get_column_num (int nbcols, const char** header, const char* column) +{ + int c; + for (c=0; c. + +;;; Commentary: + +;; Org-Babel support for evaluating J code. +;; +;; Session interaction depends on `j-console' from package `j-mode' +;; (available in MELPA). + +;;; Code: + +(require 'ob) + +(declare-function org-trim "org" (s &optional keep-lead)) +(declare-function j-console-ensure-session "ext:j-console" ()) + +(defcustom org-babel-J-command "jconsole" + "Command to call J." + :group 'org-babel + :version "26.1" + :package-version '(Org . "9.0") + :type 'string) + +(defun org-babel-expand-body:J (body _params &optional _processed-params) + "Expand BODY according to PARAMS, return the expanded body. +PROCESSED-PARAMS isn't used yet." + (org-babel-J-interleave-echos-except-functions body)) + +(defun org-babel-J-interleave-echos (body) + "Interleave echo',' between each source line of BODY." + (mapconcat #'identity (split-string body "\n") "\necho','\n")) + +(defun org-babel-J-interleave-echos-except-functions (body) + "Interleave echo',' between source lines of BODY that aren't functions." + (if (obj-string-match-m "\\(?:^\\|\n\\)[^\n]*\\(?:0\\|1\\|2\\|3\\|4\\|dyad\\) : 0\n.*\n)\\(?:\n\\|$\\)" body) + (let ((s1 (substring body 0 (match-beginning 0))) + (s2 (match-string 0 body)) + (s3 (substring body (match-end 0)))) + (concat + (if (string= s1 "") + "" + (concat (org-babel-J-interleave-echos s1) + "\necho','\n")) + s2 + "\necho','\n" + (org-babel-J-interleave-echos-except-functions s3))) + (org-babel-J-interleave-echos body))) + +(defalias 'org-babel-execute:j 'org-babel-execute:J) + +(defun org-babel-execute:J (body params) + "Execute a block of J code BODY. +PARAMS are given by org-babel. +This function is called by `org-babel-execute-src-block'" + (message "executing J source code block") + (let* ((processed-params (org-babel-process-params params)) + (sessionp (cdr (assq :session params))) + (full-body (org-babel-expand-body:J + body params processed-params)) + (tmp-script-file (org-babel-temp-file "J-src"))) + (org-babel-j-initiate-session sessionp) + (org-babel-J-strip-whitespace + (if (string= sessionp "none") + (progn + (with-temp-file tmp-script-file + (insert full-body)) + (org-babel-eval (format "%s < %s" org-babel-J-command tmp-script-file) "")) + (org-babel-J-eval-string full-body))))) + +(defun org-babel-J-eval-string (str) + "Sends STR to the `j-console-cmd' session and exectues it." + (let ((session (j-console-ensure-session))) + (with-current-buffer (process-buffer session) + (goto-char (point-max)) + (insert (format "\n%s\n" str)) + (let ((beg (point))) + (comint-send-input) + (sit-for .1) + (buffer-substring-no-properties + beg (point-max)))))) + +(defun org-babel-J-strip-whitespace (str) + "Remove whitespace from jconsole output STR." + (mapconcat + #'identity + (delete "" (mapcar + #'org-babel-J-print-block + (split-string str "^ *,\n" t))) + "\n\n")) + +(defun obj-get-string-alignment (str) + "Return a number to describe STR alignment. +STR represents a table. +Positive/negative/zero result means right/left/undetermined. +Don't trust first line." + (let* ((str (org-trim str)) + (lines (split-string str "\n" t)) + n1 n2) + (cond ((<= (length lines) 1) + 0) + ((= (length lines) 2) + ;; numbers are right-aligned + (if (and + (numberp (read (car lines))) + (numberp (read (cadr lines))) + (setq n1 (obj-match-second-space-right (nth 0 lines))) + (setq n2 (obj-match-second-space-right (nth 1 lines)))) + n2 + 0)) + ((not (obj-match-second-space-left (nth 0 lines))) + 0) + ((and + (setq n1 (obj-match-second-space-left (nth 1 lines))) + (setq n2 (obj-match-second-space-left (nth 2 lines))) + (= n1 n2)) + n1) + ((and + (setq n1 (obj-match-second-space-right (nth 1 lines))) + (setq n2 (obj-match-second-space-right (nth 2 lines))) + (= n1 n2)) + (- n1)) + (t 0)))) + +(defun org-babel-J-print-block (x) + "Prettify jconsole output X." + (let* ((x (org-trim x)) + (a (obj-get-string-alignment x)) + (lines (split-string x "\n" t)) + b) + (cond ((< a 0) + (setq b (obj-match-second-space-right (nth 0 lines))) + (concat (make-string (+ a b) ? ) x)) + ((> a 0) + (setq b (obj-match-second-space-left (nth 0 lines))) + (concat (make-string (- a b) ? ) x)) + (t x)))) + +(defun obj-match-second-space-left (s) + "Return position of leftmost space in second space block of S or nil." + (and (string-match "^ *[^ ]+\\( \\)" s) + (match-beginning 1))) + +(defun obj-match-second-space-right (s) + "Return position of rightmost space in second space block of S or nil." + (and (string-match "^ *[^ ]+ *\\( \\)[^ ]" s) + (match-beginning 1))) + +(defun obj-string-match-m (regexp string &optional start) + "Call (string-match REGEXP STRING START). +REGEXP is modified so that .* matches newlines as well." + (string-match + (replace-regexp-in-string "\\.\\*" "[\0-\377[:nonascii:]]*" regexp) + string + start)) + +(defun org-babel-j-initiate-session (&optional session) + "Initiate a J session. +SESSION is a parameter given by org-babel." + (unless (string= session "none") + (require 'j-console) + (j-console-ensure-session))) + +(provide 'ob-J) + +;;; ob-J.el ends here diff --git a/lisp/org/ob-R.el b/lisp/org/ob-R.el index 51d342702c..3accade49f 100644 --- a/lisp/org/ob-R.el +++ b/lisp/org/ob-R.el @@ -1,4 +1,4 @@ -;;; ob-R.el --- org-babel functions for R code evaluation +;;; ob-R.el --- Babel Functions for R -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -27,16 +27,17 @@ ;; Org-Babel support for evaluating R code ;;; Code: + +(require 'cl-lib) (require 'ob) -(eval-when-compile (require 'cl)) (declare-function orgtbl-to-tsv "org-table" (table params)) (declare-function R "ext:essd-r" (&optional start-args)) (declare-function inferior-ess-send-input "ext:ess-inf" ()) (declare-function ess-make-buffer-current "ext:ess-inf" ()) (declare-function ess-eval-buffer "ext:ess-inf" (vis)) -(declare-function org-number-sequence "org-compat" (from &optional to inc)) -(declare-function org-remove-if-not "org" (predicate seq)) +(declare-function ess-wait-for-process "ext:ess-inf" + (&optional proc sec-prompt wait force-redisplay)) (defconst org-babel-header-args:R '((width . :any) @@ -60,12 +61,25 @@ (useDingbats . :any) (horizontal . :any) (results . ((file list vector table scalar verbatim) - (raw org html latex code pp wrap) - (replace silent append prepend) + (raw html latex org code pp drawer) + (replace silent none append prepend) (output value graphics)))) "R-specific header arguments.") +(defconst ob-R-safe-header-args + (append org-babel-safe-header-args + '(:width :height :bg :units :pointsize :antialias :quality + :compression :res :type :family :title :fonts + :version :paper :encoding :pagecentre :colormodel + :useDingbats :horizontal)) + "Header args which are safe for R babel blocks. + +See `org-babel-safe-header-args' for documentation of the format of +this variable.") + (defvar org-babel-default-header-args:R '()) +(put 'org-babel-default-header-args:R 'safe-local-variable + (org-babel-header-args-safe-fn ob-R-safe-header-args)) (defcustom org-babel-R-command "R --slave --no-save" "Name of command to use for executing R code." @@ -73,56 +87,103 @@ :version "24.1" :type 'string) -(defvar ess-local-process-name) ; dynamically scoped +(defvar ess-current-process-name) ; dynamically scoped +(defvar ess-local-process-name) ; dynamically scoped (defun org-babel-edit-prep:R (info) - (let ((session (cdr (assoc :session (nth 2 info))))) - (when (and session (string-match "^\\*\\(.+?\\)\\*$" session)) - (save-match-data (org-babel-R-initiate-session session nil))))) - -(defun org-babel-expand-body:R (body params &optional graphics-file) + (let ((session (cdr (assq :session (nth 2 info))))) + (when (and session + (string-prefix-p "*" session) + (string-suffix-p "*" session)) + (org-babel-R-initiate-session session nil)))) + +;; The usage of utils::read.table() ensures that the command +;; read.table() can be found even in circumstances when the utils +;; package is not in the search path from R. +(defconst ob-R-transfer-variable-table-with-header + "%s <- local({ + con <- textConnection( + %S + ) + res <- utils::read.table( + con, + header = %s, + row.names = %s, + sep = \"\\t\", + as.is = TRUE + ) + close(con) + res + })" + "R code used to transfer a table defined as a variable from org to R. + +This function is used when the table contains a header.") + +(defconst ob-R-transfer-variable-table-without-header + "%s <- local({ + con <- textConnection( + %S + ) + res <- utils::read.table( + con, + header = %s, + row.names = %s, + sep = \"\\t\", + as.is = TRUE, + fill = TRUE, + col.names = paste(\"V\", seq_len(%d), sep =\"\") + ) + close(con) + res + })" + "R code used to transfer a table defined as a variable from org to R. + +This function is used when the table does not contain a header.") + +(defun org-babel-expand-body:R (body params &optional _graphics-file) "Expand BODY according to PARAMS, return the expanded body." - (let ((graphics-file - (or graphics-file (org-babel-R-graphical-output-file params)))) - (mapconcat - #'identity - (let ((inside - (append - (when (cdr (assoc :prologue params)) - (list (cdr (assoc :prologue params)))) - (org-babel-variable-assignments:R params) - (list body) - (when (cdr (assoc :epilogue params)) - (list (cdr (assoc :epilogue params))))))) - (if graphics-file - (append - (list (org-babel-R-construct-graphics-device-call - graphics-file params)) - inside - (list "dev.off()")) - inside)) - "\n"))) + (mapconcat 'identity + (append + (when (cdr (assq :prologue params)) + (list (cdr (assq :prologue params)))) + (org-babel-variable-assignments:R params) + (list body) + (when (cdr (assq :epilogue params)) + (list (cdr (assq :epilogue params))))) + "\n")) (defun org-babel-execute:R (body params) "Execute a block of R code. This function is called by `org-babel-execute-src-block'." (save-excursion - (let* ((result-params (cdr (assoc :result-params params))) - (result-type (cdr (assoc :result-type params))) + (let* ((result-params (cdr (assq :result-params params))) + (result-type (cdr (assq :result-type params))) (session (org-babel-R-initiate-session - (cdr (assoc :session params)) params)) - (colnames-p (cdr (assoc :colnames params))) - (rownames-p (cdr (assoc :rownames params))) - (graphics-file (org-babel-R-graphical-output-file params)) - (full-body (org-babel-expand-body:R body params graphics-file)) + (cdr (assq :session params)) params)) + (colnames-p (cdr (assq :colnames params))) + (rownames-p (cdr (assq :rownames params))) + (graphics-file (and (member "graphics" (assq :result-params params)) + (org-babel-graphical-output-file params))) + (full-body + (let ((inside + (list (org-babel-expand-body:R body params graphics-file)))) + (mapconcat 'identity + (if graphics-file + (append + (list (org-babel-R-construct-graphics-device-call + graphics-file params)) + inside + (list "},error=function(e){plot(x=-1:1, y=-1:1, type='n', xlab='', ylab='', axes=FALSE); text(x=0, y=0, labels=e$message, col='red'); paste('ERROR', e$message, sep=' : ')}); dev.off()")) + inside) + "\n"))) (result (org-babel-R-evaluate session full-body result-type result-params (or (equal "yes" colnames-p) (org-babel-pick-name - (cdr (assoc :colname-names params)) colnames-p)) + (cdr (assq :colname-names params)) colnames-p)) (or (equal "yes" rownames-p) (org-babel-pick-name - (cdr (assoc :rowname-names params)) rownames-p))))) + (cdr (assq :rowname-names params)) rownames-p))))) (if graphics-file nil result)))) (defun org-babel-prep-session:R (session params) @@ -148,21 +209,21 @@ This function is called by `org-babel-execute-src-block'." (defun org-babel-variable-assignments:R (params) "Return list of R statements assigning the block's variables." - (let ((vars (mapcar #'cdr (org-babel-get-header params :var)))) + (let ((vars (org-babel--get-vars params))) (mapcar (lambda (pair) (org-babel-R-assign-elisp (car pair) (cdr pair) - (equal "yes" (cdr (assoc :colnames params))) - (equal "yes" (cdr (assoc :rownames params))))) + (equal "yes" (cdr (assq :colnames params))) + (equal "yes" (cdr (assq :rownames params))))) (mapcar (lambda (i) (cons (car (nth i vars)) (org-babel-reassemble-table (cdr (nth i vars)) - (cdr (nth i (cdr (assoc :colname-names params)))) - (cdr (nth i (cdr (assoc :rowname-names params))))))) - (org-number-sequence 0 (1- (length vars))))))) + (cdr (nth i (cdr (assq :colname-names params)))) + (cdr (nth i (cdr (assq :rowname-names params))))))) + (number-sequence 0 (1- (length vars))))))) (defun org-babel-R-quote-tsv-field (s) "Quote field S for export to R." @@ -173,35 +234,25 @@ This function is called by `org-babel-execute-src-block'." (defun org-babel-R-assign-elisp (name value colnames-p rownames-p) "Construct R code assigning the elisp VALUE to a variable named NAME." (if (listp value) - (let* ((lengths (mapcar 'length (org-remove-if-not 'sequencep value))) + (let* ((lengths (mapcar 'length (cl-remove-if-not 'sequencep value))) (max (if lengths (apply 'max lengths) 0)) - (min (if lengths (apply 'min lengths) 0)) - (transition-file (org-babel-temp-file "R-import-"))) + (min (if lengths (apply 'min lengths) 0))) ;; Ensure VALUE has an orgtbl structure (depth of at least 2). (unless (listp (car value)) (setq value (list value))) - (with-temp-file transition-file - (insert - (orgtbl-to-tsv value '(:fmt org-babel-R-quote-tsv-field)) - "\n")) - (let ((file (org-babel-process-file-name transition-file 'noquote)) + (let ((file (orgtbl-to-tsv value '(:fmt org-babel-R-quote-tsv-field))) (header (if (or (eq (nth 1 value) 'hline) colnames-p) "TRUE" "FALSE")) (row-names (if rownames-p "1" "NULL"))) (if (= max min) - (format "%s <- read.table(\"%s\", - header=%s, - row.names=%s, - sep=\"\\t\", - as.is=TRUE)" name file header row-names) - (format "%s <- read.table(\"%s\", - header=%s, - row.names=%s, - sep=\"\\t\", - as.is=TRUE, - fill=TRUE, - col.names = paste(\"V\", seq_len(%d), sep =\"\"))" + (format ob-R-transfer-variable-table-with-header + name file header row-names) + (format ob-R-transfer-variable-table-without-header name file header row-names max)))) - (format "%s <- %s" name (org-babel-R-quote-tsv-field value)))) + (cond ((integerp value) (format "%s <- %s" name (concat (number-to-string value) "L"))) + ((floatp value) (format "%s <- %s" name value)) + ((stringp value) (format "%s <- %S" name (org-no-properties value))) + (t (format "%s <- %S" name (prin1-to-string value)))))) + (defvar ess-ask-for-ess-directory) ; dynamically scoped (defun org-babel-R-initiate-session (session params) @@ -209,8 +260,9 @@ This function is called by `org-babel-execute-src-block'." (unless (string= session "none") (let ((session (or session "*R*")) (ess-ask-for-ess-directory - (and (and (boundp 'ess-ask-for-ess-directory) ess-ask-for-ess-directory) - (not (cdr (assoc :dir params)))))) + (and (boundp 'ess-ask-for-ess-directory) + ess-ask-for-ess-directory + (not (cdr (assq :dir params)))))) (if (org-babel-comint-buffer-livep session) session (save-window-excursion @@ -218,6 +270,10 @@ This function is called by `org-babel-execute-src-block'." ;; Session buffer exists, but with dead process (set-buffer session)) (require 'ess) (R) + (let ((R-proc (get-process (or ess-local-process-name + ess-current-process-name)))) + (while (process-get R-proc 'callbacks) + (ess-wait-for-process R-proc))) (rename-buffer (if (bufferp session) (buffer-name session) @@ -234,11 +290,6 @@ current code buffer." (process-name (get-buffer-process session))) (ess-make-buffer-current)) -(defun org-babel-R-graphical-output-file (params) - "Name of file to which R should send graphical output." - (and (member "graphics" (cdr (assq :result-params params))) - (cdr (assq :file params)))) - (defvar org-babel-R-graphics-devices '((:bmp "bmp" "filename") (:jpg "jpeg" "filename") @@ -265,8 +316,7 @@ Each member of this list is a list with three members: :type :family :title :fonts :version :paper :encoding :pagecentre :colormodel :useDingbats :horizontal)) - (device (and (string-match ".+\\.\\([^.]+\\)" out-file) - (match-string 1 out-file))) + (device (file-name-extension out-file)) (device-info (or (assq (intern (concat ":" device)) org-babel-R-graphics-devices) (assq :png org-babel-R-graphics-devices))) @@ -280,14 +330,43 @@ Each member of this list is a list with three members: (substring (symbol-name (car pair)) 1) (cdr pair)) "")) params "")) - (format "%s(%s=\"%s\"%s%s%s)" + (format "%s(%s=\"%s\"%s%s%s); tryCatch({" device filearg out-file args (if extra-args "," "") (or extra-args "")))) -(defvar org-babel-R-eoe-indicator "'org_babel_R_eoe'") -(defvar org-babel-R-eoe-output "[1] \"org_babel_R_eoe\"") - -(defvar org-babel-R-write-object-command "{function(object,transfer.file){object;invisible(if(inherits(try({tfile<-tempfile();write.table(object,file=tfile,sep=\"\\t\",na=\"nil\",row.names=%s,col.names=%s,quote=FALSE);file.rename(tfile,transfer.file)},silent=TRUE),\"try-error\")){if(!file.exists(transfer.file))file.create(transfer.file)})}}(object=%s,transfer.file=\"%s\")") +(defconst org-babel-R-eoe-indicator "'org_babel_R_eoe'") +(defconst org-babel-R-eoe-output "[1] \"org_babel_R_eoe\"") + +(defconst org-babel-R-write-object-command "{ + function(object,transfer.file) { + object + invisible( + if ( + inherits( + try( + { + tfile<-tempfile() + write.table(object, file=tfile, sep=\"\\t\", + na=\"nil\",row.names=%s,col.names=%s, + quote=FALSE) + file.rename(tfile,transfer.file) + }, + silent=TRUE), + \"try-error\")) + { + if(!file.exists(transfer.file)) + file.create(transfer.file) + } + ) + } +}(object=%s,transfer.file=\"%s\")" + "A template for an R command to evaluate a block of code and write the result to a file. + +Has four %s escapes to be filled in: +1. Row names, \"TRUE\" or \"FALSE\" +2. Column names, \"TRUE\" or \"FALSE\" +3. The code to be run (must be an expression, not a statement) +4. The name of the file to write to") (defun org-babel-R-evaluate (session body result-type result-params column-names-p row-names-p) @@ -299,12 +378,12 @@ Each member of this list is a list with three members: body result-type result-params column-names-p row-names-p))) (defun org-babel-R-evaluate-external-process - (body result-type result-params column-names-p row-names-p) + (body result-type result-params column-names-p row-names-p) "Evaluate BODY in external R process. -If RESULT-TYPE equals 'output then return standard output as a -string. If RESULT-TYPE equals 'value then return the value of the +If RESULT-TYPE equals `output' then return standard output as a +string. If RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." - (case result-type + (cl-case result-type (value (let ((tmp-file (org-babel-temp-file "R-"))) (org-babel-eval org-babel-R-command @@ -319,7 +398,7 @@ last statement in BODY, as elisp." (org-babel-result-cond result-params (with-temp-buffer (insert-file-contents tmp-file) - (buffer-string)) + (org-babel-chomp (buffer-string) "\n")) (org-babel-import-elisp-from-file tmp-file '(16))) column-names-p))) (output (org-babel-eval org-babel-R-command body)))) @@ -327,12 +406,12 @@ last statement in BODY, as elisp." (defvar ess-eval-visibly-p) (defun org-babel-R-evaluate-session - (session body result-type result-params column-names-p row-names-p) + (session body result-type result-params column-names-p row-names-p) "Evaluate BODY in SESSION. -If RESULT-TYPE equals 'output then return standard output as a -string. If RESULT-TYPE equals 'value then return the value of the +If RESULT-TYPE equals `output' then return standard output as a +string. If RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." - (case result-type + (cl-case result-type (value (with-temp-buffer (insert (org-babel-chomp body)) @@ -353,12 +432,12 @@ last statement in BODY, as elisp." (org-babel-result-cond result-params (with-temp-buffer (insert-file-contents tmp-file) - (buffer-string)) + (org-babel-chomp (buffer-string) "\n")) (org-babel-import-elisp-from-file tmp-file '(16))) column-names-p))) (output (mapconcat - #'org-babel-chomp + 'org-babel-chomp (butlast (delq nil (mapcar @@ -366,11 +445,12 @@ last statement in BODY, as elisp." (mapcar (lambda (line) ;; cleanup extra prompts left in output (if (string-match - "^\\([ ]*[>+\\.][ ]?\\)+\\([[0-9]+\\|[ ]\\)" line) + "^\\([>+.]\\([ ][>.+]\\)*[ ]\\)" + (car (split-string line "\n"))) (substring line (match-end 1)) line)) (org-babel-comint-with-output (session org-babel-R-eoe-output) - (insert (mapconcat #'org-babel-chomp + (insert (mapconcat 'org-babel-chomp (list body org-babel-R-eoe-indicator) "\n")) (inferior-ess-send-input)))))) "\n")))) diff --git a/lisp/org/ob-abc.el b/lisp/org/ob-abc.el new file mode 100644 index 0000000000..0ce503d3b0 --- /dev/null +++ b/lisp/org/ob-abc.el @@ -0,0 +1,92 @@ +;;; ob-abc.el --- Org Babel Functions for ABC -*- lexical-binding: t; -*- + +;; Copyright (C) 2013-2017 Free Software Foundation, Inc. + +;; Author: William Waites +;; Keywords: literate programming, music +;; Homepage: http://www.tardis.ed.ac.uk/wwaites +;; Version: 0.01 + +;;; License: + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: + +;;; This file adds support to Org Babel for music in ABC notation. +;;; It requires that the abcm2ps program is installed. +;;; See http://moinejf.free.fr/ + +(require 'ob) + +;; optionally define a file extension for this language +(add-to-list 'org-babel-tangle-lang-exts '("abc" . "abc")) + +;; optionally declare default header arguments for this language +(defvar org-babel-default-header-args:abc + '((:results . "file") (:exports . "results")) + "Default arguments to use when evaluating an ABC source block.") + +(defun org-babel-expand-body:abc (body params) + "Expand BODY according to PARAMS, return the expanded body." + (let ((vars (org-babel--get-vars params))) + (mapc + (lambda (pair) + (let ((name (symbol-name (car pair))) + (value (cdr pair))) + (setq body + (replace-regexp-in-string + (concat "\$" (regexp-quote name)) + (if (stringp value) value (format "%S" value)) + body)))) + vars) + body)) + +(defun org-babel-execute:abc (body params) + "Execute a block of ABC code with org-babel. This function is + called by `org-babel-execute-src-block'" + (message "executing Abc source code block") + (let* ((cmdline (cdr (assq :cmdline params))) + (out-file (let ((file (cdr (assq :file params)))) + (if file (replace-regexp-in-string "\.pdf$" ".ps" file) + (error "abc code block requires :file header argument")))) + (in-file (org-babel-temp-file "abc-")) + (render (concat "abcm2ps" " " cmdline + " -O " (org-babel-process-file-name out-file) + " " (org-babel-process-file-name in-file)))) + (with-temp-file in-file (insert (org-babel-expand-body:abc body params))) + (org-babel-eval render "") + ;;; handle where abcm2ps changes the file name (to support multiple files + (when (or (string= (file-name-extension out-file) "eps") + (string= (file-name-extension out-file) "svg")) + (rename-file (concat + (file-name-sans-extension out-file) "001." + (file-name-extension out-file)) + out-file t)) + ;;; if we were asked for a pdf... + (when (string= (file-name-extension (cdr (assq :file params))) "pdf") + (org-babel-eval (concat "ps2pdf" " " out-file " " (cdr (assq :file params))) "")) + ;;; indicate that the file has been written + nil)) + +;; This function should be used to assign any variables in params in +;; the context of the session environment. +(defun org-babel-prep-session:abc (_session _params) + "Return an error because abc does not support sessions." + (error "ABC does not support sessions")) + +(provide 'ob-abc) +;;; ob-abc.el ends here diff --git a/lisp/org/ob-asymptote.el b/lisp/org/ob-asymptote.el index e3b73c19ac..1dbf48427f 100644 --- a/lisp/org/ob-asymptote.el +++ b/lisp/org/ob-asymptote.el @@ -1,4 +1,4 @@ -;;; ob-asymptote.el --- org-babel functions for asymptote evaluation +;;; ob-asymptote.el --- Babel Functions for Asymptote -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -43,11 +43,6 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) - -(declare-function orgtbl-to-generic "org-table" - (table params &optional backend)) -(declare-function org-combine-plists "org" (&rest plists)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("asymptote" . "asy")) @@ -59,13 +54,10 @@ (defun org-babel-execute:asymptote (body params) "Execute a block of Asymptote code. This function is called by `org-babel-execute-src-block'." - (let* ((result-params (split-string (or (cdr (assoc :results params)) ""))) - (out-file (cdr (assoc :file params))) - (format (or (and out-file - (string-match ".+\\.\\(.+\\)" out-file) - (match-string 1 out-file)) + (let* ((out-file (cdr (assq :file params))) + (format (or (file-name-extension out-file) "pdf")) - (cmdline (cdr (assoc :cmdline params))) + (cmdline (cdr (assq :cmdline params))) (in-file (org-babel-temp-file "asymptote-")) (cmd (concat "asy " @@ -83,7 +75,7 @@ This function is called by `org-babel-execute-src-block'." (message cmd) (shell-command cmd) nil)) ;; signal that output has already been written to file -(defun org-babel-prep-session:asymptote (session params) +(defun org-babel-prep-session:asymptote (_session _params) "Return an error if the :session header argument is set. Asymptote does not support sessions" (error "Asymptote does not support sessions")) @@ -91,7 +83,7 @@ Asymptote does not support sessions" (defun org-babel-variable-assignments:asymptote (params) "Return list of asymptote statements assigning the block's variables." (mapcar #'org-babel-asymptote-var-to-asymptote - (mapcar #'cdr (org-babel-get-header params :var)))) + (org-babel--get-vars params))) (defun org-babel-asymptote-var-to-asymptote (pair) "Convert an elisp value into an Asymptote variable. @@ -128,21 +120,17 @@ a variable of the same value." DATA is a list. Return type as a symbol. -The type is `string' if any element in DATA is -a string. Otherwise, it is either `real', if some elements are -floats, or `int'." - (let* ((type 'int) - find-type ; for byte-compiler - (find-type - (function - (lambda (row) - (catch 'exit - (mapc (lambda (el) - (cond ((listp el) (funcall find-type el)) - ((stringp el) (throw 'exit (setq type 'string))) - ((floatp el) (setq type 'real)))) - row)))))) - (funcall find-type data) type)) +The type is `string' if any element in DATA is a string. +Otherwise, it is either `real', if some elements are floats, or +`int'." + (letrec ((type 'int) + (find-type + (lambda (row) + (dolist (e row type) + (cond ((listp e) (setq type (funcall find-type e))) + ((stringp e) (throw 'exit 'string)) + ((floatp e) (setq type 'real))))))) + (catch 'exit (funcall find-type data)) type)) (provide 'ob-asymptote) diff --git a/lisp/org/ob-awk.el b/lisp/org/ob-awk.el index c2ac5cac3b..2db4eeae94 100644 --- a/lisp/org/ob-awk.el +++ b/lisp/org/ob-awk.el @@ -1,4 +1,4 @@ -;;; ob-awk.el --- org-babel functions for awk evaluation +;;; ob-awk.el --- Babel Functions for Awk -*- lexical-binding: t; -*- ;; Copyright (C) 2011-2017 Free Software Foundation, Inc. @@ -27,17 +27,15 @@ ;; ;; - :in-file takes a path to a file of data to be processed by awk ;; -;; - :stdin takes an Org-mode data or code block reference, the value -;; of which will be passed to the awk process through STDIN +;; - :stdin takes an Org data or code block reference, the value of +;; which will be passed to the awk process through STDIN ;;; Code: (require 'ob) (require 'org-compat) -(eval-when-compile (require 'cl)) (declare-function org-babel-ref-resolve "ob-ref" (ref)) -(declare-function orgtbl-to-generic "org-table" - (table params &optional backend)) +(declare-function orgtbl-to-generic "org-table" (table params)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("awk" . "awk")) @@ -45,34 +43,38 @@ (defvar org-babel-awk-command "awk" "Name of the awk executable command.") -(defun org-babel-expand-body:awk (body params) +(defun org-babel-expand-body:awk (body _params) "Expand BODY according to PARAMS, return the expanded body." - (dolist (pair (mapcar #'cdr (org-babel-get-header params :var))) - (setf body (replace-regexp-in-string - (regexp-quote (format "$%s" (car pair))) (cdr pair) body))) body) (defun org-babel-execute:awk (body params) "Execute a block of Awk code with org-babel. This function is called by `org-babel-execute-src-block'" (message "executing Awk source code block") - (let* ((result-params (cdr (assoc :result-params params))) - (cmd-line (cdr (assoc :cmd-line params))) - (in-file (cdr (assoc :in-file params))) + (let* ((result-params (cdr (assq :result-params params))) + (cmd-line (cdr (assq :cmd-line params))) + (in-file (cdr (assq :in-file params))) (full-body (org-babel-expand-body:awk body params)) (code-file (let ((file (org-babel-temp-file "awk-"))) (with-temp-file file (insert full-body)) file)) - (stdin (let ((stdin (cdr (assoc :stdin params)))) + (stdin (let ((stdin (cdr (assq :stdin params)))) (when stdin (let ((tmp (org-babel-temp-file "awk-stdin-")) (res (org-babel-ref-resolve stdin))) (with-temp-file tmp (insert (org-babel-awk-var-to-awk res))) tmp)))) - (cmd (mapconcat #'identity (remove nil (list org-babel-awk-command - "-f" code-file - cmd-line - in-file)) + (cmd (mapconcat #'identity + (append + (list org-babel-awk-command + "-f" code-file cmd-line) + (mapcar (lambda (pair) + (format "-v %s='%s'" + (car pair) + (org-babel-awk-var-to-awk + (cdr pair)))) + (org-babel--get-vars params)) + (list in-file)) " "))) (org-babel-reassemble-table (let ((results @@ -88,9 +90,9 @@ called by `org-babel-execute-src-block'" (with-temp-file tmp (insert results)) (org-babel-import-elisp-from-file tmp))))) (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params)))))) + (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) (defun org-babel-awk-var-to-awk (var &optional sep) "Return a printed value of VAR suitable for parsing with awk." @@ -102,11 +104,6 @@ called by `org-babel-execute-src-block'" (mapconcat echo-var var "\n")) (t (funcall echo-var var))))) -(defun org-babel-awk-table-or-string (results) - "If the results look like a table, then convert them into an -Emacs-lisp table, otherwise return the results as a string." - (org-babel-script-escape results)) - (provide 'ob-awk) diff --git a/lisp/org/ob-calc.el b/lisp/org/ob-calc.el index 6298bba522..d4b7260c57 100644 --- a/lisp/org/ob-calc.el +++ b/lisp/org/ob-calc.el @@ -1,4 +1,4 @@ -;;; ob-calc.el --- org-babel functions for calc code evaluation +;;; ob-calc.el --- Babel Functions for Calc -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -28,18 +28,18 @@ ;;; Code: (require 'ob) (require 'calc) -(unless (featurep 'xemacs) - (require 'calc-trail) - (require 'calc-store)) +(require 'calc-trail) +(require 'calc-store) (declare-function calc-store-into "calc-store" (&optional var)) (declare-function calc-recall "calc-store" (&optional var)) (declare-function math-evaluate-expr "calc-ext" (x)) +(declare-function org-trim "org" (s &optional keep-lead)) (defvar org-babel-default-header-args:calc nil "Default arguments for evaluating an calc source block.") -(defun org-babel-expand-body:calc (body params) +(defun org-babel-expand-body:calc (body _params) "Expand BODY according to PARAMS, return the expanded body." body) (defvar org--var-syms) ; Dynamically scoped from org-babel-execute:calc @@ -48,7 +48,7 @@ "Execute a block of calc code with Babel." (unless (get-buffer "*Calculator*") (save-window-excursion (calc) (calc-quit))) - (let* ((vars (mapcar #'cdr (org-babel-get-header params :var))) + (let* ((vars (org-babel--get-vars params)) (org--var-syms (mapcar #'car vars)) (var-names (mapcar #'symbol-name org--var-syms))) (mapc @@ -85,15 +85,17 @@ ;; parse line into calc objects (car (math-read-exprs line))))))))) )))))) - (mapcar #'org-babel-trim + (mapcar #'org-trim (split-string (org-babel-expand-body:calc body params) "[\n\r]")))) (save-excursion (with-current-buffer (get-buffer "*Calculator*") - (calc-eval (calc-top 1))))) + (prog1 + (calc-eval (calc-top 1)) + (calc-pop 1))))) (defun org-babel-calc-maybe-resolve-var (el) (if (consp el) - (if (and (equal 'var (car el)) (member (cadr el) org--var-syms)) + (if (and (eq 'var (car el)) (member (cadr el) org--var-syms)) (progn (calc-recall (cadr el)) (prog1 (calc-top 1) diff --git a/lisp/org/ob-clojure.el b/lisp/org/ob-clojure.el index b9af45adfe..39561572a5 100644 --- a/lisp/org/ob-clojure.el +++ b/lisp/org/ob-clojure.el @@ -1,9 +1,9 @@ -;;; ob-clojure.el --- org-babel functions for clojure evaluation +;;; ob-clojure.el --- Babel Functions for Clojure -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. -;; Author: Joel Boehland -;; Eric Schulte +;; Author: Joel Boehland, Eric Schulte, Oleh Krehel +;; ;; Keywords: literate programming, reproducible research ;; Homepage: http://orgmode.org @@ -24,21 +24,30 @@ ;;; Commentary: -;; Support for evaluating clojure code, relies on slime for all eval. +;; Support for evaluating clojure code -;;; Requirements: +;; Requirements: ;; - clojure (at least 1.2.0) ;; - clojure-mode -;; - slime +;; - either cider or SLIME -;; By far, the best way to install these components is by following +;; For Cider, see https://github.com/clojure-emacs/cider + +;; For SLIME, the best way to install these components is by following ;; the directions as set out by Phil Hagelberg (Technomancy) on the ;; web page: http://technomancy.us/126 ;;; Code: +(require 'cl-lib) (require 'ob) +(declare-function cider-current-connection "ext:cider-client" (&optional type)) +(declare-function cider-current-session "ext:cider-client" ()) +(declare-function nrepl-dict-get "ext:nrepl-client" (dict key)) +(declare-function nrepl-sync-request:eval "ext:nrepl-client" + (input connection session &optional ns)) +(declare-function org-trim "org" (s &optional keep-lead)) (declare-function slime-eval "ext:slime" (sexp &optional package)) (defvar org-babel-tangle-lang-exts) @@ -47,49 +56,63 @@ (defvar org-babel-default-header-args:clojure '()) (defvar org-babel-header-args:clojure '((package . :any))) +(defcustom org-babel-clojure-backend + (cond ((featurep 'cider) 'cider) + (t 'slime)) + "Backend used to evaluate Clojure code blocks." + :group 'org-babel + :type '(choice + (const :tag "cider" cider) + (const :tag "SLIME" slime))) + (defun org-babel-expand-body:clojure (body params) "Expand BODY according to PARAMS, return the expanded body." - (let* ((vars (mapcar #'cdr (org-babel-get-header params :var))) - (result-params (cdr (assoc :result-params params))) + (let* ((vars (org-babel--get-vars params)) + (result-params (cdr (assq :result-params params))) (print-level nil) (print-length nil) - (body (org-babel-trim - (if (> (length vars) 0) - (concat "(let [" - (mapconcat - (lambda (var) - (format "%S (quote %S)" (car var) (cdr var))) - vars "\n ") - "]\n" body ")") - body)))) - (cond ((or (member "code" result-params) (member "pp" result-params)) - (format (concat "(let [org-mode-print-catcher (java.io.StringWriter.)] " - "(clojure.pprint/with-pprint-dispatch clojure.pprint/%s-dispatch " - "(clojure.pprint/pprint (do %s) org-mode-print-catcher) " - "(str org-mode-print-catcher)))") - (if (member "code" result-params) "code" "simple") body)) - ;; if (:results output), collect printed output - ((member "output" result-params) - (format "(clojure.core/with-out-str %s)" body)) - (t body)))) + (body (org-trim + (if (null vars) (org-trim body) + (concat "(let [" + (mapconcat + (lambda (var) + (format "%S (quote %S)" (car var) (cdr var))) + vars "\n ") + "]\n" body ")"))))) + (if (or (member "code" result-params) + (member "pp" result-params)) + (format "(clojure.pprint/pprint (do %s))" body) + body))) (defun org-babel-execute:clojure (body params) "Execute a block of Clojure code with Babel." - (require 'slime) - (with-temp-buffer - (insert (org-babel-expand-body:clojure body params)) - (let ((result - (slime-eval - `(swank:eval-and-grab-output - ,(buffer-substring-no-properties (point-min) (point-max))) - (cdr (assoc :package params))))) - (let ((result-params (cdr (assoc :result-params params)))) - (org-babel-result-cond result-params - result - (condition-case nil (org-babel-script-escape result) - (error result))))))) + (let ((expanded (org-babel-expand-body:clojure body params)) + result) + (cl-case org-babel-clojure-backend + (cider + (require 'cider) + (let ((result-params (cdr (assq :result-params params)))) + (setq result + (nrepl-dict-get + (nrepl-sync-request:eval + expanded (cider-current-connection) (cider-current-session)) + (if (or (member "output" result-params) + (member "pp" result-params)) + "out" + "value"))))) + (slime + (require 'slime) + (with-temp-buffer + (insert expanded) + (setq result + (slime-eval + `(swank:eval-and-grab-output + ,(buffer-substring-no-properties (point-min) (point-max))) + (cdr (assq :package params))))))) + (org-babel-result-cond (cdr (assq :result-params params)) + result + (condition-case nil (org-babel-script-escape result) + (error result))))) (provide 'ob-clojure) - - ;;; ob-clojure.el ends here diff --git a/lisp/org/ob-comint.el b/lisp/org/ob-comint.el index 78c5021b1b..cc60f4e4a7 100644 --- a/lisp/org/ob-comint.el +++ b/lisp/org/ob-comint.el @@ -1,4 +1,4 @@ -;;; ob-comint.el --- org-babel functions for interaction with comint buffers +;;; ob-comint.el --- Babel Functions for Interaction with Comint Buffers -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -33,10 +33,7 @@ (require 'ob-core) (require 'org-compat) (require 'comint) -(eval-when-compile (require 'cl)) -(declare-function with-parsed-tramp-file-name "tramp" - (filename var &rest body) t) -(declare-function tramp-flush-directory-property "tramp-cache" (key directory)) +(require 'tramp) (defun org-babel-comint-buffer-livep (buffer) "Check if BUFFER is a comint buffer with a live process." @@ -49,12 +46,14 @@ BUFFER is checked with `org-babel-comint-buffer-livep'. BODY is executed inside the protection of `save-excursion' and `save-match-data'." (declare (indent 1)) - `(save-excursion + `(progn + (unless (org-babel-comint-buffer-livep ,buffer) + (error "Buffer %s does not exist or has no process" ,buffer)) (save-match-data - (unless (org-babel-comint-buffer-livep ,buffer) - (error "Buffer %s does not exist or has no process" ,buffer)) - (set-buffer ,buffer) - ,@body))) + (with-current-buffer ,buffer + (save-excursion + (let ((comint-input-filter (lambda (_input) nil))) + ,@body)))))) (def-edebug-spec org-babel-comint-in-buffer (form body)) (defmacro org-babel-comint-with-output (meta &rest body) @@ -70,53 +69,49 @@ elements are optional. This macro ensures that the filter is removed in case of an error or user `keyboard-quit' during execution of body." (declare (indent 1)) - (let ((buffer (car meta)) - (eoe-indicator (cadr meta)) - (remove-echo (cadr (cdr meta))) - (full-body (cadr (cdr (cdr meta))))) + (let ((buffer (nth 0 meta)) + (eoe-indicator (nth 1 meta)) + (remove-echo (nth 2 meta)) + (full-body (nth 3 meta))) `(org-babel-comint-in-buffer ,buffer - (let ((string-buffer "") dangling-text raw) - ;; setup filter - (setq comint-output-filter-functions + (let* ((string-buffer "") + (comint-output-filter-functions (cons (lambda (text) (setq string-buffer (concat string-buffer text))) comint-output-filter-functions)) - (unwind-protect - (progn - ;; got located, and save dangling text - (goto-char (process-mark (get-buffer-process (current-buffer)))) - (let ((start (point)) - (end (point-max))) - (setq dangling-text (buffer-substring start end)) - (delete-region start end)) - ;; pass FULL-BODY to process - ,@body - ;; wait for end-of-evaluation indicator - (while (progn - (goto-char comint-last-input-end) - (not (save-excursion - (and (re-search-forward - (regexp-quote ,eoe-indicator) nil t) - (re-search-forward - comint-prompt-regexp nil t))))) - (accept-process-output (get-buffer-process (current-buffer))) - ;; thought the following this would allow async - ;; background running, but I was wrong... - ;; (run-with-timer .5 .5 'accept-process-output - ;; (get-buffer-process (current-buffer))) - ) - ;; replace cut dangling text - (goto-char (process-mark (get-buffer-process (current-buffer)))) - (insert dangling-text)) - ;; remove filter - (setq comint-output-filter-functions - (cdr comint-output-filter-functions))) + dangling-text) + ;; got located, and save dangling text + (goto-char (process-mark (get-buffer-process (current-buffer)))) + (let ((start (point)) + (end (point-max))) + (setq dangling-text (buffer-substring start end)) + (delete-region start end)) + ;; pass FULL-BODY to process + ,@body + ;; wait for end-of-evaluation indicator + (while (progn + (goto-char comint-last-input-end) + (not (save-excursion + (and (re-search-forward + (regexp-quote ,eoe-indicator) nil t) + (re-search-forward + comint-prompt-regexp nil t))))) + (accept-process-output (get-buffer-process (current-buffer))) + ;; thought the following this would allow async + ;; background running, but I was wrong... + ;; (run-with-timer .5 .5 'accept-process-output + ;; (get-buffer-process (current-buffer))) + ) + ;; replace cut dangling text + (goto-char (process-mark (get-buffer-process (current-buffer)))) + (insert dangling-text) + ;; remove echo'd FULL-BODY from input - (if (and ,remove-echo ,full-body - (string-match - (replace-regexp-in-string - "\n" "[\r\n]+" (regexp-quote (or ,full-body ""))) - string-buffer)) - (setq raw (substring string-buffer (match-end 0)))) + (when (and ,remove-echo ,full-body + (string-match + (replace-regexp-in-string + "\n" "[\r\n]+" (regexp-quote (or ,full-body ""))) + string-buffer)) + (setq string-buffer (substring string-buffer (match-end 0)))) (split-string string-buffer comint-prompt-regexp))))) (def-edebug-spec org-babel-comint-with-output (sexp body)) @@ -149,15 +144,14 @@ Don't return until FILE exists. Code in STRING must ensure that FILE exists at end of evaluation." (unless (org-babel-comint-buffer-livep buffer) (error "Buffer %s does not exist or has no process" buffer)) - (if (file-exists-p file) (delete-file file)) + (when (file-exists-p file) (delete-file file)) (process-send-string (get-buffer-process buffer) - (if (string-match "\n$" string) string (concat string "\n"))) + (if (= (aref string (1- (length string))) ?\n) string (concat string "\n"))) ;; From Tramp 2.1.19 the following cache flush is not necessary - (if (file-remote-p default-directory) - (let (v) - (with-parsed-tramp-file-name default-directory nil - (tramp-flush-directory-property v "")))) + (when (file-remote-p default-directory) + (with-parsed-tramp-file-name default-directory nil + (tramp-flush-directory-property v ""))) (while (not (file-exists-p file)) (sit-for (or period 0.25)))) (provide 'ob-comint) diff --git a/lisp/org/ob-coq.el b/lisp/org/ob-coq.el new file mode 100644 index 0000000000..93d2b1f713 --- /dev/null +++ b/lisp/org/ob-coq.el @@ -0,0 +1,78 @@ +;;; ob-coq.el --- Babel Functions for Coq -*- lexical-binding: t; -*- + +;; Copyright (C) 2010-2017 Free Software Foundation, Inc. + +;; Author: Eric Schulte +;; Keywords: literate programming, reproducible research +;; Homepage: http://orgmode.org + +;; 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 . + +;;; Commentary: + +;; Rudimentary support for evaluating Coq code blocks. Currently only +;; session evaluation is supported. Requires both coq.el and +;; coq-inferior.el, both of which are distributed with Coq. +;; +;; http://coq.inria.fr/ + +;;; Code: +(require 'ob) + +(declare-function run-coq "ext:coq-inferior.el" (cmd)) +(declare-function coq-proc "ext:coq-inferior.el" ()) + +(defvar coq-program-name "coqtop" + "Name of the coq toplevel to run.") + +(defvar org-babel-coq-buffer "*coq*" + "Buffer in which to evaluate coq code blocks.") + +(defun org-babel-coq-clean-prompt (string) + (if (string-match "^[^[:space:]]+ < " string) + (substring string 0 (match-beginning 0)) + string)) + +(defun org-babel-execute:coq (body params) + (let ((full-body (org-babel-expand-body:generic body params)) + (session (org-babel-coq-initiate-session)) + (pt (lambda () + (marker-position + (process-mark (get-buffer-process (current-buffer))))))) + (org-babel-coq-clean-prompt + (org-babel-comint-in-buffer session + (let ((start (funcall pt))) + (with-temp-buffer + (insert full-body) + (comint-send-region (coq-proc) (point-min) (point-max)) + (comint-send-string (coq-proc) + (if (string= (buffer-substring (- (point-max) 1) (point-max)) ".") + "\n" + ".\n"))) + (while (equal start (funcall pt)) (sleep-for 0.1)) + (buffer-substring start (funcall pt))))))) + +(defun org-babel-coq-initiate-session () + "Initiate a coq session. +If there is not a current inferior-process-buffer in SESSION then +create one. Return the initialized session." + (unless (fboundp 'run-coq) + (error "`run-coq' not defined, load coq-inferior.el")) + (save-window-excursion (run-coq coq-program-name)) + (sit-for 0.1) + (get-buffer org-babel-coq-buffer)) + +(provide 'ob-coq) diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el index cfbcbe6ece..c630b70f91 100644 --- a/lisp/org/ob-core.el +++ b/lisp/org/ob-core.el @@ -1,4 +1,4 @@ -;;; ob-core.el --- working with code blocks in org-mode +;;; ob-core.el --- Working with Code Blocks -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -23,8 +23,7 @@ ;; along with GNU Emacs. If not, see . ;;; Code: -(eval-when-compile - (require 'cl)) +(require 'cl-lib) (require 'ob-eval) (require 'org-macs) (require 'org-compat) @@ -33,66 +32,70 @@ (if (memq system-type '(windows-nt cygwin)) ".exe" nil)) -;; dynamically scoped for tramp -(defvar org-babel-call-process-region-original nil) -(defvar org-src-lang-modes) + (defvar org-babel-library-of-babel) -(declare-function outline-show-all "outline" ()) -(declare-function org-every "org" (pred seq)) -(declare-function org-reduce "org" (CL-FUNC CL-SEQ &rest CL-KEYS)) +(defvar org-edit-src-content-indentation) +(defvar org-src-lang-modes) +(defvar org-src-preserve-indentation) + +(declare-function org-at-item-p "org-list" ()) +(declare-function org-at-table-p "org" (&optional table-type)) +(declare-function org-babel-lob-execute-maybe "ob-lob" ()) +(declare-function org-babel-ref-goto-headline-id "ob-ref" (id)) +(declare-function org-babel-ref-headline-body "ob-ref" ()) +(declare-function org-babel-ref-parse "ob-ref" (assignment)) +(declare-function org-babel-ref-resolve "ob-ref" (ref)) +(declare-function org-babel-ref-split-args "ob-ref" (arg-string)) +(declare-function org-babel-tangle-comment-links "ob-tangle" (&optional info)) +(declare-function org-completing-read "org" (&rest args)) +(declare-function org-current-level "org" ()) +(declare-function org-cycle "org" (&optional arg)) +(declare-function org-do-remove-indentation "org" (&optional n)) +(declare-function org-edit-src-code "org-src" (&optional code edit-buffer-name)) +(declare-function org-edit-src-exit "org-src" ()) +(declare-function org-element-at-point "org-element" ()) +(declare-function org-element-context "org-element" (&optional element)) +(declare-function org-element-normalize-string "org-element" (s)) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-type "org-element" (element)) +(declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) +(declare-function org-escape-code-in-region "org-src" (beg end)) +(declare-function org-get-indentation "org" (&optional line)) +(declare-function org-get-indentation "org" (&optional line)) +(declare-function org-in-regexp "org" (regexp &optional nlines visually)) +(declare-function org-indent-line "org" ()) +(declare-function org-list-get-list-end "org-list" (item struct prevs)) +(declare-function org-list-prevs-alist "org-list" (struct)) +(declare-function org-list-struct "org-list" ()) +(declare-function org-list-to-generic "org-list" (LIST PARAMS)) +(declare-function org-list-to-lisp "org-list" (&optional delete)) +(declare-function org-macro-escape-arguments "org-macro" (&rest args)) +(declare-function org-make-options-regexp "org" (kwds &optional extra)) (declare-function org-mark-ring-push "org" (&optional pos buffer)) -(declare-function tramp-compat-make-temp-file "tramp-compat" - (filename &optional dir-flag)) -(declare-function org-icompleting-read "org" (&rest args)) -(declare-function org-edit-src-code "org-src" - (&optional context code edit-buffer-name)) -(declare-function org-edit-src-exit "org-src" (&optional context)) -(declare-function org-open-at-point "org" (&optional in-emacs reference-buffer)) -(declare-function org-outline-overlay-data "org" (&optional use-markers)) -(declare-function org-set-outline-overlay-data "org" (data)) (declare-function org-narrow-to-subtree "org" ()) -(declare-function org-split-string "org" (string &optional separators)) -(declare-function org-entry-get "org" - (pom property &optional inherit literal-nil)) -(declare-function org-make-options-regexp "org" (kwds &optional extra)) -(declare-function org-do-remove-indentation "org" (&optional n)) (declare-function org-next-block "org" (arg &optional backward block-regexp)) +(declare-function org-number-sequence "org-compat" (from &optional to inc)) +(declare-function org-open-at-point "org" (&optional in-emacs reference-buffer)) +(declare-function org-outline-overlay-data "org" (&optional use-markers)) (declare-function org-previous-block "org" (arg &optional block-regexp)) +(declare-function org-remove-indentation "org" (code &optional n)) +(declare-function org-reverse-string "org" (string)) +(declare-function org-set-outline-overlay-data "org" (data)) (declare-function org-show-context "org" (&optional key)) -(declare-function org-at-table-p "org" (&optional table-type)) -(declare-function org-cycle "org" (&optional arg)) -(declare-function org-uniquify "org" (list)) -(declare-function org-current-level "org" ()) -(declare-function org-table-import "org-table" (file arg)) -(declare-function org-add-hook "org-compat" - (hook function &optional append local)) +(declare-function org-split-string "org" (string &optional separators)) +(declare-function org-src-coderef-format "org-src" (element)) +(declare-function org-src-coderef-regexp "org-src" (fmt &optional label)) (declare-function org-table-align "org-table" ()) (declare-function org-table-end "org-table" (&optional table-type)) -(declare-function orgtbl-to-generic "org-table" - (table params &optional backend)) -(declare-function orgtbl-to-orgtbl "org-table" (table params)) -(declare-function org-babel-tangle-comment-links "ob-tangle" (&optional info)) -(declare-function org-babel-lob-get-info "ob-lob" nil) -(declare-function org-babel-ref-split-args "ob-ref" (arg-string)) -(declare-function org-babel-ref-parse "ob-ref" (assignment)) -(declare-function org-babel-ref-resolve "ob-ref" (ref)) -(declare-function org-babel-ref-goto-headline-id "ob-ref" (id)) -(declare-function org-babel-ref-headline-body "ob-ref" ()) -(declare-function org-babel-lob-execute-maybe "ob-lob" ()) -(declare-function org-number-sequence "org-compat" (from &optional to inc)) -(declare-function org-at-item-p "org-list" ()) -(declare-function org-list-parse-list "org-list" (&optional delete)) -(declare-function org-list-to-generic "org-list" (LIST PARAMS)) -(declare-function org-list-struct "org-list" ()) -(declare-function org-list-prevs-alist "org-list" (struct)) -(declare-function org-list-get-list-end "org-list" (item struct prevs)) -(declare-function org-remove-if "org" (predicate seq)) -(declare-function org-completing-read "org" (&rest args)) -(declare-function org-escape-code-in-region "org-src" (beg end)) -(declare-function org-unescape-code-in-string "org-src" (s)) +(declare-function org-table-import "org-table" (file arg)) (declare-function org-table-to-lisp "org-table" (&optional txt)) -(declare-function org-reverse-string "org" (string)) -(declare-function org-element-context "org-element" (&optional ELEMENT)) +(declare-function org-trim "org" (s &optional keep-lead)) +(declare-function org-unescape-code-in-string "org-src" (s)) +(declare-function org-uniquify "org" (list)) +(declare-function orgtbl-to-generic "org-table" (table params)) +(declare-function orgtbl-to-orgtbl "org-table" (table params)) +(declare-function outline-show-all "outline" ()) +(declare-function tramp-compat-make-temp-file "tramp-compat" (filename &optional dir-flag)) (defgroup org-babel nil "Code block evaluation and management in `org-mode' documents." @@ -101,11 +104,12 @@ (defcustom org-confirm-babel-evaluate t "Confirm before evaluation. +\\\ Require confirmation before interactively evaluating code -blocks in Org-mode buffers. The default value of this variable -is t, meaning confirmation is required for any code block -evaluation. This variable can be set to nil to inhibit any -future confirmation requests. This variable can also be set to a +blocks in Org buffers. The default value of this variable is t, +meaning confirmation is required for any code block evaluation. +This variable can be set to nil to inhibit any future +confirmation requests. This variable can also be set to a function which takes two arguments the language of the code block and the body of the code block. Such a function should then return a non-nil value if the user should be prompted for @@ -113,10 +117,11 @@ execution or nil if no prompt is required. Warning: Disabling confirmation may result in accidental evaluation of potentially harmful code. It may be advisable -remove code block execution from C-c C-c as further protection +remove code block execution from `\\[org-ctrl-c-ctrl-c]' \ +as further protection against accidental code block evaluation. The `org-babel-no-eval-on-ctrl-c-ctrl-c' variable can be used to -remove code block execution from the C-c C-c keybinding." +remove code block execution from the `\\[org-ctrl-c-ctrl-c]' keybinding." :group 'org-babel :version "24.1" :type '(choice boolean function)) @@ -124,19 +129,24 @@ remove code block execution from the C-c C-c keybinding." (put 'org-confirm-babel-evaluate 'safe-local-variable (lambda (x) (eq x t))) (defcustom org-babel-no-eval-on-ctrl-c-ctrl-c nil - "Remove code block evaluation from the C-c C-c key binding." + "\\\ +Remove code block evaluation from the `\\[org-ctrl-c-ctrl-c]' key binding." :group 'org-babel :version "24.1" :type 'boolean) (defcustom org-babel-results-keyword "RESULTS" "Keyword used to name results generated by code blocks. -Should be either RESULTS or NAME however any capitalization may -be used." +It should be \"RESULTS\". However any capitalization may be +used." :group 'org-babel :version "24.4" :package-version '(Org . "8.0") - :type 'string) + :type 'string + :safe (lambda (v) + (and (stringp v) + (eq (compare-strings "RESULTS" nil nil v nil nil t) + t)))) (defcustom org-babel-noweb-wrap-start "<<" "String used to begin a noweb reference in a code block. @@ -155,6 +165,19 @@ See also `org-babel-noweb-wrap-start'." This string must include a \"%s\" which will be replaced by the results." :group 'org-babel :type 'string) +(put 'org-babel-inline-result-wrap + 'safe-local-variable + (lambda (value) + (and (stringp value) + (string-match-p "%s" value)))) + +(defcustom org-babel-hash-show-time nil + "Non-nil means show the time the code block was evaluated in the result hash." + :group 'org-babel + :type 'boolean + :version "26.1" + :package-version '(Org . "9.0") + :safe #'booleanp) (defun org-babel-noweb-wrap (&optional regexp) (concat org-babel-noweb-wrap-start @@ -169,14 +192,6 @@ This string must include a \"%s\" which will be replaced by the results." "^[ \t]*#\\+headers?:[ \t]*\\([^\n]*\\)$" "Regular expression used to match multi-line header arguments.") -(defvar org-babel-src-name-w-name-regexp - (concat org-babel-src-name-regexp - "\\(" - org-babel-multi-line-header-regexp - "\\)*" - "\\([^ ()\f\t\n\r\v]+\\)") - "Regular expression matching source name lines with a name.") - (defvar org-babel-src-block-regexp (concat ;; (1) indentation (2) lang @@ -189,168 +204,100 @@ This string must include a \"%s\" which will be replaced by the results." "\\([^\000]*?\n\\)??[ \t]*#\\+end_src") "Regexp used to identify code blocks.") -(defvar org-babel-inline-src-block-regexp - (concat - ;; (1) replacement target (2) lang - "\\(?:^\\|[^-[:alnum:]]\\)\\(src_\\([^ \f\t\n\r\v]+\\)" - ;; (3,4) (unused, headers) - "\\(\\|\\[\\(.*?\\)\\]\\)" - ;; (5) body - "{\\([^\f\n\r\v]+?\\)}\\)") - "Regexp used to identify inline src-blocks.") - -(defun org-babel-get-header (params key &optional others) - "Select only header argument of type KEY from a list. -Optional argument OTHERS indicates that only the header that do -not match KEY should be returned." - (delq nil - (mapcar - (lambda (p) (when (funcall (if others #'not #'identity) (eq (car p) key)) p)) - params))) - -(defun org-babel-get-inline-src-block-matches() - "Set match data if within body of an inline source block. -Returns non-nil if match-data set" - (let ((src-at-0-p (save-excursion - (beginning-of-line 1) - (string= "src" (thing-at-point 'word)))) - (first-line-p (= (line-beginning-position) (point-min))) - (orig (point))) - (let ((search-for (cond ((and src-at-0-p first-line-p "src_")) - (first-line-p "[[:punct:] \t]src_") - (t "[[:punct:] \f\t\n\r\v]src_"))) - (lower-limit (if first-line-p - nil - (- (point-at-bol) 1)))) - (save-excursion - (when (or (and src-at-0-p (bobp)) - (and (re-search-forward "}" (point-at-eol) t) - (re-search-backward search-for lower-limit t) - (> orig (point)))) - (when (looking-at org-babel-inline-src-block-regexp) - t )))))) - -(defvar org-babel-inline-lob-one-liner-regexp) -(defun org-babel-get-lob-one-liner-matches() - "Set match data if on line of an lob one liner. -Returns non-nil if match-data set" - (save-excursion - (unless (= (point) (point-at-bol)) ;; move before inline block - (re-search-backward "[ \f\t\n\r\v]" nil t)) - (if (looking-at org-babel-inline-lob-one-liner-regexp) - t - nil))) - -(defun org-babel-get-src-block-info (&optional light) - "Get information on the current source block. - -Optional argument LIGHT does not resolve remote variable -references; a process which could likely result in the execution -of other code blocks. +(defun org-babel--get-vars (params) + "Return the babel variable assignments in PARAMS. + +PARAMS is a quasi-alist of header args, which may contain +multiple entries for the key `:var'. This function returns a +list of the cdr of all the `:var' entries." + (mapcar #'cdr + (cl-remove-if-not (lambda (x) (eq (car x) :var)) params))) + +(defvar org-babel-exp-reference-buffer nil + "Buffer containing original contents of the exported buffer. +This is used by Babel to resolve references in source blocks. +Its value is dynamically bound during export.") + +(defun org-babel-check-confirm-evaluate (info) + "Check whether INFO allows code block evaluation. + +Returns nil if evaluation is disallowed, t if it is +unconditionally allowed, and the symbol `query' if the user +should be asked whether to allow evaluation." + (let* ((headers (nth 2 info)) + (eval (or (cdr (assq :eval headers)) + (when (assq :noeval headers) "no"))) + (eval-no (member eval '("no" "never"))) + (export org-babel-exp-reference-buffer) + (eval-no-export (and export (member eval '("no-export" "never-export")))) + (noeval (or eval-no eval-no-export)) + (query (or (equal eval "query") + (and export (equal eval "query-export")) + (if (functionp org-confirm-babel-evaluate) + (save-excursion + (goto-char (nth 5 info)) + (funcall org-confirm-babel-evaluate + ;; language, code block body + (nth 0 info) (nth 1 info))) + org-confirm-babel-evaluate)))) + (cond + (noeval nil) + (query 'query) + (t t)))) -Returns a list - (language body header-arguments-alist switches name indent block-head)." - (let ((case-fold-search t) head info name indent) - ;; full code block - (if (setq head (org-babel-where-is-src-block-head)) - (save-excursion - (goto-char head) - (setq info (org-babel-parse-src-block-match)) - (setq indent (car (last info))) - (setq info (butlast info)) - (while (and (forward-line -1) - (looking-at org-babel-multi-line-header-regexp)) - (setf (nth 2 info) - (org-babel-merge-params - (nth 2 info) - (org-babel-parse-header-arguments (match-string 1))))) - (when (looking-at org-babel-src-name-w-name-regexp) - (setq name (org-no-properties (match-string 3))))) - ;; inline source block - (when (org-babel-get-inline-src-block-matches) - (setq info (org-babel-parse-inline-src-block-match)))) - ;; resolve variable references and add summary parameters - (when (and info (not light)) - (setf (nth 2 info) (org-babel-process-params (nth 2 info)))) - (when info (append info (list name indent head))))) - -(defvar org-current-export-file) ; dynamically bound -(defmacro org-babel-check-confirm-evaluate (info &rest body) - "Evaluate BODY with special execution confirmation variables set. - -Specifically; NOEVAL will indicate if evaluation is allowed, -QUERY will indicate if a user query is required, CODE-BLOCK will -hold the language of the code block, and BLOCK-NAME will hold the -name of the code block." - (declare (indent defun)) - (org-with-gensyms - (lang block-body headers name eval eval-no export eval-no-export) - `(let* ((,lang (nth 0 ,info)) - (,block-body (nth 1 ,info)) - (,headers (nth 2 ,info)) - (,name (nth 4 ,info)) - (,eval (or (cdr (assoc :eval ,headers)) - (when (assoc :noeval ,headers) "no"))) - (,eval-no (or (equal ,eval "no") - (equal ,eval "never"))) - (,export (org-bound-and-true-p org-current-export-file)) - (,eval-no-export (and ,export (or (equal ,eval "no-export") - (equal ,eval "never-export")))) - (noeval (or ,eval-no ,eval-no-export)) - (query (or (equal ,eval "query") - (and ,export (equal ,eval "query-export")) - (if (functionp org-confirm-babel-evaluate) - (funcall org-confirm-babel-evaluate - ,lang ,block-body) - org-confirm-babel-evaluate))) - (code-block (if ,info (format " %s " ,lang) " ")) - (block-name (if ,name (format " (%s) " ,name) " "))) - ;; Silence byte-compiler is `body' doesn't use those vars. - (ignore noeval query) - ,@body))) - -(defsubst org-babel-check-evaluate (info) +(defun org-babel-check-evaluate (info) "Check if code block INFO should be evaluated. -Do not query the user." - (org-babel-check-confirm-evaluate info - (not (when noeval - (message "Evaluation of this%scode-block%sis disabled." - code-block block-name))))) - - ;; dynamically scoped for asynchronous export +Do not query the user, but do display an informative message if +evaluation is blocked. Returns non-nil if evaluation is not blocked." + (let ((confirmed (org-babel-check-confirm-evaluate info))) + (unless confirmed + (message "Evaluation of this %s code block%sis disabled." + (nth 0 info) + (let ((name (nth 4 info))) + (if name (format " (%s) " name) " ")))) + confirmed)) + +;; Dynamically scoped for asynchronous export. (defvar org-babel-confirm-evaluate-answer-no) -(defsubst org-babel-confirm-evaluate (info) +(defun org-babel-confirm-evaluate (info) "Confirm evaluation of the code block INFO. -If the variable `org-babel-confirm-evaluate-answer-no' is bound -to a non-nil value, auto-answer with \"no\". - This query can also be suppressed by setting the value of `org-confirm-babel-evaluate' to nil, in which case all future interactive code block evaluations will proceed without any confirmation from the user. Note disabling confirmation may result in accidental evaluation -of potentially harmful code." - (org-babel-check-confirm-evaluate info - (not (when query - (unless - (and (not (org-bound-and-true-p +of potentially harmful code. + +The variable `org-babel-confirm-evaluate-answer-no' is used by +the async export process, which requires a non-interactive +environment, to override this check." + (let* ((evalp (org-babel-check-confirm-evaluate info)) + (lang (nth 0 info)) + (name (nth 4 info)) + (name-string (if name (format " (%s) " name) " "))) + (pcase evalp + (`nil nil) + (`t t) + (`query (or + (and (not (bound-and-true-p org-babel-confirm-evaluate-answer-no)) (yes-or-no-p - (format "Evaluate this%scode block%son your system? " - code-block block-name))) - (message "Evaluation of this%scode-block%sis aborted." - code-block block-name)))))) + (format "Evaluate this %s code block%son your system? " + lang name-string))) + (progn + (message "Evaluation of this %s code block%sis aborted." + lang name-string) + nil))) + (x (error "Unexpected value `%s' from `org-babel-check-confirm-evaluate'" x))))) ;;;###autoload (defun org-babel-execute-safely-maybe () (unless org-babel-no-eval-on-ctrl-c-ctrl-c (org-babel-execute-maybe))) -(add-hook 'org-ctrl-c-ctrl-c-hook 'org-babel-execute-safely-maybe) - ;;;###autoload (defun org-babel-execute-maybe () (interactive) @@ -361,8 +308,8 @@ of potentially harmful code." "Execute BODY if point is in a source block and return t. Otherwise do nothing and return nil." - `(if (or (org-babel-where-is-src-block-head) - (org-babel-get-inline-src-block-matches)) + `(if (memq (org-element-type (org-element-context)) + '(inline-src-block src-block)) (progn ,@body t) @@ -394,12 +341,16 @@ a window into the `org-babel-get-src-block-info' function." (header-args (nth 2 info))) (when name (funcall printf "Name: %s\n" name)) (when lang (funcall printf "Lang: %s\n" lang)) + (funcall printf "Properties:\n") + (funcall printf "\t:header-args \t%s\n" (org-entry-get (point) "header-args" t)) + (funcall printf "\t:header-args:%s \t%s\n" lang (org-entry-get (point) (concat "header-args:" lang) t)) + (when (funcall full switches) (funcall printf "Switches: %s\n" switches)) (funcall printf "Header Arguments:\n") (dolist (pair (sort header-args (lambda (a b) (string< (symbol-name (car a)) (symbol-name (car b)))))) - (when (funcall full (cdr pair)) + (when (funcall full (format "%s" (cdr pair))) (funcall printf "\t%S%s\t%s\n" (car pair) (if (> (length (format "%S" (car pair))) 7) "" "\t") @@ -442,11 +393,13 @@ then run `org-babel-switch-to-session'." (colnames . ((nil no yes))) (comments . ((no link yes org both noweb))) (dir . :any) - (eval . ((never query))) + (eval . ((yes no no-export strip-export never-export eval never + query))) (exports . ((code results both none))) (epilogue . :any) (file . :any) (file-desc . :any) + (file-ext . :any) (hlines . ((no yes))) (mkdirp . ((yes no))) (no-expand) @@ -454,6 +407,7 @@ then run `org-babel-switch-to-session'." (noweb . ((yes no tangle no-export strip-export))) (noweb-ref . :any) (noweb-sep . :any) + (output-dir . :any) (padline . ((yes no))) (post . :any) (prologue . :any) @@ -476,31 +430,76 @@ then run `org-babel-switch-to-session'." Note that individual languages may define their own language specific header arguments as well.") +(defconst org-babel-safe-header-args + '(:cache :colnames :comments :exports :epilogue :hlines :noeval + :noweb :noweb-ref :noweb-sep :padline :prologue :rownames + :sep :session :tangle :wrap + (:eval . ("never" "query")) + (:results . (lambda (str) (not (string-match "file" str))))) + "A list of safe header arguments for babel source blocks. + +The list can have entries of the following forms: +- :ARG -> :ARG is always a safe header arg +- (:ARG . (VAL1 VAL2 ...)) -> :ARG is safe as a header arg if it is + `equal' to one of the VALs. +- (:ARG . FN) -> :ARG is safe as a header arg if the function FN + returns non-nil. FN is passed one + argument, the value of the header arg + (as a string).") + +(defmacro org-babel-header-args-safe-fn (safe-list) + "Return a function that determines whether a list of header args are safe. + +Intended usage is: +\(put \\='org-babel-default-header-args \\='safe-local-variable + (org-babel-header-args-safe-p org-babel-safe-header-args) + +This allows org-babel languages to extend the list of safe values for +their `org-babel-default-header-args:foo' variable. + +For the format of SAFE-LIST, see `org-babel-safe-header-args'." + `(lambda (value) + (and (listp value) + (cl-every + (lambda (pair) + (and (consp pair) + (org-babel-one-header-arg-safe-p pair ,safe-list))) + value)))) + (defvar org-babel-default-header-args '((:session . "none") (:results . "replace") (:exports . "code") (:cache . "no") (:noweb . "no") (:hlines . "no") (:tangle . "no")) "Default arguments to use when evaluating a source block.") +(put 'org-babel-default-header-args 'safe-local-variable + (org-babel-header-args-safe-fn org-babel-safe-header-args)) (defvar org-babel-default-inline-header-args - '((:session . "none") (:results . "replace") (:exports . "results")) + '((:session . "none") (:results . "replace") + (:exports . "results") (:hlines . "yes")) "Default arguments to use when evaluating an inline source block.") - -(defvar org-babel-data-names '("tblname" "results" "name")) - -(defvar org-babel-result-regexp - (concat "^[ \t]*#\\+" - (regexp-opt org-babel-data-names t) - "\\(\\[\\(" - ;; FIXME The string below is `org-ts-regexp' - "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^\r\n>]*?\\)>" - " \\)?\\([[:alnum:]]+\\)\\]\\)?\\:[ \t]*") +(put 'org-babel-default-inline-header-args 'safe-local-variable + (org-babel-header-args-safe-fn org-babel-safe-header-args)) + +(defconst org-babel-name-regexp + (format "^[ \t]*#\\+%s:[ \t]*" + ;; FIXME: TBLNAME is for backward compatibility. + (regexp-opt '("NAME" "TBLNAME"))) + "Regexp matching a NAME keyword.") + +(defconst org-babel-result-regexp + (format "^[ \t]*#\\+%s\\(?:\\[\\(?:%s \\)?\\([[:alnum:]]+\\)\\]\\)?:[ \t]*" + org-babel-results-keyword + ;; <%Y-%m-%d %H:%M:%S> + "<\\(?:[0-9]\\{4\\}-[0-1][0-9]-[0-3][0-9] \ +[0-2][0-9]\\(?::[0-5][0-9]\\)\\{2\\}\\)>") "Regular expression used to match result lines. If the results are associated with a hash key then the hash will -be saved in the second match data.") +be saved in match group 1.") -(defvar org-babel-result-w-name-regexp - (concat org-babel-result-regexp - "\\([^ ()\f\t\n\r\v]+\\)\\((\\(.*\\))\\|\\)")) +(defconst org-babel-result-w-name-regexp + (concat org-babel-result-regexp "\\(?9:[^ \t\n\r\v\f]+\\)") + "Regexp matching a RESULTS keyword with a name. +Name is saved in match group 9.") (defvar org-babel-min-lines-for-block-output 10 "The minimum number of lines for block output. @@ -510,33 +509,58 @@ block. Otherwise the output is marked as literal by inserting colons at the starts of the lines. This variable only takes effect if the :results output option is in effect.") +(defvar org-babel-noweb-error-all-langs nil + "Raise errors when noweb references don't resolve. +Also see `org-babel-noweb-error-langs' to control noweb errors on +a language by language bases.") + (defvar org-babel-noweb-error-langs nil "Languages for which Babel will raise literate programming errors. List of languages for which errors should be raised when the source code block satisfying a noweb reference in this language -can not be resolved.") +can not be resolved. Also see `org-babel-noweb-error-all-langs' +to raise errors for all languages.") (defvar org-babel-hash-show 4 "Number of initial characters to show of a hidden results hash.") -(defvar org-babel-hash-show-time nil - "Non-nil means show the time the code block was evaluated in the result hash.") - (defvar org-babel-after-execute-hook nil "Hook for functions to be called after `org-babel-execute-src-block'") -(defun org-babel-named-src-block-regexp-for-name (name) - "This generates a regexp used to match a src block named NAME." - (concat org-babel-src-name-regexp (regexp-quote name) - "[ \t(]*[\r\n]\\(?:^#.*[\r\n]\\)*" +(defun org-babel-named-src-block-regexp-for-name (&optional name) + "This generates a regexp used to match a src block named NAME. +If NAME is nil, match any name. Matched name is then put in +match group 9. Other match groups are defined in +`org-babel-src-block-regexp'." + (concat org-babel-src-name-regexp + (concat (if name (regexp-quote name) "\\(?9:.*?\\)") "[ \t]*" ) + "\\(?:\n[ \t]*#\\+\\S-+:.*\\)*?" + "\n" (substring org-babel-src-block-regexp 1))) (defun org-babel-named-data-regexp-for-name (name) "This generates a regexp used to match data named NAME." - (concat org-babel-result-regexp (regexp-quote name) "\\([ \t]\\|$\\)")) + (concat org-babel-name-regexp (regexp-quote name) "[ \t]*$")) + +(defun org-babel--normalize-body (datum) + "Normalize body for element or object DATUM. +DATUM is a source block element or an inline source block object. +Remove final newline character and spurious indentation." + (let* ((value (org-element-property :value datum)) + (body (if (string-suffix-p "\n" value) + (substring value 0 -1) + value))) + (cond ((eq (org-element-type datum) 'inline-src-block) + ;; Newline characters and indentation in an inline + ;; src-block are not meaningful, since they could come from + ;; some paragraph filling. Treat them as a white space. + (replace-regexp-in-string "\n[ \t]*" " " body)) + ((or org-src-preserve-indentation + (org-element-property :preserve-indent datum)) + body) + (t (org-remove-indentation body))))) ;;; functions -(defvar call-process-region) (defvar org-babel-current-src-block-location nil "Marker pointing to the src block currently being executed. This may also point to a call line or an inline code block. If @@ -546,6 +570,56 @@ the outer-most code block.") (defvar *this*) +(defun org-babel-get-src-block-info (&optional light datum) + "Extract information from a source block or inline source block. + +Optional argument LIGHT does not resolve remote variable +references; a process which could likely result in the execution +of other code blocks. + +By default, consider the block at point. However, when optional +argument DATUM is provided, extract information from that parsed +object instead. + +Return nil if point is not on a source block. Otherwise, return +a list with the following pattern: + + (language body arguments switches name start coderef)" + (let* ((datum (or datum (org-element-context))) + (type (org-element-type datum)) + (inline (eq type 'inline-src-block))) + (when (memq type '(inline-src-block src-block)) + (let* ((lang (org-element-property :language datum)) + (lang-headers (intern + (concat "org-babel-default-header-args:" lang))) + (name (org-element-property :name datum)) + (info + (list + lang + (org-babel--normalize-body datum) + (apply #'org-babel-merge-params + (if inline org-babel-default-inline-header-args + org-babel-default-header-args) + (and (boundp lang-headers) (eval lang-headers t)) + (append + ;; If DATUM is provided, make sure we get node + ;; properties applicable to its location within + ;; the document. + (org-with-point-at (org-element-property :begin datum) + (org-babel-params-from-properties lang)) + (mapcar #'org-babel-parse-header-arguments + (cons (org-element-property :parameters datum) + (org-element-property :header datum))))) + (or (org-element-property :switches datum) "") + name + (org-element-property (if inline :begin :post-affiliated) + datum) + (and (not inline) (org-src-coderef-format datum))))) + (unless light + (setf (nth 2 info) (org-babel-process-params (nth 2 info)))) + (setf (nth 2 info) (org-babel-generate-file-param name (nth 2 info))) + info)))) + ;;;###autoload (defun org-babel-execute-src-block (&optional arg info params) "Execute the current source code block. @@ -565,110 +639,91 @@ block." (interactive) (let* ((org-babel-current-src-block-location (or org-babel-current-src-block-location - (nth 6 info) + (nth 5 info) (org-babel-where-is-src-block-head))) - (info (if info - (copy-tree info) - (org-babel-get-src-block-info))) - (merged-params (org-babel-merge-params (nth 2 info) params))) - (when (org-babel-check-evaluate - (let ((i info)) (setf (nth 2 i) merged-params) i)) - (let* ((params (if params - (org-babel-process-params merged-params) - (nth 2 info))) - (cachep (and (not arg) (cdr (assoc :cache params)) - (string= "yes" (cdr (assoc :cache params))))) - (new-hash (when cachep (org-babel-sha1-hash info))) - (old-hash (when cachep (org-babel-current-result-hash))) - (cache-current-p (and (not arg) new-hash - (equal new-hash old-hash)))) + (info (if info (copy-tree info) (org-babel-get-src-block-info)))) + ;; Merge PARAMS with INFO before considering source block + ;; evaluation since both could disagree. + (cl-callf org-babel-merge-params (nth 2 info) params) + (when (org-babel-check-evaluate info) + (cl-callf org-babel-process-params (nth 2 info)) + (let* ((params (nth 2 info)) + (cache (let ((c (cdr (assq :cache params)))) + (and (not arg) c (string= "yes" c)))) + (new-hash (and cache (org-babel-sha1-hash info))) + (old-hash (and cache (org-babel-current-result-hash))) + (current-cache (and new-hash (equal new-hash old-hash)))) (cond - (cache-current-p - (save-excursion ;; return cached result + (current-cache + (save-excursion ;Return cached result. (goto-char (org-babel-where-is-src-block-result nil info)) - (end-of-line 1) (forward-char 1) + (forward-line) + (skip-chars-forward " \t") (let ((result (org-babel-read-result))) - (message (replace-regexp-in-string - "%" "%%" (format "%S" result))) - result))) - ((org-babel-confirm-evaluate - (let ((i info)) (setf (nth 2 i) merged-params) i)) + (message (replace-regexp-in-string "%" "%%" (format "%S" result))) + result))) + ((org-babel-confirm-evaluate info) (let* ((lang (nth 0 info)) - (result-params (cdr (assoc :result-params params))) - (body (setf (nth 1 info) - (if (org-babel-noweb-p params :eval) - (org-babel-expand-noweb-references info) - (nth 1 info)))) - (dir (cdr (assoc :dir params))) + (result-params (cdr (assq :result-params params))) + ;; Expand noweb references in BODY and remove any + ;; coderef. + (body + (let ((coderef (nth 6 info)) + (expand + (if (org-babel-noweb-p params :eval) + (org-babel-expand-noweb-references info) + (nth 1 info)))) + (if (not coderef) expand + (replace-regexp-in-string + (org-src-coderef-regexp coderef) "" expand nil nil 1)))) + (dir (cdr (assq :dir params))) (default-directory (or (and dir (file-name-as-directory (expand-file-name dir))) default-directory)) - (org-babel-call-process-region-original ;; for tramp handler - (or (org-bound-and-true-p - org-babel-call-process-region-original) - (symbol-function 'call-process-region))) - (indent (nth 5 info)) - result cmd) - (unwind-protect - (let ((call-process-region - (lambda (&rest args) - (apply 'org-babel-tramp-handle-call-process-region - args)))) - (let ((lang-check - (lambda (f) - (let ((f (intern (concat "org-babel-execute:" f)))) - (when (fboundp f) f))))) - (setq cmd - (or (funcall lang-check lang) - (funcall lang-check - (symbol-name - (cdr (assoc lang org-src-lang-modes)))) - (error "No org-babel-execute function for %s!" - lang)))) - (message "executing %s code block%s..." - (capitalize lang) - (if (nth 4 info) (format " (%s)" (nth 4 info)) "")) - (if (member "none" result-params) - (progn - (funcall cmd body params) - (message "result silenced") - (setq result nil)) - (setq result - (let ((result (funcall cmd body params))) - (if (and (eq (cdr (assoc :result-type params)) - 'value) - (or (member "vector" result-params) - (member "table" result-params)) - (not (listp result))) - (list (list result)) result))) - ;; If non-empty result and :file then write to :file. - (when (cdr (assoc :file params)) - (when result - (with-temp-file (cdr (assoc :file params)) - (insert - (org-babel-format-result - result (cdr (assoc :sep (nth 2 info))))))) - (setq result (cdr (assoc :file params)))) - ;; Possibly perform post process provided its appropriate. - (when (cdr (assoc :post params)) - (let ((*this* (if (cdr (assoc :file params)) - (org-babel-result-to-file - (cdr (assoc :file params)) - (when (assoc :file-desc params) - (or (cdr (assoc :file-desc params)) - result))) - result))) - (setq result (org-babel-ref-resolve - (cdr (assoc :post params)))) - (when (cdr (assoc :file params)) - (setq result-params - (remove "file" result-params))))) - (org-babel-insert-result - result result-params info new-hash indent lang)) - (run-hooks 'org-babel-after-execute-hook) - result) - (setq call-process-region - 'org-babel-call-process-region-original))))))))) + (cmd (intern (concat "org-babel-execute:" lang))) + result) + (unless (fboundp cmd) + (error "No org-babel-execute function for %s!" lang)) + (message "executing %s code block%s..." + (capitalize lang) + (let ((name (nth 4 info))) + (if name (format " (%s)" name) ""))) + (if (member "none" result-params) + (progn (funcall cmd body params) + (message "result silenced")) + (setq result + (let ((r (funcall cmd body params))) + (if (and (eq (cdr (assq :result-type params)) 'value) + (or (member "vector" result-params) + (member "table" result-params)) + (not (listp r))) + (list (list r)) + r))) + (let ((file (cdr (assq :file params)))) + ;; If non-empty result and :file then write to :file. + (when file + (when result + (with-temp-file file + (insert (org-babel-format-result + result (cdr (assq :sep params)))))) + (setq result file)) + ;; Possibly perform post process provided its + ;; appropriate. Dynamically bind "*this*" to the + ;; actual results of the block. + (let ((post (cdr (assq :post params)))) + (when post + (let ((*this* (if (not file) result + (org-babel-result-to-file + file + (let ((desc (assq :file-desc params))) + (and desc (or (cdr desc) result))))))) + (setq result (org-babel-ref-resolve post)) + (when file + (setq result-params (remove "file" result-params)))))) + (org-babel-insert-result + result result-params info new-hash lang))) + (run-hooks 'org-babel-after-execute-hook) + result))))))) (defun org-babel-expand-body:generic (body params &optional var-lines) "Expand BODY with PARAMS. @@ -676,8 +731,8 @@ Expand a block of code with org-babel according to its header arguments. This generic implementation of body expansion is called for languages which have not defined their own specific org-babel-expand-body:lang function." - (let ((pro (cdr (assoc :prologue params))) - (epi (cdr (assoc :epilogue params)))) + (let ((pro (cdr (assq :prologue params))) + (epi (cdr (assq :epilogue params)))) (mapconcat #'identity (append (when pro (list pro)) var-lines @@ -708,10 +763,9 @@ arguments and pop open the results in a preview buffer." (org-babel-expand-body:generic body params (and (fboundp assignments-cmd) (funcall assignments-cmd params)))))) - (if (org-called-interactively-p 'any) + (if (called-interactively-p 'any) (org-edit-src-code - nil expanded - (concat "*Org-Babel Preview " (buffer-name) "[ " lang " ]*")) + expanded (concat "*Org-Babel Preview " (buffer-name) "[ " lang " ]*")) expanded))) (defun org-babel-edit-distance (s1 s2) @@ -742,7 +796,7 @@ arguments and pop open the results in a preview buffer." (dolist (arg-pair new-list) (let ((header (car arg-pair))) (setq results - (cons arg-pair (org-remove-if + (cons arg-pair (cl-remove-if (lambda (pair) (equal header (car pair))) results)))))) results)) @@ -770,37 +824,43 @@ arguments and pop open the results in a preview buffer." (message "No suspicious header arguments found."))) ;;;###autoload -(defun org-babel-insert-header-arg () +(defun org-babel-insert-header-arg (&optional header-arg value) "Insert a header argument selecting from lists of common args and values." (interactive) - (let* ((lang (car (org-babel-get-src-block-info 'light))) + (let* ((info (org-babel-get-src-block-info 'light)) + (lang (car info)) + (begin (nth 5 info)) (lang-headers (intern (concat "org-babel-header-args:" lang))) (headers (org-babel-combine-header-arg-lists org-babel-common-header-args-w-values - (when (boundp lang-headers) (eval lang-headers)))) - (arg (org-icompleting-read - "Header Arg: " - (mapcar - (lambda (header-spec) (symbol-name (car header-spec))) - headers)))) - (insert ":" arg) - (let ((vals (cdr (assoc (intern arg) headers)))) - (when vals - (insert - " " - (cond - ((eq vals :any) - (read-from-minibuffer "value: ")) - ((listp vals) - (mapconcat - (lambda (group) - (let ((arg (org-icompleting-read - "value: " - (cons "default" (mapcar #'symbol-name group))))) - (if (and arg (not (string= "default" arg))) - (concat arg " ") - ""))) - vals "")))))))) + (when (boundp lang-headers) (eval lang-headers t)))) + (header-arg (or header-arg + (completing-read + "Header Arg: " + (mapcar + (lambda (header-spec) (symbol-name (car header-spec))) + headers)))) + (vals (cdr (assoc (intern header-arg) headers))) + (value (or value + (cond + ((eq vals :any) + (read-from-minibuffer "value: ")) + ((listp vals) + (mapconcat + (lambda (group) + (let ((arg (completing-read + "Value: " + (cons "default" + (mapcar #'symbol-name group))))) + (if (and arg (not (string= "default" arg))) + (concat arg " ") + ""))) + vals "")))))) + (save-excursion + (goto-char begin) + (goto-char (point-at-eol)) + (unless (= (char-before (point)) ?\ ) (insert " ")) + (insert ":" header-arg) (when value (insert " " value))))) ;; Add support for completing-read insertion of header arguments after ":" (defun org-babel-header-arg-expand () @@ -811,7 +871,7 @@ arguments and pop open the results in a preview buffer." (defun org-babel-enter-header-arg-w-completion (&optional lang) "Insert header argument appropriate for LANG with completion." (let* ((lang-headers-var (intern (concat "org-babel-header-args:" lang))) - (lang-headers (when (boundp lang-headers-var) (eval lang-headers-var))) + (lang-headers (when (boundp lang-headers-var) (eval lang-headers-var t))) (headers-w-values (org-babel-combine-header-arg-lists org-babel-common-header-args-w-values lang-headers)) (headers (mapcar #'symbol-name (mapcar #'car headers-w-values))) @@ -842,8 +902,8 @@ session." (if (org-babel-noweb-p params :eval) (org-babel-expand-noweb-references info) (nth 1 info))))) - (session (cdr (assoc :session params))) - (dir (cdr (assoc :dir params))) + (session (cdr (assq :session params))) + (dir (cdr (assq :dir params))) (default-directory (or (and dir (file-name-as-directory dir)) default-directory)) (cmd (intern (concat "org-babel-load-session:" lang)))) @@ -863,17 +923,17 @@ the session. Copy the body of the code block to the kill ring." (lang (nth 0 info)) (body (nth 1 info)) (params (nth 2 info)) - (session (cdr (assoc :session params))) - (dir (cdr (assoc :dir params))) + (session (cdr (assq :session params))) + (dir (cdr (assq :dir params))) (default-directory (or (and dir (file-name-as-directory dir)) default-directory)) (init-cmd (intern (format "org-babel-%s-initiate-session" lang))) (prep-cmd (intern (concat "org-babel-prep-session:" lang)))) - (if (and (stringp session) (string= session "none")) - (error "This block is not using a session!")) + (when (and (stringp session) (string= session "none")) + (error "This block is not using a session!")) (unless (fboundp init-cmd) (error "No org-babel-initiate-session function for %s!" lang)) - (with-temp-buffer (insert (org-babel-trim body)) + (with-temp-buffer (insert (org-trim body)) (copy-region-as-kill (point-min) (point-max))) (when arg (unless (fboundp prep-cmd) @@ -912,15 +972,15 @@ with a prefix argument then this is passed on to (org-edit-src-code) (funcall swap-windows))) +;;;###autoload (defmacro org-babel-do-in-edit-buffer (&rest body) "Evaluate BODY in edit buffer if there is a code block at point. Return t if a code block was found at point, nil otherwise." `(let ((org-src-window-setup 'switch-invisibly)) (when (and (org-babel-where-is-src-block-head) - (org-edit-src-code nil nil nil)) + (org-edit-src-code)) (unwind-protect (progn ,@body) - (if (org-bound-and-true-p org-edit-src-from-org-mode) - (org-edit-src-exit))) + (org-edit-src-exit)) t))) (def-edebug-spec org-babel-do-in-edit-buffer (body)) @@ -928,10 +988,10 @@ Return t if a code block was found at point, nil otherwise." "Read key sequence and execute the command in edit buffer. Enter a key sequence to be executed in the language major-mode edit buffer. For example, TAB will alter the contents of the -Org-mode code block according to the effect of TAB in the -language major-mode buffer. For languages that support -interactive sessions, this can be used to send code from the Org -buffer to the session for evaluation using the native major-mode +Org code block according to the effect of TAB in the language +major mode buffer. For languages that support interactive +sessions, this can be used to send code from the Org buffer +to the session for evaluation using the native major mode evaluation mechanisms." (interactive "kEnter key-sequence to execute in edit buffer: ") (org-babel-do-in-edit-buffer @@ -941,7 +1001,7 @@ evaluation mechanisms." (defvar org-bracket-link-regexp) (defun org-babel-active-location-p () - (memq (car (save-match-data (org-element-context))) + (memq (org-element-type (save-match-data (org-element-context))) '(babel-call inline-babel-call inline-src-block src-block))) ;;;###autoload @@ -965,7 +1025,7 @@ results already exist." ;; file results (org-open-at-point) (let ((r (org-babel-format-result - (org-babel-read-result) (cdr (assoc :sep (nth 2 info)))))) + (org-babel-read-result) (cdr (assq :sep (nth 2 info)))))) (pop-to-buffer (get-buffer-create "*Org-Babel Results*")) (delete-region (point-min) (point-max)) (insert r))) @@ -995,7 +1055,8 @@ beg-body --------- point at the beginning of the body end-body --------- point at the end of the body" (declare (indent 1)) (let ((tempvar (make-symbol "file"))) - `(let* ((,tempvar ,file) + `(let* ((case-fold-search t) + (,tempvar ,file) (visited-p (or (null ,tempvar) (get-file-buffer (expand-file-name ,tempvar)))) (point (point)) to-be-removed) @@ -1035,80 +1096,91 @@ end-body --------- point at the end of the body" ;;;###autoload (defmacro org-babel-map-inline-src-blocks (file &rest body) - "Evaluate BODY forms on each inline source-block in FILE. + "Evaluate BODY forms on each inline source block in FILE. If FILE is nil evaluate BODY forms on source blocks in current buffer." - (declare (indent 1)) - (let ((tempvar (make-symbol "file"))) - `(let* ((,tempvar ,file) - (visited-p (or (null ,tempvar) + (declare (indent 1) (debug (form body))) + (org-with-gensyms (datum end point tempvar to-be-removed visitedp) + `(let* ((case-fold-search t) + (,tempvar ,file) + (,visitedp (or (null ,tempvar) (get-file-buffer (expand-file-name ,tempvar)))) - (point (point)) to-be-removed) + (,point (point)) + ,to-be-removed) (save-window-excursion (when ,tempvar (find-file ,tempvar)) - (setq to-be-removed (current-buffer)) + (setq ,to-be-removed (current-buffer)) (goto-char (point-min)) - (while (re-search-forward org-babel-inline-src-block-regexp nil t) - (when (org-babel-active-location-p) - (goto-char (match-beginning 1)) - (save-match-data ,@body)) - (goto-char (match-end 0)))) - (unless visited-p (kill-buffer to-be-removed)) - (goto-char point)))) -(def-edebug-spec org-babel-map-inline-src-blocks (form body)) - -(defvar org-babel-lob-one-liner-regexp) + (while (re-search-forward "src_\\S-" nil t) + (let ((,datum (save-match-data (org-element-context)))) + (when (eq (org-element-type ,datum) 'inline-src-block) + (goto-char (match-beginning 0)) + (let ((,end (copy-marker (org-element-property :end ,datum)))) + ,@body + (goto-char ,end) + (set-marker ,end nil)))))) + (unless ,visitedp (kill-buffer ,to-be-removed)) + (goto-char ,point)))) ;;;###autoload (defmacro org-babel-map-call-lines (file &rest body) "Evaluate BODY forms on each call line in FILE. If FILE is nil evaluate BODY forms on source blocks in current buffer." - (declare (indent 1)) - (let ((tempvar (make-symbol "file"))) - `(let* ((,tempvar ,file) - (visited-p (or (null ,tempvar) + (declare (indent 1) (debug (form body))) + (org-with-gensyms (datum end point tempvar to-be-removed visitedp) + `(let* ((case-fold-search t) + (,tempvar ,file) + (,visitedp (or (null ,tempvar) (get-file-buffer (expand-file-name ,tempvar)))) - (point (point)) to-be-removed) + (,point (point)) + ,to-be-removed) (save-window-excursion (when ,tempvar (find-file ,tempvar)) - (setq to-be-removed (current-buffer)) + (setq ,to-be-removed (current-buffer)) (goto-char (point-min)) - (while (re-search-forward org-babel-lob-one-liner-regexp nil t) - (when (org-babel-active-location-p) - (goto-char (match-beginning 1)) - (save-match-data ,@body)) - (goto-char (match-end 0)))) - (unless visited-p (kill-buffer to-be-removed)) - (goto-char point)))) -(def-edebug-spec org-babel-map-call-lines (form body)) + (while (re-search-forward "call_\\S-\\|^[ \t]*#\\+CALL:" nil t) + (let ((,datum (save-match-data (org-element-context)))) + (when (memq (org-element-type ,datum) + '(babel-call inline-babel-call)) + (goto-char (match-beginning 0)) + (let ((,end (copy-marker (org-element-property :end ,datum)))) + ,@body + (goto-char ,end) + (set-marker ,end nil)))))) + (unless ,visitedp (kill-buffer ,to-be-removed)) + (goto-char ,point)))) ;;;###autoload (defmacro org-babel-map-executables (file &rest body) - (declare (indent 1)) - (let ((tempvar (make-symbol "file")) - (rx (make-symbol "rx"))) - `(let* ((,tempvar ,file) - (,rx (concat "\\(" org-babel-src-block-regexp - "\\|" org-babel-inline-src-block-regexp - "\\|" org-babel-lob-one-liner-regexp "\\)")) - (visited-p (or (null ,tempvar) + "Evaluate BODY forms on each active Babel code in FILE. +If FILE is nil evaluate BODY forms on source blocks in current +buffer." + (declare (indent 1) (debug (form body))) + (org-with-gensyms (datum end point tempvar to-be-removed visitedp) + `(let* ((case-fold-search t) + (,tempvar ,file) + (,visitedp (or (null ,tempvar) (get-file-buffer (expand-file-name ,tempvar)))) - (point (point)) to-be-removed) + (,point (point)) + ,to-be-removed) (save-window-excursion (when ,tempvar (find-file ,tempvar)) - (setq to-be-removed (current-buffer)) + (setq ,to-be-removed (current-buffer)) (goto-char (point-min)) - (while (re-search-forward ,rx nil t) - (when (org-babel-active-location-p) - (goto-char (match-beginning 1)) - (when (looking-at org-babel-inline-src-block-regexp) - (forward-char 1)) - (save-match-data ,@body)) - (goto-char (match-end 0)))) - (unless visited-p (kill-buffer to-be-removed)) - (goto-char point)))) -(def-edebug-spec org-babel-map-executables (form body)) + (while (re-search-forward + "\\(call\\|src\\)_\\|^[ \t]*#\\+\\(BEGIN_SRC\\|CALL:\\)" nil t) + (let ((,datum (save-match-data (org-element-context)))) + (when (memq (org-element-type ,datum) + '(babel-call inline-babel-call inline-src-block + src-block)) + (goto-char (match-beginning 0)) + (let ((,end (copy-marker (org-element-property :end ,datum)))) + ,@body + (goto-char ,end) + (set-marker ,end nil)))))) + (unless ,visitedp (kill-buffer ,to-be-removed)) + (goto-char ,point)))) ;;;###autoload (defun org-babel-execute-buffer (&optional arg) @@ -1119,7 +1191,8 @@ the current buffer." (org-babel-eval-wipe-error-buffer) (org-save-outline-visibility t (org-babel-map-executables nil - (if (looking-at org-babel-lob-one-liner-regexp) + (if (memq (org-element-type (org-element-context)) + '(babel-call inline-babel-call)) (org-babel-lob-execute-maybe) (org-babel-execute-src-block arg))))) @@ -1164,7 +1237,20 @@ the current subtree." (member (car arg) '(:results :exports))) (mapconcat #'identity (sort (funcall rm (split-string v)) #'string<) " ")) - (t v))))))) + (t v)))))) + ;; expanded body + (lang (nth 0 info)) + (params (nth 2 info)) + (body (if (org-babel-noweb-p params :eval) + (org-babel-expand-noweb-references info) (nth 1 info))) + (expand-cmd (intern (concat "org-babel-expand-body:" lang))) + (assignments-cmd (intern (concat "org-babel-variable-assignments:" + lang))) + (expanded + (if (fboundp expand-cmd) (funcall expand-cmd body params) + (org-babel-expand-body:generic + body params (and (fboundp assignments-cmd) + (funcall assignments-cmd params)))))) (let* ((it (format "%s-%s" (mapconcat #'identity @@ -1173,26 +1259,32 @@ the current subtree." (when normalized (format "%S" normalized)))) (nth 2 info))) ":") - (nth 1 info))) + expanded)) (hash (sha1 it))) - (when (org-called-interactively-p 'interactive) (message hash)) + (when (called-interactively-p 'interactive) (message hash)) hash)))) -(defun org-babel-current-result-hash () +(defun org-babel-current-result-hash (&optional info) "Return the current in-buffer hash." - (org-babel-where-is-src-block-result) - (org-no-properties (match-string 5))) + (let ((result (org-babel-where-is-src-block-result nil info))) + (when result + (org-with-wide-buffer + (goto-char result) + (looking-at org-babel-result-regexp) + (match-string-no-properties 1))))) -(defun org-babel-set-current-result-hash (hash) +(defun org-babel-set-current-result-hash (hash info) "Set the current in-buffer hash to HASH." - (org-babel-where-is-src-block-result) - (save-excursion (goto-char (match-beginning 5)) - (mapc #'delete-overlay (overlays-at (point))) - (forward-char org-babel-hash-show) - (mapc #'delete-overlay (overlays-at (point))) - (replace-match hash nil nil nil 5) - (goto-char (point-at-bol)) - (org-babel-hide-hash))) + (org-with-wide-buffer + (goto-char (org-babel-where-is-src-block-result nil info)) + (looking-at org-babel-result-regexp) + (goto-char (match-beginning 1)) + (mapc #'delete-overlay (overlays-at (point))) + (forward-char org-babel-hash-show) + (mapc #'delete-overlay (overlays-at (point))) + (replace-match hash nil nil nil 1) + (beginning-of-line) + (org-babel-hide-hash))) (defun org-babel-hide-hash () "Hide the hash in the current results line. @@ -1201,11 +1293,11 @@ will remain visible." (add-to-invisibility-spec '(org-babel-hide-hash . t)) (save-excursion (when (and (re-search-forward org-babel-result-regexp nil t) - (match-string 5)) - (let* ((start (match-beginning 5)) + (match-string 1)) + (let* ((start (match-beginning 1)) (hide-start (+ org-babel-hash-show start)) - (end (match-end 5)) - (hash (match-string 5)) + (end (match-end 1)) + (hash (match-string 1)) ov1 ov2) (setq ov1 (make-overlay start hide-start)) (setq ov2 (make-overlay hide-start end)) @@ -1227,14 +1319,14 @@ the `org-mode-hook'." (defun org-babel-hash-at-point (&optional point) "Return the value of the hash at POINT. +\\\ The hash is also added as the last element of the kill ring. -This can be called with C-c C-c." +This can be called with `\\[org-ctrl-c-ctrl-c]'." (interactive) (let ((hash (car (delq nil (mapcar (lambda (ol) (overlay-get ol 'babel-hash)) (overlays-at (or point (point)))))))) (when hash (kill-new hash) (message hash)))) -(add-hook 'org-ctrl-c-ctrl-c-hook 'org-babel-hash-at-point) (defun org-babel-result-hide-spec () "Hide portions of results lines. @@ -1288,15 +1380,15 @@ portions of results lines." (eq (overlay-get overlay 'invisible) 'org-babel-hide-result)) (overlays-at start))) - (if (or (not force) (eq force 'off)) - (mapc (lambda (ov) - (when (member ov org-babel-hide-result-overlays) - (setq org-babel-hide-result-overlays - (delq ov org-babel-hide-result-overlays))) - (when (eq (overlay-get ov 'invisible) - 'org-babel-hide-result) - (delete-overlay ov))) - (overlays-at start))) + (when (or (not force) (eq force 'off)) + (mapc (lambda (ov) + (when (member ov org-babel-hide-result-overlays) + (setq org-babel-hide-result-overlays + (delq ov org-babel-hide-result-overlays))) + (when (eq (overlay-get ov 'invisible) + 'org-babel-hide-result) + (delete-overlay ov))) + (overlays-at start))) (setq ov (make-overlay start end)) (overlay-put ov 'invisible 'org-babel-hide-result) ;; make the block accessible to isearch @@ -1316,8 +1408,8 @@ portions of results lines." (add-hook 'org-tab-first-hook 'org-babel-hide-result-toggle-maybe) ;; Remove overlays when changing major mode (add-hook 'org-mode-hook - (lambda () (org-add-hook 'change-major-mode-hook - 'org-babel-show-result-all 'append 'local))) + (lambda () (add-hook 'change-major-mode-hook + 'org-babel-show-result-all 'append 'local))) (defvar org-file-properties) (defun org-babel-params-from-properties (&optional lang) @@ -1326,122 +1418,98 @@ Return a list of association lists of source block params specified in the properties of the current outline entry." (save-match-data (list - ;; DEPRECATED header arguments specified as separate property at - ;; point of definition - (let (val sym) - (org-babel-parse-multiple-vars - (delq nil - (mapcar - (lambda (header-arg) - (and (setq val (org-entry-get (point) header-arg t)) - (cons (intern (concat ":" header-arg)) - (org-babel-read val)))) - (mapcar - #'symbol-name - (mapcar - #'car - (org-babel-combine-header-arg-lists - org-babel-common-header-args-w-values - (progn - (setq sym (intern (concat "org-babel-header-args:" lang))) - (and (boundp sym) (eval sym)))))))))) ;; header arguments specified with the header-args property at - ;; point of call + ;; point of call. (org-babel-parse-header-arguments (org-entry-get org-babel-current-src-block-location - "header-args" 'inherit)) - (when lang ;; language-specific header arguments at point of call - (org-babel-parse-header-arguments - (org-entry-get org-babel-current-src-block-location - (concat "header-args:" lang) 'inherit)))))) - -(defvar org-src-preserve-indentation) ;; declare defcustom from org-src -(defun org-babel-parse-src-block-match () - "Parse the results from a match of the `org-babel-src-block-regexp'." - (let* ((block-indentation (length (match-string 1))) - (lang (org-no-properties (match-string 2))) - (lang-headers (intern (concat "org-babel-default-header-args:" lang))) - (switches (match-string 3)) - (body (org-no-properties - (let* ((body (match-string 5)) - (sub-length (- (length body) 1))) - (if (and (> sub-length 0) - (string= "\n" (substring body sub-length))) - (substring body 0 sub-length) - (or body ""))))) - (preserve-indentation (or org-src-preserve-indentation - (save-match-data - (string-match "-i\\>" switches))))) - (list lang - ;; get block body less properties, protective commas, and indentation - (with-temp-buffer - (save-match-data - (insert (org-unescape-code-in-string body)) - (unless preserve-indentation (org-do-remove-indentation)) - (buffer-string))) - (apply #'org-babel-merge-params - org-babel-default-header-args - (when (boundp lang-headers) (eval lang-headers)) - (append - (org-babel-params-from-properties lang) - (list (org-babel-parse-header-arguments - (org-no-properties (or (match-string 4) "")))))) - switches - block-indentation))) - -(defun org-babel-parse-inline-src-block-match () - "Parse the results from a match of the `org-babel-inline-src-block-regexp'." - (let* ((lang (org-no-properties (match-string 2))) - (lang-headers (intern (concat "org-babel-default-header-args:" lang)))) - (list lang - (org-unescape-code-in-string (org-no-properties (match-string 5))) - (apply #'org-babel-merge-params - org-babel-default-inline-header-args - (if (boundp lang-headers) (eval lang-headers) nil) - (append - (org-babel-params-from-properties lang) - (list (org-babel-parse-header-arguments - (org-no-properties (or (match-string 4) ""))))))))) + "header-args" + 'inherit)) + (and lang ; language-specific header arguments at point of call + (org-babel-parse-header-arguments + (org-entry-get org-babel-current-src-block-location + (concat "header-args:" lang) + 'inherit)))))) (defun org-babel-balanced-split (string alts) "Split STRING on instances of ALTS. -ALTS is a cons of two character options where each option may be -either the numeric code of a single character or a list of -character alternatives. For example to split on balanced -instances of \"[ \t]:\" set ALTS to ((32 9) . 58)." - (let* ((matches (lambda (ch spec) (if (listp spec) (member ch spec) (equal spec ch)))) - (matched (lambda (ch last) - (if (consp alts) - (and (funcall matches ch (cdr alts)) - (funcall matches last (car alts))) - (funcall matches ch alts)))) - (balance 0) (last 0) - quote partial lst) - (mapc (lambda (ch) ; split on [], (), "" balanced instances of [ \t]: - (setq balance (+ balance - (cond ((or (equal 91 ch) (equal 40 ch)) 1) - ((or (equal 93 ch) (equal 41 ch)) -1) - (t 0)))) - (when (and (equal 34 ch) (not (equal 92 last))) - (setq quote (not quote))) - (setq partial (cons ch partial)) - (when (and (= balance 0) (not quote) (funcall matched ch last)) - (setq lst (cons (apply #'string (nreverse - (if (consp alts) - (cddr partial) - (cdr partial)))) - lst)) - (setq partial nil)) - (setq last ch)) - (string-to-list string)) - (nreverse (cons (apply #'string (nreverse partial)) lst)))) +ALTS is a character, or cons of two character options where each +option may be either the numeric code of a single character or +a list of character alternatives. For example, to split on +balanced instances of \"[ \t]:\", set ALTS to ((32 9) . 58)." + (with-temp-buffer + (insert string) + (goto-char (point-min)) + (let ((splitp (lambda (past next) + ;; Non-nil when there should be a split after NEXT + ;; character. PAST is the character before NEXT. + (pcase alts + (`(,(and first (pred consp)) . ,(and second (pred consp))) + (and (memq past first) (memq next second))) + (`(,first . ,(and second (pred consp))) + (and (eq past first) (memq next second))) + (`(,(and first (pred consp)) . ,second) + (and (memq past first) (eq next second))) + (`(,first . ,second) + (and (eq past first) (eq next second))) + ((pred (eq next)) t) + (_ nil)))) + (partial nil) + (result nil)) + (while (not (eobp)) + (cond + ((funcall splitp (char-before) (char-after)) + ;; There is a split after point. If ALTS is two-folds, + ;; remove last parsed character as it belongs to ALTS. + (when (consp alts) (pop partial)) + ;; Include elements parsed so far in RESULTS and flush + ;; partial parsing. + (when partial + (push (apply #'string (nreverse partial)) result) + (setq partial nil)) + (forward-char)) + ((memq (char-after) '(?\( ?\[)) + ;; Include everything between balanced brackets. + (let* ((origin (point)) + (after (char-after)) + (openings (list after))) + (forward-char) + (while (and openings (re-search-forward "[]()]" nil t)) + (pcase (char-before) + ((and match (or ?\[ ?\()) (push match openings)) + (?\] (when (eq ?\[ (car openings)) (pop openings))) + (_ (when (eq ?\( (car openings)) (pop openings))))) + (if (null openings) + (setq partial + (nconc (nreverse (string-to-list + (buffer-substring origin (point)))) + partial)) + ;; Un-balanced bracket. Backtrack. + (push after partial) + (goto-char (1+ origin))))) + ((and (eq ?\" (char-after)) (not (eq ?\\ (char-before)))) + ;; Include everything from current double quote to next + ;; non-escaped double quote. + (let ((origin (point))) + (if (re-search-forward "[^\\]\"" nil t) + (setq partial + (nconc (nreverse (string-to-list + (buffer-substring origin (point)))) + partial)) + ;; No closing double quote. Backtrack. + (push ?\" partial) + (forward-char)))) + (t (push (char-after) partial) + (forward-char)))) + ;; Add pending parsing and return result. + (when partial (push (apply #'string (nreverse partial)) result)) + (nreverse result)))) (defun org-babel-join-splits-near-ch (ch list) "Join splits where \"=\" is on either end of the split." (let ((last= (lambda (str) (= ch (aref str (1- (length str)))))) (first= (lambda (str) (= ch (aref str 0))))) (reverse - (org-reduce (lambda (acc el) + (cl-reduce (lambda (acc el) (let ((head (car acc))) (if (and head (or (funcall last= head) (funcall first= el))) (cons (concat head el) (cdr acc)) @@ -1474,7 +1542,7 @@ shown below. (let (results) (mapc (lambda (pair) (if (eq (car pair) :var) - (mapcar (lambda (v) (push (cons :var (org-babel-trim v)) results)) + (mapcar (lambda (v) (push (cons :var (org-trim v)) results)) (org-babel-join-splits-near-ch 61 (org-babel-balanced-split (cdr pair) 32))) (push pair results))) @@ -1484,48 +1552,52 @@ shown below. (defun org-babel-process-params (params) "Expand variables in PARAMS and add summary parameters." (let* ((processed-vars (mapcar (lambda (el) - (if (consp (cdr el)) - (cdr el) - (org-babel-ref-parse (cdr el)))) - (org-babel-get-header params :var))) - (vars-and-names (if (and (assoc :colname-names params) - (assoc :rowname-names params)) + (if (consp el) + el + (org-babel-ref-parse el))) + (org-babel--get-vars params))) + (vars-and-names (if (and (assq :colname-names params) + (assq :rowname-names params)) (list processed-vars) (org-babel-disassemble-tables processed-vars - (cdr (assoc :hlines params)) - (cdr (assoc :colnames params)) - (cdr (assoc :rownames params))))) - (raw-result (or (cdr (assoc :results params)) "")) - (result-params (append - (split-string (if (stringp raw-result) - raw-result - (eval raw-result))) - (cdr (assoc :result-params params))))) + (cdr (assq :hlines params)) + (cdr (assq :colnames params)) + (cdr (assq :rownames params))))) + (raw-result (or (cdr (assq :results params)) "")) + (result-params (delete-dups + (append + (split-string (if (stringp raw-result) + raw-result + (eval raw-result t))) + (cdr (assq :result-params params)))))) (append (mapcar (lambda (var) (cons :var var)) (car vars-and-names)) (list - (cons :colname-names (or (cdr (assoc :colname-names params)) + (cons :colname-names (or (cdr (assq :colname-names params)) (cadr vars-and-names))) - (cons :rowname-names (or (cdr (assoc :rowname-names params)) - (caddr vars-and-names))) + (cons :rowname-names (or (cdr (assq :rowname-names params)) + (cl-caddr vars-and-names))) (cons :result-params result-params) (cons :result-type (cond ((member "output" result-params) 'output) ((member "value" result-params) 'value) (t 'value)))) - (org-babel-get-header params :var 'other)))) + (cl-remove-if + (lambda (x) (memq (car x) '(:colname-names :rowname-names :result-params + :result-type :var))) + params)))) ;; row and column names (defun org-babel-del-hlines (table) "Remove all `hline's from TABLE." - (remove 'hline table)) + (remq 'hline table)) (defun org-babel-get-colnames (table) "Return the column names of TABLE. Return a cons cell, the `car' of which contains the TABLE less colnames, and the `cdr' of which contains a list of the column names." - (if (equal 'hline (nth 1 table)) + (if (eq 'hline (nth 1 table)) (cons (cddr table) (car table)) (cons (cdr table) (car table)))) @@ -1583,7 +1655,7 @@ of the vars, cnames and rnames." (lambda (var) (when (listp (cdr var)) (when (and (not (equal colnames "no")) - (or colnames (and (equal (nth 1 (cdr var)) 'hline) + (or colnames (and (eq (nth 1 (cdr var)) 'hline) (not (member 'hline (cddr (cdr var))))))) (let ((both (org-babel-get-colnames (cdr var)))) (setq cnames (cons (cons (car var) (cdr both)) @@ -1612,35 +1684,26 @@ to the table for reinsertion to org-mode." (org-babel-put-colnames table colnames) table)) table)) -(defun org-babel-where-is-src-block-head () +(defun org-babel-where-is-src-block-head (&optional src-block) "Find where the current source block begins. -Return the point at the beginning of the current source -block. Specifically at the beginning of the #+BEGIN_SRC line. + +If optional argument SRC-BLOCK is `src-block' type element, find +its current beginning instead. + +Return the point at the beginning of the current source block. +Specifically at the beginning of the #+BEGIN_SRC line. Also set +match-data relatively to `org-babel-src-block-regexp', which see. If the point is not on a source block then return nil." - (let ((initial (point)) (case-fold-search t) top bottom) - (or - (save-excursion ;; on a source name line or a #+header line - (beginning-of-line 1) - (and (or (looking-at org-babel-src-name-regexp) - (looking-at org-babel-multi-line-header-regexp)) - (progn - (while (and (forward-line 1) - (or (looking-at org-babel-src-name-regexp) - (looking-at org-babel-multi-line-header-regexp)))) - (looking-at org-babel-src-block-regexp)) - (point))) - (save-excursion ;; on a #+begin_src line - (beginning-of-line 1) - (and (looking-at org-babel-src-block-regexp) - (point))) - (save-excursion ;; inside a src block - (and - (re-search-backward "^[ \t]*#\\+begin_src" nil t) (setq top (point)) - (re-search-forward "^[ \t]*#\\+end_src" nil t) (setq bottom (point)) - (< top initial) (< initial bottom) - (progn (goto-char top) (beginning-of-line 1) - (looking-at org-babel-src-block-regexp)) - (point-marker)))))) + (let ((element (or src-block (org-element-at-point)))) + (when (eq (org-element-type element) 'src-block) + (let ((end (org-element-property :end element))) + (org-with-wide-buffer + ;; Ensure point is not on a blank line after the block. + (beginning-of-line) + (skip-chars-forward " \r\t\n" end) + (when (< (point) end) + (prog1 (goto-char (org-element-property :post-affiliated element)) + (looking-at org-babel-src-block-regexp)))))))) ;;;###autoload (defun org-babel-goto-src-block-head () @@ -1655,56 +1718,52 @@ If the point is not on a source block then return nil." (interactive (let ((completion-ignore-case t) (case-fold-search t) - (under-point (thing-at-point 'line))) - (list (org-icompleting-read - "source-block name: " (org-babel-src-block-names) nil t - (cond - ;; noweb - ((string-match (org-babel-noweb-wrap) under-point) - (let ((block-name (match-string 1 under-point))) - (string-match "[^(]*" block-name) - (match-string 0 block-name))) - ;; #+call: - ((string-match org-babel-lob-one-liner-regexp under-point) - (let ((source-info (car (org-babel-lob-get-info)))) - (if (string-match "^\\([^\\[]+?\\)\\(\\[.*\\]\\)?(" source-info) - (let ((source-name (match-string 1 source-info))) - source-name)))) - ;; #+results: - ((string-match (concat "#\\+" org-babel-results-keyword - "\\:\s+\\([^\\(]*\\)") under-point) - (match-string 1 under-point)) - ;; symbol-at-point - ((and (thing-at-point 'symbol)) - (org-babel-find-named-block (thing-at-point 'symbol)) - (thing-at-point 'symbol)) - ("")))))) + (all-block-names (org-babel-src-block-names))) + (list (completing-read + "source-block name: " all-block-names nil t + (let* ((context (org-element-context)) + (type (org-element-type context)) + (noweb-ref + (and (memq type '(inline-src-block src-block)) + (org-in-regexp (org-babel-noweb-wrap))))) + (cond + (noweb-ref + (buffer-substring + (+ (car noweb-ref) (length org-babel-noweb-wrap-start)) + (- (cdr noweb-ref) (length org-babel-noweb-wrap-end)))) + ((memq type '(babel-call inline-babel-call)) ;#+CALL: + (org-element-property :call context)) + ((car (org-element-property :results context))) ;#+RESULTS: + ((let ((symbol (thing-at-point 'symbol))) ;Symbol. + (and symbol + (member-ignore-case symbol all-block-names) + symbol))) + (t ""))))))) (let ((point (org-babel-find-named-block name))) (if point - ;; taken from `org-open-at-point' + ;; Taken from `org-open-at-point'. (progn (org-mark-ring-push) (goto-char point) (org-show-context)) (message "source-code block `%s' not found in this buffer" name)))) (defun org-babel-find-named-block (name) "Find a named source-code block. Return the location of the source block identified by source -NAME, or nil if no such block exists. Set match data according to -org-babel-named-src-block-regexp." +NAME, or nil if no such block exists. Set match data according +to `org-babel-named-src-block-regexp'." (save-excursion - (let ((case-fold-search t) - (regexp (org-babel-named-src-block-regexp-for-name name))) - (goto-char (point-min)) - (when (or (re-search-forward regexp nil t) - (re-search-backward regexp nil t)) - (match-beginning 0))))) + (goto-char (point-min)) + (ignore-errors + (org-next-block 1 nil (org-babel-named-src-block-regexp-for-name name))))) (defun org-babel-src-block-names (&optional file) "Returns the names of source blocks in FILE or the current buffer." + (when file (find-file file)) (save-excursion - (when file (find-file file)) (goto-char (point-min)) - (let ((case-fold-search t) names) - (while (re-search-forward org-babel-src-name-w-name-regexp nil t) - (setq names (cons (match-string 3) names))) + (goto-char (point-min)) + (let ((re (org-babel-named-src-block-regexp-for-name)) + names) + (while (ignore-errors (org-next-block 1 nil re)) + (push (match-string-no-properties 9) names)) names))) ;;;###autoload @@ -1712,33 +1771,31 @@ org-babel-named-src-block-regexp." "Go to a named result." (interactive (let ((completion-ignore-case t)) - (list (org-icompleting-read "source-block name: " - (org-babel-result-names) nil t)))) + (list (completing-read "Source-block name: " + (org-babel-result-names) nil t)))) (let ((point (org-babel-find-named-result name))) (if point ;; taken from `org-open-at-point' (progn (goto-char point) (org-show-context)) (message "result `%s' not found in this buffer" name)))) -(defun org-babel-find-named-result (name &optional point) +(defun org-babel-find-named-result (name) "Find a named result. Return the location of the result named NAME in the current buffer or nil if no such result exists." (save-excursion - (let ((case-fold-search t)) - (goto-char (or point (point-min))) - (catch 'is-a-code-block - (when (re-search-forward - (concat org-babel-result-regexp - "[ \t]" (regexp-quote name) "[ \t]*[\n\f\v\r]") - nil t) - (when (and (string= "name" (downcase (match-string 1))) - (or (beginning-of-line 1) - (looking-at org-babel-src-block-regexp) - (looking-at org-babel-multi-line-header-regexp) - (looking-at org-babel-lob-one-liner-regexp))) - (throw 'is-a-code-block (org-babel-find-named-result name (point)))) - (beginning-of-line 0) (point)))))) + (goto-char (point-min)) + (let ((case-fold-search t) + (re (format "^[ \t]*#\\+%s.*?:[ \t]*%s[ \t]*$" + org-babel-results-keyword + (regexp-quote name)))) + (catch :found + (while (re-search-forward re nil t) + (let ((element (org-element-at-point))) + (when (or (eq (org-element-type element) 'keyword) + (< (point) + (org-element-property :post-affiliated element))) + (throw :found (line-beginning-position))))))))) (defun org-babel-result-names (&optional file) "Returns the names of results in FILE or the current buffer." @@ -1746,7 +1803,7 @@ buffer or nil if no such result exists." (when file (find-file file)) (goto-char (point-min)) (let ((case-fold-search t) names) (while (re-search-forward org-babel-result-w-name-regexp nil t) - (setq names (cons (match-string 4) names))) + (setq names (cons (match-string-no-properties 9) names))) names))) ;;;###autoload @@ -1784,26 +1841,31 @@ split. When called from outside of a code block a new code block is created. In both cases if the region is demarcated and if the region is not active then the point is demarcated." (interactive "P") - (let ((info (org-babel-get-src-block-info 'light)) - (headers (progn (org-babel-where-is-src-block-head) - (match-string 4))) - (stars (concat (make-string (or (org-current-level) 1) ?*) " "))) + (let* ((info (org-babel-get-src-block-info 'light)) + (start (org-babel-where-is-src-block-head)) + (block (and start (match-string 0))) + (headers (and start (match-string 4))) + (stars (concat (make-string (or (org-current-level) 1) ?*) " ")) + (lower-case-p (and block + (let (case-fold-search) + (string-match-p "#\\+begin_src" block))))) (if info (mapc (lambda (place) (save-excursion (goto-char place) (let ((lang (nth 0 info)) - (indent (make-string (nth 5 info) ? ))) + (indent (make-string (org-get-indentation) ?\s))) (when (string-match "^[[:space:]]*$" (buffer-substring (point-at-bol) (point-at-eol))) (delete-region (point-at-bol) (point-at-eol))) (insert (concat (if (looking-at "^") "" "\n") - indent "#+end_src\n" + indent (funcall (if lower-case-p 'downcase 'upcase) "#+end_src\n") (if arg stars indent) "\n" - indent "#+begin_src " lang + indent (funcall (if lower-case-p 'downcase 'upcase) "#+begin_src ") + lang (if (> (length headers) 1) (concat " " headers) headers) (if (looking-at "[\n\r]") @@ -1812,7 +1874,7 @@ region is not active then the point is demarcated." (move-end-of-line 2)) (sort (if (org-region-active-p) (list (mark) (point)) (list (point))) #'>)) (let ((start (point)) - (lang (org-icompleting-read + (lang (completing-read "Lang: " (mapcar #'symbol-name (delete-dups @@ -1823,134 +1885,222 @@ region is not active then the point is demarcated." (if (org-region-active-p) (mark) (point)) (point)))) (insert (concat (if (looking-at "^") "" "\n") (if arg (concat stars "\n") "") - "#+begin_src " lang "\n" + (funcall (if lower-case-p 'downcase 'upcase) "#+begin_src ") + lang "\n" body (if (or (= (length body) 0) - (string-match "[\r\n]$" body)) "" "\n") - "#+end_src\n")) + (string-suffix-p "\r" body) + (string-suffix-p "\n" body)) "" "\n") + (funcall (if lower-case-p 'downcase 'upcase) "#+end_src\n"))) (goto-char start) (move-end-of-line 1))))) -(defvar org-babel-lob-one-liner-regexp) -(defun org-babel-where-is-src-block-result (&optional insert info hash indent) +(defun org-babel--insert-results-keyword (name hash) + "Insert RESULTS keyword with NAME value at point. +If NAME is nil, results are anonymous. HASH is a string used as +the results hash, or nil. Leave point before the keyword." + (save-excursion (insert "\n")) ;open line to indent. + (org-indent-line) + (delete-char 1) + (insert (concat "#+" org-babel-results-keyword + (cond ((not hash) nil) + (org-babel-hash-show-time + (format "[%s %s]" + (format-time-string "<%F %T>") + hash)) + (t (format "[%s]" hash))) + ":" + (when name (concat " " name)) + "\n")) + ;; Make sure results are going to be followed by at least one blank + ;; line so they do not get merged with the next element, e.g., + ;; + ;; #+results: + ;; : 1 + ;; + ;; : fixed-width area, unrelated to the above. + (unless (looking-at "^[ \t]*$") (save-excursion (insert "\n"))) + (beginning-of-line 0) + (when hash (org-babel-hide-hash))) + +(defun org-babel--clear-results-maybe (hash) + "Clear results when hash doesn't match HASH. + +When results hash does not match HASH, remove RESULTS keyword at +point, along with related contents. Do nothing if HASH is nil. + +Return a non-nil value if results were cleared. In this case, +leave point where new results should be inserted." + (when hash + (looking-at org-babel-result-regexp) + (unless (string= (match-string 1) hash) + (let* ((e (org-element-at-point)) + (post (copy-marker (org-element-property :post-affiliated e)))) + ;; Delete contents. + (delete-region post + (save-excursion + (goto-char (org-element-property :end e)) + (skip-chars-backward " \t\n") + (line-beginning-position 2))) + ;; Delete RESULT keyword. However, if RESULTS keyword is + ;; orphaned, ignore this part. The deletion above already + ;; took care of it. + (unless (= (point) post) + (delete-region (line-beginning-position) + (line-beginning-position 2))) + (goto-char post) + (set-marker post nil) + t)))) + +(defun org-babel-where-is-src-block-result (&optional insert _info hash) "Find where the current source block results begin. + Return the point at the beginning of the result of the current -source block. Specifically at the beginning of the results line. -If no result exists for this block then create a results line -following the source block." - (save-excursion - (let* ((case-fold-search t) - (on-lob-line (save-excursion - (beginning-of-line 1) - (looking-at org-babel-lob-one-liner-regexp))) - (inlinep (when (org-babel-get-inline-src-block-matches) - (match-end 0))) - (name (nth 4 (or info (org-babel-get-src-block-info 'light)))) - (head (unless on-lob-line (org-babel-where-is-src-block-head))) - found beg end) - (when head (goto-char head)) +source block, specifically at the beginning of the results line. + +If no result exists for this block return nil, unless optional +argument INSERT is non-nil. In this case, create a results line +following the source block and return the position at its +beginning. In the case of inline code, remove the results part +instead. + +If optional argument HASH is a string, remove contents related to +RESULTS keyword if its hash is different. Then update the latter +to HASH." + (let ((context (org-element-context))) + (catch :found (org-with-wide-buffer - (setq - found ;; was there a result (before we potentially insert one) - (or - inlinep - (and - ;; named results: - ;; - return t if it is found, else return nil - ;; - if it does not need to be rebuilt, then don't set end - ;; - if it does need to be rebuilt then do set end - name (setq beg (org-babel-find-named-result name)) - (prog1 beg - (when (and hash (not (string= hash (match-string 5)))) - (goto-char beg) (setq end beg) ;; beginning of result - (forward-line 1) - (delete-region end (org-babel-result-end)) nil))) - (and - ;; unnamed results: - ;; - return t if it is found, else return nil - ;; - if it is found, and the hash doesn't match, delete and set end - (or on-lob-line (re-search-forward "^[ \t]*#\\+end_src" nil t)) - (progn (end-of-line 1) - (if (eobp) (insert "\n") (forward-char 1)) - (setq end (point)) - (or (and - (not name) - (progn ;; unnamed results line already exists - (catch 'non-comment - (while (re-search-forward "[^ \f\t\n\r\v]" nil t) - (beginning-of-line 1) - (cond - ((looking-at (concat org-babel-result-regexp "\n")) - (throw 'non-comment t)) - ((looking-at "^[ \t]*#") (end-of-line 1)) - (t (throw 'non-comment nil)))))) - (let ((this-hash (match-string 5))) - (prog1 (point) - ;; must remove and rebuild if hash!=old-hash - (if (and hash (not (string= hash this-hash))) - (prog1 nil - (forward-line 1) - (delete-region - end (org-babel-result-end))) - (setq end nil))))))))))) - (if (not (and insert end)) found - (goto-char end) - (unless beg - (if (looking-at "[\n\r]") (forward-char 1) (insert "\n"))) - (insert (concat - (when (wholenump indent) (make-string indent ? )) - "#+" org-babel-results-keyword - (when hash - (if org-babel-hash-show-time - (concat - "["(format-time-string "<%Y-%m-%d %H:%M:%S>")" "hash"]") - (concat "["hash"]"))) - ":" - (when name (concat " " name)) "\n")) - (unless beg (insert "\n") (backward-char)) - (beginning-of-line 0) - (if hash (org-babel-hide-hash)) - (point))))) - -(defvar org-block-regexp) + (pcase (org-element-type context) + ((or `inline-babel-call `inline-src-block) + ;; Results for inline objects are located right after them. + ;; There is no RESULTS line to insert either. + (let ((limit (org-element-property + :contents-end (org-element-property :parent context)))) + (goto-char (org-element-property :end context)) + (skip-chars-forward " \t\n" limit) + (throw :found + (and + (< (point) limit) + (let ((result (org-element-context))) + (and (eq (org-element-type result) 'macro) + (string= (org-element-property :key result) + "results") + (if (not insert) (point) + (delete-region + (point) + (progn + (goto-char (org-element-property :end result)) + (skip-chars-backward " \t") + (point))) + (point)))))))) + ((or `babel-call `src-block) + (let* ((name (org-element-property :name context)) + (named-results (and name (org-babel-find-named-result name)))) + (goto-char (or named-results (org-element-property :end context))) + (cond + ;; Existing results named after the current source. + (named-results + (when (org-babel--clear-results-maybe hash) + (org-babel--insert-results-keyword name hash)) + (throw :found (point))) + ;; Named results expect but none to be found. + (name) + ;; No possible anonymous results at the very end of + ;; buffer or outside CONTEXT parent. + ((eq (point) + (or (org-element-property + :contents-end (org-element-property :parent context)) + (point-max)))) + ;; Check if next element is an anonymous result below + ;; the current block. + ((let* ((next (org-element-at-point)) + (end (save-excursion + (goto-char + (org-element-property :post-affiliated next)) + (line-end-position))) + (empty-result-re (concat org-babel-result-regexp "$")) + (case-fold-search t)) + (re-search-forward empty-result-re end t)) + (beginning-of-line) + (when (org-babel--clear-results-maybe hash) + (org-babel--insert-results-keyword nil hash)) + (throw :found (point)))))) + ;; Ignore other elements. + (_ (throw :found nil)))) + ;; No result found. Insert a RESULTS keyword below element, if + ;; appropriate. In this case, ensure there is an empty line + ;; after the previous element. + (when insert + (save-excursion + (goto-char (min (org-element-property :end context) (point-max))) + (skip-chars-backward " \t\n") + (forward-line) + (unless (bolp) (insert "\n")) + (insert "\n") + (org-babel--insert-results-keyword + (org-element-property :name context) hash) + (point)))))) + +(defun org-babel-read-element (element) + "Read ELEMENT into emacs-lisp. +Return nil if ELEMENT cannot be read." + (org-with-wide-buffer + (goto-char (org-element-property :post-affiliated element)) + (pcase (org-element-type element) + (`fixed-width + (let ((v (org-trim (org-element-property :value element)))) + (or (org-babel--string-to-number v) v))) + (`table (org-babel-read-table)) + (`plain-list (org-babel-read-list)) + (`example-block + (let ((v (org-element-property :value element))) + (if (or org-src-preserve-indentation + (org-element-property :preserve-indent element)) + v + (org-remove-indentation v)))) + (`export-block + (org-remove-indentation (org-element-property :value element))) + (`paragraph + ;; Treat paragraphs containing a single link specially. + (skip-chars-forward " \t") + (if (and (looking-at org-bracket-link-regexp) + (save-excursion + (goto-char (match-end 0)) + (skip-chars-forward " \r\t\n") + (<= (org-element-property :end element) + (point)))) + (org-babel-read-link) + (buffer-substring-no-properties + (org-element-property :contents-begin element) + (org-element-property :contents-end element)))) + ((or `center-block `quote-block `verse-block `special-block) + (org-remove-indentation + (buffer-substring-no-properties + (org-element-property :contents-begin element) + (org-element-property :contents-end element)))) + (_ nil)))) + (defun org-babel-read-result () - "Read the result at `point' into emacs-lisp." - (let ((case-fold-search t) result-string) - (cond - ((org-at-table-p) (org-babel-read-table)) - ((org-at-item-p) (org-babel-read-list)) - ((looking-at org-bracket-link-regexp) (org-babel-read-link)) - ((looking-at org-block-regexp) (org-babel-trim (match-string 4))) - ((or (looking-at "^[ \t]*: ") (looking-at "^[ \t]*:$")) - (setq result-string - (org-babel-trim - (mapconcat (lambda (line) - (or (and (> (length line) 1) - (string-match "^[ \t]*: ?\\(.+\\)" line) - (match-string 1 line)) - "")) - (split-string - (buffer-substring - (point) (org-babel-result-end)) "[\r\n]+") - "\n"))) - (or (org-babel-number-p result-string) result-string)) - ((looking-at org-babel-result-regexp) - (save-excursion (forward-line 1) (org-babel-read-result)))))) + "Read the result at point into emacs-lisp." + (and (not (save-excursion + (beginning-of-line) + (looking-at-p "[ \t]*$"))) + (org-babel-read-element (org-element-at-point)))) (defun org-babel-read-table () - "Read the table at `point' into emacs-lisp." + "Read the table at point into emacs-lisp." (mapcar (lambda (row) (if (and (symbolp row) (equal row 'hline)) row (mapcar (lambda (el) (org-babel-read el 'inhibit-lisp-eval)) row))) (org-table-to-lisp))) (defun org-babel-read-list () - "Read the list at `point' into emacs-lisp." + "Read the list at point into emacs-lisp." (mapcar (lambda (el) (org-babel-read el 'inhibit-lisp-eval)) - (mapcar #'cadr (cdr (org-list-parse-list))))) + (cdr (org-list-to-lisp)))) (defvar org-link-types-re) (defun org-babel-read-link () - "Read the link at `point' into emacs-lisp. + "Read the link at point into emacs-lisp. If the path of the link is a file path it is expanded using `expand-file-name'." (let* ((case-fold-search t) @@ -1975,204 +2125,320 @@ If the path of the link is a file path it is expanded using ;; scalar result (funcall echo-res result)))) -(defun org-babel-insert-result - (result &optional result-params info hash indent lang) +(defun org-babel-insert-result (result &optional result-params info hash lang) "Insert RESULT into the current buffer. -By default RESULT is inserted after the end of the -current source block. With optional argument RESULT-PARAMS -controls insertion of results in the org-mode file. -RESULT-PARAMS can take the following values: + +By default RESULT is inserted after the end of the current source +block. The RESULT of an inline source block usually will be +wrapped inside a `results' macro and placed on the same line as +the inline source block. The macro is stripped upon export. +Multiline and non-scalar RESULTS from inline source blocks are +not allowed. With optional argument RESULT-PARAMS controls +insertion of results in the Org mode file. RESULT-PARAMS can +take the following values: replace - (default option) insert results after the source block - replacing any previously inserted results + or inline source block replacing any previously + inserted results. -silent -- no results are inserted into the Org-mode buffer but +silent -- no results are inserted into the Org buffer but the results are echoed to the minibuffer and are ingested by Emacs (a potentially time consuming - process) + process). file ---- the results are interpreted as a file path, and are - inserted into the buffer using the Org-mode file syntax + inserted into the buffer using the Org file syntax. -list ---- the results are interpreted as an Org-mode list. +list ---- the results are interpreted as an Org list. -raw ----- results are added directly to the Org-mode file. This - is a good option if you code block will output org-mode +raw ----- results are added directly to the Org file. This is + a good option if you code block will output Org formatted text. -drawer -- results are added directly to the Org-mode file as with - \"raw\", but are wrapped in a RESULTS drawer, allowing - them to later be replaced or removed automatically. +drawer -- results are added directly to the Org file as with + \"raw\", but are wrapped in a RESULTS drawer or results + macro, allowing them to later be replaced or removed + automatically. -org ----- results are added inside of a \"#+BEGIN_SRC org\" block. - They are not comma-escaped when inserted, but Org syntax - here will be discarded when exporting the file. +org ----- results are added inside of a \"src_org{}\" or \"#+BEGIN_SRC + org\" block depending on whether the current source block is + inline or not. They are not comma-escaped when inserted, + but Org syntax here will be discarded when exporting the + file. -html ---- results are added inside of a #+BEGIN_HTML block. This - is a good option if you code block will output html - formatted text. +html ---- results are added inside of a #+BEGIN_EXPORT HTML block + or html export snippet depending on whether the current + source block is inline or not. This is a good option + if your code block will output html formatted text. -latex --- results are added inside of a #+BEGIN_LATEX block. - This is a good option if you code block will output - latex formatted text. +latex --- results are added inside of a #+BEGIN_EXPORT LATEX + block or latex export snippet depending on whether the + current source block is inline or not. This is a good + option if your code block will output latex formatted + text. code ---- the results are extracted in the syntax of the source code of the language being evaluated and are added - inside of a #+BEGIN_SRC block with the source-code - language set appropriately. Note this relies on the - optional LANG argument." - (if (stringp result) - (progn - (setq result (org-no-properties result)) - (when (member "file" result-params) - (setq result (org-babel-result-to-file - result (when (assoc :file-desc (nth 2 info)) - (or (cdr (assoc :file-desc (nth 2 info))) - result)))))) - (unless (listp result) (setq result (format "%S" result)))) + inside of a source block with the source-code language + set appropriately. Also, source block inlining is + preserved in this case. Note this relies on the + optional LANG argument. + +list ---- the results are rendered as a list. This option not + allowed for inline src blocks. + +table --- the results are rendered as a table. This option not + allowed for inline src blocks. + +INFO may provide the values of these header arguments (in the +`header-arguments-alist' see the docstring for +`org-babel-get-src-block-info'): + +:file --- the name of the file to which output should be written. + +:wrap --- the effect is similar to `latex' in RESULT-PARAMS but + using the argument supplied to specify the export block + or snippet type." + (cond ((stringp result) + (setq result (org-no-properties result)) + (when (member "file" result-params) + (setq result (org-babel-result-to-file + result (when (assq :file-desc (nth 2 info)) + (or (cdr (assq :file-desc (nth 2 info))) + result)))))) + ((listp result)) + (t (setq result (format "%S" result)))) (if (and result-params (member "silent" result-params)) - (progn - (message (replace-regexp-in-string "%" "%%" (format "%S" result))) - result) - (save-excursion - (let* ((inlinep - (save-excursion - (when (or (org-babel-get-inline-src-block-matches) - (org-babel-get-lob-one-liner-matches)) - (goto-char (match-end 0)) - (insert (if (listp result) "\n" " ")) - (point)))) - (existing-result (unless inlinep - (org-babel-where-is-src-block-result - t info hash indent))) - (results-switches - (cdr (assoc :results_switches (nth 2 info)))) - (visible-beg (point-min-marker)) - (visible-end (point-max-marker)) - ;; When results exist outside of the current visible - ;; region of the buffer, be sure to widen buffer to - ;; update them. - (outside-scope-p (and existing-result + (progn (message (replace-regexp-in-string "%" "%%" (format "%S" result))) + result) + (let ((inline (let ((context (org-element-context))) + (and (memq (org-element-type context) + '(inline-babel-call inline-src-block)) + context)))) + (when inline + (let ((warning + (or (and (member "table" result-params) "`:results table'") + (and (listp result) "list result") + (and (string-match-p "\n." result) "multiline result") + (and (member "list" result-params) "`:results list'")))) + (when warning + (user-error "Inline error: %s cannot be used" warning)))) + (save-excursion + (let* ((visible-beg (point-min-marker)) + (visible-end (copy-marker (point-max) t)) + (inline (let ((context (org-element-context))) + (and (memq (org-element-type context) + '(inline-babel-call inline-src-block)) + context))) + (existing-result (org-babel-where-is-src-block-result t nil hash)) + (results-switches (cdr (assq :results_switches (nth 2 info)))) + ;; When results exist outside of the current visible + ;; region of the buffer, be sure to widen buffer to + ;; update them. + (outside-scope (and existing-result + (buffer-narrowed-p) (or (> visible-beg existing-result) (<= visible-end existing-result)))) - beg end) - (when (and (stringp result) ; ensure results end in a newline - (not inlinep) - (> (length result) 0) - (not (or (string-equal (substring result -1) "\n") - (string-equal (substring result -1) "\r")))) - (setq result (concat result "\n"))) - (unwind-protect - (progn - (when outside-scope-p (widen)) - (if (not existing-result) - (setq beg (or inlinep (point))) - (goto-char existing-result) - (save-excursion - (re-search-forward "#" nil t) - (setq indent (- (current-column) 1))) - (forward-line 1) + beg end indent) + ;; Ensure non-inline results end in a newline. + (when (and (org-string-nw-p result) + (not inline) + (not (string-equal (substring result -1) "\n"))) + (setq result (concat result "\n"))) + (unwind-protect + (progn + (when outside-scope (widen)) + (if existing-result (goto-char existing-result) + (goto-char (org-element-property :end inline)) + (skip-chars-backward " \t")) + (unless inline + (setq indent (org-get-indentation)) + (forward-line 1)) (setq beg (point)) (cond + (inline + ;; Make sure new results are separated from the + ;; source code by one space. + (unless existing-result + (insert " ") + (setq beg (point)))) ((member "replace" result-params) (delete-region (point) (org-babel-result-end))) ((member "append" result-params) (goto-char (org-babel-result-end)) (setq beg (point-marker))) - ((member "prepend" result-params)))) ; already there - (setq results-switches - (if results-switches (concat " " results-switches) "")) - (let ((wrap (lambda (start finish &optional no-escape) - (goto-char end) (insert (concat finish "\n")) - (goto-char beg) (insert (concat start "\n")) - (unless no-escape - (org-escape-code-in-region (min (point) end) end)) - (goto-char end) (goto-char (point-at-eol)) - (setq end (point-marker)))) - (proper-list-p (lambda (it) (and (listp it) (null (cdr (last it))))))) - ;; insert results based on type - (cond - ;; do nothing for an empty result - ((null result)) - ;; insert a list if preferred - ((member "list" result-params) - (insert - (org-babel-trim - (org-list-to-generic - (cons 'unordered - (mapcar - (lambda (el) (list nil (if (stringp el) el (format "%S" el)))) - (if (listp result) result (split-string result "\n" t)))) - '(:splicep nil :istart "- " :iend "\n"))) - "\n")) - ;; assume the result is a table if it's not a string - ((funcall proper-list-p result) - (goto-char beg) - (insert (concat (orgtbl-to-orgtbl - (if (org-every - (lambda (el) (or (listp el) (eq el 'hline))) - result) - result (list result)) - '(:fmt (lambda (cell) (format "%s" cell)))) "\n")) - (goto-char beg) (when (org-at-table-p) (org-table-align))) - ((and (listp result) (not (funcall proper-list-p result))) - (insert (format "%s\n" result))) - ((member "file" result-params) - (when inlinep (goto-char inlinep)) - (insert result)) - (t (goto-char beg) (insert result))) - (when (funcall proper-list-p result) (goto-char (org-table-end))) - (setq end (point-marker)) - ;; possibly wrap result - (cond - ((assoc :wrap (nth 2 info)) - (let ((name (or (cdr (assoc :wrap (nth 2 info))) "RESULTS"))) - (funcall wrap (concat "#+BEGIN_" name) - (concat "#+END_" (car (org-split-string name)))))) - ((member "html" result-params) - (funcall wrap "#+BEGIN_HTML" "#+END_HTML")) - ((member "latex" result-params) - (funcall wrap "#+BEGIN_LaTeX" "#+END_LaTeX")) - ((member "org" result-params) - (goto-char beg) (if (org-at-table-p) (org-cycle)) - (funcall wrap "#+BEGIN_SRC org" "#+END_SRC")) - ((member "code" result-params) - (funcall wrap (format "#+BEGIN_SRC %s%s" (or lang "none") results-switches) - "#+END_SRC")) - ((member "raw" result-params) - (goto-char beg) (if (org-at-table-p) (org-cycle))) - ((or (member "drawer" result-params) - ;; Stay backward compatible with <7.9.2 - (member "wrap" result-params)) - (goto-char beg) (if (org-at-table-p) (org-cycle)) - (funcall wrap ":RESULTS:" ":END:" 'no-escape)) - ((and (not (funcall proper-list-p result)) - (not (member "file" result-params))) - (org-babel-examplize-region beg end results-switches) - (setq end (point))))) - ;; possibly indent the results to match the #+results line - (when (and (not inlinep) (numberp indent) indent (> indent 0) - ;; in this case `table-align' does the work for us - (not (and (listp result) - (member "append" result-params)))) - (indent-rigidly beg end indent)) - (if (null result) - (if (member "value" result-params) - (message "Code block returned no value.") - (message "Code block produced no output.")) - (message "Code block evaluation complete."))) - (when outside-scope-p (narrow-to-region visible-beg visible-end)) - (set-marker visible-beg nil) - (set-marker visible-end nil)))))) - -(defun org-babel-remove-result (&optional info) + ((member "prepend" result-params))) ; already there + (setq results-switches + (if results-switches (concat " " results-switches) "")) + (let ((wrap (lambda (start finish &optional no-escape no-newlines + inline-start inline-finish) + (when inline + (setq start inline-start) + (setq finish inline-finish) + (setq no-newlines t)) + (goto-char end) + (insert (concat finish (unless no-newlines "\n"))) + (goto-char beg) + (insert (concat start (unless no-newlines "\n"))) + (unless no-escape + (org-escape-code-in-region (min (point) end) end)) + (goto-char end) + (unless no-newlines (goto-char (point-at-eol))) + (setq end (point-marker)))) + (tabulablep + (lambda (r) + ;; Non-nil when result R can be turned into + ;; a table. + (and (listp r) + (null (cdr (last r))) + (cl-every + (lambda (e) (or (atom e) (null (cdr (last e))))) + result))))) + ;; insert results based on type + (cond + ;; Do nothing for an empty result. + ((null result)) + ;; Insert a list if preferred. + ((member "list" result-params) + (insert + (org-trim + (org-list-to-generic + (cons 'unordered + (mapcar + (lambda (e) + (list (if (stringp e) e (format "%S" e)))) + (if (listp result) result + (split-string result "\n" t)))) + '(:splicep nil :istart "- " :iend "\n"))) + "\n")) + ;; Try hard to print RESULT as a table. Give up if + ;; it contains an improper list. + ((funcall tabulablep result) + (goto-char beg) + (insert (concat (orgtbl-to-orgtbl + (if (cl-every + (lambda (e) + (or (eq e 'hline) (listp e))) + result) + result + (list result)) + nil) + "\n")) + (goto-char beg) + (when (org-at-table-p) (org-table-align)) + (goto-char (org-table-end))) + ;; Print verbatim a list that cannot be turned into + ;; a table. + ((listp result) (insert (format "%s\n" result))) + ((member "file" result-params) + (when inline + (setq result (org-macro-escape-arguments result))) + (insert result)) + ((and inline (not (member "raw" result-params))) + (insert (org-macro-escape-arguments + (org-babel-chomp result "\n")))) + (t (goto-char beg) (insert result))) + (setq end (point-marker)) + ;; possibly wrap result + (cond + ((assq :wrap (nth 2 info)) + (let ((name (or (cdr (assq :wrap (nth 2 info))) "RESULTS"))) + (funcall wrap (concat "#+BEGIN_" name) + (concat "#+END_" (car (org-split-string name))) + nil nil (concat "{{{results(@@" name ":") "@@)}}}"))) + ((member "html" result-params) + (funcall wrap "#+BEGIN_EXPORT html" "#+END_EXPORT" nil nil + "{{{results(@@html:" "@@)}}}")) + ((member "latex" result-params) + (funcall wrap "#+BEGIN_EXPORT latex" "#+END_EXPORT" nil nil + "{{{results(@@latex:" "@@)}}}")) + ((member "org" result-params) + (goto-char beg) (when (org-at-table-p) (org-cycle)) + (funcall wrap "#+BEGIN_SRC org" "#+END_SRC" nil nil + "{{{results(src_org{" "})}}}")) + ((member "code" result-params) + (let ((lang (or lang "none"))) + (funcall wrap (format "#+BEGIN_SRC %s%s" lang results-switches) + "#+END_SRC" nil nil + (format "{{{results(src_%s[%s]{" lang results-switches) + "})}}}"))) + ((member "raw" result-params) + (goto-char beg) (when (org-at-table-p) (org-cycle))) + ((or (member "drawer" result-params) + ;; Stay backward compatible with <7.9.2 + (member "wrap" result-params)) + (goto-char beg) (when (org-at-table-p) (org-cycle)) + (funcall wrap ":RESULTS:" ":END:" 'no-escape nil + "{{{results(" ")}}}")) + ((and inline (member "file" result-params)) + (funcall wrap nil nil nil nil "{{{results(" ")}}}")) + ((and (not (funcall tabulablep result)) + (not (member "file" result-params))) + (let ((org-babel-inline-result-wrap + ;; Hard code {{{results(...)}}} on top of customization. + (format "{{{results(%s)}}}" + org-babel-inline-result-wrap))) + (org-babel-examplify-region beg end results-switches inline) + (setq end (point)))))) + ;; Possibly indent results in par with #+results line. + (when (and (not inline) (numberp indent) (> indent 0) + ;; In this case `table-align' does the work + ;; for us. + (not (and (listp result) + (member "append" result-params)))) + (indent-rigidly beg end indent)) + (if (null result) + (if (member "value" result-params) + (message "Code block returned no value.") + (message "Code block produced no output.")) + (message "Code block evaluation complete."))) + (when outside-scope (narrow-to-region visible-beg visible-end)) + (set-marker visible-beg nil) + (set-marker visible-end nil))))))) + +(defun org-babel-remove-result (&optional info keep-keyword) "Remove the result of the current source block." (interactive) - (let ((location (org-babel-where-is-src-block-result nil info)) start) + (let ((location (org-babel-where-is-src-block-result nil info))) (when location - (setq start (- location 1)) (save-excursion - (goto-char location) (forward-line 1) - (delete-region start (org-babel-result-end)))))) + (goto-char location) + (when (looking-at (concat org-babel-result-regexp ".*$")) + (delete-region + (if keep-keyword (1+ (match-end 0)) (1- (match-beginning 0))) + (progn (forward-line 1) (org-babel-result-end)))))))) + +(defun org-babel-remove-inline-result (&optional datum) + "Remove the result of the current inline-src-block or babel call. +The result must be wrapped in a `results' macro to be removed. +Leading white space is trimmed." + (interactive) + (let* ((el (or datum (org-element-context)))) + (when (memq (org-element-type el) '(inline-src-block inline-babel-call)) + (org-with-wide-buffer + (goto-char (org-element-property :end el)) + (skip-chars-backward " \t") + (let ((result (save-excursion + (skip-chars-forward + " \t\n" + (org-element-property + :contents-end (org-element-property :parent el))) + (org-element-context)))) + (when (and (eq (org-element-type result) 'macro) + (string= (org-element-property :key result) "results")) + (delete-region ; And leading whitespace. + (point) + (progn (goto-char (org-element-property :end result)) + (skip-chars-backward " \t\n") + (point))))))))) + +(defun org-babel-remove-result-one-or-many (x) + "Remove the result of the current source block. +If called with a prefix argument, remove all result blocks +in the buffer." + (interactive "P") + (if x + (org-babel-map-src-blocks nil (org-babel-remove-result)) + (org-babel-remove-result))) (defun org-babel-result-end () "Return the point at the end of the current set of results." @@ -2210,29 +2476,26 @@ file's directory then expand relative links." result) (if description (concat "[" description "]") "")))) -(defvar org-babel-capitalize-examplize-region-markers nil +(defvar org-babel-capitalize-example-region-markers nil "Make true to capitalize begin/end example markers inserted by code blocks.") -(defun org-babel-examplize-region (beg end &optional results-switches) +(defun org-babel-examplify-region (beg end &optional results-switches inline) "Comment out region using the inline `==' or `: ' org example quote." (interactive "*r") - (let ((chars-between (lambda (b e) - (not (string-match "^[\\s]*$" (buffer-substring b e))))) - (maybe-cap (lambda (str) (if org-babel-capitalize-examplize-region-markers - (upcase str) str)))) - (if (or (funcall chars-between (save-excursion (goto-char beg) (point-at-bol)) beg) - (funcall chars-between end (save-excursion (goto-char end) (point-at-eol)))) + (let ((maybe-cap + (lambda (str) + (if org-babel-capitalize-example-region-markers (upcase str) str)))) + (if inline (save-excursion (goto-char beg) (insert (format org-babel-inline-result-wrap - (prog1 (buffer-substring beg end) - (delete-region beg end))))) + (delete-and-extract-region beg end)))) (let ((size (count-lines beg end))) (save-excursion (cond ((= size 0)) ; do nothing for an empty result ((< size org-babel-min-lines-for-block-output) (goto-char beg) - (dotimes (n size) + (dotimes (_ size) (beginning-of-line 1) (insert ": ") (forward-line 1))) (t (goto-char beg) @@ -2241,16 +2504,37 @@ file's directory then expand relative links." (funcall maybe-cap "#+begin_example") results-switches) (funcall maybe-cap "#+begin_example\n"))) - (if (markerp end) (goto-char end) (forward-char (- end beg))) + (let ((p (point))) + (if (markerp end) (goto-char end) (forward-char (- end beg))) + (org-escape-code-in-region p (point))) (insert (funcall maybe-cap "#+end_example\n"))))))))) (defun org-babel-update-block-body (new-body) "Update the body of the current code block to NEW-BODY." - (if (not (org-babel-where-is-src-block-head)) - (error "Not in a source block") - (save-match-data - (replace-match (concat (org-babel-trim new-body) "\n") nil t nil 5)) - (indent-rigidly (match-beginning 5) (match-end 5) 2))) + (let ((element (org-element-at-point))) + (unless (eq (org-element-type element) 'src-block) + (error "Not in a source block")) + (goto-char (org-babel-where-is-src-block-head element)) + (let* ((ind (org-get-indentation)) + (body-start (line-beginning-position 2)) + (body (org-element-normalize-string + (if (or org-src-preserve-indentation + (org-element-property :preserve-indent element)) + new-body + (with-temp-buffer + (insert (org-remove-indentation new-body)) + (indent-rigidly + (point-min) + (point-max) + (+ ind org-edit-src-content-indentation)) + (buffer-string)))))) + (delete-region body-start + (org-with-wide-buffer + (goto-char (org-element-property :end element)) + (skip-chars-backward " \t\n") + (line-beginning-position))) + (goto-char body-start) + (insert body)))) (defun org-babel-merge-params (&rest plists) "Combine all parameter association lists in PLISTS. @@ -2259,133 +2543,103 @@ This takes into account some special considerations for certain parameters when merging lists." (let* ((results-exclusive-groups (mapcar (lambda (group) (mapcar #'symbol-name group)) - (cdr (assoc 'results org-babel-common-header-args-w-values)))) + (cdr (assq 'results org-babel-common-header-args-w-values)))) (exports-exclusive-groups (mapcar (lambda (group) (mapcar #'symbol-name group)) - (cdr (assoc 'exports org-babel-common-header-args-w-values)))) - (variable-index 0) - (e-merge (lambda (exclusive-groups &rest result-params) - ;; maintain exclusivity of mutually exclusive parameters - (let (output) - (mapc (lambda (new-params) - (mapc (lambda (new-param) - (mapc (lambda (exclusive-group) - (when (member new-param exclusive-group) - (mapcar (lambda (excluded-param) - (setq output - (delete - excluded-param - output))) - exclusive-group))) - exclusive-groups) - (setq output (org-uniquify - (cons new-param output)))) - new-params)) - result-params) - output))) - params results exports tangle noweb cache vars shebang comments padline - clearnames) - - (mapc - (lambda (plist) - (mapc - (lambda (pair) - (case (car pair) - (:var - (let ((name (if (listp (cdr pair)) - (cadr pair) - (and (string-match "^\\([^= \f\t\n\r\v]+\\)[ \t]*=" - (cdr pair)) - (intern (match-string 1 (cdr pair))))))) - (if name - (setq vars - (append - (if (member name (mapcar #'car vars)) - (progn - (push name clearnames) - (delq nil - (mapcar - (lambda (p) - (unless (equal (car p) name) p)) - vars))) - vars) - (list (cons name pair)))) - ;; if no name is given and we already have named variables - ;; then assign to named variables in order - (if (and vars (nth variable-index vars)) - (let ((name (car (nth variable-index vars)))) - (push name clearnames) ; clear out colnames - ; and rownames - ; for replace vars - (prog1 (setf (cddr (nth variable-index vars)) - (concat (symbol-name name) "=" (cdr pair))) - (incf variable-index))) - (error "Variable \"%s\" must be assigned a default value" - (cdr pair)))))) - (:results - (setq results (funcall e-merge results-exclusive-groups - results - (split-string - (let ((r (cdr pair))) - (if (stringp r) r (eval r))))))) - (:file - (when (cdr pair) - (setq results (funcall e-merge results-exclusive-groups - results '("file"))) - (unless (or (member "both" exports) - (member "none" exports) - (member "code" exports)) - (setq exports (funcall e-merge exports-exclusive-groups - exports '("results")))) - (setq params (cons pair (assq-delete-all (car pair) params))))) - (:exports - (setq exports (funcall e-merge exports-exclusive-groups - exports (split-string (cdr pair))))) - (:tangle ;; take the latest -- always overwrite - (setq tangle (or (list (cdr pair)) tangle))) - (:noweb - (setq noweb (funcall e-merge - '(("yes" "no" "tangle" "no-export" - "strip-export" "eval")) - noweb - (split-string (or (cdr pair) ""))))) - (:cache - (setq cache (funcall e-merge '(("yes" "no")) cache - (split-string (or (cdr pair) ""))))) - (:padline - (setq padline (funcall e-merge '(("yes" "no")) padline - (split-string (or (cdr pair) ""))))) - (:shebang ;; take the latest -- always overwrite - (setq shebang (or (list (cdr pair)) shebang))) - (:comments - (setq comments (funcall e-merge '(("yes" "no")) comments - (split-string (or (cdr pair) ""))))) - (t ;; replace: this covers e.g. :session - (setq params (cons pair (assq-delete-all (car pair) params)))))) - plist)) - plists) - (setq vars (reverse vars)) - (while vars (setq params (cons (cons :var (cddr (pop vars))) params))) - ;; clear out col-names and row-names for replaced variables - (mapc - (lambda (name) - (mapc - (lambda (param) - (when (assoc param params) - (setf (cdr (assoc param params)) - (org-remove-if (lambda (pair) (equal (car pair) name)) - (cdr (assoc param params)))) - (setf params (org-remove-if (lambda (pair) (and (equal (car pair) param) - (null (cdr pair)))) - params)))) - (list :colname-names :rowname-names))) - clearnames) - (mapc - (lambda (hd) - (let ((key (intern (concat ":" (symbol-name hd)))) - (val (eval hd))) - (setf params (cons (cons key (mapconcat 'identity val " ")) params)))) - '(results exports tangle noweb padline cache shebang comments)) + (cdr (assq 'exports org-babel-common-header-args-w-values)))) + (merge + (lambda (exclusive-groups &rest result-params) + ;; Maintain exclusivity of mutually exclusive parameters, + ;; as defined in EXCLUSIVE-GROUPS while merging lists in + ;; RESULT-PARAMS. + (let (output) + (dolist (new-params result-params (delete-dups output)) + (dolist (new-param new-params) + (dolist (exclusive-group exclusive-groups) + (when (member new-param exclusive-group) + (setq output (cl-remove-if + (lambda (o) (member o exclusive-group)) + output)))) + (push new-param output)))))) + (variable-index 0) ;Handle positional arguments. + clearnames + params ;Final parameters list. + ;; Some keywords accept multiple values. We need to treat + ;; them specially. + vars results exports) + (dolist (plist plists) + (dolist (pair plist) + (pcase pair + (`(:var . ,value) + (let ((name (cond + ((listp value) (car value)) + ((string-match "^\\([^= \f\t\n\r\v]+\\)[ \t]*=" value) + (intern (match-string 1 value))) + (t nil)))) + (cond + (name + (setq vars + (append (if (not (assoc name vars)) vars + (push name clearnames) + (cl-remove-if (lambda (p) (equal name (car p))) + vars)) + (list (cons name pair))))) + ((and vars (nth variable-index vars)) + ;; If no name is given and we already have named + ;; variables then assign to named variables in order. + (let ((name (car (nth variable-index vars)))) + ;; Clear out colnames and rownames for replace vars. + (push name clearnames) + (setf (cddr (nth variable-index vars)) + (concat (symbol-name name) "=" value)) + (cl-incf variable-index))) + (t (error "Variable \"%s\" must be assigned a default value" + (cdr pair)))))) + (`(:results . ,value) + (setq results (funcall merge + results-exclusive-groups + results + (split-string + (if (stringp value) value (eval value t)))))) + (`(,(or :file :file-ext) . ,value) + ;; `:file' and `:file-ext' are regular keywords but they + ;; imply a "file" `:results' and a "results" `:exports'. + (when value + (setq results + (funcall merge results-exclusive-groups results '("file"))) + (unless (or (member "both" exports) + (member "none" exports) + (member "code" exports)) + (setq exports + (funcall merge + exports-exclusive-groups exports '("results")))) + (push pair params))) + (`(:exports . ,value) + (setq exports (funcall merge + exports-exclusive-groups + exports + (split-string (or value ""))))) + ;; Regular keywords: any value overwrites the previous one. + (_ (setq params (cons pair (assq-delete-all (car pair) params))))))) + ;; Handle `:var' and clear out colnames and rownames for replaced + ;; variables. + (setq params (nconc (mapcar (lambda (v) (cons :var (cddr v))) vars) + params)) + (dolist (name clearnames) + (dolist (param '(:colname-names :rowname-names)) + (when (assq param params) + (setf (cdr (assq param params)) + (cl-remove-if (lambda (pair) (equal name (car pair))) + (cdr (assq param params)))) + (setq params + (cl-remove-if (lambda (pair) (and (equal (car pair) param) + (null (cdr pair)))) + params))))) + ;; Handle other special keywords, which accept multiple values. + (setq params (nconc (list (cons :results (mapconcat #'identity results " ")) + (cons :exports (mapconcat #'identity exports " "))) + params)) + ;; Return merged params. params)) (defvar org-babel-use-quick-and-dirty-noweb-expansion nil @@ -2397,17 +2651,12 @@ header argument from buffer or subtree wide properties.") (defun org-babel-noweb-p (params context) "Check if PARAMS require expansion in CONTEXT. CONTEXT may be one of :tangle, :export or :eval." - (let* (intersect - (intersect (lambda (as bs) - (when as - (if (member (car as) bs) - (car as) - (funcall intersect (cdr as) bs)))))) - (funcall intersect (case context - (:tangle '("yes" "tangle" "no-export" "strip-export")) - (:eval '("yes" "no-export" "strip-export" "eval")) - (:export '("yes"))) - (split-string (or (cdr (assoc :noweb params)) ""))))) + (let ((allowed-values (cl-case context + (:tangle '("yes" "tangle" "no-export" "strip-export")) + (:eval '("yes" "no-export" "strip-export" "eval")) + (:export '("yes"))))) + (cl-some (lambda (v) (member v allowed-values)) + (split-string (or (cdr (assq :noweb params)) ""))))) (defun org-babel-expand-noweb-references (&optional info parent-buffer) "Expand Noweb references in the body of the current source code block. @@ -2445,7 +2694,7 @@ block but are passed literally to the \"example-block\"." (body (nth 1 info)) (ob-nww-start org-babel-noweb-wrap-start) (ob-nww-end org-babel-noweb-wrap-end) - (comment (string= "noweb" (cdr (assoc :comments (nth 2 info))))) + (comment (string= "noweb" (cdr (assq :comments (nth 2 info))))) (rx-prefix (concat "\\(" org-babel-src-name-regexp "\\|" ":noweb-ref[ \t]+" "\\)")) (new-body "") @@ -2454,11 +2703,11 @@ block but are passed literally to the \"example-block\"." (with-temp-buffer (funcall (intern (concat lang "-mode"))) (comment-region (point) (progn (insert text) (point))) - (org-babel-trim (buffer-string))))) + (org-trim (buffer-string))))) index source-name evaluate prefix) (with-temp-buffer - (org-set-local 'org-babel-noweb-wrap-start ob-nww-start) - (org-set-local 'org-babel-noweb-wrap-end ob-nww-end) + (setq-local org-babel-noweb-wrap-start ob-nww-start) + (setq-local org-babel-noweb-wrap-end ob-nww-end) (insert body) (goto-char (point-min)) (setq index (point)) (while (and (re-search-forward (org-babel-noweb-wrap) nil t)) @@ -2502,7 +2751,7 @@ block but are passed literally to the \"example-block\"." (while (re-search-forward rx nil t) (let* ((i (org-babel-get-src-block-info 'light)) (body (org-babel-expand-noweb-references i)) - (sep (or (cdr (assoc :noweb-sep (nth 2 i))) + (sep (or (cdr (assq :noweb-sep (nth 2 i))) "\n")) (full (if comment (let ((cs (org-babel-tangle-comment-links i))) @@ -2513,11 +2762,11 @@ block but are passed literally to the \"example-block\"." (setq expansion (cons sep (cons full expansion))))) (org-babel-map-src-blocks nil (let ((i (org-babel-get-src-block-info 'light))) - (when (equal (or (cdr (assoc :noweb-ref (nth 2 i))) + (when (equal (or (cdr (assq :noweb-ref (nth 2 i))) (nth 4 i)) source-name) (let* ((body (org-babel-expand-noweb-references i)) - (sep (or (cdr (assoc :noweb-sep (nth 2 i))) + (sep (or (cdr (assq :noweb-sep (nth 2 i))) "\n")) (full (if comment (let ((cs (org-babel-tangle-comment-links i))) @@ -2530,7 +2779,8 @@ block but are passed literally to the \"example-block\"." (and expansion (mapconcat #'identity (nreverse (cdr expansion)) ""))) ;; Possibly raise an error if named block doesn't exist. - (if (member lang org-babel-noweb-error-langs) + (if (or org-babel-noweb-error-all-langs + (member lang org-babel-noweb-error-langs)) (error "%s" (concat (org-babel-noweb-wrap source-name) "could not be resolved (see " @@ -2540,79 +2790,120 @@ block but are passed literally to the \"example-block\"." (funcall nb-add (buffer-substring index (point-max)))) new-body)) +(defun org-babel--script-escape-inner (str) + (let (in-single in-double backslash out) + (mapc + (lambda (ch) + (setq + out + (if backslash + (progn + (setq backslash nil) + (cond + ((and in-single (eq ch ?')) + ;; Escaped single quote inside single quoted string: + ;; emit just a single quote, since we've changed the + ;; outer quotes to double. + (cons ch out)) + ((eq ch ?\") + ;; Escaped double quote + (if in-single + ;; This should be interpreted as backslash+quote, + ;; not an escape. Emit a three backslashes + ;; followed by a quote (because one layer of + ;; quoting will be stripped by `org-babel-read'). + (append (list ch ?\\ ?\\ ?\\) out) + ;; Otherwise we are in a double-quoted string. Emit + ;; a single escaped quote + (append (list ch ?\\) out))) + ((eq ch ?\\) + ;; Escaped backslash: emit a single escaped backslash + (append (list ?\\ ?\\) out)) + ;; Other: emit a quoted backslash followed by whatever + ;; the character was (because one layer of quoting will + ;; be stripped by `org-babel-read'). + (t (append (list ch ?\\ ?\\) out)))) + (cl-case ch + (?\[ (if (or in-double in-single) + (cons ?\[ out) + (cons ?\( out))) + (?\] (if (or in-double in-single) + (cons ?\] out) + (cons ?\) out))) + (?\{ (if (or in-double in-single) + (cons ?\{ out) + (cons ?\( out))) + (?\} (if (or in-double in-single) + (cons ?\} out) + (cons ?\) out))) + (?, (if (or in-double in-single) + (cons ?, out) (cons ?\s out))) + (?\' (if in-double + (cons ?\' out) + (setq in-single (not in-single)) (cons ?\" out))) + (?\" (if in-single + (append (list ?\" ?\\) out) + (setq in-double (not in-double)) (cons ?\" out))) + (?\\ (unless (or in-single in-double) + (error "Can't handle backslash outside string in `org-babel-script-escape'")) + (setq backslash t) + out) + (t (cons ch out)))))) + (string-to-list str)) + (when (or in-single in-double) + (error "Unterminated string in `org-babel-script-escape'")) + (apply #'string (reverse out)))) + (defun org-babel-script-escape (str &optional force) "Safely convert tables into elisp lists." + (unless (stringp str) + (error "`org-babel-script-escape' expects a string")) (let ((escaped - (if (or force - (and (stringp str) - (> (length str) 2) - (or (and (string-equal "[" (substring str 0 1)) - (string-equal "]" (substring str -1))) - (and (string-equal "{" (substring str 0 1)) - (string-equal "}" (substring str -1))) - (and (string-equal "(" (substring str 0 1)) - (string-equal ")" (substring str -1)))))) - (org-babel-read - (concat - "'" - (let (in-single in-double out) - (mapc - (lambda (ch) - (setq - out - (case ch - (91 (if (or in-double in-single) ; [ - (cons 91 out) - (cons 40 out))) - (93 (if (or in-double in-single) ; ] - (cons 93 out) - (cons 41 out))) - (123 (if (or in-double in-single) ; { - (cons 123 out) - (cons 40 out))) - (125 (if (or in-double in-single) ; } - (cons 125 out) - (cons 41 out))) - (44 (if (or in-double in-single) ; , - (cons 44 out) (cons 32 out))) - (39 (if in-double ; ' - (cons 39 out) - (setq in-single (not in-single)) (cons 34 out))) - (34 (if in-single ; " - (append (list 34 32) out) - (setq in-double (not in-double)) (cons 34 out))) - (t (cons ch out))))) - (string-to-list str)) - (apply #'string (reverse out))))) - str))) + (cond + ((and (> (length str) 2) + (or (and (string-equal "[" (substring str 0 1)) + (string-equal "]" (substring str -1))) + (and (string-equal "{" (substring str 0 1)) + (string-equal "}" (substring str -1))) + (and (string-equal "(" (substring str 0 1)) + (string-equal ")" (substring str -1))))) + + (concat "'" (org-babel--script-escape-inner str))) + ((or force + (and (> (length str) 2) + (or (and (string-equal "'" (substring str 0 1)) + (string-equal "'" (substring str -1))) + ;; We need to pass double-quoted strings + ;; through the backslash-twiddling bits, even + ;; though we don't need to change their + ;; delimiters. + (and (string-equal "\"" (substring str 0 1)) + (string-equal "\"" (substring str -1)))))) + (org-babel--script-escape-inner str)) + (t str)))) (condition-case nil (org-babel-read escaped) (error escaped)))) (defun org-babel-read (cell &optional inhibit-lisp-eval) "Convert the string value of CELL to a number if appropriate. -Otherwise if cell looks like lisp (meaning it starts with a -\"(\", \"\\='\", \"\\=`\" or a \"[\") then read it as lisp, -otherwise return it unmodified as a string. Optional argument -NO-LISP-EVAL inhibits lisp evaluation for situations in which is -it not appropriate." - (if (and (stringp cell) (not (equal cell ""))) - (or (org-babel-number-p cell) - (if (and (not inhibit-lisp-eval) - (or (member (substring cell 0 1) '("(" "'" "`" "[")) - (string= cell "*this*"))) - (eval (read cell)) - (if (string= (substring cell 0 1) "\"") - (read cell) - (progn (set-text-properties 0 (length cell) nil cell) cell)))) - cell)) - -(defun org-babel-number-p (string) - "If STRING represents a number return its value." - (if (and (string-match "[0-9]+" string) - (string-match "^-?[0-9]*\\.?[0-9]*$" string) - (= (length (substring string (match-beginning 0) - (match-end 0))) - (length string))) - (string-to-number string))) +Otherwise if CELL looks like lisp (meaning it starts with a +\"(\", \"\\='\", \"\\=`\" or a \"[\") then read and evaluate it as +lisp, otherwise return it unmodified as a string. Optional +argument INHIBIT-LISP-EVAL inhibits lisp evaluation for +situations in which is it not appropriate." + (cond ((not (org-string-nw-p cell)) cell) + ((org-babel--string-to-number cell)) + ((and (not inhibit-lisp-eval) + (or (memq (string-to-char cell) '(?\( ?' ?` ?\[)) + (string= cell "*this*"))) + (eval (read cell) t)) + ((eq (string-to-char cell) ?\") (read cell)) + (t (org-no-properties cell)))) + +(defun org-babel--string-to-number (string) + "If STRING represents a number return its value. +Otherwise return nil." + (and (string-match-p "\\`-?[0-9]*\\.?[0-9]*\\'" string) + (string-to-number string))) (defun org-babel-import-elisp-from-file (file-name &optional separator) "Read the results located at FILE-NAME into an elisp table. @@ -2644,49 +2935,15 @@ If the table is trivial, then return it as a scalar." cell) t)) (defun org-babel-chomp (string &optional regexp) - "Strip trailing spaces and carriage returns from STRING. -Default regexp used is \"[ \f\t\n\r\v]\" but can be -overwritten by specifying a regexp as a second argument." + "Strip a trailing space or carriage return from STRING. +The default regexp used is \"[ \\f\\t\\n\\r\\v]\" but another one +can be specified as the REGEXP argument." (let ((regexp (or regexp "[ \f\t\n\r\v]"))) (while (and (> (length string) 0) (string-match regexp (substring string -1))) (setq string (substring string 0 -1))) string)) -(defun org-babel-trim (string &optional regexp) - "Strip leading and trailing spaces and carriage returns from STRING. -Like `org-babel-chomp' only it runs on both the front and back -of the string." - (org-babel-chomp (org-reverse-string - (org-babel-chomp (org-reverse-string string) regexp)) - regexp)) - -(defun org-babel-tramp-handle-call-process-region - (start end program &optional delete buffer display &rest args) - "Use Tramp to handle `call-process-region'. -Fixes a bug in `tramp-handle-call-process-region'." - (if (file-remote-p default-directory) - (let ((tmpfile (tramp-compat-make-temp-file ""))) - (write-region start end tmpfile) - (when delete (delete-region start end)) - (unwind-protect - ;; (apply 'call-process program tmpfile buffer display args) - ;; bug in tramp - (apply 'process-file program tmpfile buffer display args) - (delete-file tmpfile))) - ;; org-babel-call-process-region-original is the original emacs - ;; definition. It is in scope from the let binding in - ;; org-babel-execute-src-block - (apply org-babel-call-process-region-original - start end program delete buffer display args))) - -(defalias 'org-babel-local-file-name - (if (fboundp 'file-local-name) - 'file-local-name - (lambda (file) - "Return the local name component of FILE." - (or (file-remote-p file 'localname) file)))) - (defun org-babel-process-file-name (name &optional no-quote-p) "Prepare NAME to be used in an external process. If NAME specifies a remote location, the remote portion of the @@ -2694,7 +2951,7 @@ name is removed, since in that case the process will be executing remotely. The file name is then processed by `expand-file-name'. Unless second argument NO-QUOTE-P is non-nil, the file name is additionally processed by `shell-quote-argument'" - (let ((f (expand-file-name (org-babel-local-file-name name)))) + (let ((f (org-babel-local-file-name (expand-file-name name)))) (if no-quote-p f (shell-quote-argument f)))) (defvar org-babel-temporary-directory) @@ -2708,6 +2965,11 @@ additionally processed by `shell-quote-argument'" Used by `org-babel-temp-file'. This directory will be removed on Emacs shutdown.")) +(defcustom org-babel-remote-temporary-directory "/tmp/" + "Directory to hold temporary files on remote hosts." + :group 'org-babel + :type 'string) + (defmacro org-babel-result-cond (result-params scalar-form &rest table-forms) "Call the code to parse raw string results according to RESULT-PARAMS." (declare (indent 1) @@ -2720,6 +2982,7 @@ Emacs shutdown.")) (member "html" ,params) (member "code" ,params) (member "pp" ,params) + (member "file" ,params) (and (or (member "output" ,params) (member "raw" ,params) (member "org" ,params) @@ -2737,7 +3000,8 @@ of `org-babel-temporary-directory'." (if (file-remote-p default-directory) (let ((prefix (concat (file-remote-p default-directory) - (expand-file-name prefix temporary-file-directory)))) + (expand-file-name + prefix org-babel-remote-temporary-directory)))) (make-temp-file prefix nil suffix)) (let ((temporary-file-directory (or (and (boundp 'org-babel-temporary-directory) @@ -2772,6 +3036,96 @@ of `org-babel-temporary-directory'." (add-hook 'kill-emacs-hook 'org-babel-remove-temporary-directory) +(defun org-babel-one-header-arg-safe-p (pair safe-list) + "Determine if the PAIR is a safe babel header arg according to SAFE-LIST. + +For the format of SAFE-LIST, see `org-babel-safe-header-args'." + (and (consp pair) + (keywordp (car pair)) + (stringp (cdr pair)) + (or + (memq (car pair) safe-list) + (let ((entry (assq (car pair) safe-list))) + (and entry + (consp entry) + (cond ((functionp (cdr entry)) + (funcall (cdr entry) (cdr pair))) + ((listp (cdr entry)) + (member (cdr pair) (cdr entry))) + (t nil))))))) + +(defun org-babel-generate-file-param (src-name params) + "Calculate the filename for source block results. + +The directory is calculated from the :output-dir property of the +source block; if not specified, use the current directory. + +If the source block has a #+NAME and the :file parameter does not +contain any period characters, then the :file parameter is +treated as an extension, and the output file name is the +concatenation of the directory (as calculated above), the block +name, a period, and the parameter value as a file extension. +Otherwise, the :file parameter is treated as a full file name, +and the output file name is the directory (as calculated above) +plus the parameter value." + (let* ((file-cons (assq :file params)) + (file-ext-cons (assq :file-ext params)) + (file-ext (cdr-safe file-ext-cons)) + (dir (cdr-safe (assq :output-dir params))) + fname) + ;; create the output-dir if it does not exist + (when dir + (make-directory dir t)) + (if file-cons + ;; :file given; add :output-dir if given + (when dir + (setcdr file-cons (concat (file-name-as-directory dir) (cdr file-cons)))) + ;; :file not given; compute from name and :file-ext if possible + (when (and src-name file-ext) + (if dir + (setq fname (concat (file-name-as-directory (or dir "")) + src-name "." file-ext)) + (setq fname (concat src-name "." file-ext))) + (setq params (cons (cons :file fname) params)))) + params)) + +(defun org-babel-graphical-output-file (params) + "File where a babel block should send graphical output, per PARAMS. +Return nil if no graphical output is expected. Raise an error if +the output file is ill-defined." + (let ((file (cdr (assq :file params)))) + (cond (file (and (member "graphics" (cdr (assq :result-params params))) + file)) + ((assq :file-ext params) + (user-error ":file-ext given but no :file generated; did you forget \ +to name a block?")) + (t (user-error "No :file header argument given; cannot create \ +graphical result"))))) + +(defun org-babel-make-language-alias (new old) + "Make source blocks of type NEW aliases for those of type OLD. + +NEW and OLD should be strings. This function should be called +after the babel API for OLD-type source blocks is fully defined. + +Callers of this function will probably want to add an entry to +`org-src-lang-modes' as well." + (dolist (fn '("execute" "expand-body" "prep-session" + "variable-assignments" "load-session")) + (let ((sym (intern-soft (concat "org-babel-" fn ":" old)))) + (when (and sym (fboundp sym)) + (defalias (intern (concat "org-babel-" fn ":" new)) sym)))) + ;; Technically we don't need a `dolist' for just one variable, but + ;; we keep it for symmetry/ease of future expansion. + (dolist (var '("default-header-args")) + (let ((sym (intern-soft (concat "org-babel-" var ":" old)))) + (when (and sym (boundp sym)) + (defvaralias (intern (concat "org-babel-" var ":" new)) sym))))) + +(defun org-babel-strip-quotes (string) + "Strip \\\"s from around a string, if applicable." + (org-unbracket-string "\"" "\"" string)) + (provide 'ob-core) ;; Local variables: diff --git a/lisp/org/ob-css.el b/lisp/org/ob-css.el index 70c66d4670..4203b1258c 100644 --- a/lisp/org/ob-css.el +++ b/lisp/org/ob-css.el @@ -1,4 +1,4 @@ -;;; ob-css.el --- org-babel functions for css evaluation +;;; ob-css.el --- Babel Functions for CSS -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -24,19 +24,19 @@ ;;; Commentary: ;; Since CSS can't be executed, this file exists solely for tangling -;; CSS from org-mode files. +;; CSS from Org files. ;;; Code: (require 'ob) (defvar org-babel-default-header-args:css '()) -(defun org-babel-execute:css (body params) +(defun org-babel-execute:css (body _params) "Execute a block of CSS code. This function is called by `org-babel-execute-src-block'." body) -(defun org-babel-prep-session:css (session params) +(defun org-babel-prep-session:css (_session _params) "Return an error if the :session header argument is set. CSS does not support sessions." (error "CSS sessions are nonsensical")) diff --git a/lisp/org/ob-ditaa.el b/lisp/org/ob-ditaa.el index 5eb8e2fdb4..89b5d2465c 100644 --- a/lisp/org/ob-ditaa.el +++ b/lisp/org/ob-ditaa.el @@ -1,4 +1,4 @@ -;;; ob-ditaa.el --- org-babel functions for ditaa evaluation +;;; ob-ditaa.el --- Babel Functions for ditaa -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -81,15 +81,21 @@ Do not leave leading or trailing spaces in this string." (defun org-babel-execute:ditaa (body params) "Execute a block of Ditaa code with org-babel. This function is called by `org-babel-execute-src-block'." - (let* ((result-params (split-string (or (cdr (assoc :results params)) ""))) - (out-file (let ((el (cdr (assoc :file params)))) - (or el - (error - "ditaa code block requires :file header argument")))) - (cmdline (cdr (assoc :cmdline params))) - (java (cdr (assoc :java params))) + (let* ((out-file (or (cdr (assq :file params)) + (error + "ditaa code block requires :file header argument"))) + (cmdline (cdr (assq :cmdline params))) + (java (cdr (assq :java params))) (in-file (org-babel-temp-file "ditaa-")) - (eps (cdr (assoc :eps params))) + (eps (cdr (assq :eps params))) + (eps-file (when eps + (org-babel-process-file-name (concat in-file ".eps")))) + (pdf-cmd (when (and (or (string= (file-name-extension out-file) "pdf") + (cdr (assq :pdf params)))) + (concat + "epstopdf" + " " eps-file + " -o=" (org-babel-process-file-name out-file)))) (cmd (concat org-babel-ditaa-java-cmd " " java " " org-ditaa-jar-option " " (shell-quote-argument @@ -97,13 +103,9 @@ This function is called by `org-babel-execute-src-block'." (if eps org-ditaa-eps-jar-path org-ditaa-jar-path))) " " cmdline " " (org-babel-process-file-name in-file) - " " (org-babel-process-file-name out-file))) - (pdf-cmd (when (and (or (string= (file-name-extension out-file) "pdf") - (cdr (assoc :pdf params)))) - (concat - "epstopdf" - " " (org-babel-process-file-name (concat in-file ".eps")) - " -o=" (org-babel-process-file-name out-file))))) + " " (if pdf-cmd + eps-file + (org-babel-process-file-name out-file))))) (unless (file-exists-p org-ditaa-jar-path) (error "Could not find ditaa.jar at %s" org-ditaa-jar-path)) (with-temp-file in-file (insert body)) @@ -111,7 +113,7 @@ This function is called by `org-babel-execute-src-block'." (when pdf-cmd (message pdf-cmd) (shell-command pdf-cmd)) nil)) ;; signal that output has already been written to file -(defun org-babel-prep-session:ditaa (session params) +(defun org-babel-prep-session:ditaa (_session _params) "Return an error because ditaa does not support sessions." (error "Ditaa does not support sessions")) diff --git a/lisp/org/ob-dot.el b/lisp/org/ob-dot.el index aa0445b4ca..81442bfc1c 100644 --- a/lisp/org/ob-dot.el +++ b/lisp/org/ob-dot.el @@ -1,4 +1,4 @@ -;;; ob-dot.el --- org-babel functions for dot evaluation +;;; ob-dot.el --- Babel Functions for dot -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -46,7 +46,7 @@ (defun org-babel-expand-body:dot (body params) "Expand BODY according to PARAMS, return the expanded body." - (let ((vars (mapcar #'cdr (org-babel-get-header params :var)))) + (let ((vars (org-babel--get-vars params))) (mapc (lambda (pair) (let ((name (symbol-name (car pair))) @@ -55,19 +55,20 @@ (replace-regexp-in-string (concat "$" (regexp-quote name)) (if (stringp value) value (format "%S" value)) - body)))) + body + t + t)))) vars) body)) (defun org-babel-execute:dot (body params) "Execute a block of Dot code with org-babel. This function is called by `org-babel-execute-src-block'." - (let* ((result-params (cdr (assoc :result-params params))) - (out-file (cdr (or (assoc :file params) + (let* ((out-file (cdr (or (assq :file params) (error "You need to specify a :file parameter")))) - (cmdline (or (cdr (assoc :cmdline params)) + (cmdline (or (cdr (assq :cmdline params)) (format "-T%s" (file-name-extension out-file)))) - (cmd (or (cdr (assoc :cmd params)) "dot")) + (cmd (or (cdr (assq :cmd params)) "dot")) (in-file (org-babel-temp-file "dot-"))) (with-temp-file in-file (insert (org-babel-expand-body:dot body params))) @@ -78,7 +79,7 @@ This function is called by `org-babel-execute-src-block'." " -o " (org-babel-process-file-name out-file)) "") nil)) ;; signal that output has already been written to file -(defun org-babel-prep-session:dot (session params) +(defun org-babel-prep-session:dot (_session _params) "Return an error because Dot does not support sessions." (error "Dot does not support sessions")) diff --git a/lisp/org/ob-ebnf.el b/lisp/org/ob-ebnf.el new file mode 100644 index 0000000000..410570bc5d --- /dev/null +++ b/lisp/org/ob-ebnf.el @@ -0,0 +1,83 @@ +;;; ob-ebnf.el --- Babel Functions for EBNF -*- lexical-binding: t; -*- + +;; Copyright (C) 2013-2017 Free Software Foundation, Inc. + +;; Author: Michael Gauland +;; Keywords: literate programming, reproducible research +;; Homepage: http://orgmode.org +;; Version: 1.00 + +;;; License: + +;; 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, 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: + +;;; Org-Babel support for using ebnf2ps to generate encapsulated postscript +;;; railroad diagrams. It recogises these arguments: +;;; +;;; :file is required; it must include the extension '.eps.' All the rules +;;; in the block will be drawn in the same file. This is done by +;;; inserting a '[' comment at the start of the block (see the +;;; documentation for ebnf-eps-buffer for more information). +;;; +;;; :style specifies a value in ebnf-style-database. This provides the +;;; ability to customise the output. The style can also specify the +;;; grammar syntax (by setting ebnf-syntax); note that only ebnf, +;;; iso-ebnf, and yacc are supported by this file. + +;;; Requirements: + +;;; Code: +(require 'ob) +(require 'ebnf2ps) + +;; optionally declare default header arguments for this language +(defvar org-babel-default-header-args:ebnf '((:style . nil))) + +;; Use ebnf-eps-buffer to produce an encapsulated postscript file. +;; +(defun org-babel-execute:ebnf (body params) + "Execute a block of Ebnf code with org-babel. This function is +called by `org-babel-execute-src-block'" + (save-excursion + (let* ((dest-file (cdr (assq :file params))) + (dest-dir (file-name-directory dest-file)) + (dest-root (file-name-sans-extension + (file-name-nondirectory dest-file))) + (style (cdr (assq :style params))) + (result nil)) + (with-temp-buffer + (when style (ebnf-push-style style)) + (let ((comment-format + (cond ((string= ebnf-syntax 'yacc) "/*%s*/") + ((string= ebnf-syntax 'ebnf) ";%s") + ((string= ebnf-syntax 'iso-ebnf) "(*%s*)") + (t (setq result + (format "EBNF error: format %s not supported." + ebnf-syntax)))))) + (setq ebnf-eps-prefix dest-dir) + (insert (format comment-format (format "[%s" dest-root))) + (newline) + (insert body) + (newline) + (insert (format comment-format (format "]%s" dest-root))) + (ebnf-eps-buffer) + (when style (ebnf-pop-style)))) + result))) + +(provide 'ob-ebnf) +;;; ob-ebnf.el ends here diff --git a/lisp/org/ob-emacs-lisp.el b/lisp/org/ob-emacs-lisp.el index d95c475c4e..c0bd12a879 100644 --- a/lisp/org/ob-emacs-lisp.el +++ b/lisp/org/ob-emacs-lisp.el @@ -1,4 +1,4 @@ -;;; ob-emacs-lisp.el --- org-babel functions for emacs-lisp code evaluation +;;; ob-emacs-lisp.el --- Babel Functions for Emacs-lisp Code -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -28,17 +28,21 @@ ;;; Code: (require 'ob) -(defvar org-babel-default-header-args:emacs-lisp - '((:hlines . "yes") (:colnames . "no")) - "Default arguments for evaluating an emacs-lisp source block.") +(defconst org-babel-header-args:emacs-lisp '((lexical . :any)) + "Emacs-lisp specific header arguments.") -(declare-function orgtbl-to-generic "org-table" - (table params &optional backend)) +(defvar org-babel-default-header-args:emacs-lisp '((:lexical . "no")) + "Default arguments for evaluating an emacs-lisp source block. + +A value of \"yes\" or t causes src blocks to be eval'd using +lexical scoping. It can also be an alist mapping symbols to +their value. It is used as the optional LEXICAL argument to +`eval', which see.") (defun org-babel-expand-body:emacs-lisp (body params) "Expand BODY according to PARAMS, return the expanded body." - (let* ((vars (mapcar #'cdr (org-babel-get-header params :var))) - (result-params (cdr (assoc :result-params params))) + (let* ((vars (org-babel--get-vars params)) + (result-params (cdr (assq :result-params params))) (print-level nil) (print-length nil) (body (if (> (length vars) 0) (concat "(let (" @@ -55,26 +59,33 @@ (defun org-babel-execute:emacs-lisp (body params) "Execute a block of emacs-lisp code with Babel." (save-window-excursion - (let ((result - (eval (read (format (if (member "output" - (cdr (assoc :result-params params))) - "(with-output-to-string %s)" - "(progn %s)") - (org-babel-expand-body:emacs-lisp - body params)))))) - (org-babel-result-cond (cdr (assoc :result-params params)) + (let* ((lexical (cdr (assq :lexical params))) + (result + (eval (read (format (if (member "output" + (cdr (assq :result-params params))) + "(with-output-to-string %s)" + "(progn %s)") + (org-babel-expand-body:emacs-lisp + body params))) + + (if (listp lexical) + lexical + (member lexical '("yes" "t")))))) + (org-babel-result-cond (cdr (assq :result-params params)) (let ((print-level nil) (print-length nil)) - (if (or (member "scalar" (cdr (assoc :result-params params))) - (member "verbatim" (cdr (assoc :result-params params)))) + (if (or (member "scalar" (cdr (assq :result-params params))) + (member "verbatim" (cdr (assq :result-params params)))) (format "%S" result) (format "%s" result))) (org-babel-reassemble-table result - (org-babel-pick-name (cdr (assoc :colname-names params)) - (cdr (assoc :colnames params))) - (org-babel-pick-name (cdr (assoc :rowname-names params)) - (cdr (assoc :rownames params)))))))) + (org-babel-pick-name (cdr (assq :colname-names params)) + (cdr (assq :colnames params))) + (org-babel-pick-name (cdr (assq :rowname-names params)) + (cdr (assq :rownames params)))))))) + +(org-babel-make-language-alias "elisp" "emacs-lisp") (provide 'ob-emacs-lisp) diff --git a/lisp/org/ob-eval.el b/lisp/org/ob-eval.el index 46d21c88e8..324cf5fb27 100644 --- a/lisp/org/ob-eval.el +++ b/lisp/org/ob-eval.el @@ -1,4 +1,4 @@ -;;; ob-eval.el --- org-babel functions for external code evaluation +;;; ob-eval.el --- Babel Functions for External Code Evaluation -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -28,7 +28,6 @@ ;;; Code: (require 'org-macs) -(eval-when-compile (require 'cl)) (defvar org-babel-error-buffer-name "*Org-Babel Error Output*") (declare-function org-babel-temp-file "ob-core" (prefix &optional suffix)) @@ -57,6 +56,13 @@ STDERR with `org-babel-eval-error-notify'." (progn (with-current-buffer err-buff (org-babel-eval-error-notify exit-code (buffer-string))) + (save-excursion + (when (get-buffer org-babel-error-buffer-name) + (with-current-buffer org-babel-error-buffer-name + (unless (derived-mode-p 'compilation-mode) + (compilation-mode)) + ;; Compilation-mode enforces read-only, but Babel expects the buffer modifiable. + (setq buffer-read-only nil)))) nil) (buffer-string))))) @@ -114,18 +120,18 @@ function in various versions of Emacs. (delete-file input-file)) (when (and error-file (file-exists-p error-file)) - (if (< 0 (nth 7 (file-attributes error-file))) - (with-current-buffer (get-buffer-create error-buffer) - (let ((pos-from-end (- (point-max) (point)))) - (or (bobp) - (insert "\f\n")) - ;; Do no formatting while reading error file, - ;; because that can run a shell command, and we - ;; don't want that to cause an infinite recursion. - (format-insert-file error-file nil) - ;; Put point after the inserted errors. - (goto-char (- (point-max) pos-from-end))) - (current-buffer))) + (when (< 0 (nth 7 (file-attributes error-file))) + (with-current-buffer (get-buffer-create error-buffer) + (let ((pos-from-end (- (point-max) (point)))) + (or (bobp) + (insert "\f\n")) + ;; Do no formatting while reading error file, + ;; because that can run a shell command, and we + ;; don't want that to cause an infinite recursion. + (format-insert-file error-file nil) + ;; Put point after the inserted errors. + (goto-char (- (point-max) pos-from-end))) + (current-buffer))) (delete-file error-file)) exit-status)) diff --git a/lisp/org/ob-exp.el b/lisp/org/ob-exp.el index 2677fe59cb..2556362f92 100644 --- a/lisp/org/ob-exp.el +++ b/lisp/org/ob-exp.el @@ -1,4 +1,4 @@ -;;; ob-exp.el --- Exportation of org-babel source blocks +;;; ob-exp.el --- Exportation of Babel Source Blocks -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -24,81 +24,49 @@ ;;; Code: (require 'ob-core) -(require 'org-src) -(eval-when-compile - (require 'cl)) - -(defvar org-current-export-file) -(defvar org-babel-lob-one-liner-regexp) -(defvar org-babel-ref-split-regexp) -(defvar org-list-forbidden-blocks) - -(declare-function org-babel-lob-get-info "ob-lob" ()) -(declare-function org-babel-eval-wipe-error-buffer "ob-eval" ()) -(declare-function org-between-regexps-p "org" - (start-re end-re &optional lim-up lim-down)) -(declare-function org-get-indentation "org" (&optional line)) -(declare-function org-heading-components "org" ()) -(declare-function org-in-block-p "org" (names)) -(declare-function org-in-verbatim-emphasis "org" ()) -(declare-function org-link-search "org" (s &optional type avoid-pos stealth)) -(declare-function org-fill-template "org" (template alist)) -(declare-function org-split-string "org" (string &optional separators)) -(declare-function org-element-at-point "org-element" (&optional keep-trail)) + +(declare-function org-babel-lob-get-info "ob-lob" (&optional datum)) +(declare-function org-element-at-point "org-element" ()) (declare-function org-element-context "org-element" (&optional element)) (declare-function org-element-property "org-element" (property element)) (declare-function org-element-type "org-element" (element)) (declare-function org-escape-code-in-string "org-src" (s)) +(declare-function org-export-copy-buffer "ox" ()) +(declare-function org-fill-template "org" (template alist)) +(declare-function org-get-indentation "org" (&optional line)) +(declare-function org-in-commented-heading-p "org" (&optional no-inheritance)) + +(defvar org-src-preserve-indentation) (defcustom org-export-babel-evaluate t "Switch controlling code evaluation during export. When set to nil no code will be evaluated as part of the export -process. When set to `inline-only', only inline code blocks will -be executed." +process and no header argumentss will be obeyed. When set to +`inline-only', only inline code blocks will be executed. Users +who wish to avoid evaluating code on export should use the header +argument `:eval never-export'." :group 'org-babel :version "24.1" :type '(choice (const :tag "Never" nil) (const :tag "Only inline code" inline-only) (const :tag "Always" t))) -(put 'org-export-babel-evaluate 'safe-local-variable (lambda (x) (eq x nil))) - -(defun org-babel-exp-get-export-buffer () - "Return the current export buffer if possible." - (cond - ((bufferp org-current-export-file) org-current-export-file) - (org-current-export-file (get-file-buffer org-current-export-file)) - ('otherwise - (error "Requested export buffer when `org-current-export-file' is nil")))) - -(defvar org-link-search-inhibit-query) - -(defmacro org-babel-exp-in-export-file (lang &rest body) - (declare (indent 1)) - `(let* ((lang-headers (intern (concat "org-babel-default-header-args:" ,lang))) - (heading (nth 4 (ignore-errors (org-heading-components)))) - (export-buffer (current-buffer)) - (original-buffer (org-babel-exp-get-export-buffer)) results) - (when original-buffer - ;; resolve parameters in the original file so that - ;; headline and file-wide parameters are included, attempt - ;; to go to the same heading in the original file - (set-buffer original-buffer) - (save-restriction - (when heading - (condition-case nil - (let ((org-link-search-inhibit-query t)) - (org-link-search heading)) - (error (when heading - (goto-char (point-min)) - (re-search-forward (regexp-quote heading) nil t))))) - (setq results ,@body)) - (set-buffer export-buffer) - results))) -(def-edebug-spec org-babel-exp-in-export-file (form body)) - -(defun org-babel-exp-src-block (&rest headers) +(put 'org-export-babel-evaluate 'safe-local-variable #'null) + +(defmacro org-babel-exp--at-source (&rest body) + "Evaluate BODY at the source of the Babel block at point. +Source is located in `org-babel-exp-reference-buffer'. The value +returned is the value of the last form in BODY. Assume that +point is at the beginning of the Babel block." + (declare (indent 1) (debug body)) + `(let ((source (get-text-property (point) 'org-reference))) + (with-current-buffer org-babel-exp-reference-buffer + (org-with-wide-buffer + (goto-char source) + ,@body)))) + +(defun org-babel-exp-src-block () "Process source block for export. -Depending on the `export' headers argument, replace the source +Depending on the \":export\" header argument, replace the source code block like this: both ---- display the code and the results @@ -107,29 +75,36 @@ code ---- the default, display the code inside the block but do not process results - just like none only the block is run on export ensuring - that it's results are present in the org-mode buffer + that its results are present in the Org mode buffer none ---- do not display either code or results upon export -Assume point is at the beginning of block's starting line." +Assume point is at block opening line." (interactive) - (unless noninteractive (message "org-babel-exp processing...")) (save-excursion (let* ((info (org-babel-get-src-block-info 'light)) (lang (nth 0 info)) - (raw-params (nth 2 info)) hash) + (raw-params (nth 2 info)) + hash) ;; bail if we couldn't get any info from the block + (unless noninteractive + (message "org-babel-exp process %s at position %d..." + lang + (line-beginning-position))) (when info ;; if we're actually going to need the parameters - (when (member (cdr (assoc :exports (nth 2 info))) '("both" "results")) - (org-babel-exp-in-export-file lang - (setf (nth 2 info) - (org-babel-process-params - (apply #'org-babel-merge-params - org-babel-default-header-args - (if (boundp lang-headers) (eval lang-headers) nil) - (append (org-babel-params-from-properties lang) - (list raw-params)))))) + (when (member (cdr (assq :exports (nth 2 info))) '("both" "results")) + (let ((lang-headers (intern (concat "org-babel-default-header-args:" + lang)))) + (org-babel-exp--at-source + (setf (nth 2 info) + (org-babel-process-params + (apply #'org-babel-merge-params + org-babel-default-header-args + (and (boundp lang-headers) + (symbol-value lang-headers)) + (append (org-babel-params-from-properties lang) + (list raw-params))))))) (setf hash (org-babel-sha1-hash info))) (org-babel-exp-do-export info 'block hash))))) @@ -150,166 +125,180 @@ this template." :group 'org-babel :type 'string) -(defvar org-babel-default-lob-header-args) (defun org-babel-exp-process-buffer () "Execute all Babel blocks in current buffer." (interactive) - (save-window-excursion - (save-excursion + (when org-export-babel-evaluate + (save-window-excursion (let ((case-fold-search t) - (regexp (concat org-babel-inline-src-block-regexp "\\|" - org-babel-lob-one-liner-regexp "\\|" - "^[ \t]*#\\+BEGIN_SRC"))) - (goto-char (point-min)) - (while (re-search-forward regexp nil t) - (let* ((element (save-excursion - ;; If match is inline, point is at its - ;; end. Move backward so - ;; `org-element-context' can get the - ;; object, not the following one. - (backward-char) - (save-match-data (org-element-context)))) - (type (org-element-type element)) - (begin (copy-marker (org-element-property :begin element))) - (end (copy-marker - (save-excursion - (goto-char (org-element-property :end element)) - (skip-chars-backward " \r\t\n") - (point))))) - (case type - (inline-src-block - (let* ((info (org-babel-parse-inline-src-block-match)) - (params (nth 2 info))) - (setf (nth 1 info) - (if (and (cdr (assoc :noweb params)) - (string= "yes" (cdr (assoc :noweb params)))) - (org-babel-expand-noweb-references - info (org-babel-exp-get-export-buffer)) - (nth 1 info))) - (goto-char begin) - (let ((replacement (org-babel-exp-do-export info 'inline))) - (if (equal replacement "") - ;; Replacement code is empty: remove inline src - ;; block, including extra white space that - ;; might have been created when inserting - ;; results. - (delete-region begin - (progn (goto-char end) - (skip-chars-forward " \t") - (point))) - ;; Otherwise: remove inline src block but - ;; preserve following white spaces. Then insert - ;; value. - (delete-region begin end) - (insert replacement))))) - ((babel-call inline-babel-call) - (let* ((lob-info (org-babel-lob-get-info)) - (results - (org-babel-exp-do-export - (list "emacs-lisp" "results" - (apply #'org-babel-merge-params - org-babel-default-header-args - org-babel-default-lob-header-args - (append - (org-babel-params-from-properties) - (list - (org-babel-parse-header-arguments - (org-no-properties - (concat - ":var results=" - (mapconcat 'identity - (butlast lob-info 2) - " "))))))) - "" (nth 3 lob-info) (nth 2 lob-info)) - 'lob)) - (rep (org-fill-template - org-babel-exp-call-line-template - `(("line" . ,(nth 0 lob-info)))))) - ;; If replacement is empty, completely remove the - ;; object/element, including any extra white space - ;; that might have been created when including - ;; results. - (if (equal rep "") - (delete-region - begin - (progn (goto-char end) - (if (not (eq type 'babel-call)) - (progn (skip-chars-forward " \t") (point)) - (skip-chars-forward " \r\t\n") - (line-beginning-position)))) - ;; Otherwise, preserve following white - ;; spaces/newlines and then, insert replacement - ;; string. - (goto-char begin) - (delete-region begin end) - (insert rep)))) - (src-block - (let* ((match-start (copy-marker (match-beginning 0))) - (ind (org-get-indentation)) - (headers - (cons - (org-element-property :language element) - (let ((params (org-element-property :parameters - element))) - (and params (org-split-string params "[ \t]+")))))) - ;; Take care of matched block: compute replacement - ;; string. In particular, a nil REPLACEMENT means - ;; the block should be left as-is while an empty - ;; string should remove the block. - (let ((replacement (progn (goto-char match-start) - (org-babel-exp-src-block headers)))) - (cond ((not replacement) (goto-char end)) - ((equal replacement "") - (goto-char end) - (skip-chars-forward " \r\t\n") - (beginning-of-line) - (delete-region begin (point))) - (t - (goto-char match-start) - (delete-region (point) - (save-excursion (goto-char end) - (line-end-position))) - (insert replacement) - (if (or org-src-preserve-indentation - (org-element-property :preserve-indent - element)) - ;; Indent only the code block markers. - (save-excursion (skip-chars-backward " \r\t\n") - (indent-line-to ind) - (goto-char match-start) - (indent-line-to ind)) - ;; Indent everything. - (indent-rigidly match-start (point) ind))))) - (set-marker match-start nil)))) - (set-marker begin nil) - (set-marker end nil))))))) - -(defun org-babel-in-example-or-verbatim () - "Return true if point is in example or verbatim code. -Example and verbatim code include escaped portions of -an org-mode buffer code that should be treated as normal -org-mode text." - (or (save-match-data - (save-excursion - (goto-char (point-at-bol)) - (looking-at "[ \t]*:[ \t]"))) - (org-in-verbatim-emphasis) - (org-in-block-p org-list-forbidden-blocks) - (org-between-regexps-p "^[ \t]*#\\+begin_src" "^[ \t]*#\\+end_src"))) + (regexp (if (eq org-export-babel-evaluate 'inline-only) + "\\(call\\|src\\)_" + "\\(call\\|src\\)_\\|^[ \t]*#\\+\\(BEGIN_SRC\\|CALL:\\)")) + ;; Get a pristine copy of current buffer so Babel + ;; references are properly resolved and source block + ;; context is preserved. + (org-babel-exp-reference-buffer (org-export-copy-buffer))) + (unwind-protect + (save-excursion + ;; First attach to every source block their original + ;; position, so that they can be retrieved within + ;; `org-babel-exp-reference-buffer', even after heavy + ;; modifications on current buffer. + ;; + ;; False positives are harmless, so we don't check if + ;; we're really at some Babel object. Moreover, + ;; `line-end-position' ensures that we propertize + ;; a noticeable part of the object, without affecting + ;; multiple objects on the same line. + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (let ((s (match-beginning 0))) + (put-text-property s (line-end-position) 'org-reference s))) + ;; Evaluate from top to bottom every Babel block + ;; encountered. + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (unless (save-match-data (org-in-commented-heading-p)) + (let* ((object? (match-end 1)) + (element (save-match-data + (if object? (org-element-context) + ;; No deep inspection if we're + ;; just looking for an element. + (org-element-at-point)))) + (type + (pcase (org-element-type element) + ;; Discard block elements if we're looking + ;; for inline objects. False results + ;; happen when, e.g., "call_" syntax is + ;; located within affiliated keywords: + ;; + ;; #+name: call_src + ;; #+begin_src ... + ((and (or `babel-call `src-block) (guard object?)) + nil) + (type type))) + (begin + (copy-marker (org-element-property :begin element))) + (end + (copy-marker + (save-excursion + (goto-char (org-element-property :end element)) + (skip-chars-backward " \r\t\n") + (point))))) + (pcase type + (`inline-src-block + (let* ((info + (org-babel-get-src-block-info nil element)) + (params (nth 2 info))) + (setf (nth 1 info) + (if (and (cdr (assq :noweb params)) + (string= "yes" + (cdr (assq :noweb params)))) + (org-babel-expand-noweb-references + info org-babel-exp-reference-buffer) + (nth 1 info))) + (goto-char begin) + (let ((replacement + (org-babel-exp-do-export info 'inline))) + (if (equal replacement "") + ;; Replacement code is empty: remove + ;; inline source block, including extra + ;; white space that might have been + ;; created when inserting results. + (delete-region begin + (progn (goto-char end) + (skip-chars-forward " \t") + (point))) + ;; Otherwise: remove inline src block but + ;; preserve following white spaces. Then + ;; insert value. + (delete-region begin end) + (insert replacement))))) + ((or `babel-call `inline-babel-call) + (org-babel-exp-do-export (org-babel-lob-get-info element) + 'lob) + (let ((rep + (org-fill-template + org-babel-exp-call-line-template + `(("line" . + ,(org-element-property :value element)))))) + ;; If replacement is empty, completely remove + ;; the object/element, including any extra + ;; white space that might have been created + ;; when including results. + (if (equal rep "") + (delete-region + begin + (progn (goto-char end) + (if (not (eq type 'babel-call)) + (progn (skip-chars-forward " \t") + (point)) + (skip-chars-forward " \r\t\n") + (line-beginning-position)))) + ;; Otherwise, preserve trailing + ;; spaces/newlines and then, insert + ;; replacement string. + (goto-char begin) + (delete-region begin end) + (insert rep)))) + (`src-block + (let ((match-start (copy-marker (match-beginning 0))) + (ind (org-get-indentation))) + ;; Take care of matched block: compute + ;; replacement string. In particular, a nil + ;; REPLACEMENT means the block is left as-is + ;; while an empty string removes the block. + (let ((replacement + (progn (goto-char match-start) + (org-babel-exp-src-block)))) + (cond ((not replacement) (goto-char end)) + ((equal replacement "") + (goto-char end) + (skip-chars-forward " \r\t\n") + (beginning-of-line) + (delete-region begin (point))) + (t + (goto-char match-start) + (delete-region (point) + (save-excursion + (goto-char end) + (line-end-position))) + (insert replacement) + (if (or org-src-preserve-indentation + (org-element-property + :preserve-indent element)) + ;; Indent only code block + ;; markers. + (save-excursion + (skip-chars-backward " \r\t\n") + (indent-line-to ind) + (goto-char match-start) + (indent-line-to ind)) + ;; Indent everything. + (indent-rigidly + match-start (point) ind))))) + (set-marker match-start nil)))) + (set-marker begin nil) + (set-marker end nil))))) + (kill-buffer org-babel-exp-reference-buffer) + (remove-text-properties (point-min) (point-max) '(org-reference))))))) (defun org-babel-exp-do-export (info type &optional hash) "Return a string with the exported content of a code block. The function respects the value of the :exports header argument." - (let ((silently (lambda () (let ((session (cdr (assoc :session (nth 2 info))))) - (when (not (and session (equal "none" session))) - (org-babel-exp-results info type 'silent))))) - (clean (lambda () (unless (eq type 'inline) (org-babel-remove-result info))))) - (case (intern (or (cdr (assoc :exports (nth 2 info))) "code")) - ('none (funcall silently) (funcall clean) "") - ('code (funcall silently) (funcall clean) (org-babel-exp-code info)) - ('results (org-babel-exp-results info type nil hash) "") - ('both (org-babel-exp-results info type nil hash) - (org-babel-exp-code info))))) + (let ((silently (lambda () (let ((session (cdr (assq :session (nth 2 info))))) + (unless (equal "none" session) + (org-babel-exp-results info type 'silent))))) + (clean (lambda () (if (eq type 'inline) + (org-babel-remove-inline-result) + (org-babel-remove-result info))))) + (pcase (or (cdr (assq :exports (nth 2 info))) "code") + ("none" (funcall silently) (funcall clean) "") + ("code" (funcall silently) (funcall clean) (org-babel-exp-code info type)) + ("results" (org-babel-exp-results info type nil hash) "") + ("both" + (org-babel-exp-results info type nil hash) + (org-babel-exp-code info type))))) (defcustom org-babel-exp-code-template "#+BEGIN_SRC %lang%switches%flags\n%body\n#+END_SRC" @@ -331,18 +320,42 @@ replaced with its value." :group 'org-babel :type 'string) -(defun org-babel-exp-code (info) +(defcustom org-babel-exp-inline-code-template + "src_%lang[%switches%flags]{%body}" + "Template used to export the body of inline code blocks. +This template may be customized to include additional information +such as the code block name, or the values of particular header +arguments. The template is filled out using `org-fill-template', +and the following %keys may be used. + + lang ------ the language of the code block + name ------ the name of the code block + body ------ the body of the code block + switches -- the switches associated to the code block + flags ----- the flags passed to the code block + +In addition to the keys mentioned above, every header argument +defined for the code block may be used as a key and will be +replaced with its value." + :group 'org-babel + :type 'string + :version "26.1" + :package-version '(Org . "8.3")) + +(defun org-babel-exp-code (info type) "Return the original code block formatted for export." (setf (nth 1 info) - (if (string= "strip-export" (cdr (assoc :noweb (nth 2 info)))) + (if (string= "strip-export" (cdr (assq :noweb (nth 2 info)))) (replace-regexp-in-string (org-babel-noweb-wrap) "" (nth 1 info)) (if (org-babel-noweb-p (nth 2 info) :export) (org-babel-expand-noweb-references - info (org-babel-exp-get-export-buffer)) + info org-babel-exp-reference-buffer) (nth 1 info)))) (org-fill-template - org-babel-exp-code-template + (if (eq type 'inline) + org-babel-exp-inline-code-template + org-babel-exp-code-template) `(("lang" . ,(nth 0 info)) ("body" . ,(org-escape-code-in-string (nth 1 info))) ("switches" . ,(let ((f (nth 3 info))) @@ -357,48 +370,41 @@ replaced with its value." (defun org-babel-exp-results (info type &optional silent hash) "Evaluate and return the results of the current code block for export. -Results are prepared in a manner suitable for export by org-mode. +Results are prepared in a manner suitable for export by Org mode. This function is called by `org-babel-exp-do-export'. The code block will be evaluated. Optional argument SILENT can be used to inhibit insertion of results into the buffer." - (when (and (or (eq org-export-babel-evaluate t) - (and (eq type 'inline) - (eq org-export-babel-evaluate 'inline-only))) - (not (and hash (equal hash (org-babel-current-result-hash))))) + (unless (and hash (equal hash (org-babel-current-result-hash))) (let ((lang (nth 0 info)) (body (if (org-babel-noweb-p (nth 2 info) :eval) (org-babel-expand-noweb-references - info (org-babel-exp-get-export-buffer)) + info org-babel-exp-reference-buffer) (nth 1 info))) (info (copy-sequence info)) (org-babel-current-src-block-location (point-marker))) - ;; skip code blocks which we can't evaluate + ;; Skip code blocks which we can't evaluate. (when (fboundp (intern (concat "org-babel-execute:" lang))) (org-babel-eval-wipe-error-buffer) - (prog1 nil - (setf (nth 1 info) body) - (setf (nth 2 info) - (org-babel-exp-in-export-file lang - (org-babel-process-params - (org-babel-merge-params - (nth 2 info) - `((:results . ,(if silent "silent" "replace"))))))) - (cond - ((equal type 'block) - (org-babel-execute-src-block nil info)) - ((equal type 'inline) - ;; position the point on the inline source block allowing - ;; `org-babel-insert-result' to check that the block is - ;; inline - (re-search-backward "[ \f\t\n\r\v]" nil t) - (re-search-forward org-babel-inline-src-block-regexp nil t) - (re-search-backward "src_" nil t) + (setf (nth 1 info) body) + (setf (nth 2 info) + (org-babel-exp--at-source + (org-babel-process-params + (org-babel-merge-params + (nth 2 info) + `((:results . ,(if silent "silent" "replace"))))))) + (pcase type + (`block (org-babel-execute-src-block nil info)) + (`inline + ;; Position the point on the inline source block + ;; allowing `org-babel-insert-result' to check that the + ;; block is inline. + (goto-char (nth 5 info)) (org-babel-execute-src-block nil info)) - ((equal type 'lob) - (save-excursion - (re-search-backward org-babel-lob-one-liner-regexp nil t) - (let (org-confirm-babel-evaluate) - (org-babel-execute-src-block nil info)))))))))) + (`lob + (save-excursion + (goto-char (nth 5 info)) + (let (org-confirm-babel-evaluate) + (org-babel-execute-src-block nil info))))))))) (provide 'ob-exp) diff --git a/lisp/org/ob-forth.el b/lisp/org/ob-forth.el new file mode 100644 index 0000000000..152cf727e2 --- /dev/null +++ b/lisp/org/ob-forth.el @@ -0,0 +1,87 @@ +;;; ob-forth.el --- Babel Functions for Forth -*- lexical-binding: t; -*- + +;; Copyright (C) 2014-2017 Free Software Foundation, Inc. + +;; Author: Eric Schulte +;; Keywords: literate programming, reproducible research, forth +;; Homepage: http://orgmode.org + +;; 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 . + +;;; Commentary: + +;; Requires the gforth forth compiler and `forth-mode' (see below). +;; https://www.gnu.org/software/gforth/ + +;;; Requirements: + +;; Session evaluation requires the gforth forth compiler as well as +;; `forth-mode' which is distributed with gforth (in gforth.el). + +;;; Code: +(require 'ob) + +(declare-function forth-proc "ext:gforth" ()) +(declare-function org-trim "org" (s &optional keep-lead)) + +(defvar org-babel-default-header-args:forth '((:session . "yes")) + "Default header arguments for forth code blocks.") + +(defun org-babel-execute:forth (body params) + "Execute a block of Forth code with org-babel. +This function is called by `org-babel-execute-src-block'" + (if (string= "none" (cdr (assq :session params))) + (error "Non-session evaluation not supported for Forth code blocks") + (let ((all-results (org-babel-forth-session-execute body params))) + (if (member "output" (cdr (assq :result-params params))) + (mapconcat #'identity all-results "\n") + (car (last all-results)))))) + +(defun org-babel-forth-session-execute (body params) + (require 'forth-mode) + (let ((proc (forth-proc)) + (rx " \\(\n:\\|compiled\n\\\|ok\n\\)") + (result-start)) + (with-current-buffer (process-buffer (forth-proc)) + (mapcar (lambda (line) + (setq result-start (progn (goto-char (process-mark proc)) + (point))) + (comint-send-string proc (concat line "\n")) + ;; wait for forth to say "ok" + (while (not (progn (goto-char result-start) + (re-search-forward rx nil t))) + (accept-process-output proc 0.01)) + (let ((case (match-string 1))) + (cond + ((string= "ok\n" case) + ;; Collect intermediate output. + (buffer-substring (+ result-start 1 (length line)) + (match-beginning 0))) + ((string= "compiled\n" case)) + ;; Ignore partial compilation. + ((string= "\n:" case) + ;; Report errors. + (org-babel-eval-error-notify 1 + (buffer-substring + (+ (match-beginning 0) 1) (point-max))) nil)))) + (split-string (org-trim + (org-babel-expand-body:generic body params)) + "\n" + 'omit-nulls))))) + +(provide 'ob-forth) + +;;; ob-forth.el ends here diff --git a/lisp/org/ob-fortran.el b/lisp/org/ob-fortran.el index 6a6112df9b..d059245b30 100644 --- a/lisp/org/ob-fortran.el +++ b/lisp/org/ob-fortran.el @@ -1,4 +1,4 @@ -;;; ob-fortran.el --- org-babel functions for fortran +;;; ob-fortran.el --- Babel Functions for Fortran -*- lexical-binding: t; -*- ;; Copyright (C) 2011-2017 Free Software Foundation, Inc. @@ -29,10 +29,12 @@ ;;; Code: (require 'ob) (require 'cc-mode) +(require 'cl-lib) (declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) -(declare-function org-every "org" (pred seq)) +(declare-function org-remove-indentation "org" (code &optional n)) +(declare-function org-trim "org" (s &optional keep-lead)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("fortran" . "F90")) @@ -47,43 +49,42 @@ "This function should only be called by `org-babel-execute:fortran'" (let* ((tmp-src-file (org-babel-temp-file "fortran-src-" ".F90")) (tmp-bin-file (org-babel-temp-file "fortran-bin-" org-babel-exeext)) - (cmdline (cdr (assoc :cmdline params))) - (flags (cdr (assoc :flags params))) - (full-body (org-babel-expand-body:fortran body params)) - (compile - (progn - (with-temp-file tmp-src-file (insert full-body)) - (org-babel-eval - (format "%s -o %s %s %s" - org-babel-fortran-compiler - (org-babel-process-file-name tmp-bin-file) - (mapconcat 'identity - (if (listp flags) flags (list flags)) " ") - (org-babel-process-file-name tmp-src-file)) "")))) + (cmdline (cdr (assq :cmdline params))) + (flags (cdr (assq :flags params))) + (full-body (org-babel-expand-body:fortran body params))) + (with-temp-file tmp-src-file (insert full-body)) + (org-babel-eval + (format "%s -o %s %s %s" + org-babel-fortran-compiler + (org-babel-process-file-name tmp-bin-file) + (mapconcat 'identity + (if (listp flags) flags (list flags)) " ") + (org-babel-process-file-name tmp-src-file)) "") (let ((results - (org-babel-trim - (org-babel-eval - (concat tmp-bin-file (if cmdline (concat " " cmdline) "")) "")))) + (org-trim + (org-remove-indentation + (org-babel-eval + (concat tmp-bin-file (if cmdline (concat " " cmdline) "")) ""))))) (org-babel-reassemble-table - (org-babel-result-cond (cdr (assoc :result-params params)) + (org-babel-result-cond (cdr (assq :result-params params)) (org-babel-read results) (let ((tmp-file (org-babel-temp-file "f-"))) (with-temp-file tmp-file (insert results)) (org-babel-import-elisp-from-file tmp-file))) (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params))))))) + (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))) (defun org-babel-expand-body:fortran (body params) "Expand a block of fortran or fortran code with org-babel according to -it's header arguments." - (let ((vars (mapcar #'cdr (org-babel-get-header params :var))) - (main-p (not (string= (cdr (assoc :main params)) "no"))) - (includes (or (cdr (assoc :includes params)) +its header arguments." + (let ((vars (org-babel--get-vars params)) + (main-p (not (string= (cdr (assq :main params)) "no"))) + (includes (or (cdr (assq :includes params)) (org-babel-read (org-entry-get nil "includes" t)))) (defines (org-babel-read - (or (cdr (assoc :defines params)) + (or (cdr (assq :defines params)) (org-babel-read (org-entry-get nil "defines" t)))))) (mapconcat 'identity (list @@ -107,17 +108,17 @@ it's header arguments." (defun org-babel-fortran-ensure-main-wrap (body params) "Wrap body in a \"program ... end program\" block if none exists." (if (string-match "^[ \t]*program[ \t]*.*" (capitalize body)) - (let ((vars (mapcar #'cdr (org-babel-get-header params :var)))) + (let ((vars (org-babel--get-vars params))) (if vars (error "Cannot use :vars if `program' statement is present")) body) (format "program main\n%s\nend program main\n" body))) -(defun org-babel-prep-session:fortran (session params) +(defun org-babel-prep-session:fortran (_session _params) "This function does nothing as fortran is a compiled language with no support for sessions" (error "Fortran is a compiled languages -- no support for sessions")) -(defun org-babel-load-session:fortran (session body params) +(defun org-babel-load-session:fortran (_session _body _params) "This function does nothing as fortran is a compiled language with no support for sessions" (error "Fortran is a compiled languages -- no support for sessions")) @@ -145,7 +146,7 @@ of the same value." (format "character(len=%d), parameter :: %S = '%s'\n" (length val) var val)) ;; val is a matrix - ((and (listp val) (org-every #'listp val)) + ((and (listp val) (cl-every #'listp val)) (format "real, parameter :: %S(%d,%d) = transpose( reshape( %s , (/ %d, %d /) ) )\n" var (length val) (length (car val)) (org-babel-fortran-transform-list val) diff --git a/lisp/org/ob-gnuplot.el b/lisp/org/ob-gnuplot.el index 82b103e52c..400823b2d7 100644 --- a/lisp/org/ob-gnuplot.el +++ b/lisp/org/ob-gnuplot.el @@ -1,4 +1,4 @@ -;;; ob-gnuplot.el --- org-babel functions for gnuplot evaluation +;;; ob-gnuplot.el --- Babel Functions for Gnuplot -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -39,12 +39,10 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) (declare-function org-time-string-to-time "org" (s &optional buffer pos)) (declare-function org-combine-plists "org" (&rest plists)) -(declare-function orgtbl-to-generic "org-table" - (table params &optional backend)) +(declare-function orgtbl-to-generic "org-table" (table params)) (declare-function gnuplot-mode "ext:gnuplot-mode" ()) (declare-function gnuplot-send-string-to-gnuplot "ext:gnuplot-mode" (str txt)) (declare-function gnuplot-send-buffer-to-gnuplot "ext:gnuplot-mode" ()) @@ -65,7 +63,7 @@ (term . :any)) "Gnuplot specific header args.") -(defvar org-babel-gnuplot-timestamp-fmt nil) +(defvar org-babel-gnuplot-timestamp-fmt nil) ; Dynamically scoped. (defvar *org-babel-gnuplot-missing* nil) @@ -81,7 +79,7 @@ Dumps all vectors into files and returns an association list of variable names and the related value to be used in the gnuplot code." - (let ((*org-babel-gnuplot-missing* (cdr (assoc :missing params)))) + (let ((*org-babel-gnuplot-missing* (cdr (assq :missing params)))) (mapcar (lambda (pair) (cons @@ -95,38 +93,33 @@ code." (if tablep val (mapcar 'list val))) (org-babel-temp-file "gnuplot-") params) val)))) - (mapcar #'cdr (org-babel-get-header params :var))))) + (org-babel--get-vars params)))) (defun org-babel-expand-body:gnuplot (body params) "Expand BODY according to PARAMS, return the expanded body." (save-window-excursion (let* ((vars (org-babel-gnuplot-process-vars params)) - (out-file (cdr (assoc :file params))) - (prologue (cdr (assoc :prologue params))) - (epilogue (cdr (assoc :epilogue params))) - (term (or (cdr (assoc :term params)) + (out-file (cdr (assq :file params))) + (prologue (cdr (assq :prologue params))) + (epilogue (cdr (assq :epilogue params))) + (term (or (cdr (assq :term params)) (when out-file (let ((ext (file-name-extension out-file))) (or (cdr (assoc (intern (downcase ext)) *org-babel-gnuplot-terms*)) ext))))) - (cmdline (cdr (assoc :cmdline params))) - (title (cdr (assoc :title params))) - (lines (cdr (assoc :line params))) - (sets (cdr (assoc :set params))) - (x-labels (cdr (assoc :xlabels params))) - (y-labels (cdr (assoc :ylabels params))) - (timefmt (cdr (assoc :timefmt params))) - (time-ind (or (cdr (assoc :timeind params)) + (title (cdr (assq :title params))) + (lines (cdr (assq :line params))) + (sets (cdr (assq :set params))) + (x-labels (cdr (assq :xlabels params))) + (y-labels (cdr (assq :ylabels params))) + (timefmt (cdr (assq :timefmt params))) + (time-ind (or (cdr (assq :timeind params)) (when timefmt 1))) - (missing (cdr (assoc :missing params))) - (add-to-body (lambda (text) (setq body (concat text "\n" body)))) - output) + (add-to-body (lambda (text) (setq body (concat text "\n" body))))) ;; append header argument settings to body (when title (funcall add-to-body (format "set title '%s'" title))) (when lines (mapc (lambda (el) (funcall add-to-body el)) lines)) - (when missing - (funcall add-to-body (format "set datafile missing '%s'" missing))) (when sets (mapc (lambda (el) (funcall add-to-body (format "set %s" el))) sets)) (when x-labels @@ -175,9 +168,8 @@ code." "Execute a block of Gnuplot code. This function is called by `org-babel-execute-src-block'." (require 'gnuplot) - (let ((session (cdr (assoc :session params))) - (result-type (cdr (assoc :results params))) - (out-file (cdr (assoc :file params))) + (let ((session (cdr (assq :session params))) + (result-type (cdr (assq :results params))) (body (org-babel-expand-body:gnuplot body params)) output) (save-window-excursion @@ -195,7 +187,7 @@ This function is called by `org-babel-execute-src-block'." script-file (if (member system-type '(cygwin windows-nt ms-dos)) t nil))))) - (message output)) + (message "%s" output)) (with-temp-buffer (insert (concat body "\n")) (gnuplot-mode) @@ -210,10 +202,12 @@ This function is called by `org-babel-execute-src-block'." (var-lines (org-babel-variable-assignments:gnuplot params))) (message "%S" session) (org-babel-comint-in-buffer session - (mapc (lambda (var-line) - (insert var-line) (comint-send-input nil t) - (org-babel-comint-wait-for-output session) - (sit-for .1) (goto-char (point-max))) var-lines)) + (dolist (var-line var-lines) + (insert var-line) + (comint-send-input nil t) + (org-babel-comint-wait-for-output session) + (sit-for .1) + (goto-char (point-max)))) session)) (defun org-babel-load-session:gnuplot (session body params) @@ -232,7 +226,7 @@ This function is called by `org-babel-execute-src-block'." (org-babel-gnuplot-process-vars params))) (defvar gnuplot-buffer) -(defun org-babel-gnuplot-initiate-session (&optional session params) +(defun org-babel-gnuplot-initiate-session (&optional session _params) "Initiate a gnuplot session. If there is not a current inferior-process-buffer in SESSION then create one. Return the initialized session. The current @@ -268,15 +262,13 @@ then create one. Return the initialized session. The current "Export TABLE to DATA-FILE in a format readable by gnuplot. Pass PARAMS through to `orgtbl-to-generic' when exporting TABLE." (with-temp-file data-file - (make-local-variable 'org-babel-gnuplot-timestamp-fmt) - (setq org-babel-gnuplot-timestamp-fmt (or - (plist-get params :timefmt) - "%Y-%m-%d-%H:%M:%S")) - (insert (orgtbl-to-generic - table - (org-combine-plists - '(:sep "\t" :fmt org-babel-gnuplot-quote-tsv-field) - params)))) + (insert (let ((org-babel-gnuplot-timestamp-fmt + (or (plist-get params :timefmt) "%Y-%m-%d-%H:%M:%S"))) + (orgtbl-to-generic + table + (org-combine-plists + '(:sep "\t" :fmt org-babel-gnuplot-quote-tsv-field) + params))))) data-file) (provide 'ob-gnuplot) diff --git a/lisp/org/ob-groovy.el b/lisp/org/ob-groovy.el new file mode 100644 index 0000000000..69993c0f6a --- /dev/null +++ b/lisp/org/ob-groovy.el @@ -0,0 +1,116 @@ +;;; ob-groovy.el --- Babel Functions for Groovy -*- lexical-binding: t; -*- + +;; Copyright (C) 2013-2017 Free Software Foundation, Inc. + +;; Author: Miro Bezjak +;; Keywords: literate programming, reproducible research +;; Homepage: http://orgmode.org + +;; 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 . + +;;; Commentary: +;; Currently only supports the external execution. No session support yet. + +;;; Requirements: +;; - Groovy language :: http://groovy.codehaus.org +;; - Groovy major mode :: Can be installed from MELPA or +;; https://github.com/russel/Emacs-Groovy-Mode + +;;; Code: +(require 'ob) + +(defvar org-babel-tangle-lang-exts) ;; Autoloaded +(add-to-list 'org-babel-tangle-lang-exts '("groovy" . "groovy")) +(defvar org-babel-default-header-args:groovy '()) +(defcustom org-babel-groovy-command "groovy" + "Name of the command to use for executing Groovy code. +May be either a command in the path, like groovy +or an absolute path name, like /usr/local/bin/groovy +parameters may be used, like groovy -v" + :group 'org-babel + :version "24.3" + :type 'string) + +(defun org-babel-execute:groovy (body params) + "Execute a block of Groovy code with org-babel. This function is +called by `org-babel-execute-src-block'" + (message "executing Groovy source code block") + (let* ((processed-params (org-babel-process-params params)) + (session (org-babel-groovy-initiate-session (nth 0 processed-params))) + (result-params (nth 2 processed-params)) + (result-type (cdr (assq :result-type params))) + (full-body (org-babel-expand-body:generic + body params)) + (result (org-babel-groovy-evaluate + session full-body result-type result-params))) + + (org-babel-reassemble-table + result + (org-babel-pick-name + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) + (org-babel-pick-name + (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) + +(defvar org-babel-groovy-wrapper-method + + "class Runner extends Script { + def out = new PrintWriter(new ByteArrayOutputStream()) + def run() { %s } +} + +println(new Runner().run()) +") + + +(defun org-babel-groovy-evaluate + (session body &optional result-type result-params) + "Evaluate BODY in external Groovy process. +If RESULT-TYPE equals `output' then return standard output as a string. +If RESULT-TYPE equals `value' then return the value of the last statement +in BODY as elisp." + (when session (error "Sessions are not (yet) supported for Groovy")) + (pcase result-type + (`output + (let ((src-file (org-babel-temp-file "groovy-"))) + (progn (with-temp-file src-file (insert body)) + (org-babel-eval + (concat org-babel-groovy-command " " src-file) "")))) + (`value + (let* ((src-file (org-babel-temp-file "groovy-")) + (wrapper (format org-babel-groovy-wrapper-method body))) + (with-temp-file src-file (insert wrapper)) + (let ((raw (org-babel-eval + (concat org-babel-groovy-command " " src-file) ""))) + (org-babel-result-cond result-params + raw + (org-babel-script-escape raw))))))) + + +(defun org-babel-prep-session:groovy (_session _params) + "Prepare SESSION according to the header arguments specified in PARAMS." + (error "Sessions are not (yet) supported for Groovy")) + +(defun org-babel-groovy-initiate-session (&optional _session) + "If there is not a current inferior-process-buffer in SESSION +then create. Return the initialized session. Sessions are not +supported in Groovy." + nil) + +(provide 'ob-groovy) + + + +;;; ob-groovy.el ends here diff --git a/lisp/org/ob-haskell.el b/lisp/org/ob-haskell.el index ce6b8edbeb..ecce6dcd5d 100644 --- a/lisp/org/ob-haskell.el +++ b/lisp/org/ob-haskell.el @@ -1,4 +1,4 @@ -;;; ob-haskell.el --- org-babel functions for haskell evaluation +;;; ob-haskell.el --- Babel Functions for Haskell -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -41,9 +41,9 @@ ;;; Code: (require 'ob) (require 'comint) -(eval-when-compile (require 'cl)) (declare-function org-remove-indentation "org" (code &optional n)) +(declare-function org-trim "org" (s &optional keep-lead)) (declare-function haskell-mode "ext:haskell-mode" ()) (declare-function run-haskell "ext:inf-haskell" (&optional arg)) (declare-function inferior-haskell-load-file @@ -61,42 +61,35 @@ (defun org-babel-execute:haskell (body params) "Execute a block of Haskell code." - (let* ((session (cdr (assoc :session params))) - (vars (mapcar #'cdr (org-babel-get-header params :var))) - (result-type (cdr (assoc :result-type params))) + (let* ((session (cdr (assq :session params))) + (result-type (cdr (assq :result-type params))) (full-body (org-babel-expand-body:generic body params (org-babel-variable-assignments:haskell params))) (session (org-babel-haskell-initiate-session session params)) (raw (org-babel-comint-with-output (session org-babel-haskell-eoe t full-body) - (insert (org-babel-trim full-body)) + (insert (org-trim full-body)) (comint-send-input nil t) (insert org-babel-haskell-eoe) (comint-send-input nil t))) (results (mapcar - #'org-babel-haskell-read-string + #'org-babel-strip-quotes (cdr (member org-babel-haskell-eoe - (reverse (mapcar #'org-babel-trim raw))))))) + (reverse (mapcar #'org-trim raw))))))) (org-babel-reassemble-table (let ((result - (case result-type - (output (mapconcat #'identity (reverse (cdr results)) "\n")) - (value (car results))))) - (org-babel-result-cond (cdr (assoc :result-params params)) - result (org-babel-haskell-table-or-string result))) - (org-babel-pick-name (cdr (assoc :colname-names params)) - (cdr (assoc :colname-names params))) - (org-babel-pick-name (cdr (assoc :rowname-names params)) - (cdr (assoc :rowname-names params)))))) - -(defun org-babel-haskell-read-string (string) - "Strip \\\"s from around a haskell string." - (if (string-match "^\"\\([^\000]+\\)\"$" string) - (match-string 1 string) - string)) - -(defun org-babel-haskell-initiate-session (&optional session params) + (pcase result-type + (`output (mapconcat #'identity (reverse (cdr results)) "\n")) + (`value (car results))))) + (org-babel-result-cond (cdr (assq :result-params params)) + result (org-babel-script-escape result))) + (org-babel-pick-name (cdr (assq :colname-names params)) + (cdr (assq :colname-names params))) + (org-babel-pick-name (cdr (assq :rowname-names params)) + (cdr (assq :rowname-names params)))))) + +(defun org-babel-haskell-initiate-session (&optional _session _params) "Initiate a haskell session. If there is not a current inferior-process-buffer in SESSION then create one. Return the initialized session." @@ -131,13 +124,7 @@ then create one. Return the initialized session." (format "let %s = %s" (car pair) (org-babel-haskell-var-to-haskell (cdr pair)))) - (mapcar #'cdr (org-babel-get-header params :var)))) - -(defun org-babel-haskell-table-or-string (results) - "Convert RESULTS to an Emacs-lisp table or string. -If RESULTS look like a table, then convert them into an -Emacs-lisp table, otherwise return the results as a string." - (org-babel-script-escape results)) + (org-babel--get-vars params))) (defun org-babel-haskell-var-to-haskell (var) "Convert an elisp value VAR into a haskell variable. @@ -157,7 +144,7 @@ specifying a variable of the same value." When called with a prefix argument the resulting .lhs file will be exported to a .tex file. This function will create two new files, base-name.lhs and base-name.tex where -base-name is the name of the current org-mode file. +base-name is the name of the current Org file. Note that all standard Babel literate programming constructs (header arguments, no-web syntax etc...) are ignored." @@ -185,12 +172,12 @@ constructs (header arguments, no-web syntax etc...) are ignored." (save-match-data (setq indentation (length (match-string 1)))) (replace-match (save-match-data (concat - "#+begin_latex\n\\begin{code}\n" + "#+begin_export latex\n\\begin{code}\n" (if (or preserve-indentp (string-match "-i" (match-string 2))) (match-string 3) (org-remove-indentation (match-string 3))) - "\n\\end{code}\n#+end_latex\n")) + "\n\\end{code}\n#+end_export\n")) t t) (indent-code-rigidly (match-beginning 0) (match-end 0) indentation))) (save-excursion diff --git a/lisp/org/ob-io.el b/lisp/org/ob-io.el index 1d3a42aa38..5dd611098e 100644 --- a/lisp/org/ob-io.el +++ b/lisp/org/ob-io.el @@ -1,4 +1,4 @@ -;;; ob-io.el --- org-babel functions for Io evaluation +;;; ob-io.el --- Babel Functions for Io -*- lexical-binding: t; -*- ;; Copyright (C) 2012-2017 Free Software Foundation, Inc. @@ -33,7 +33,6 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) (defvar org-babel-tangle-lang-exts) ;; Autoloaded (add-to-list 'org-babel-tangle-lang-exts '("io" . "io")) @@ -47,9 +46,8 @@ called by `org-babel-execute-src-block'" (message "executing Io source code block") (let* ((processed-params (org-babel-process-params params)) (session (org-babel-io-initiate-session (nth 0 processed-params))) - (vars (nth 1 processed-params)) (result-params (nth 2 processed-params)) - (result-type (cdr (assoc :result-type params))) + (result-type (cdr (assq :result-type params))) (full-body (org-babel-expand-body:generic body params)) (result (org-babel-io-evaluate @@ -58,17 +56,9 @@ called by `org-babel-execute-src-block'" (org-babel-reassemble-table result (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params)))))) - - -(defun org-babel-io-table-or-string (results) - "Convert RESULTS into an appropriate elisp value. -If RESULTS look like a table, then convert them into an -Emacs-lisp table, otherwise return the results as a string." - (org-babel-script-escape results)) - + (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) (defvar org-babel-io-wrapper-method "( @@ -79,33 +69,33 @@ Emacs-lisp table, otherwise return the results as a string." (defun org-babel-io-evaluate (session body &optional result-type result-params) "Evaluate BODY in external Io process. -If RESULT-TYPE equals 'output then return standard output as a string. -If RESULT-TYPE equals 'value then return the value of the last statement +If RESULT-TYPE equals `output' then return standard output as a string. +If RESULT-TYPE equals `value' then return the value of the last statement in BODY as elisp." (when session (error "Sessions are not (yet) supported for Io")) - (case result-type - (output + (pcase result-type + (`output (if (member "repl" result-params) (org-babel-eval org-babel-io-command body) (let ((src-file (org-babel-temp-file "io-"))) (progn (with-temp-file src-file (insert body)) (org-babel-eval (concat org-babel-io-command " " src-file) ""))))) - (value (let* ((src-file (org-babel-temp-file "io-")) - (wrapper (format org-babel-io-wrapper-method body))) - (with-temp-file src-file (insert wrapper)) - (let ((raw (org-babel-eval - (concat org-babel-io-command " " src-file) ""))) - (org-babel-result-cond result-params - raw - (org-babel-io-table-or-string raw))))))) + (`value (let* ((src-file (org-babel-temp-file "io-")) + (wrapper (format org-babel-io-wrapper-method body))) + (with-temp-file src-file (insert wrapper)) + (let ((raw (org-babel-eval + (concat org-babel-io-command " " src-file) ""))) + (org-babel-result-cond result-params + raw + (org-babel-script-escape raw))))))) -(defun org-babel-prep-session:io (session params) +(defun org-babel-prep-session:io (_session _params) "Prepare SESSION according to the header arguments specified in PARAMS." (error "Sessions are not (yet) supported for Io")) -(defun org-babel-io-initiate-session (&optional session) +(defun org-babel-io-initiate-session (&optional _session) "If there is not a current inferior-process-buffer in SESSION then create. Return the initialized session. Sessions are not supported in Io." diff --git a/lisp/org/ob-java.el b/lisp/org/ob-java.el index 70a10e0131..7e720231e4 100644 --- a/lisp/org/ob-java.el +++ b/lisp/org/ob-java.el @@ -1,4 +1,4 @@ -;;; ob-java.el --- org-babel functions for java evaluation +;;; ob-java.el --- Babel Functions for Java -*- lexical-binding: t; -*- ;; Copyright (C) 2011-2017 Free Software Foundation, Inc. @@ -32,41 +32,51 @@ (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("java" . "java")) -(defvar org-babel-java-command "java" - "Name of the java command.") - -(defvar org-babel-java-compiler "javac" - "Name of the java compiler.") +(defcustom org-babel-java-command "java" + "Name of the java command. +May be either a command in the path, like java +or an absolute path name, like /usr/local/bin/java +parameters may be used, like java -verbose" + :group 'org-babel + :version "24.3" + :type 'string) + +(defcustom org-babel-java-compiler "javac" + "Name of the java compiler. +May be either a command in the path, like javac +or an absolute path name, like /usr/local/bin/javac +parameters may be used, like javac -verbose" + :group 'org-babel + :version "24.3" + :type 'string) (defun org-babel-execute:java (body params) - (let* ((classname (or (cdr (assoc :classname params)) + (let* ((classname (or (cdr (assq :classname params)) (error "Can't compile a java block without a classname"))) (packagename (file-name-directory classname)) (src-file (concat classname ".java")) - (cmpflag (or (cdr (assoc :cmpflag params)) "")) - (cmdline (or (cdr (assoc :cmdline params)) "")) - (full-body (org-babel-expand-body:generic body params)) - (compile - (progn (with-temp-file src-file (insert full-body)) - (org-babel-eval - (concat org-babel-java-compiler - " " cmpflag " " src-file) "")))) + (cmpflag (or (cdr (assq :cmpflag params)) "")) + (cmdline (or (cdr (assq :cmdline params)) "")) + (full-body (org-babel-expand-body:generic body params))) + (with-temp-file src-file (insert full-body)) + (org-babel-eval + (concat org-babel-java-compiler " " cmpflag " " src-file) "") ;; created package-name directories if missing (unless (or (not packagename) (file-exists-p packagename)) (make-directory packagename 'parents)) (let ((results (org-babel-eval (concat org-babel-java-command " " cmdline " " classname) ""))) (org-babel-reassemble-table - (org-babel-result-cond (cdr (assoc :result-params params)) + (org-babel-result-cond (cdr (assq :result-params params)) (org-babel-read results) (let ((tmp-file (org-babel-temp-file "c-"))) (with-temp-file tmp-file (insert results)) (org-babel-import-elisp-from-file tmp-file))) (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params))))))) + (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))) (provide 'ob-java) diff --git a/lisp/org/ob-js.el b/lisp/org/ob-js.el index f4f8116dfd..91be6b0735 100644 --- a/lisp/org/ob-js.el +++ b/lisp/org/ob-js.el @@ -1,4 +1,4 @@ -;;; ob-js.el --- org-babel functions for Javascript +;;; ob-js.el --- Babel Functions for Javascript -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -39,7 +39,6 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) (declare-function run-mozilla "ext:moz" (arg)) @@ -56,20 +55,20 @@ :type 'string) (defvar org-babel-js-function-wrapper - "require('sys').print(require('sys').inspect(function(){%s}()));" + "require('sys').print(require('sys').inspect(function(){\n%s\n}()));" "Javascript code to print value of body.") (defun org-babel-execute:js (body params) "Execute a block of Javascript code with org-babel. This function is called by `org-babel-execute-src-block'" - (let* ((org-babel-js-cmd (or (cdr (assoc :cmd params)) org-babel-js-cmd)) - (result-type (cdr (assoc :result-type params))) + (let* ((org-babel-js-cmd (or (cdr (assq :cmd params)) org-babel-js-cmd)) + (result-type (cdr (assq :result-type params))) (full-body (org-babel-expand-body:generic body params (org-babel-variable-assignments:js params))) - (result (if (not (string= (cdr (assoc :session params)) "none")) + (result (if (not (string= (cdr (assq :session params)) "none")) ;; session evaluation (let ((session (org-babel-prep-session:js - (cdr (assoc :session params)) params))) + (cdr (assq :session params)) params))) (nth 1 (org-babel-comint-with-output (session (format "%S" org-babel-js-eoe) t body) @@ -89,7 +88,7 @@ This function is called by `org-babel-execute-src-block'" (org-babel-eval (format "%s %s" org-babel-js-cmd (org-babel-process-file-name script-file)) ""))))) - (org-babel-result-cond (cdr (assoc :result-params params)) + (org-babel-result-cond (cdr (assq :result-params params)) result (org-babel-js-read result)))) (defun org-babel-js-read (results) @@ -97,14 +96,17 @@ This function is called by `org-babel-execute-src-block'" If RESULTS look like a table, then convert them into an Emacs-lisp table, otherwise return the results as a string." (org-babel-read - (if (and (stringp results) (string-match "^\\[.+\\]$" results)) + (if (and (stringp results) + (string-prefix-p "[" results) + (string-suffix-p "]" results)) (org-babel-read (concat "'" (replace-regexp-in-string "\\[" "(" (replace-regexp-in-string "\\]" ")" (replace-regexp-in-string - ", " " " (replace-regexp-in-string - "'" "\"" results)))))) + ",[[:space:]]" " " + (replace-regexp-in-string + "'" "\"" results)))))) results))) (defun org-babel-js-var-to-js (var) @@ -113,7 +115,7 @@ Convert an elisp value into a string of js source code specifying a variable of the same value." (if (listp var) (concat "[" (mapconcat #'org-babel-js-var-to-js var ", ") "]") - (format "%S" var))) + (replace-regexp-in-string "\n" "\\\\n" (format "%S" var)))) (defun org-babel-prep-session:js (session params) "Prepare SESSION according to the header arguments specified in PARAMS." @@ -133,7 +135,7 @@ specifying a variable of the same value." (mapcar (lambda (pair) (format "var %s=%s;" (car pair) (org-babel-js-var-to-js (cdr pair)))) - (mapcar #'cdr (org-babel-get-header params :var)))) + (org-babel--get-vars params))) (defun org-babel-js-initiate-session (&optional session) "If there is not a current inferior-process-buffer in SESSION diff --git a/lisp/org/ob-keys.el b/lisp/org/ob-keys.el index b71fba416f..f5fb910123 100644 --- a/lisp/org/ob-keys.el +++ b/lisp/org/ob-keys.el @@ -1,4 +1,4 @@ -;;; ob-keys.el --- key bindings for org-babel +;;; ob-keys.el --- Key Bindings for Babel -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -23,8 +23,8 @@ ;;; Commentary: -;; Add org-babel keybindings to the org-mode keymap for exposing -;; org-babel functions. These will all share a common prefix. See +;; Add Org Babel keybindings to the Org mode keymap for exposing +;; Org Babel functions. These will all share a common prefix. See ;; the value of `org-babel-key-bindings' for a list of interactive ;; functions and their associated keys. @@ -89,6 +89,7 @@ functions which are assigned key bindings, and see ("h" . org-babel-describe-bindings) ("\C-x" . org-babel-do-key-sequence-in-edit-buffer) ("x" . org-babel-do-key-sequence-in-edit-buffer) + ("k" . org-babel-remove-result-one-or-many) ("\C-\M-h" . org-babel-mark-block)) "Alist of key bindings and interactive Babel functions. This list associates interactive Babel functions diff --git a/lisp/org/ob-latex.el b/lisp/org/ob-latex.el index d00827645e..763ffb16ff 100644 --- a/lisp/org/ob-latex.el +++ b/lisp/org/ob-latex.el @@ -1,4 +1,4 @@ -;;; ob-latex.el --- org-babel functions for latex "evaluation" +;;; ob-latex.el --- Babel Functions for LaTeX -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -32,12 +32,11 @@ ;;; Code: (require 'ob) -(declare-function org-create-formula-image "org" - (string tofile options buffer &optional type)) -(declare-function org-splice-latex-header "org" - (tpl def-pkg pkg snippets-p &optional extra)) -(declare-function org-latex-guess-inputenc "ox-latex" (header)) +(declare-function org-create-formula-image "org" (string tofile options buffer &optional type)) (declare-function org-latex-compile "ox-latex" (texfile &optional snippet)) +(declare-function org-latex-guess-inputenc "ox-latex" (header)) +(declare-function org-splice-latex-header "org" (tpl def-pkg pkg snippets-p &optional extra)) +(declare-function org-trim "org" (s &optional keep-lead)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("latex" . "tex")) @@ -51,7 +50,22 @@ '((:results . "latex") (:exports . "results")) "Default arguments to use when evaluating a LaTeX source block.") -(defcustom org-babel-latex-htlatex "" +(defconst org-babel-header-args:latex + '((border . :any) + (fit . :any) + (imagemagick . ((nil t))) + (iminoptions . :any) + (imoutoptions . :any) + (packages . :any) + (pdfheight . :any) + (pdfpng . :any) + (pdfwidth . :any) + (headers . :any) + (packages . :any) + (buffer . ((yes no)))) + "LaTeX-specific header arguments.") + +(defcustom org-babel-latex-htlatex "htlatex" "The htlatex command to enable conversion of latex to SVG or HTML." :group 'org-babel :type 'string) @@ -70,37 +84,82 @@ (regexp-quote (format "%S" (car pair))) (if (stringp (cdr pair)) (cdr pair) (format "%S" (cdr pair))) - body))) (mapcar #'cdr (org-babel-get-header params :var))) - (org-babel-trim body)) + body))) (org-babel--get-vars params)) + (org-trim body)) (defun org-babel-execute:latex (body params) "Execute a block of Latex code with Babel. This function is called by `org-babel-execute-src-block'." (setq body (org-babel-expand-body:latex body params)) - (if (cdr (assoc :file params)) - (let* ((out-file (cdr (assoc :file params))) + (if (cdr (assq :file params)) + (let* ((out-file (cdr (assq :file params))) + (extension (file-name-extension out-file)) (tex-file (org-babel-temp-file "latex-" ".tex")) - (border (cdr (assoc :border params))) - (imagemagick (cdr (assoc :imagemagick params))) - (im-in-options (cdr (assoc :iminoptions params))) - (im-out-options (cdr (assoc :imoutoptions params))) - (pdfpng (cdr (assoc :pdfpng params))) - (fit (or (cdr (assoc :fit params)) border)) - (height (and fit (cdr (assoc :pdfheight params)))) - (width (and fit (cdr (assoc :pdfwidth params)))) - (headers (cdr (assoc :headers params))) - (in-buffer (not (string= "no" (cdr (assoc :buffer params))))) + (border (cdr (assq :border params))) + (imagemagick (cdr (assq :imagemagick params))) + (im-in-options (cdr (assq :iminoptions params))) + (im-out-options (cdr (assq :imoutoptions params))) + (fit (or (cdr (assq :fit params)) border)) + (height (and fit (cdr (assq :pdfheight params)))) + (width (and fit (cdr (assq :pdfwidth params)))) + (headers (cdr (assq :headers params))) + (in-buffer (not (string= "no" (cdr (assq :buffer params))))) (org-latex-packages-alist - (append (cdr (assoc :packages params)) org-latex-packages-alist))) + (append (cdr (assq :packages params)) org-latex-packages-alist))) (cond - ((and (string-match "\\.png$" out-file) (not imagemagick)) + ((and (string-suffix-p ".png" out-file) (not imagemagick)) (org-create-formula-image body out-file org-format-latex-options in-buffer)) - ((string-match "\\.tikz$" out-file) + ((string-suffix-p ".tikz" out-file) (when (file-exists-p out-file) (delete-file out-file)) (with-temp-file out-file (insert body))) - ((or (string-match "\\.pdf$" out-file) imagemagick) + ((and (or (string= "svg" extension) + (string= "html" extension)) + (executable-find org-babel-latex-htlatex)) + ;; TODO: this is a very different way of generating the + ;; frame latex document than in the pdf case. Ideally, both + ;; would be unified. This would prevent bugs creeping in + ;; such as the one fixed on Aug 16 2014 whereby :headers was + ;; not included in the SVG/HTML case. + (with-temp-file tex-file + (insert (concat + "\\documentclass[preview]{standalone} +\\def\\pgfsysdriver{pgfsys-tex4ht.def} +" + (mapconcat (lambda (pkg) + (concat "\\usepackage" pkg)) + org-babel-latex-htlatex-packages + "\n") + (if headers + (concat "\n" + (if (listp headers) + (mapconcat #'identity headers "\n") + headers) "\n") + "") + "\\begin{document}" + body + "\\end{document}"))) + (when (file-exists-p out-file) (delete-file out-file)) + (let ((default-directory (file-name-directory tex-file))) + (shell-command (format "%s %s" org-babel-latex-htlatex tex-file))) + (cond + ((file-exists-p (concat (file-name-sans-extension tex-file) "-1.svg")) + (if (string-suffix-p ".svg" out-file) + (progn + (shell-command "pwd") + (shell-command (format "mv %s %s" + (concat (file-name-sans-extension tex-file) "-1.svg") + out-file))) + (error "SVG file produced but HTML file requested"))) + ((file-exists-p (concat (file-name-sans-extension tex-file) ".html")) + (if (string-suffix-p ".html" out-file) + (shell-command "mv %s %s" + (concat (file-name-sans-extension tex-file) + ".html") + out-file) + (error "HTML file produced but SVG file requested"))))) + ((or (string= "pdf" extension) imagemagick) (with-temp-file tex-file (require 'ox-latex) (insert @@ -133,54 +192,20 @@ This function is called by `org-babel-execute-src-block'." (when (file-exists-p out-file) (delete-file out-file)) (let ((transient-pdf-file (org-babel-latex-tex-to-pdf tex-file))) (cond - ((string-match "\\.pdf$" out-file) + ((string= "pdf" extension) (rename-file transient-pdf-file out-file)) (imagemagick - (convert-pdf + (org-babel-latex-convert-pdf transient-pdf-file out-file im-in-options im-out-options) (when (file-exists-p transient-pdf-file) - (delete-file transient-pdf-file)))))) - ((and (or (string-match "\\.svg$" out-file) - (string-match "\\.html$" out-file)) - (not (string= "" org-babel-latex-htlatex))) - (with-temp-file tex-file - (insert (concat - "\\documentclass[preview]{standalone} -\\def\\pgfsysdriver{pgfsys-tex4ht.def} -" - (mapconcat (lambda (pkg) - (concat "\\usepackage" pkg)) - org-babel-latex-htlatex-packages - "\n") - "\\begin{document}" - body - "\\end{document}"))) - (when (file-exists-p out-file) (delete-file out-file)) - (let ((default-directory (file-name-directory tex-file))) - (shell-command (format "%s %s" org-babel-latex-htlatex tex-file))) - (cond - ((file-exists-p (concat (file-name-sans-extension tex-file) "-1.svg")) - (if (string-match "\\.svg$" out-file) - (progn - (shell-command "pwd") - (shell-command (format "mv %s %s" - (concat (file-name-sans-extension tex-file) "-1.svg") - out-file))) - (error "SVG file produced but HTML file requested."))) - ((file-exists-p (concat (file-name-sans-extension tex-file) ".html")) - (if (string-match "\\.html$" out-file) - (shell-command "mv %s %s" - (concat (file-name-sans-extension tex-file) - ".html") - out-file) - (error "HTML file produced but SVG file requested."))))) - ((string-match "\\.\\([^\\.]+\\)$" out-file) - (error "Can not create %s files, please specify a .png or .pdf file or try the :imagemagick header argument" - (match-string 1 out-file)))) + (delete-file transient-pdf-file))) + (t + (error "Can not create %s files, please specify a .png or .pdf file or try the :imagemagick header argument" + extension)))))) nil) ;; signal that output has already been written to file body)) -(defun convert-pdf (pdffile out-file im-in-options im-out-options) +(defun org-babel-latex-convert-pdf (pdffile out-file im-in-options im-out-options) "Generate a file from a pdf file using imagemagick." (let ((cmd (concat "convert " im-in-options " " pdffile " " im-out-options " " out-file))) @@ -192,7 +217,7 @@ This function is called by `org-babel-execute-src-block'." (require 'ox-latex) (org-latex-compile file)) -(defun org-babel-prep-session:latex (session params) +(defun org-babel-prep-session:latex (_session _params) "Return an error because LaTeX doesn't support sessions." (error "LaTeX does not support sessions")) diff --git a/lisp/org/ob-ledger.el b/lisp/org/ob-ledger.el index 154e75c0e0..c02069e283 100644 --- a/lisp/org/ob-ledger.el +++ b/lisp/org/ob-ledger.el @@ -1,4 +1,4 @@ -;;; ob-ledger.el --- org-babel functions for ledger evaluation +;;; ob-ledger.el --- Babel Functions for Ledger -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -46,8 +46,7 @@ "Execute a block of Ledger entries with org-babel. This function is called by `org-babel-execute-src-block'." (message "executing Ledger source code block") - (let ((result-params (split-string (or (cdr (assoc :results params)) ""))) - (cmdline (cdr (assoc :cmdline params))) + (let ((cmdline (cdr (assq :cmdline params))) (in-file (org-babel-temp-file "ledger-")) (out-file (org-babel-temp-file "ledger-output-"))) (with-temp-file in-file (insert body)) @@ -61,7 +60,7 @@ called by `org-babel-execute-src-block'." " > " (org-babel-process-file-name out-file)))) (with-temp-buffer (insert-file-contents out-file) (buffer-string)))) -(defun org-babel-prep-session:ledger (session params) +(defun org-babel-prep-session:ledger (_session _params) (error "Ledger does not support sessions")) (provide 'ob-ledger) diff --git a/lisp/org/ob-lilypond.el b/lisp/org/ob-lilypond.el index b37ecd87a7..37a7a6b57e 100644 --- a/lisp/org/ob-lilypond.el +++ b/lisp/org/ob-lilypond.el @@ -1,4 +1,4 @@ -;;; ob-lilypond.el --- org-babel functions for lilypond evaluation +;;; ob-lilypond.el --- Babel Functions for Lilypond -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -28,6 +28,8 @@ ;; ;; Lilypond documentation can be found at ;; http://lilypond.org/manuals.html +;; +;; This depends on epstopdf --- See http://www.ctan.org/pkg/epstopdf. ;;; Code: (require 'ob) @@ -60,51 +62,68 @@ org-babel-lilypond-play-midi-post-tangle determines whether to automate the playing of the resultant midi file. If the value is nil, the midi file is not automatically played. Default value is t") -(defvar org-babel-lilypond-OSX-ly-path - "/Applications/lilypond.app/Contents/Resources/bin/lilypond") -(defvar org-babel-lilypond-OSX-pdf-path "open") -(defvar org-babel-lilypond-OSX-midi-path "open") - -(defvar org-babel-lilypond-nix-ly-path "/usr/bin/lilypond") -(defvar org-babel-lilypond-nix-pdf-path "evince") -(defvar org-babel-lilypond-nix-midi-path "timidity") - -(defvar org-babel-lilypond-w32-ly-path "lilypond") -(defvar org-babel-lilypond-w32-pdf-path "") -(defvar org-babel-lilypond-w32-midi-path "") +(defvar org-babel-lilypond-ly-command "" + "Command to execute lilypond on your system. +Do not set it directly. Customize `org-babel-lilypond-commands' instead.") +(defvar org-babel-lilypond-pdf-command "" + "Command to show a PDF file on your system. +Do not set it directly. Customize `org-babel-lilypond-commands' instead.") +(defvar org-babel-lilypond-midi-command "" + "Command to play a MIDI file on your system. +Do not set it directly. Customize `org-babel-lilypond-commands' instead.") +(defcustom org-babel-lilypond-commands + (cond + ((eq system-type 'darwin) + '("/Applications/lilypond.app/Contents/Resources/bin/lilypond" "open" "open")) + ((eq system-type 'windows-nt) + '("lilypond" "" "")) + (t + '("lilypond" "xdg-open" "xdg-open"))) + "Commands to run lilypond and view or play the results. +These should be executables that take a filename as an argument. +On some system it is possible to specify the filename directly +and the viewer or player will be determined from the file type; +you can leave the string empty on this case." + :group 'org-babel + :type '(list + (string :tag "Lilypond ") + (string :tag "PDF Viewer ") + (string :tag "MIDI Player")) + :version "24.3" + :package-version '(Org . "8.2.7") + :set + (lambda (_symbol value) + (setq + org-babel-lilypond-ly-command (nth 0 value) + org-babel-lilypond-pdf-command (nth 1 value) + org-babel-lilypond-midi-command (nth 2 value)))) (defvar org-babel-lilypond-gen-png nil - "Image generation (png) can be turned on by default by setting -ORG-BABEL-LILYPOND-GEN-PNG to t") + "Non-nil means image generation (PNG) is turned on by default.") (defvar org-babel-lilypond-gen-svg nil - "Image generation (SVG) can be turned on by default by setting -ORG-BABEL-LILYPOND-GEN-SVG to t") + "Non-nil means image generation (SVG) is be turned on by default.") (defvar org-babel-lilypond-gen-html nil - "HTML generation can be turned on by default by setting -ORG-BABEL-LILYPOND-GEN-HTML to t") + "Non-nil means HTML generation is turned on by default.") (defvar org-babel-lilypond-gen-pdf nil - "PDF generation can be turned on by default by setting -ORG-BABEL-LILYPOND-GEN-PDF to t") + "Non-nil means PDF generation is be turned on by default.") (defvar org-babel-lilypond-use-eps nil - "You can force the compiler to use the EPS backend by setting -ORG-BABEL-LILYPOND-USE-EPS to t") + "Non-nil forces the compiler to use the EPS backend.") (defvar org-babel-lilypond-arrange-mode nil - "Arrange mode is turned on by setting ORG-BABEL-LILYPOND-ARRANGE-MODE -to t. In Arrange mode the following settings are altered -from default... + "Non-nil turns Arrange mode on. +In Arrange mode the following settings are altered from default: :tangle yes, :noweb yes :results silent :comments yes. In addition lilypond block execution causes tangling of all lilypond -blocks") +blocks.") (defun org-babel-expand-body:lilypond (body params) "Expand BODY according to PARAMS, return the expanded body." - (let ((vars (mapcar #'cdr (org-babel-get-header params :var)))) + (let ((vars (org-babel--get-vars params))) (mapc (lambda (pair) (let ((name (symbol-name (car pair))) @@ -138,9 +157,8 @@ specific arguments to =org-babel-tangle=" (defun org-babel-lilypond-process-basic (body params) "Execute a lilypond block in basic mode." - (let* ((result-params (cdr (assoc :result-params params))) - (out-file (cdr (assoc :file params))) - (cmdline (or (cdr (assoc :cmdline params)) + (let* ((out-file (cdr (assq :file params))) + (cmdline (or (cdr (assq :cmdline params)) "")) (in-file (org-babel-temp-file "lilypond-"))) @@ -148,7 +166,7 @@ specific arguments to =org-babel-tangle=" (insert (org-babel-expand-body:generic body params))) (org-babel-eval (concat - (org-babel-lilypond-determine-ly-path) + org-babel-lilypond-ly-command " -dbackend=eps " "-dno-gs-load-fonts " "-dinclude-eps-fonts " @@ -163,7 +181,7 @@ specific arguments to =org-babel-tangle=" cmdline in-file) "")) nil) -(defun org-babel-prep-session:lilypond (session params) +(defun org-babel-prep-session:lilypond (_session _params) "Return an error because LilyPond exporter does not support sessions." (error "Sorry, LilyPond does not currently support sessions!")) @@ -175,29 +193,27 @@ If error in compilation, attempt to mark the error in lilypond org file" (buffer-file-name) ".lilypond")) (org-babel-lilypond-temp-file (org-babel-lilypond-switch-extension (buffer-file-name) ".ly"))) - (if (file-exists-p org-babel-lilypond-tangled-file) - (progn - (when (file-exists-p org-babel-lilypond-temp-file) - (delete-file org-babel-lilypond-temp-file)) - (rename-file org-babel-lilypond-tangled-file - org-babel-lilypond-temp-file)) - (error "Error: Tangle Failed!") t) + (if (not (file-exists-p org-babel-lilypond-tangled-file)) + (error "Error: Tangle Failed!") + (when (file-exists-p org-babel-lilypond-temp-file) + (delete-file org-babel-lilypond-temp-file)) + (rename-file org-babel-lilypond-tangled-file + org-babel-lilypond-temp-file)) (switch-to-buffer-other-window "*lilypond*") (erase-buffer) (org-babel-lilypond-compile-lilyfile org-babel-lilypond-temp-file) (goto-char (point-min)) - (if (not (org-babel-lilypond-check-for-compile-error org-babel-lilypond-temp-file)) - (progn - (other-window -1) - (org-babel-lilypond-attempt-to-open-pdf org-babel-lilypond-temp-file) - (org-babel-lilypond-attempt-to-play-midi org-babel-lilypond-temp-file)) - (error "Error in Compilation!")))) nil) + (if (org-babel-lilypond-check-for-compile-error org-babel-lilypond-temp-file) + (error "Error in Compilation!") + (other-window -1) + (org-babel-lilypond-attempt-to-open-pdf org-babel-lilypond-temp-file) + (org-babel-lilypond-attempt-to-play-midi org-babel-lilypond-temp-file))))) (defun org-babel-lilypond-compile-lilyfile (file-name &optional test) "Compile lilypond file and check for compile errors FILE-NAME is full path to lilypond (.ly) file" (message "Compiling LilyPond...") - (let ((arg-1 (org-babel-lilypond-determine-ly-path)) ;program + (let ((arg-1 org-babel-lilypond-ly-command) ;program (arg-2 nil) ;infile (arg-3 "*lilypond*") ;buffer (arg-4 t) ;display @@ -223,11 +239,10 @@ FILE-NAME is full path to lilypond file. If TEST is t just return nil if no error found, and pass nil as file-name since it is unused in this context" (let ((is-error (search-forward "error:" nil t))) - (if (not test) - (if (not is-error) - nil - (org-babel-lilypond-process-compile-error file-name)) - is-error))) + (if test + is-error + (when is-error + (org-babel-lilypond-process-compile-error file-name))))) (defun org-babel-lilypond-process-compile-error (file-name) "Process the compilation error that has occurred. @@ -249,32 +264,26 @@ LINE is the erroneous line" (setq case-fold-search nil) (if (search-forward line nil t) (progn - (show-all) + (outline-show-all) (set-mark (point)) (goto-char (- (point) (length line)))) (goto-char temp)))) (defun org-babel-lilypond-parse-line-num (&optional buffer) "Extract error line number." - (when buffer - (set-buffer buffer)) + (when buffer (set-buffer buffer)) (let ((start (and (search-backward ":" nil t) (search-backward ":" nil t) (search-backward ":" nil t) - (search-backward ":" nil t))) - (num nil)) - (if start - (progn - (forward-char) - (let ((num (buffer-substring - (+ 1 start) - (- (search-forward ":" nil t) 1)))) - (setq num (string-to-number num)) - (if (numberp num) - num - nil))) - nil))) + (search-backward ":" nil t)))) + (when start + (forward-char) + (let ((num (string-to-number + (buffer-substring + (+ 1 start) + (- (search-forward ":" nil t) 1))))) + (and (numberp num) num))))) (defun org-babel-lilypond-parse-error-line (file-name lineNo) "Extract the erroneous line from the tangled .ly file @@ -298,13 +307,13 @@ If TEST is non-nil, the shell command is returned and is not run" (let ((pdf-file (org-babel-lilypond-switch-extension file-name ".pdf"))) (if (file-exists-p pdf-file) (let ((cmd-string - (concat (org-babel-lilypond-determine-pdf-path) " " pdf-file))) + (concat org-babel-lilypond-pdf-command " " pdf-file))) (if test cmd-string (start-process "\"Audition pdf\"" "*lilypond*" - (org-babel-lilypond-determine-pdf-path) + org-babel-lilypond-pdf-command pdf-file))) (message "No pdf file generated so can't display!"))))) @@ -316,49 +325,16 @@ If TEST is non-nil, the shell command is returned and is not run" (let ((midi-file (org-babel-lilypond-switch-extension file-name ".midi"))) (if (file-exists-p midi-file) (let ((cmd-string - (concat (org-babel-lilypond-determine-midi-path) " " midi-file))) + (concat org-babel-lilypond-midi-command " " midi-file))) (if test cmd-string (start-process "\"Audition midi\"" "*lilypond*" - (org-babel-lilypond-determine-midi-path) + org-babel-lilypond-midi-command midi-file))) (message "No midi file generated so can't play!"))))) -(defun org-babel-lilypond-determine-ly-path (&optional test) - "Return correct path to ly binary depending on OS -If TEST is non-nil, it contains a simulation of the OS for test purposes" - (let ((sys-type - (or test system-type))) - (cond ((string= sys-type "darwin") - org-babel-lilypond-OSX-ly-path) - ((string= sys-type "windows-nt") - org-babel-lilypond-w32-ly-path) - (t org-babel-lilypond-nix-ly-path)))) - -(defun org-babel-lilypond-determine-pdf-path (&optional test) - "Return correct path to pdf viewer depending on OS -If TEST is non-nil, it contains a simulation of the OS for test purposes" - (let ((sys-type - (or test system-type))) - (cond ((string= sys-type "darwin") - org-babel-lilypond-OSX-pdf-path) - ((string= sys-type "windows-nt") - org-babel-lilypond-w32-pdf-path) - (t org-babel-lilypond-nix-pdf-path)))) - -(defun org-babel-lilypond-determine-midi-path (&optional test) - "Return correct path to midi player depending on OS -If TEST is non-nil, it contains a simulation of the OS for test purposes" - (let ((sys-type - (or test test system-type))) - (cond ((string= sys-type "darwin") - org-babel-lilypond-OSX-midi-path) - ((string= sys-type "windows-nt") - org-babel-lilypond-w32-midi-path) - (t org-babel-lilypond-nix-midi-path)))) - (defun org-babel-lilypond-toggle-midi-play () "Toggle whether midi will be played following a successful compilation." (interactive) diff --git a/lisp/org/ob-lisp.el b/lisp/org/ob-lisp.el index 2f66549fc3..1e381d0ce2 100644 --- a/lisp/org/ob-lisp.el +++ b/lisp/org/ob-lisp.el @@ -1,4 +1,4 @@ -;;; ob-lisp.el --- org-babel functions for common lisp evaluation +;;; ob-lisp.el --- Babel Functions for Common Lisp -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -25,17 +25,22 @@ ;;; Commentary: -;;; support for evaluating common lisp code, relies on slime for all eval +;;; Support for evaluating Common Lisp code, relies on SLY or SLIME +;;; for all eval. ;;; Requirements: -;; Requires SLIME (Superior Lisp Interaction Mode for Emacs.) -;; See http://common-lisp.net/project/slime/ +;; Requires SLY (Sylvester the Cat's Common Lisp IDE) or SLIME +;; (Superior Lisp Interaction Mode for Emacs). See: +;; - https://github.com/capitaomorte/sly +;; - http://common-lisp.net/project/slime/ ;;; Code: (require 'ob) +(declare-function sly-eval "ext:sly" (sexp &optional package)) (declare-function slime-eval "ext:slime" (sexp &optional package)) +(declare-function org-trim "org" (s &optional keep-lead)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("lisp" . "lisp")) @@ -43,8 +48,16 @@ (defvar org-babel-default-header-args:lisp '()) (defvar org-babel-header-args:lisp '((package . :any))) +(defcustom org-babel-lisp-eval-fn #'slime-eval + "The function to be called to evaluate code on the Lisp side. +Valid values include `slime-eval' and `sly-eval'." + :group 'org-babel + :version "26.1" + :package-version '(Org . "9.0") + :type 'function) + (defcustom org-babel-lisp-dir-fmt - "(let ((*default-pathname-defaults* #P%S)) %%s)" + "(let ((*default-pathname-defaults* #P%S\n)) %%s\n)" "Format string used to wrap code bodies to set the current directory. For example a value of \"(progn ;; %s\\n %%s)\" would ignore the current directory string." @@ -54,49 +67,54 @@ current directory string." (defun org-babel-expand-body:lisp (body params) "Expand BODY according to PARAMS, return the expanded body." - (let* ((vars (mapcar #'cdr (org-babel-get-header params :var))) - (result-params (cdr (assoc :result-params params))) + (let* ((vars (org-babel--get-vars params)) + (result-params (cdr (assq :result-params params))) (print-level nil) (print-length nil) - (body (org-babel-trim - (if (> (length vars) 0) - (concat "(let (" - (mapconcat - (lambda (var) - (format "(%S (quote %S))" (car var) (cdr var))) - vars "\n ") - ")\n" body ")") - body)))) + (body (if (null vars) (org-trim body) + (concat "(let (" + (mapconcat + (lambda (var) + (format "(%S (quote %S))" (car var) (cdr var))) + vars "\n ") + ")\n" body ")")))) (if (or (member "code" result-params) (member "pp" result-params)) (format "(pprint %s)" body) body))) (defun org-babel-execute:lisp (body params) - "Execute a block of Common Lisp code with Babel." - (require 'slime) + "Execute a block of Common Lisp code with Babel. +BODY is the contents of the block, as a string. PARAMS is +a property list containing the parameters of the block." + (require (pcase org-babel-lisp-eval-fn + (`slime-eval 'slime) + (`sly-eval 'sly))) (org-babel-reassemble-table (let ((result - (with-temp-buffer - (insert (org-babel-expand-body:lisp body params)) - (slime-eval `(swank:eval-and-grab-output - ,(let ((dir (if (assoc :dir params) - (cdr (assoc :dir params)) - default-directory))) - (format - (if dir (format org-babel-lisp-dir-fmt dir) - "(progn %s)") - (buffer-substring-no-properties - (point-min) (point-max))))) - (cdr (assoc :package params)))))) - (org-babel-result-cond (cdr (assoc :result-params params)) - (car result) + (funcall (if (member "output" (cdr (assq :result-params params))) + #'car #'cadr) + (with-temp-buffer + (insert (org-babel-expand-body:lisp body params)) + (funcall org-babel-lisp-eval-fn + `(swank:eval-and-grab-output + ,(let ((dir (if (assq :dir params) + (cdr (assq :dir params)) + default-directory))) + (format + (if dir (format org-babel-lisp-dir-fmt dir) + "(progn %s\n)") + (buffer-substring-no-properties + (point-min) (point-max))))) + (cdr (assq :package params))))))) + (org-babel-result-cond (cdr (assq :result-params params)) + result (condition-case nil - (read (org-babel-lisp-vector-to-list (cadr result))) - (error (cadr result))))) - (org-babel-pick-name (cdr (assoc :colname-names params)) - (cdr (assoc :colnames params))) - (org-babel-pick-name (cdr (assoc :rowname-names params)) - (cdr (assoc :rownames params))))) + (read (org-babel-lisp-vector-to-list result)) + (error result)))) + (org-babel-pick-name (cdr (assq :colname-names params)) + (cdr (assq :colnames params))) + (org-babel-pick-name (cdr (assq :rowname-names params)) + (cdr (assq :rownames params))))) (defun org-babel-lisp-vector-to-list (results) ;; TODO: better would be to replace #(...) with [...] diff --git a/lisp/org/ob-lob.el b/lisp/org/ob-lob.el index ddfac2afee..b6f50d33ed 100644 --- a/lisp/org/ob-lob.el +++ b/lisp/org/ob-lob.el @@ -1,4 +1,4 @@ -;;; ob-lob.el --- functions supporting the Library of Babel +;;; ob-lob.el --- Functions Supporting the Library of Babel -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -23,27 +23,27 @@ ;; along with GNU Emacs. If not, see . ;;; Code: -(eval-when-compile - (require 'cl)) +(require 'cl-lib) (require 'ob-core) (require 'ob-table) -(declare-function org-babel-in-example-or-verbatim "ob-exp" nil) +(declare-function org-babel-ref-split-args "ob-ref" (arg-string)) +(declare-function org-element-at-point "org-element" ()) +(declare-function org-element-context "org-element" (&optional element)) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-type "org-element" (element)) (defvar org-babel-library-of-babel nil "Library of source-code blocks. -This is an association list. Populate the library by adding -files to `org-babel-lob-files'.") - -(defcustom org-babel-lob-files nil - "Files used to populate the `org-babel-library-of-babel'. -To add files to this list use the `org-babel-lob-ingest' command." - :group 'org-babel - :version "24.1" - :type '(repeat file)) +This is an association list. Populate the library by calling +`org-babel-lob-ingest' on files containing source blocks.") (defvar org-babel-default-lob-header-args '((:exports . "results")) - "Default header arguments to use when exporting #+lob/call lines.") + "Default header arguments to use when exporting Babel calls. +By default, a Babel call inherits its arguments from the source +block being called. Header arguments defined in this variable +take precedence over these. It is useful for properties that +should not be inherited from a source block.") (defun org-babel-lob-ingest (&optional file) "Add all named source blocks defined in FILE to `org-babel-library-of-babel'." @@ -62,24 +62,7 @@ To add files to this list use the `org-babel-lob-ingest' command." lob-ingest-count (if (> lob-ingest-count 1) "s" "")) lob-ingest-count)) -(defconst org-babel-block-lob-one-liner-regexp - (concat - "^\\([ \t]*?\\)#\\+call:[ \t]+\\([^()\n]+?\\)\\(\\[\\(.*\\)\\]\\|\\(\\)\\)" - "(\\([^\n]*?\\))\\(\\[.+\\]\\|\\)[ \t]*\\(\\([^\n]*\\)\\)?") - "Regexp to match non-inline calls to predefined source block functions.") - -(defconst org-babel-inline-lob-one-liner-regexp - (concat - "\\([^\n]*?\\)call_\\([^()\n]+?\\)\\(\\[\\(.*?\\)\\]\\|\\(\\)\\)" - "(\\([^\n]*?\\))\\(\\[\\(.*?\\)\\]\\)?") - "Regexp to match inline calls to predefined source block functions.") - -(defconst org-babel-lob-one-liner-regexp - (concat "\\(" org-babel-block-lob-one-liner-regexp - "\\|" org-babel-inline-lob-one-liner-regexp "\\)") - "Regexp to match calls to predefined source block functions.") - -;; functions for executing lob one-liners +;; Functions for executing lob one-liners. ;;;###autoload (defun org-babel-lob-execute-maybe () @@ -88,72 +71,76 @@ Detect if this is context for a Library Of Babel source block and if so then run the appropriate source block from the Library." (interactive) (let ((info (org-babel-lob-get-info))) - (if (and (nth 0 info) (not (org-babel-in-example-or-verbatim))) - (progn (org-babel-lob-execute info) t) - nil))) + (when info + (org-babel-execute-src-block nil info) + t))) + +(defun org-babel-lob--src-info (name) + "Return internal representation for Babel data named NAME. +NAME is a string. This function looks into the current document +for a Babel call or source block. If none is found, it looks +after NAME in the Library of Babel. Eventually, if that also +fails, it returns nil." + ;; During export, look into the pristine copy of the document being + ;; exported instead of the current one, which could miss some data. + (with-current-buffer (or org-babel-exp-reference-buffer (current-buffer)) + (org-with-wide-buffer + (goto-char (point-min)) + (catch :found + (let ((case-fold-search t) + (regexp (org-babel-named-data-regexp-for-name name))) + (while (re-search-forward regexp nil t) + (let ((element (org-element-at-point))) + (when (equal name (org-element-property :name element)) + (throw :found + (pcase (org-element-type element) + (`src-block (org-babel-get-src-block-info t element)) + (`babel-call (org-babel-lob-get-info element)) + ;; Non-executable data found. Since names are + ;; supposed to be unique throughout a document, + ;; bail out. + (_ nil)))))) + ;; No element named NAME in buffer. Try Library of Babel. + (cdr (assoc-string name org-babel-library-of-babel))))))) ;;;###autoload -(defun org-babel-lob-get-info () - "Return a Library of Babel function call as a string." - (let ((case-fold-search t) - (nonempty (lambda (a b) - (let ((it (match-string a))) - (if (= (length it) 0) (match-string b) it))))) - (save-excursion - (beginning-of-line 1) - (when (looking-at org-babel-lob-one-liner-regexp) - (append - (mapcar #'org-no-properties - (list - (format "%s%s(%s)%s" - (funcall nonempty 3 12) - (if (not (= 0 (length (funcall nonempty 5 14)))) - (concat "[" (funcall nonempty 5 14) "]") "") - (or (funcall nonempty 7 16) "") - (or (funcall nonempty 8 19) "")) - (funcall nonempty 9 18))) - (list (length (if (= (length (match-string 12)) 0) - (match-string 2) (match-string 11))) - (save-excursion - (forward-line -1) - (and (looking-at (concat org-babel-src-name-regexp - "\\([^\n]*\\)$")) - (org-no-properties (match-string 1)))))))))) - -(defvar org-babel-default-header-args:emacs-lisp) ; Defined in ob-emacs-lisp.el -(defun org-babel-lob-execute (info) - "Execute the lob call specified by INFO." - (let* ((mkinfo (lambda (p) - (list "emacs-lisp" "results" p nil - (nth 3 info) ;; name - (nth 2 info)))) - (pre-params (apply #'org-babel-merge-params - org-babel-default-header-args - org-babel-default-header-args:emacs-lisp - (append - (org-babel-params-from-properties) - (list - (org-babel-parse-header-arguments - (org-no-properties - (concat - ":var results=" - (mapconcat #'identity (butlast info 2) - " ")))))))) - (pre-info (funcall mkinfo pre-params)) - (cache-p (and (cdr (assoc :cache pre-params)) - (string= "yes" (cdr (assoc :cache pre-params))))) - (new-hash (when cache-p (org-babel-sha1-hash pre-info))) - (old-hash (when cache-p (org-babel-current-result-hash))) - (org-babel-current-src-block-location (point-marker))) - (if (and cache-p (equal new-hash old-hash)) - (save-excursion (goto-char (org-babel-where-is-src-block-result)) - (forward-line 1) - (message "%S" (org-babel-read-result))) - (prog1 (let* ((proc-params (org-babel-process-params pre-params)) - org-confirm-babel-evaluate) - (org-babel-execute-src-block nil (funcall mkinfo proc-params))) - ;; update the hash - (when new-hash (org-babel-set-current-result-hash new-hash)))))) +(defun org-babel-lob-get-info (&optional datum) + "Return internal representation for Library of Babel function call. +Consider DATUM, when provided, or element at point. Return nil +when not on an appropriate location. Otherwise return a list +compatible with `org-babel-get-src-block-info', which see." + (let* ((context (or datum (org-element-context))) + (type (org-element-type context))) + (when (memq type '(babel-call inline-babel-call)) + (pcase (org-babel-lob--src-info (org-element-property :call context)) + (`(,language ,body ,header ,_ ,_ ,_ ,coderef) + (let ((begin (org-element-property (if (eq type 'inline-babel-call) + :begin + :post-affiliated) + context))) + (list language + body + (apply #'org-babel-merge-params + header + org-babel-default-lob-header-args + (append + (org-with-wide-buffer + (goto-char begin) + (org-babel-params-from-properties language)) + (list + (org-babel-parse-header-arguments + (org-element-property :inside-header context)) + (let ((args (org-element-property :arguments context))) + (and args + (mapcar (lambda (ref) (cons :var ref)) + (org-babel-ref-split-args args)))) + (org-babel-parse-header-arguments + (org-element-property :end-header context))))) + nil + (org-element-property :name context) + begin + coderef))) + (_ nil))))) (provide 'ob-lob) diff --git a/lisp/org/ob-lua.el b/lisp/org/ob-lua.el new file mode 100644 index 0000000000..fa60b0ee2d --- /dev/null +++ b/lisp/org/ob-lua.el @@ -0,0 +1,403 @@ +;;; ob-lua.el --- Org Babel functions for Lua evaluation -*- lexical-binding: t; -*- + +;; Copyright (C) 2014, 2016, 2017 Free Software Foundation, Inc. + +;; Authors: Dieter Schoen +;; Keywords: literate programming, reproducible research +;; Homepage: http://orgmode.org + +;; 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 . + +;; Requirements: +;; for session support, lua-mode is needed. +;; lua-mode is not part of GNU Emacs/orgmode, but can be obtained +;; from marmalade or melpa. +;; The source respository is here: +;; https://github.com/immerrr/lua-mode + +;; However, sessions are not yet working. + +;; Org-Babel support for evaluating lua source code. + +;;; Code: +(require 'ob) +(require 'cl-lib) + +(declare-function org-remove-indentation "org" (code &optional n)) +(declare-function org-trim "org" (s &optional keep-lead)) +(declare-function lua-shell "ext:lua-mode" (&optional argprompt)) +(declare-function lua-toggle-shells "ext:lua-mode" (arg)) +(declare-function run-lua "ext:lua" (cmd &optional dedicated show)) + +(defvar org-babel-tangle-lang-exts) +(add-to-list 'org-babel-tangle-lang-exts '("lua" . "lua")) + +(defvar org-babel-default-header-args:lua '()) + +(defcustom org-babel-lua-command "lua" + "Name of the command for executing Lua code." + :version "24.5" + :package-version '(Org . "8.3") + :group 'org-babel + :type 'string) + +(defcustom org-babel-lua-mode 'lua-mode + "Preferred lua mode for use in running lua interactively. +This will typically be 'lua-mode." + :group 'org-babel + :version "24.5" + :package-version '(Org . "8.3") + :type 'symbol) + +(defcustom org-babel-lua-hline-to "None" + "Replace hlines in incoming tables with this when translating to lua." + :group 'org-babel + :version "24.5" + :package-version '(Org . "8.3") + :type 'string) + +(defcustom org-babel-lua-None-to 'hline + "Replace 'None' in lua tables with this before returning." + :group 'org-babel + :version "24.5" + :package-version '(Org . "8.3") + :type 'symbol) + +(defun org-babel-execute:lua (body params) + "Execute a block of Lua code with Babel. +This function is called by `org-babel-execute-src-block'." + (let* ((session (org-babel-lua-initiate-session + (cdr (assq :session params)))) + (result-params (cdr (assq :result-params params))) + (result-type (cdr (assq :result-type params))) + (return-val (when (and (eq result-type 'value) (not session)) + (cdr (assq :return params)))) + (preamble (cdr (assq :preamble params))) + (full-body + (org-babel-expand-body:generic + (concat body (if return-val (format "\nreturn %s" return-val) "")) + params (org-babel-variable-assignments:lua params))) + (result (org-babel-lua-evaluate + session full-body result-type result-params preamble))) + (org-babel-reassemble-table + result + (org-babel-pick-name (cdr (assq :colname-names params)) + (cdr (assq :colnames params))) + (org-babel-pick-name (cdr (assq :rowname-names params)) + (cdr (assq :rownames params)))))) + +(defun org-babel-prep-session:lua (session params) + "Prepare SESSION according to the header arguments in PARAMS. +VARS contains resolved variable references" + (let* ((session (org-babel-lua-initiate-session session)) + (var-lines + (org-babel-variable-assignments:lua params))) + (org-babel-comint-in-buffer session + (mapc (lambda (var) + (end-of-line 1) (insert var) (comint-send-input) + (org-babel-comint-wait-for-output session)) var-lines)) + session)) + +(defun org-babel-load-session:lua (session body params) + "Load BODY into SESSION." + (save-window-excursion + (let ((buffer (org-babel-prep-session:lua session params))) + (with-current-buffer buffer + (goto-char (process-mark (get-buffer-process (current-buffer)))) + (insert (org-babel-chomp body))) + buffer))) + +;; helper functions + +(defun org-babel-variable-assignments:lua (params) + "Return a list of Lua statements assigning the block's variables." + (mapcar + (lambda (pair) + (format "%s=%s" + (car pair) + (org-babel-lua-var-to-lua (cdr pair)))) + (org-babel--get-vars params))) + +(defun org-babel-lua-var-to-lua (var) + "Convert an elisp value to a lua variable. +Convert an elisp value, VAR, into a string of lua source code +specifying a variable of the same value." + (if (listp var) + (if (and (= 1 (length var)) (not (listp (car var)))) + (org-babel-lua-var-to-lua (car var)) + (if (and + (= 2 (length var)) + (not (listp (car var)))) + (concat + (substring-no-properties (car var)) + "=" + (org-babel-lua-var-to-lua (cdr var))) + (concat "{" (mapconcat #'org-babel-lua-var-to-lua var ", ") "}"))) + (if (eq var 'hline) + org-babel-lua-hline-to + (format + (if (and (stringp var) (string-match "[\n\r]" var)) "\"\"%S\"\"" "%S") + (if (stringp var) (substring-no-properties var) var))))) + +(defun org-babel-lua-table-or-string (results) + "Convert RESULTS into an appropriate elisp value. +If the results look like a list or tuple, then convert them into an +Emacs-lisp table, otherwise return the results as a string." + (let ((res (org-babel-script-escape results))) + (if (listp res) + (mapcar (lambda (el) (if (eq el 'None) + org-babel-lua-None-to el)) + res) + res))) + +(defvar org-babel-lua-buffers '((:default . "*Lua*"))) + +(defun org-babel-lua-session-buffer (session) + "Return the buffer associated with SESSION." + (cdr (assoc session org-babel-lua-buffers))) + +(defun org-babel-lua-with-earmuffs (session) + (let ((name (if (stringp session) session (format "%s" session)))) + (if (and (string= "*" (substring name 0 1)) + (string= "*" (substring name (- (length name) 1)))) + name + (format "*%s*" name)))) + +(defun org-babel-lua-without-earmuffs (session) + (let ((name (if (stringp session) session (format "%s" session)))) + (if (and (string= "*" (substring name 0 1)) + (string= "*" (substring name (- (length name) 1)))) + (substring name 1 (- (length name) 1)) + name))) + +(defvar lua-default-interpreter) +(defvar lua-which-bufname) +(defvar lua-shell-buffer-name) +(defun org-babel-lua-initiate-session-by-key (&optional session) + "Initiate a lua session. +If there is not a current inferior-process-buffer in SESSION +then create. Return the initialized session." + ;; (require org-babel-lua-mode) + (save-window-excursion + (let* ((session (if session (intern session) :default)) + (lua-buffer (org-babel-lua-session-buffer session)) + ;; (cmd (if (member system-type '(cygwin windows-nt ms-dos)) + ;; (concat org-babel-lua-command " -i") + ;; org-babel-lua-command)) + ) + (cond + ((and (eq 'lua-mode org-babel-lua-mode) + (fboundp 'lua-start-process)) ; lua-mode.el + ;; Make sure that lua-which-bufname is initialized, as otherwise + ;; it will be overwritten the first time a Lua buffer is + ;; created. + ;;(lua-toggle-shells lua-default-interpreter) + ;; `lua-shell' creates a buffer whose name is the value of + ;; `lua-which-bufname' with '*'s at the beginning and end + (let* ((bufname (if (and lua-buffer (buffer-live-p lua-buffer)) + (replace-regexp-in-string ;; zap surrounding * + "^\\*\\([^*]+\\)\\*$" "\\1" (buffer-name lua-buffer)) + (concat "Lua-" (symbol-name session)))) + (lua-which-bufname bufname)) + (lua-start-process) + (setq lua-buffer (org-babel-lua-with-earmuffs bufname)))) + (t + (error "No function available for running an inferior Lua"))) + (setq org-babel-lua-buffers + (cons (cons session lua-buffer) + (assq-delete-all session org-babel-lua-buffers))) + session))) + +(defun org-babel-lua-initiate-session (&optional session _params) + "Create a session named SESSION according to PARAMS." + (unless (string= session "none") + (error "Sessions currently not supported, work in progress") + (org-babel-lua-session-buffer + (org-babel-lua-initiate-session-by-key session)))) + +(defvar org-babel-lua-eoe-indicator "--eoe" + "A string to indicate that evaluation has completed.") + +(defvar org-babel-lua-wrapper-method + " +function main() +%s +end + +fd=io.open(\"%s\", \"w\") +fd:write( main() ) +fd:close()") +(defvar org-babel-lua-pp-wrapper-method + " +-- table to string +function t2s(t, indent) + if indent == nil then + indent = \"\" + end + if type(t) == \"table\" then + ts = \"\" + for k,v in pairs(t) do + if type(v) == \"table\" then + ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \\n\" .. + t2s(v, indent .. \" \") + else + ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \" .. + t2s(v, indent .. \" \") .. \"\\n\" + end + end + return ts + else + return tostring(t) + end +end + + +function main() +%s +end + +fd=io.open(\"%s\", \"w\") +fd:write(t2s(main())) +fd:close()") + +(defun org-babel-lua-evaluate + (session body &optional result-type result-params preamble) + "Evaluate BODY as Lua code." + (if session + (org-babel-lua-evaluate-session + session body result-type result-params) + (org-babel-lua-evaluate-external-process + body result-type result-params preamble))) + +(defun org-babel-lua-evaluate-external-process + (body &optional result-type result-params preamble) + "Evaluate BODY in external lua process. +If RESULT-TYPE equals 'output then return standard output as a +string. If RESULT-TYPE equals 'value then return the value of the +last statement in BODY, as elisp." + (let ((raw + (pcase result-type + (`output (org-babel-eval org-babel-lua-command + (concat (if preamble (concat preamble "\n")) + body))) + (`value (let ((tmp-file (org-babel-temp-file "lua-"))) + (org-babel-eval + org-babel-lua-command + (concat + (if preamble (concat preamble "\n") "") + (format + (if (member "pp" result-params) + org-babel-lua-pp-wrapper-method + org-babel-lua-wrapper-method) + (mapconcat + (lambda (line) (format "\t%s" line)) + (split-string + (org-remove-indentation + (org-trim body)) + "[\r\n]") "\n") + (org-babel-process-file-name tmp-file 'noquote)))) + (org-babel-eval-read-file tmp-file)))))) + (org-babel-result-cond result-params + raw + (org-babel-lua-table-or-string (org-trim raw))))) + +(defun org-babel-lua-evaluate-session + (session body &optional result-type result-params) + "Pass BODY to the Lua process in SESSION. +If RESULT-TYPE equals 'output then return standard output as a +string. If RESULT-TYPE equals 'value then return the value of the +last statement in BODY, as elisp." + (let* ((send-wait (lambda () (comint-send-input nil t) (sleep-for 0 5))) + (dump-last-value + (lambda + (tmp-file pp) + (mapc + (lambda (statement) (insert statement) (funcall send-wait)) + (if pp + (list + "-- table to string +function t2s(t, indent) + if indent == nil then + indent = \"\" + end + if type(t) == \"table\" then + ts = \"\" + for k,v in pairs(t) do + if type(v) == \"table\" then + ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \\n\" .. + t2s(v, indent .. \" \") + else + ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \" .. + t2s(v, indent .. \" \") .. \"\\n\" + end + end + return ts + else + return tostring(t) + end +end +" + (concat "fd:write(_)) +fd:close()" + (org-babel-process-file-name tmp-file 'noquote))) + (list (format "fd=io.open(\"%s\", \"w\") +fd:write( _ ) +fd:close()" + (org-babel-process-file-name tmp-file + 'noquote))))))) + (input-body (lambda (body) + (mapc (lambda (line) (insert line) (funcall send-wait)) + (split-string body "[\r\n]")) + (funcall send-wait))) + (results + (pcase result-type + (`output + (mapconcat + #'org-trim + (butlast + (org-babel-comint-with-output + (session org-babel-lua-eoe-indicator t body) + (funcall input-body body) + (funcall send-wait) (funcall send-wait) + (insert org-babel-lua-eoe-indicator) + (funcall send-wait)) + 2) "\n")) + (`value + (let ((tmp-file (org-babel-temp-file "lua-"))) + (org-babel-comint-with-output + (session org-babel-lua-eoe-indicator nil body) + (let ((comint-process-echoes nil)) + (funcall input-body body) + (funcall dump-last-value tmp-file + (member "pp" result-params)) + (funcall send-wait) (funcall send-wait) + (insert org-babel-lua-eoe-indicator) + (funcall send-wait))) + (org-babel-eval-read-file tmp-file)))))) + (unless (string= (substring org-babel-lua-eoe-indicator 1 -1) results) + (org-babel-result-cond result-params + results + (org-babel-lua-table-or-string results))))) + +(defun org-babel-lua-read-string (string) + "Strip 's from around Lua string." + (org-unbracket-string "'" "'" string)) + +(provide 'ob-lua) + + + +;;; ob-lua.el ends here diff --git a/lisp/org/ob-makefile.el b/lisp/org/ob-makefile.el index a292800dc1..2aa04fd2af 100644 --- a/lisp/org/ob-makefile.el +++ b/lisp/org/ob-makefile.el @@ -1,4 +1,4 @@ -;;; ob-makefile.el --- org-babel functions for makefile evaluation +;;; ob-makefile.el --- Babel Functions for Makefile -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -24,19 +24,19 @@ ;;; Commentary: -;; This file exists solely for tangling a Makefile from org-mode files. +;; This file exists solely for tangling a Makefile from Org files. ;;; Code: (require 'ob) (defvar org-babel-default-header-args:makefile '()) -(defun org-babel-execute:makefile (body params) +(defun org-babel-execute:makefile (body _params) "Execute a block of makefile code. This function is called by `org-babel-execute-src-block'." body) -(defun org-babel-prep-session:makefile (session params) +(defun org-babel-prep-session:makefile (_session _params) "Return an error if the :session header argument is set. Make does not support sessions." (error "Makefile sessions are nonsensical")) diff --git a/lisp/org/ob-matlab.el b/lisp/org/ob-matlab.el index 42bbd2b907..23cfa36d1e 100644 --- a/lisp/org/ob-matlab.el +++ b/lisp/org/ob-matlab.el @@ -1,4 +1,4 @@ -;;; ob-matlab.el --- org-babel support for matlab evaluation +;;; ob-matlab.el --- Babel support for Matlab -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. diff --git a/lisp/org/ob-maxima.el b/lisp/org/ob-maxima.el index b567fd484a..0a4d835a3a 100644 --- a/lisp/org/ob-maxima.el +++ b/lisp/org/ob-maxima.el @@ -1,4 +1,4 @@ -;;; ob-maxima.el --- org-babel functions for maxima evaluation +;;; ob-maxima.el --- Babel Functions for Maxima -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -48,11 +48,11 @@ (defun org-babel-maxima-expand (body params) "Expand a block of Maxima code according to its header arguments." - (let ((vars (mapcar #'cdr (org-babel-get-header params :var)))) + (let ((vars (org-babel--get-vars params))) (mapconcat 'identity (list ;; graphic output - (let ((graphic-file (org-babel-maxima-graphical-output-file params))) + (let ((graphic-file (ignore-errors (org-babel-graphical-output-file params)))) (if graphic-file (format "set_plot_option ([gnuplot_term, png]); set_plot_option ([gnuplot_out_file, %S]);" @@ -69,9 +69,9 @@ "Execute a block of Maxima entries with org-babel. This function is called by `org-babel-execute-src-block'." (message "executing Maxima source code block") - (let ((result-params (split-string (or (cdr (assoc :results params)) ""))) + (let ((result-params (split-string (or (cdr (assq :results params)) ""))) (result - (let* ((cmdline (or (cdr (assoc :cmdline params)) "")) + (let* ((cmdline (or (cdr (assq :cmdline params)) "")) (in-file (org-babel-temp-file "maxima-" ".max")) (cmd (format "%s --very-quiet -r 'batchload(%S)$' %s" org-babel-maxima-command in-file cmdline))) @@ -89,7 +89,7 @@ This function is called by `org-babel-execute-src-block'." (= 0 (length line))) line)) (split-string raw "[\r\n]"))) "\n"))))) - (if (org-babel-maxima-graphical-output-file params) + (if (ignore-errors (org-babel-graphical-output-file params)) nil (org-babel-result-cond result-params result @@ -98,7 +98,7 @@ This function is called by `org-babel-execute-src-block'." (org-babel-import-elisp-from-file tmp-file)))))) -(defun org-babel-prep-session:maxima (session params) +(defun org-babel-prep-session:maxima (_session _params) (error "Maxima does not support sessions")) (defun org-babel-maxima-var-to-maxima (pair) @@ -113,11 +113,6 @@ of the same value." (format "%S: %s$" var (org-babel-maxima-elisp-to-maxima val)))) -(defun org-babel-maxima-graphical-output-file (params) - "Name of file to which maxima should send graphical output." - (and (member "graphics" (cdr (assq :result-params params))) - (cdr (assq :file params)))) - (defun org-babel-maxima-elisp-to-maxima (val) "Return a string of maxima code which evaluates to VAL." (if (listp val) diff --git a/lisp/org/ob-mscgen.el b/lisp/org/ob-mscgen.el index b764475cb2..5c9dccc67c 100644 --- a/lisp/org/ob-mscgen.el +++ b/lisp/org/ob-mscgen.el @@ -1,4 +1,4 @@ -;;; ob-msc.el --- org-babel functions for mscgen evaluation +;;; ob-msc.el --- Babel Functions for Mscgen -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -65,15 +65,15 @@ This function is called by `org-babel-execute-src-block'. Default filetype is png. Modify by setting :filetype parameter to mscgen supported formats." - (let* ((out-file (or (cdr (assoc :file params)) "output.png" )) - (filetype (or (cdr (assoc :filetype params)) "png" ))) - (unless (cdr (assoc :file params)) + (let* ((out-file (or (cdr (assq :file params)) "output.png" )) + (filetype (or (cdr (assq :filetype params)) "png" ))) + (unless (cdr (assq :file params)) (error " ERROR: no output file specified. Add \":file name.png\" to the src header")) (org-babel-eval (concat "mscgen -T " filetype " -o " out-file) body) nil)) ;; signal that output has already been written to file -(defun org-babel-prep-session:mscgen (session params) +(defun org-babel-prep-session:mscgen (_session _params) "Raise an error because Mscgen doesn't support sessions." (error "Mscgen does not support sessions")) diff --git a/lisp/org/ob-ocaml.el b/lisp/org/ob-ocaml.el index 31f0d01d7f..7997ff03a6 100644 --- a/lisp/org/ob-ocaml.el +++ b/lisp/org/ob-ocaml.el @@ -1,4 +1,4 @@ -;;; ob-ocaml.el --- org-babel functions for ocaml evaluation +;;; ob-ocaml.el --- Babel Functions for Ocaml -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -37,11 +37,11 @@ ;;; Code: (require 'ob) (require 'comint) -(eval-when-compile (require 'cl)) (declare-function tuareg-run-caml "ext:tuareg" ()) (declare-function tuareg-run-ocaml "ext:tuareg" ()) (declare-function tuareg-interactive-send-input "ext:tuareg" ()) +(declare-function org-trim "org" (s &optional keep-lead)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("ocaml" . "ml")) @@ -60,17 +60,17 @@ (defun org-babel-execute:ocaml (body params) "Execute a block of Ocaml code with Babel." - (let* ((vars (mapcar #'cdr (org-babel-get-header params :var))) - (full-body (org-babel-expand-body:generic + (let* ((full-body (org-babel-expand-body:generic body params (org-babel-variable-assignments:ocaml params))) (session (org-babel-prep-session:ocaml - (cdr (assoc :session params)) params)) + (cdr (assq :session params)) params)) (raw (org-babel-comint-with-output - (session org-babel-ocaml-eoe-output t full-body) + (session org-babel-ocaml-eoe-output nil full-body) (insert (concat - (org-babel-chomp full-body)";;\n"org-babel-ocaml-eoe-indicator)) + (org-babel-chomp full-body) ";;\n" + org-babel-ocaml-eoe-indicator)) (tuareg-interactive-send-input))) (clean (car (let ((re (regexp-quote org-babel-ocaml-eoe-output)) out) @@ -79,23 +79,31 @@ (progn (setq out nil) line) (when (string-match re line) (progn (setq out t) nil)))) - (mapcar #'org-babel-trim (reverse raw)))))))) - (org-babel-reassemble-table - (let ((raw (org-babel-trim clean)) - (result-params (cdr (assoc :result-params params)))) + (mapcar #'org-trim (reverse raw))))))) + (raw (org-trim clean)) + (result-params (cdr (assq :result-params params)))) + (string-match + "\\(\\(.*\n\\)*\\)[^:\n]+ : \\([^=\n]+\\) =\\(\n\\| \\)\\(.+\\)$" + raw) + (let ((output (match-string 1 raw)) + (type (match-string 3 raw)) + (value (match-string 5 raw))) + (org-babel-reassemble-table (org-babel-result-cond result-params - ;; strip type information from output unless verbatim is specified - (if (and (not (member "verbatim" result-params)) - (string-match "= \\(.+\\)$" raw)) - (match-string 1 raw) raw) - (org-babel-ocaml-parse-output raw))) - (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) - (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params)))))) + (cond + ((member "verbatim" result-params) raw) + ((member "output" result-params) output) + (t raw)) + (if (and value type) + (org-babel-ocaml-parse-output value type) + raw)) + (org-babel-pick-name + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) + (org-babel-pick-name + (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))) (defvar tuareg-interactive-buffer-name) -(defun org-babel-prep-session:ocaml (session params) +(defun org-babel-prep-session:ocaml (session _params) "Prepare SESSION according to the header arguments in PARAMS." (require 'tuareg) (let ((tuareg-interactive-buffer-name (if (and (not (string= session "none")) @@ -113,7 +121,7 @@ (mapcar (lambda (pair) (format "let %s = %s;;" (car pair) (org-babel-ocaml-elisp-to-ocaml (cdr pair)))) - (mapcar #'cdr (org-babel-get-header params :var)))) + (org-babel--get-vars params))) (defun org-babel-ocaml-elisp-to-ocaml (val) "Return a string of ocaml code which evaluates to VAL." @@ -121,26 +129,29 @@ (concat "[|" (mapconcat #'org-babel-ocaml-elisp-to-ocaml val "; ") "|]") (format "%S" val))) -(defun org-babel-ocaml-parse-output (output) - "Parse OUTPUT. -OUTPUT is string output from an ocaml process." - (let ((regexp "[^:]+ : %s = \\(.+\\)$")) - (cond - ((string-match (format regexp "string") output) - (org-babel-read (match-string 1 output))) - ((or (string-match (format regexp "int") output) - (string-match (format regexp "float") output)) - (string-to-number (match-string 1 output))) - ((string-match (format regexp "list") output) - (org-babel-ocaml-read-list (match-string 1 output))) - ((string-match (format regexp "array") output) - (org-babel-ocaml-read-array (match-string 1 output))) - (t (message "don't recognize type of %s" output) output)))) +(defun org-babel-ocaml-parse-output (value type) + "Parse VALUE of type TYPE. +VALUE and TYPE are string output from an ocaml process." + (cond + ((string= "string" type) + (org-babel-read value)) + ((or (string= "int" type) + (string= "float" type)) + (string-to-number value)) + ((string-match "list" type) + (org-babel-ocaml-read-list value)) + ((string-match "array" type) + (org-babel-ocaml-read-array value)) + (t (message "don't recognize type %s" type) value))) (defun org-babel-ocaml-read-list (results) "Convert RESULTS into an elisp table or string. If the results look like a table, then convert them into an Emacs-lisp table, otherwise return the results as a string." + ;; XXX: This probably does not behave as expected when a semicolon + ;; is in a string in a list. The same comment applies to + ;; `org-babel-ocaml-read-array' below (with even more failure + ;; modes). (org-babel-script-escape (replace-regexp-in-string ";" "," results))) (defun org-babel-ocaml-read-array (results) diff --git a/lisp/org/ob-octave.el b/lisp/org/ob-octave.el index 4a96cdbf03..90735b11fb 100644 --- a/lisp/org/ob-octave.el +++ b/lisp/org/ob-octave.el @@ -1,4 +1,4 @@ -;;; ob-octave.el --- org-babel functions for octave and matlab evaluation +;;; ob-octave.el --- Babel Functions for Octave and Matlab -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -30,10 +30,10 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) (declare-function matlab-shell "ext:matlab-mode") (declare-function matlab-shell-run-region "ext:matlab-mode") +(declare-function org-trim "org" (s &optional keep-lead)) (defvar org-babel-default-header-args:matlab '()) (defvar org-babel-default-header-args:octave '()) @@ -74,33 +74,31 @@ end") (let* ((session (funcall (intern (format "org-babel-%s-initiate-session" (if matlabp "matlab" "octave"))) - (cdr (assoc :session params)) params)) - (vars (mapcar #'cdr (org-babel-get-header params :var))) - (result-params (cdr (assoc :result-params params))) - (result-type (cdr (assoc :result-type params))) - (out-file (cdr (assoc :file params))) + (cdr (assq :session params)) params)) + (result-type (cdr (assq :result-type params))) (full-body (org-babel-expand-body:generic body params (org-babel-variable-assignments:octave params))) + (gfx-file (ignore-errors (org-babel-graphical-output-file params))) (result (org-babel-octave-evaluate session - (if (org-babel-octave-graphical-output-file params) + (if gfx-file (mapconcat 'identity (list "set (0, \"defaultfigurevisible\", \"off\");" full-body - (format "print -dpng %s" (org-babel-octave-graphical-output-file params))) + (format "print -dpng %s" gfx-file)) "\n") full-body) result-type matlabp))) - (if (org-babel-octave-graphical-output-file params) + (if gfx-file nil (org-babel-reassemble-table result (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params))))))) + (cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))) (defun org-babel-prep-session:matlab (session params) "Prepare SESSION according to PARAMS." @@ -113,7 +111,7 @@ end") (format "%s=%s;" (car pair) (org-babel-octave-var-to-octave (cdr pair)))) - (mapcar #'cdr (org-babel-get-header params :var)))) + (org-babel--get-vars params))) (defalias 'org-babel-variable-assignments:matlab 'org-babel-variable-assignments:octave) @@ -147,7 +145,7 @@ If there is not a current inferior-process-buffer in SESSION then create. Return the initialized session." (org-babel-octave-initiate-session session params 'matlab)) -(defun org-babel-octave-initiate-session (&optional session params matlabp) +(defun org-babel-octave-initiate-session (&optional session _params matlabp) "Create an octave inferior process buffer. If there is not a current inferior-process-buffer in SESSION then create. Return the initialized session." @@ -167,8 +165,8 @@ create. Return the initialized session." (defun org-babel-octave-evaluate (session body result-type &optional matlabp) "Pass BODY to the octave process in SESSION. -If RESULT-TYPE equals 'output then return the outputs of the -statements in BODY, if RESULT-TYPE equals 'value then return the +If RESULT-TYPE equals `output' then return the outputs of the +statements in BODY, if RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." (if session (org-babel-octave-evaluate-session session body result-type matlabp) @@ -179,9 +177,9 @@ value of the last statement in BODY, as elisp." (let ((cmd (if matlabp org-babel-matlab-shell-command org-babel-octave-shell-command))) - (case result-type - (output (org-babel-eval cmd body)) - (value (let ((tmp-file (org-babel-temp-file "octave-"))) + (pcase result-type + (`output (org-babel-eval cmd body)) + (`value (let ((tmp-file (org-babel-temp-file "octave-"))) (org-babel-eval cmd (format org-babel-octave-wrapper-method body @@ -190,17 +188,17 @@ value of the last statement in BODY, as elisp." (org-babel-octave-import-elisp-from-file tmp-file)))))) (defun org-babel-octave-evaluate-session - (session body result-type &optional matlabp) + (session body result-type &optional matlabp) "Evaluate BODY in SESSION." (let* ((tmp-file (org-babel-temp-file (if matlabp "matlab-" "octave-"))) (wait-file (org-babel-temp-file "matlab-emacs-link-wait-signal-")) (full-body - (case result-type - (output + (pcase result-type + (`output (mapconcat #'org-babel-chomp (list body org-babel-octave-eoe-indicator) "\n")) - (value + (`value (if (and matlabp org-babel-matlab-with-emacs-link) (concat (format org-babel-matlab-emacs-link-wrapper-method @@ -233,21 +231,20 @@ value of the last statement in BODY, as elisp." org-babel-octave-eoe-output) t full-body) (insert full-body) (comint-send-input nil t)))) results) - (case result-type - (value + (pcase result-type + (`value (org-babel-octave-import-elisp-from-file tmp-file)) - (output - (progn - (setq results - (if matlabp - (cdr (reverse (delq "" (mapcar - #'org-babel-octave-read-string - (mapcar #'org-babel-trim raw))))) - (cdr (member org-babel-octave-eoe-output - (reverse (mapcar - #'org-babel-octave-read-string - (mapcar #'org-babel-trim raw))))))) - (mapconcat #'identity (reverse results) "\n")))))) + (`output + (setq results + (if matlabp + (cdr (reverse (delq "" (mapcar + #'org-babel-strip-quotes + (mapcar #'org-trim raw))))) + (cdr (member org-babel-octave-eoe-output + (reverse (mapcar + #'org-babel-strip-quotes + (mapcar #'org-trim raw))))))) + (mapconcat #'identity (reverse results) "\n"))))) (defun org-babel-octave-import-elisp-from-file (file-name) "Import data from FILE-NAME. @@ -262,17 +259,6 @@ This removes initial blank and comment lines and then calls (delete-region beg end))) (org-babel-import-elisp-from-file temp-file '(16)))) -(defun org-babel-octave-read-string (string) - "Strip \\\"s from around octave string." - (if (string-match "^\"\\([^\000]+\\)\"$" string) - (match-string 1 string) - string)) - -(defun org-babel-octave-graphical-output-file (params) - "Name of file to which maxima should send graphical output." - (and (member "graphics" (cdr (assq :result-params params))) - (cdr (assq :file params)))) - (provide 'ob-octave) diff --git a/lisp/org/ob-org.el b/lisp/org/ob-org.el index 3535891613..5683b96fca 100644 --- a/lisp/org/ob-org.el +++ b/lisp/org/ob-org.el @@ -1,4 +1,4 @@ -;;; ob-org.el --- org-babel functions for org code block evaluation +;;; ob-org.el --- Babel Functions for Org Code Blocks -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -41,7 +41,7 @@ "Default header inserted during export of org blocks.") (defun org-babel-expand-body:org (body params) - (dolist (var (mapcar #'cdr (org-babel-get-header params :var))) + (dolist (var (org-babel--get-vars params)) (setq body (replace-regexp-in-string (regexp-quote (format "$%s" (car var))) (format "%s" (cdr var)) @@ -51,7 +51,7 @@ (defun org-babel-execute:org (body params) "Execute a block of Org code with. This function is called by `org-babel-execute-src-block'." - (let ((result-params (split-string (or (cdr (assoc :results params)) ""))) + (let ((result-params (split-string (or (cdr (assq :results params)) ""))) (body (org-babel-expand-body:org (replace-regexp-in-string "^," "" body) params))) (cond @@ -61,7 +61,7 @@ This function is called by `org-babel-execute-src-block'." ((member "ascii" result-params) (org-export-string-as body 'ascii t)) (t body)))) -(defun org-babel-prep-session:org (session params) +(defun org-babel-prep-session:org (_session _params) "Return an error because org does not support sessions." (error "Org does not support sessions")) diff --git a/lisp/org/ob-perl.el b/lisp/org/ob-perl.el index 4e4407d176..62df8c555f 100644 --- a/lisp/org/ob-perl.el +++ b/lisp/org/ob-perl.el @@ -1,4 +1,4 @@ -;;; ob-perl.el --- org-babel functions for perl evaluation +;;; ob-perl.el --- Babel Functions for Perl -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -28,7 +28,6 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("perl" . "pl")) @@ -41,20 +40,20 @@ (defun org-babel-execute:perl (body params) "Execute a block of Perl code with Babel. This function is called by `org-babel-execute-src-block'." - (let* ((session (cdr (assoc :session params))) - (result-params (cdr (assoc :result-params params))) - (result-type (cdr (assoc :result-type params))) + (let* ((session (cdr (assq :session params))) + (result-params (cdr (assq :result-params params))) + (result-type (cdr (assq :result-type params))) (full-body (org-babel-expand-body:generic body params (org-babel-variable-assignments:perl params))) (session (org-babel-perl-initiate-session session))) (org-babel-reassemble-table (org-babel-perl-evaluate session full-body result-type result-params) (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params)))))) + (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) -(defun org-babel-prep-session:perl (session params) +(defun org-babel-prep-session:perl (_session _params) "Prepare SESSION according to the header arguments in PARAMS." (error "Sessions are not supported for Perl")) @@ -63,7 +62,7 @@ This function is called by `org-babel-execute-src-block'." (mapcar (lambda (pair) (org-babel-perl--var-to-perl (cdr pair) (car pair))) - (mapcar #'cdr (org-babel-get-header params :var)))) + (org-babel--get-vars params))) ;; helper functions @@ -76,7 +75,7 @@ This function is called by `org-babel-execute-src-block'." The elisp value, VAR, is converted to a string of perl source code specifying a var of the same value." (if varn - (let ((org-babel-perl--lvl 0) (lvar (listp var)) prefix) + (let ((org-babel-perl--lvl 0) (lvar (listp var))) (concat "my $" (symbol-name varn) "=" (when lvar "\n") (org-babel-perl--var-to-perl var) ";\n")) @@ -92,7 +91,7 @@ specifying a var of the same value." (defvar org-babel-perl-buffers '(:default . nil)) -(defun org-babel-perl-initiate-session (&optional session params) +(defun org-babel-perl-initiate-session (&optional _session _params) "Return nil because sessions are not supported by perl." nil) @@ -127,8 +126,8 @@ specifying a var of the same value." (defun org-babel-perl-evaluate (session ibody &optional result-type result-params) "Pass BODY to the Perl process in SESSION. -If RESULT-TYPE equals 'output then return a list of the outputs -of the statements in BODY, if RESULT-TYPE equals 'value then +If RESULT-TYPE equals `output' then return a list of the outputs +of the statements in BODY, if RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." (when session (error "Sessions are not supported for Perl")) (let* ((body (concat org-babel-perl-preface ibody)) @@ -136,13 +135,13 @@ return the value of the last statement in BODY, as elisp." (tmp-babel-file (org-babel-process-file-name tmp-file 'noquote))) (let ((results - (case result-type - (output + (pcase result-type + (`output (with-temp-file tmp-file (insert (org-babel-eval org-babel-perl-command body)) (buffer-string))) - (value + (`value (org-babel-eval org-babel-perl-command (format org-babel-perl-wrapper-method body tmp-babel-file)))))) diff --git a/lisp/org/ob-picolisp.el b/lisp/org/ob-picolisp.el index a87c15ea97..f577381557 100644 --- a/lisp/org/ob-picolisp.el +++ b/lisp/org/ob-picolisp.el @@ -1,4 +1,4 @@ -;;; ob-picolisp.el --- org-babel functions for picolisp evaluation +;;; ob-picolisp.el --- Babel Functions for Picolisp -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -55,7 +55,6 @@ ;;; Code: (require 'ob) (require 'comint) -(eval-when-compile (require 'cl)) (declare-function run-picolisp "ext:inferior-picolisp" (cmd)) (defvar org-babel-tangle-lang-exts) ;; Autoloaded @@ -80,9 +79,9 @@ (defun org-babel-expand-body:picolisp (body params) "Expand BODY according to PARAMS, return the expanded body." - (let ((vars (mapcar #'cdr (org-babel-get-header params :var))) - (result-params (cdr (assoc :result-params params))) - (print-level nil) (print-length nil)) + (let ((vars (org-babel--get-vars params)) + (print-level nil) + (print-length nil)) (if (> (length vars) 0) (concat "(prog (let (" (mapconcat @@ -100,12 +99,11 @@ (message "executing Picolisp source code block") (let* ( ;; Name of the session or "none". - (session-name (cdr (assoc :session params))) + (session-name (cdr (assq :session params))) ;; Set the session if the session variable is non-nil. (session (org-babel-picolisp-initiate-session session-name)) ;; Either OUTPUT or VALUE which should behave as described above. - (result-type (cdr (assoc :result-type params))) - (result-params (cdr (assoc :result-params params))) + (result-params (cdr (assq :result-params params))) ;; Expand the body with `org-babel-expand-body:picolisp'. (full-body (org-babel-expand-body:picolisp body params)) ;; Wrap body appropriately for the type of evaluation and results. diff --git a/lisp/org/ob-plantuml.el b/lisp/org/ob-plantuml.el index e05565e32c..e90021a52a 100644 --- a/lisp/org/ob-plantuml.el +++ b/lisp/org/ob-plantuml.el @@ -1,4 +1,4 @@ -;;; ob-plantuml.el --- org-babel functions for plantuml evaluation +;;; ob-plantuml.el --- Babel Functions for Plantuml -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -49,21 +49,36 @@ (defun org-babel-execute:plantuml (body params) "Execute a block of plantuml code with org-babel. This function is called by `org-babel-execute-src-block'." - (let* ((result-params (split-string (or (cdr (assoc :results params)) ""))) - (out-file (or (cdr (assoc :file params)) + (let* ((out-file (or (cdr (assq :file params)) (error "PlantUML requires a \":file\" header argument"))) - (cmdline (cdr (assoc :cmdline params))) + (cmdline (cdr (assq :cmdline params))) (in-file (org-babel-temp-file "plantuml-")) - (java (or (cdr (assoc :java params)) "")) + (java (or (cdr (assq :java params)) "")) (cmd (if (string= "" org-plantuml-jar-path) (error "`org-plantuml-jar-path' is not set") (concat "java " java " -jar " (shell-quote-argument (expand-file-name org-plantuml-jar-path)) + (if (string= (file-name-extension out-file) "png") + " -tpng" "") (if (string= (file-name-extension out-file) "svg") " -tsvg" "") (if (string= (file-name-extension out-file) "eps") " -teps" "") + (if (string= (file-name-extension out-file) "pdf") + " -tpdf" "") + (if (string= (file-name-extension out-file) "vdx") + " -tvdx" "") + (if (string= (file-name-extension out-file) "xmi") + " -txmi" "") + (if (string= (file-name-extension out-file) "scxml") + " -tscxml" "") + (if (string= (file-name-extension out-file) "html") + " -thtml" "") + (if (string= (file-name-extension out-file) "txt") + " -ttxt" "") + (if (string= (file-name-extension out-file) "utxt") + " -utxt" "") " -p " cmdline " < " (org-babel-process-file-name in-file) " > " @@ -74,7 +89,7 @@ This function is called by `org-babel-execute-src-block'." (message "%s" cmd) (org-babel-eval cmd "") nil)) ;; signal that output has already been written to file -(defun org-babel-prep-session:plantuml (session params) +(defun org-babel-prep-session:plantuml (_session _params) "Return an error because plantuml does not support sessions." (error "Plantuml does not support sessions")) diff --git a/lisp/org/ob-processing.el b/lisp/org/ob-processing.el new file mode 100644 index 0000000000..a18a53cbf1 --- /dev/null +++ b/lisp/org/ob-processing.el @@ -0,0 +1,195 @@ +;;; ob-processing.el --- Babel functions for processing -*- lexical-binding: t; -*- + +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. + +;; Author: Jarmo Hurri (adapted from ob-asymptote.el written by Eric Schulte) +;; Keywords: literate programming, reproducible research +;; Homepage: http://orgmode.org + +;; 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 . + +;;; Commentary: + +;; Babel support for evaluating processing source code. +;; +;; This differs from most standard languages in that +;; +;; 1) there is no such thing as a "session" in processing +;; +;; 2) results can only be exported as html; in this case, the +;; processing code is embedded via a file into a javascript block +;; using the processing.js module; the script then draws the +;; resulting output when the web page is viewed in a browser; note +;; that the user is responsible for making sure that processing.js +;; is available on the website +;; +;; 3) it is possible to interactively view the sketch of the +;; Processing code block via Processing 2.0 Emacs mode, using +;; `org-babel-processing-view-sketch'. You can bind this command +;; to, e.g., C-c C-v C-k with +;; +;; (define-key org-babel-map (kbd "C-k") 'org-babel-processing-view-sketch) + + +;;; Requirements: + +;; - processing2-emacs mode :: https://github.com/ptrv/processing2-emacs +;; - Processing.js module :: http://processingjs.org/ + +;;; Code: +(require 'ob) +(require 'sha1) + +(declare-function processing-sketch-run "ext:processing-mode" ()) + +(defvar org-babel-temporary-directory) + +(defvar org-babel-tangle-lang-exts) +(add-to-list 'org-babel-tangle-lang-exts '("processing" . "pde")) + +;; Default header tags depend on whether exporting html or not; if not +;; exporting html, then no results are produced; otherwise results are +;; HTML. +(defvar org-babel-default-header-args:processing + '((:results . "html") (:exports . "results")) + "Default arguments when evaluating a Processing source block.") + +(defvar org-babel-processing-processing-js-filename "processing.js" + "Filename of the processing.js file.") + +(defun org-babel-processing-view-sketch () + "Show the sketch of the Processing block under point in an external viewer." + (interactive) + (require 'processing-mode) + (let ((info (org-babel-get-src-block-info))) + (if (string= (nth 0 info) "processing") + (let* ((body (nth 1 info)) + (params (org-babel-process-params (nth 2 info))) + (sketch-code + (org-babel-expand-body:generic + body + params + (org-babel-variable-assignments:processing params)))) + ;; Note: sketch filename can not contain a hyphen, since it + ;; has to be a valid java class name; for this reason + ;; make-temp-file is repeated until no hyphen is in the + ;; name; also sketch dir name must be the same as the + ;; basename of the sketch file. + (let* ((temporary-file-directory org-babel-temporary-directory) + (sketch-dir + (let (sketch-dir-candidate) + (while + (progn + (setq sketch-dir-candidate + (make-temp-file "processing" t)) + (when (string-match-p + "-" + (file-name-nondirectory sketch-dir-candidate)) + (delete-directory sketch-dir-candidate) + t))) + sketch-dir-candidate)) + (sketch-filename + (concat sketch-dir + "/" + (file-name-nondirectory sketch-dir) + ".pde"))) + (with-temp-file sketch-filename (insert sketch-code)) + (find-file sketch-filename) + (processing-sketch-run) + (kill-buffer))) + (message "Not inside a Processing source block.")))) + +(defun org-babel-execute:processing (body params) + "Execute a block of Processing code. +This function is called by `org-babel-execute-src-block'." + (let ((sketch-code + (org-babel-expand-body:generic + body + params + (org-babel-variable-assignments:processing params)))) + ;; Results are HTML. + (let ((sketch-canvas-id (concat "ob-" (sha1 sketch-code)))) + (concat "\n ")))) + +(defun org-babel-prep-session:processing (_session _params) + "Return an error if the :session header argument is set. +Processing does not support sessions" + (error "Processing does not support sessions")) + +(defun org-babel-variable-assignments:processing (params) + "Return list of processing statements assigning the block's variables." + (mapcar #'org-babel-processing-var-to-processing + (org-babel--get-vars params))) + +(defun org-babel-processing-var-to-processing (pair) + "Convert an elisp value into a Processing variable. +The elisp value PAIR is converted into Processing code specifying +a variable of the same value." + (let ((var (car pair)) + (val (let ((v (cdr pair))) + (if (symbolp v) (symbol-name v) v)))) + (cond + ((integerp val) + (format "int %S=%S;" var val)) + ((floatp val) + (format "float %S=%S;" var val)) + ((stringp val) + (format "String %S=\"%s\";" var val)) + ((and (listp val) (not (listp (car val)))) + (let* ((type (org-babel-processing-define-type val)) + (fmt (if (eq 'String type) "\"%s\"" "%s")) + (vect (mapconcat (lambda (e) (format fmt e)) val ", "))) + (format "%s[] %S={%s};" type var vect))) + ((listp val) + (let* ((type (org-babel-processing-define-type val)) + (fmt (if (eq 'String type) "\"%s\"" "%s")) + (array (mapconcat (lambda (row) + (concat "{" + (mapconcat (lambda (e) (format fmt e)) + row ", ") + "}")) + val ","))) + (format "%S[][] %S={%s};" type var array)))))) + +(defun org-babel-processing-define-type (data) + "Determine type of DATA. + +DATA is a list. Return type as a symbol. + +The type is `String' if any element in DATA is a string. +Otherwise, it is either `float', if some elements are floats, or +`int'." + (letrec ((type 'int) + (find-type + (lambda (row) + (dolist (e row type) + (cond ((listp e) (setq type (funcall find-type e))) + ((stringp e) (throw 'exit 'String)) + ((floatp e) (setq type 'float))))))) + (catch 'exit (funcall find-type data)))) + +(provide 'ob-processing) + +;;; ob-processing.el ends here diff --git a/lisp/org/ob-python.el b/lisp/org/ob-python.el index dfad47bf9e..302f8bd451 100644 --- a/lisp/org/ob-python.el +++ b/lisp/org/ob-python.el @@ -1,4 +1,4 @@ -;;; ob-python.el --- org-babel functions for python evaluation +;;; ob-python.el --- Babel Functions for Python -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -28,9 +28,9 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) (declare-function org-remove-indentation "org" ) +(declare-function org-trim "org" (s &optional keep-lead)) (declare-function py-shell "ext:python-mode" (&optional argprompt)) (declare-function py-toggle-shells "ext:python-mode" (arg)) (declare-function run-python "ext:python" (&optional cmd dedicated show)) @@ -48,9 +48,9 @@ :type 'string) (defcustom org-babel-python-mode - (if (or (featurep 'xemacs) (featurep 'python-mode)) 'python-mode 'python) + (if (featurep 'python-mode) 'python-mode 'python) "Preferred python mode for use in running python interactively. -This will typically be either 'python or 'python-mode." +This will typically be either `python' or `python-mode'." :group 'org-babel :version "24.4" :package-version '(Org . "8.0") @@ -73,13 +73,16 @@ This will typically be either 'python or 'python-mode." (defun org-babel-execute:python (body params) "Execute a block of Python code with Babel. This function is called by `org-babel-execute-src-block'." - (let* ((session (org-babel-python-initiate-session - (cdr (assoc :session params)))) - (result-params (cdr (assoc :result-params params))) - (result-type (cdr (assoc :result-type params))) + (let* ((org-babel-python-command + (or (cdr (assq :python params)) + org-babel-python-command)) + (session (org-babel-python-initiate-session + (cdr (assq :session params)))) + (result-params (cdr (assq :result-params params))) + (result-type (cdr (assq :result-type params))) (return-val (when (and (eq result-type 'value) (not session)) - (cdr (assoc :return params)))) - (preamble (cdr (assoc :preamble params))) + (cdr (assq :return params)))) + (preamble (cdr (assq :preamble params))) (full-body (org-babel-expand-body:generic (concat body (if return-val (format "\nreturn %s" return-val) "")) @@ -88,10 +91,10 @@ This function is called by `org-babel-execute-src-block'." session full-body result-type result-params preamble))) (org-babel-reassemble-table result - (org-babel-pick-name (cdr (assoc :colname-names params)) - (cdr (assoc :colnames params))) - (org-babel-pick-name (cdr (assoc :rowname-names params)) - (cdr (assoc :rownames params)))))) + (org-babel-pick-name (cdr (assq :colname-names params)) + (cdr (assq :colnames params))) + (org-babel-pick-name (cdr (assq :rowname-names params)) + (cdr (assq :rownames params)))))) (defun org-babel-prep-session:python (session params) "Prepare SESSION according to the header arguments in PARAMS. @@ -123,7 +126,7 @@ VARS contains resolved variable references" (format "%s=%s" (car pair) (org-babel-python-var-to-python (cdr pair)))) - (mapcar #'cdr (org-babel-get-header params :var)))) + (org-babel--get-vars params))) (defun org-babel-python-var-to-python (var) "Convert an elisp value to a python variable. @@ -131,7 +134,7 @@ Convert an elisp value, VAR, into a string of python source code specifying a variable of the same value." (if (listp var) (concat "[" (mapconcat #'org-babel-python-var-to-python var ", ") "]") - (if (equal var 'hline) + (if (eq var 'hline) org-babel-python-hline-to (format (if (and (stringp var) (string-match "[\n\r]" var)) "\"\"%S\"\"" "%S") @@ -143,7 +146,7 @@ If the results look like a list or tuple, then convert them into an Emacs-lisp table, otherwise return the results as a string." (let ((res (org-babel-script-escape results))) (if (listp res) - (mapcar (lambda (el) (if (equal el 'None) + (mapcar (lambda (el) (if (eq el 'None) org-babel-python-None-to el)) res) res))) @@ -214,7 +217,7 @@ then create. Return the initialized session." (assq-delete-all session org-babel-python-buffers))) session))) -(defun org-babel-python-initiate-session (&optional session params) +(defun org-babel-python-initiate-session (&optional session _params) "Create a session named SESSION according to PARAMS." (unless (string= session "none") (org-babel-python-session-buffer @@ -222,13 +225,13 @@ then create. Return the initialized session." (defvar org-babel-python-eoe-indicator "'org_babel_python_eoe'" "A string to indicate that evaluation has completed.") -(defvar org-babel-python-wrapper-method +(defconst org-babel-python-wrapper-method " def main(): %s open('%s', 'w').write( str(main()) )") -(defvar org-babel-python-pp-wrapper-method +(defconst org-babel-python-pp-wrapper-method " import pprint def main(): @@ -246,42 +249,41 @@ open('%s', 'w').write( pprint.pformat(main()) )") body result-type result-params preamble))) (defun org-babel-python-evaluate-external-process - (body &optional result-type result-params preamble) + (body &optional result-type result-params preamble) "Evaluate BODY in external python process. -If RESULT-TYPE equals 'output then return standard output as a -string. If RESULT-TYPE equals 'value then return the value of the +If RESULT-TYPE equals `output' then return standard output as a +string. If RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." (let ((raw - (case result-type - (output (org-babel-eval org-babel-python-command - (concat (if preamble (concat preamble "\n")) - body))) - (value (let ((tmp-file (org-babel-temp-file "python-"))) - (org-babel-eval - org-babel-python-command - (concat - (if preamble (concat preamble "\n") "") - (format - (if (member "pp" result-params) - org-babel-python-pp-wrapper-method - org-babel-python-wrapper-method) - (mapconcat - (lambda (line) (format "\t%s" line)) - (split-string - (org-remove-indentation - (org-babel-trim body)) - "[\r\n]") "\n") - (org-babel-process-file-name tmp-file 'noquote)))) - (org-babel-eval-read-file tmp-file)))))) + (pcase result-type + (`output (org-babel-eval org-babel-python-command + (concat (if preamble (concat preamble "\n")) + body))) + (`value (let ((tmp-file (org-babel-temp-file "python-"))) + (org-babel-eval + org-babel-python-command + (concat + (if preamble (concat preamble "\n") "") + (format + (if (member "pp" result-params) + org-babel-python-pp-wrapper-method + org-babel-python-wrapper-method) + (mapconcat + (lambda (line) (format "\t%s" line)) + (split-string (org-remove-indentation (org-trim body)) + "[\r\n]") + "\n") + (org-babel-process-file-name tmp-file 'noquote)))) + (org-babel-eval-read-file tmp-file)))))) (org-babel-result-cond result-params raw - (org-babel-python-table-or-string (org-babel-trim raw))))) + (org-babel-python-table-or-string (org-trim raw))))) (defun org-babel-python-evaluate-session (session body &optional result-type result-params) "Pass BODY to the Python process in SESSION. -If RESULT-TYPE equals 'output then return standard output as a -string. If RESULT-TYPE equals 'value then return the value of the +If RESULT-TYPE equals `output' then return standard output as a +string. If RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." (let* ((send-wait (lambda () (comint-send-input nil t) (sleep-for 0 5))) (dump-last-value @@ -302,10 +304,10 @@ last statement in BODY, as elisp." (split-string body "[\r\n]")) (funcall send-wait))) (results - (case result-type - (output + (pcase result-type + (`output (mapconcat - #'org-babel-trim + #'org-trim (butlast (org-babel-comint-with-output (session org-babel-python-eoe-indicator t body) @@ -314,7 +316,7 @@ last statement in BODY, as elisp." (insert org-babel-python-eoe-indicator) (funcall send-wait)) 2) "\n")) - (value + (`value (let ((tmp-file (org-babel-temp-file "python-"))) (org-babel-comint-with-output (session org-babel-python-eoe-indicator nil body) @@ -332,9 +334,10 @@ last statement in BODY, as elisp." (org-babel-python-table-or-string results))))) (defun org-babel-python-read-string (string) - "Strip 's from around Python string." - (if (string-match "^'\\([^\000]+\\)'$" string) - (match-string 1 string) + "Strip \\='s from around Python string." + (if (and (string-prefix-p "'" string) + (string-suffix-p "'" string)) + (substring string 1 -1) string)) (provide 'ob-python) diff --git a/lisp/org/ob-ref.el b/lisp/org/ob-ref.el index 1d26403035..f8b9ea4509 100644 --- a/lisp/org/ob-ref.el +++ b/lisp/org/ob-ref.el @@ -1,4 +1,4 @@ -;;; ob-ref.el --- org-babel functions for referencing external data +;;; ob-ref.el --- Babel Functions for Referencing External Data -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -50,19 +50,20 @@ ;;; Code: (require 'ob-core) -(eval-when-compile - (require 'cl)) - -(declare-function org-remove-if-not "org" (predicate seq)) -(declare-function org-at-table-p "org" (&optional table-type)) -(declare-function org-count "org" (CL-ITEM CL-SEQ)) -(declare-function org-at-item-p "org-list" ()) -(declare-function org-narrow-to-subtree "org" ()) -(declare-function org-id-find-id-in-file "org-id" (id file &optional markerp)) +(require 'cl-lib) + +(declare-function org-babel-lob-get-info "ob-lob" (&optional datum)) +(declare-function org-element-at-point "org-element" ()) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-type "org-element" (element)) +(declare-function org-end-of-meta-data "org" (&optional full)) +(declare-function org-find-property "org" (property &optional value)) (declare-function org-id-find-id-file "org-id" (id)) +(declare-function org-id-find-id-in-file "org-id" (id file &optional markerp)) +(declare-function org-in-commented-heading-p "org" (&optional no-inheritance)) +(declare-function org-narrow-to-subtree "org" ()) (declare-function org-show-context "org" (&optional key)) -(declare-function org-pop-to-buffer-same-window - "org-compat" (&optional buffer-or-name norecord label)) +(declare-function org-trim "org" (s &optional keep-lead)) (defvar org-babel-ref-split-regexp "[ \f\t\n\r\v]*\\(.+?\\)[ \f\t\n\r\v]*=[ \f\t\n\r\v]*\\(.+\\)[ \f\t\n\r\v]*") @@ -90,35 +91,31 @@ the variable." org-babel-current-src-block-location))) (org-babel-read ref)))) (if (equal out ref) - (if (string-match "^\".*\"$" ref) + (if (and (string-prefix-p "\"" ref) + (string-suffix-p "\"" ref)) (read ref) (org-babel-ref-resolve ref)) out)))))) (defun org-babel-ref-goto-headline-id (id) - (goto-char (point-min)) - (let ((rx (regexp-quote id))) - (or (re-search-forward - (concat "^[ \t]*:CUSTOM_ID:[ \t]+" rx "[ \t]*$") nil t) - (let* ((file (org-id-find-id-file id)) - (m (when file (org-id-find-id-in-file id file 'marker)))) - (when (and file m) - (message "file:%S" file) - (org-pop-to-buffer-same-window (marker-buffer m)) - (goto-char m) - (move-marker m nil) - (org-show-context) - t))))) + (or (let ((h (org-find-property "CUSTOM_ID" id))) + (when h (goto-char h))) + (let* ((file (org-id-find-id-file id)) + (m (when file (org-id-find-id-in-file id file 'marker)))) + (when (and file m) + (message "file:%S" file) + (pop-to-buffer-same-window (marker-buffer m)) + (goto-char m) + (move-marker m nil) + (org-show-context) + t)))) (defun org-babel-ref-headline-body () (save-restriction (org-narrow-to-subtree) (buffer-substring (save-excursion (goto-char (point-min)) - (forward-line 1) - (when (looking-at "[ \t]*:PROPERTIES:") - (re-search-forward ":END:" nil) - (forward-char)) + (org-end-of-meta-data) (point)) (point-max)))) @@ -126,89 +123,82 @@ the variable." (defun org-babel-ref-resolve (ref) "Resolve the reference REF and return its value." (save-window-excursion - (save-excursion - (let ((case-fold-search t) - type args new-refere new-header-args new-referent result - lob-info split-file split-ref index index-row index-col id) - ;; if ref is indexed grab the indices -- beware nested indices - (when (and (string-match "\\[\\([^\\[]+\\)\\]$" ref) - (let ((str (substring ref 0 (match-beginning 0)))) - (= (org-count ?\( str) (org-count ?\) str)))) - (setq index (match-string 1 ref)) - (setq ref (substring ref 0 (match-beginning 0)))) - ;; assign any arguments to pass to source block - (when (string-match - "^\\(.+?\\)\\(\\[\\(.*\\)\\]\\|\\(\\)\\)(\\(.*\\))$" ref) - (setq new-refere (match-string 1 ref)) - (setq new-header-args (match-string 3 ref)) - (setq new-referent (match-string 5 ref)) - (when (> (length new-refere) 0) - (when (> (length new-referent) 0) - (setq args (mapcar (lambda (ref) (cons :var ref)) - (org-babel-ref-split-args new-referent)))) - (when (> (length new-header-args) 0) - (setq args (append (org-babel-parse-header-arguments - new-header-args) args))) - (setq ref new-refere))) - (when (string-match "^\\(.+\\):\\(.+\\)$" ref) - (setq split-file (match-string 1 ref)) - (setq split-ref (match-string 2 ref)) - (find-file split-file) (setq ref split-ref)) - (save-restriction - (widen) - (goto-char (point-min)) - (if (let ((src-rx (org-babel-named-src-block-regexp-for-name ref)) - (res-rx (org-babel-named-data-regexp-for-name ref))) - ;; goto ref in the current buffer - (or - ;; check for code blocks - (re-search-forward src-rx nil t) - ;; check for named data - (re-search-forward res-rx nil t) - ;; check for local or global headlines by id - (setq id (org-babel-ref-goto-headline-id ref)) - ;; check the Library of Babel - (setq lob-info (cdr (assoc (intern ref) - org-babel-library-of-babel))))) - (unless (or lob-info id) (goto-char (match-beginning 0))) - ;; ;; TODO: allow searching for names in other buffers - ;; (setq id-loc (org-id-find ref 'marker) - ;; buffer (marker-buffer id-loc) - ;; loc (marker-position id-loc)) - ;; (move-marker id-loc nil) - (error "Reference `%s' not found in this buffer" ref)) - (cond - (lob-info (setq type 'lob)) - (id (setq type 'id)) - ((and (looking-at org-babel-src-name-regexp) - (save-excursion - (forward-line 1) - (or (looking-at org-babel-src-block-regexp) - (looking-at org-babel-multi-line-header-regexp)))) - (setq type 'source-block)) - (t (while (not (setq type (org-babel-ref-at-ref-p))) - (forward-line 1) - (beginning-of-line) - (if (or (= (point) (point-min)) (= (point) (point-max))) - (error "Reference not found"))))) - (let ((params (append args '((:results . "silent"))))) - (setq result - (case type - (results-line (org-babel-read-result)) - (table (org-babel-read-table)) - (list (org-babel-read-list)) - (file (org-babel-read-link)) - (source-block (org-babel-execute-src-block - nil nil (if org-babel-update-intermediate - nil params))) - (lob (org-babel-execute-src-block - nil lob-info params)) - (id (org-babel-ref-headline-body))))) - (if (symbolp result) - (format "%S" result) - (if (and index (listp result)) - (org-babel-ref-index-list index result) - result))))))) + (with-current-buffer (or org-babel-exp-reference-buffer (current-buffer)) + (save-excursion + (let ((case-fold-search t) + args new-refere new-header-args new-referent split-file split-ref + index) + ;; if ref is indexed grab the indices -- beware nested indices + (when (and (string-match "\\[\\([^\\[]+\\)\\]$" ref) + (let ((str (substring ref 0 (match-beginning 0)))) + (= (cl-count ?\( str) (cl-count ?\) str)))) + (setq index (match-string 1 ref)) + (setq ref (substring ref 0 (match-beginning 0)))) + ;; assign any arguments to pass to source block + (when (string-match + "^\\(.+?\\)\\(\\[\\(.*\\)\\]\\|\\(\\)\\)(\\(.*\\))$" ref) + (setq new-refere (match-string 1 ref)) + (setq new-header-args (match-string 3 ref)) + (setq new-referent (match-string 5 ref)) + (when (> (length new-refere) 0) + (when (> (length new-referent) 0) + (setq args (mapcar (lambda (ref) (cons :var ref)) + (org-babel-ref-split-args new-referent)))) + (when (> (length new-header-args) 0) + (setq args (append (org-babel-parse-header-arguments + new-header-args) args))) + (setq ref new-refere))) + (when (string-match "^\\(.+\\):\\(.+\\)$" ref) + (setq split-file (match-string 1 ref)) + (setq split-ref (match-string 2 ref)) + (find-file split-file) + (setq ref split-ref)) + (org-with-wide-buffer + (goto-char (point-min)) + (let* ((params (append args '((:results . "silent")))) + (regexp (org-babel-named-data-regexp-for-name ref)) + (result + (catch :found + ;; Check for code blocks or named data. + (while (re-search-forward regexp nil t) + ;; Ignore COMMENTed headings and orphaned + ;; affiliated keywords. + (unless (org-in-commented-heading-p) + (let ((e (org-element-at-point))) + (when (equal (org-element-property :name e) ref) + (goto-char + (org-element-property :post-affiliated e)) + (pcase (org-element-type e) + (`babel-call + (throw :found + (org-babel-execute-src-block + nil (org-babel-lob-get-info e) params))) + (`src-block + (throw :found + (org-babel-execute-src-block + nil nil + (and + (not org-babel-update-intermediate) + params)))) + ((and (let v (org-babel-read-element e)) + (guard v)) + (throw :found v)) + (_ (error "Reference not found"))))))) + ;; Check for local or global headlines by ID. + (when (org-babel-ref-goto-headline-id ref) + (throw :found (org-babel-ref-headline-body))) + ;; Check the Library of Babel. + (let ((info (cdr (assq (intern ref) + org-babel-library-of-babel)))) + (when info + (throw :found + (org-babel-execute-src-block nil info params)))) + (error "Reference `%s' not found in this buffer" ref)))) + (cond + ((symbolp result) (format "%S" result)) + ((and index (listp result)) + (org-babel-ref-index-list index result)) + (t result))))))))) (defun org-babel-ref-index-list (index lis) "Return the subset of LIS indexed by INDEX. @@ -251,21 +241,9 @@ to \"0:-1\"." (defun org-babel-ref-split-args (arg-string) "Split ARG-STRING into top-level arguments of balanced parenthesis." - (mapcar #'org-babel-trim (org-babel-balanced-split arg-string 44))) + (mapcar #'org-trim (org-babel-balanced-split arg-string 44))) -(defvar org-bracket-link-regexp) -(defun org-babel-ref-at-ref-p () - "Return the type of reference located at point. -Return nil if none of the supported reference types are found. -Supported reference types are tables and source blocks." - (cond ((org-at-table-p) 'table) - ((org-at-item-p) 'list) - ((looking-at "^[ \t]*#\\+BEGIN_SRC") 'source-block) - ((looking-at org-bracket-link-regexp) 'file) - ((looking-at org-babel-result-regexp) 'results-line))) (provide 'ob-ref) - - ;;; ob-ref.el ends here diff --git a/lisp/org/ob-ruby.el b/lisp/org/ob-ruby.el index 88a9987696..d055783514 100644 --- a/lisp/org/ob-ruby.el +++ b/lisp/org/ob-ruby.el @@ -1,4 +1,4 @@ -;;; ob-ruby.el --- org-babel functions for ruby evaluation +;;; ob-ruby.el --- Babel Functions for Ruby -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -37,11 +37,14 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) +(declare-function org-trim "org" (s &optional keep-lead)) (declare-function run-ruby "ext:inf-ruby" (&optional command name)) (declare-function xmp "ext:rcodetools" (&optional option)) +(defvar inf-ruby-default-implementation) +(defvar inf-ruby-implementations) + (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("ruby" . "rb")) @@ -68,16 +71,16 @@ "Execute a block of Ruby code with Babel. This function is called by `org-babel-execute-src-block'." (let* ((session (org-babel-ruby-initiate-session - (cdr (assoc :session params)))) - (result-params (cdr (assoc :result-params params))) - (result-type (cdr (assoc :result-type params))) + (cdr (assq :session params)))) + (result-params (cdr (assq :result-params params))) + (result-type (cdr (assq :result-type params))) (full-body (org-babel-expand-body:generic body params (org-babel-variable-assignments:ruby params))) (result (if (member "xmp" result-params) (with-temp-buffer (require 'rcodetools) (insert full-body) - (xmp (cdr (assoc :xmp-option params))) + (xmp (cdr (assq :xmp-option params))) (buffer-string)) (org-babel-ruby-evaluate session full-body result-type result-params)))) @@ -85,10 +88,10 @@ This function is called by `org-babel-execute-src-block'." (org-babel-result-cond result-params result (org-babel-ruby-table-or-string result)) - (org-babel-pick-name (cdr (assoc :colname-names params)) - (cdr (assoc :colnames params))) - (org-babel-pick-name (cdr (assoc :rowname-names params)) - (cdr (assoc :rownames params)))))) + (org-babel-pick-name (cdr (assq :colname-names params)) + (cdr (assq :colnames params))) + (org-babel-pick-name (cdr (assq :rowname-names params)) + (cdr (assq :rownames params)))))) (defun org-babel-prep-session:ruby (session params) "Prepare SESSION according to the header arguments specified in PARAMS." @@ -121,7 +124,7 @@ This function is called by `org-babel-execute-src-block'." (format "%s=%s" (car pair) (org-babel-ruby-var-to-ruby (cdr pair)))) - (mapcar #'cdr (org-babel-get-header params :var)))) + (org-babel--get-vars params))) (defun org-babel-ruby-var-to-ruby (var) "Convert VAR into a ruby variable. @@ -129,7 +132,7 @@ Convert an elisp value into a string of ruby source code specifying a variable of the same value." (if (listp var) (concat "[" (mapconcat #'org-babel-ruby-var-to-ruby var ", ") "]") - (if (equal var 'hline) + (if (eq var 'hline) org-babel-ruby-hline-to (format "%S" var)))) @@ -139,23 +142,27 @@ If RESULTS look like a table, then convert them into an Emacs-lisp table, otherwise return the results as a string." (let ((res (org-babel-script-escape results))) (if (listp res) - (mapcar (lambda (el) (if (equal el 'nil) - org-babel-ruby-nil-to el)) + (mapcar (lambda (el) (if (not el) + org-babel-ruby-nil-to el)) res) res))) -(defun org-babel-ruby-initiate-session (&optional session params) +(defun org-babel-ruby-initiate-session (&optional session _params) "Initiate a ruby session. If there is not a current inferior-process-buffer in SESSION then create one. Return the initialized session." (unless (string= session "none") (require 'inf-ruby) - (let ((session-buffer (save-window-excursion - (run-ruby nil session) (current-buffer)))) + (let* ((cmd (cdr (assoc inf-ruby-default-implementation + inf-ruby-implementations))) + (buffer (get-buffer (format "*%s*" session))) + (session-buffer (or buffer (save-window-excursion + (run-ruby cmd session) + (current-buffer))))) (if (org-babel-comint-buffer-livep session-buffer) (progn (sit-for .25) session-buffer) - (sit-for .5) - (org-babel-ruby-initiate-session session))))) + (sit-for .5) + (org-babel-ruby-initiate-session session))))) (defvar org-babel-ruby-eoe-indicator ":org_babel_ruby_eoe" "String to indicate that evaluation has completed.") @@ -185,46 +192,53 @@ end ") (defun org-babel-ruby-evaluate - (buffer body &optional result-type result-params) + (buffer body &optional result-type result-params) "Pass BODY to the Ruby process in BUFFER. -If RESULT-TYPE equals 'output then return a list of the outputs -of the statements in BODY, if RESULT-TYPE equals 'value then +If RESULT-TYPE equals `output' then return a list of the outputs +of the statements in BODY, if RESULT-TYPE equals `value' then return the value of the last statement in BODY, as elisp." (if (not buffer) ;; external process evaluation - (case result-type - (output (org-babel-eval org-babel-ruby-command body)) - (value (let ((tmp-file (org-babel-temp-file "ruby-"))) - (org-babel-eval - org-babel-ruby-command - (format (if (member "pp" result-params) - org-babel-ruby-pp-wrapper-method - org-babel-ruby-wrapper-method) - body (org-babel-process-file-name tmp-file 'noquote))) - (let ((raw (org-babel-eval-read-file tmp-file))) - (if (or (member "code" result-params) - (member "pp" result-params)) - raw - (org-babel-ruby-table-or-string raw)))))) + (pcase result-type + (`output (org-babel-eval org-babel-ruby-command body)) + (`value (let ((tmp-file (org-babel-temp-file "ruby-"))) + (org-babel-eval + org-babel-ruby-command + (format (if (member "pp" result-params) + org-babel-ruby-pp-wrapper-method + org-babel-ruby-wrapper-method) + body (org-babel-process-file-name tmp-file 'noquote))) + (org-babel-eval-read-file tmp-file)))) ;; comint session evaluation - (case result-type - (output - (mapconcat - #'identity - (butlast - (split-string - (mapconcat - #'org-babel-trim - (butlast - (org-babel-comint-with-output - (buffer org-babel-ruby-eoe-indicator t body) - (mapc - (lambda (line) - (insert (org-babel-chomp line)) (comint-send-input nil t)) - (list body org-babel-ruby-eoe-indicator)) - (comint-send-input nil t)) 2) - "\n") "[\r\n]")) "\n")) - (value + (pcase result-type + (`output + (let ((eoe-string (format "puts \"%s\"" org-babel-ruby-eoe-indicator))) + ;; Force the session to be ready before the actual session + ;; code is run. There is some problem in comint that will + ;; sometimes show the prompt after the the input has already + ;; been inserted and that throws off the extraction of the + ;; result for Babel. + (org-babel-comint-with-output + (buffer org-babel-ruby-eoe-indicator t eoe-string) + (insert eoe-string) (comint-send-input nil t)) + ;; Now we can start the evaluation. + (mapconcat + #'identity + (butlast + (split-string + (mapconcat + #'org-trim + (org-babel-comint-with-output + (buffer org-babel-ruby-eoe-indicator t body) + (mapc + (lambda (line) + (insert (org-babel-chomp line)) (comint-send-input nil t)) + (list "conf.echo=false;_org_prompt_mode=conf.prompt_mode;conf.prompt_mode=:NULL" + body + "conf.prompt_mode=_org_prompt_mode;conf.echo=true" + eoe-string))) + "\n") "[\r\n]") 4) "\n"))) + (`value (let* ((tmp-file (org-babel-temp-file "ruby-")) (ppp (or (member "code" result-params) (member "pp" result-params)))) @@ -247,12 +261,6 @@ return the value of the last statement in BODY, as elisp." (comint-send-input nil t)) (org-babel-eval-read-file tmp-file)))))) -(defun org-babel-ruby-read-string (string) - "Strip \\\"s from around a ruby string." - (if (string-match "^\"\\([^\000]+\\)\"$" string) - (match-string 1 string) - string)) - (provide 'ob-ruby) diff --git a/lisp/org/ob-sass.el b/lisp/org/ob-sass.el index 847c144e80..a9a2a9f030 100644 --- a/lisp/org/ob-sass.el +++ b/lisp/org/ob-sass.el @@ -1,4 +1,4 @@ -;;; ob-sass.el --- org-babel functions for the sass css generation language +;;; ob-sass.el --- Babel Functions for the Sass CSS generation language -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -45,10 +45,9 @@ (defun org-babel-execute:sass (body params) "Execute a block of Sass code with Babel. This function is called by `org-babel-execute-src-block'." - (let* ((result-params (split-string (or (cdr (assoc :results params)) ""))) - (file (cdr (assoc :file params))) + (let* ((file (cdr (assq :file params))) (out-file (or file (org-babel-temp-file "sass-out-"))) - (cmdline (cdr (assoc :cmdline params))) + (cmdline (cdr (assq :cmdline params))) (in-file (org-babel-temp-file "sass-in-")) (cmd (concat "sass " (or cmdline "") " " (org-babel-process-file-name in-file) @@ -60,7 +59,7 @@ This function is called by `org-babel-execute-src-block'." nil ;; signal that output has already been written to file (with-temp-buffer (insert-file-contents out-file) (buffer-string))))) -(defun org-babel-prep-session:sass (session params) +(defun org-babel-prep-session:sass (_session _params) "Raise an error because sass does not support sessions." (error "Sass does not support sessions")) diff --git a/lisp/org/ob-scala.el b/lisp/org/ob-scala.el index 9bddeed6e6..7d5f299ec6 100644 --- a/lisp/org/ob-scala.el +++ b/lisp/org/ob-scala.el @@ -1,4 +1,4 @@ -;;; ob-scala.el --- org-babel functions for Scala evaluation +;;; ob-scala.el --- Babel Functions for Scala -*- lexical-binding: t; -*- ;; Copyright (C) 2012-2017 Free Software Foundation, Inc. @@ -31,7 +31,6 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) (defvar org-babel-tangle-lang-exts) ;; Autoloaded (add-to-list 'org-babel-tangle-lang-exts '("scala" . "scala")) @@ -45,9 +44,8 @@ called by `org-babel-execute-src-block'" (message "executing Scala source code block") (let* ((processed-params (org-babel-process-params params)) (session (org-babel-scala-initiate-session (nth 0 processed-params))) - (vars (nth 1 processed-params)) (result-params (nth 2 processed-params)) - (result-type (cdr (assoc :result-type params))) + (result-type (cdr (assq :result-type params))) (full-body (org-babel-expand-body:generic body params)) (result (org-babel-scala-evaluate @@ -56,17 +54,9 @@ called by `org-babel-execute-src-block'" (org-babel-reassemble-table result (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params)))))) - - -(defun org-babel-scala-table-or-string (results) - "Convert RESULTS into an appropriate elisp value. -If RESULTS look like a table, then convert them into an -Emacs-lisp table, otherwise return the results as a string." - (org-babel-script-escape results)) - + (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) (defvar org-babel-scala-wrapper-method @@ -84,19 +74,19 @@ print(str_result) (defun org-babel-scala-evaluate - (session body &optional result-type result-params) + (session body &optional result-type result-params) "Evaluate BODY in external Scala process. -If RESULT-TYPE equals 'output then return standard output as a string. -If RESULT-TYPE equals 'value then return the value of the last statement +If RESULT-TYPE equals `output' then return standard output as a string. +If RESULT-TYPE equals `value' then return the value of the last statement in BODY as elisp." (when session (error "Sessions are not (yet) supported for Scala")) - (case result-type - (output + (pcase result-type + (`output (let ((src-file (org-babel-temp-file "scala-"))) - (progn (with-temp-file src-file (insert body)) - (org-babel-eval - (concat org-babel-scala-command " " src-file) "")))) - (value + (with-temp-file src-file (insert body)) + (org-babel-eval + (concat org-babel-scala-command " " src-file) ""))) + (`value (let* ((src-file (org-babel-temp-file "scala-")) (wrapper (format org-babel-scala-wrapper-method body))) (with-temp-file src-file (insert wrapper)) @@ -104,14 +94,14 @@ in BODY as elisp." (concat org-babel-scala-command " " src-file) ""))) (org-babel-result-cond result-params raw - (org-babel-scala-table-or-string raw))))))) + (org-babel-script-escape raw))))))) -(defun org-babel-prep-session:scala (session params) +(defun org-babel-prep-session:scala (_session _params) "Prepare SESSION according to the header arguments specified in PARAMS." (error "Sessions are not (yet) supported for Scala")) -(defun org-babel-scala-initiate-session (&optional session) +(defun org-babel-scala-initiate-session (&optional _session) "If there is not a current inferior-process-buffer in SESSION then create. Return the initialized session. Sessions are not supported in Scala." diff --git a/lisp/org/ob-scheme.el b/lisp/org/ob-scheme.el index ae77c7c3ed..cd8c3860e2 100644 --- a/lisp/org/ob-scheme.el +++ b/lisp/org/ob-scheme.el @@ -1,4 +1,4 @@ -;;; ob-scheme.el --- org-babel functions for Scheme +;;; ob-scheme.el --- Babel Functions for Scheme -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -56,7 +56,7 @@ (defun org-babel-expand-body:scheme (body params) "Expand BODY according to PARAMS, return the expanded body." - (let ((vars (mapcar #'cdr (org-babel-get-header params :var)))) + (let ((vars (org-babel--get-vars params))) (if (> (length vars) 0) (concat "(let (" (mapconcat @@ -119,6 +119,22 @@ org-babel-scheme-execute-with-geiser will use a temporary session." (name)))) result)) +(defmacro org-babel-scheme-capture-current-message (&rest body) + "Capture current message in both interactive and noninteractive mode" + `(if noninteractive + (let ((original-message (symbol-function 'message)) + (current-message nil)) + (unwind-protect + (progn + (defun message (&rest args) + (setq current-message (apply original-message args))) + ,@body + current-message) + (fset 'message original-message))) + (progn + ,@body + (current-message)))) + (defun org-babel-scheme-execute-with-geiser (code output impl repl) "Execute code in specified REPL. If the REPL doesn't exist, create it using the given scheme implementation. @@ -143,10 +159,11 @@ is true; otherwise returns the last value." (current-buffer))))) (setq geiser-repl--repl repl-buffer) (setq geiser-impl--implementation nil) - (geiser-eval-region (point-min) (point-max)) + (setq result (org-babel-scheme-capture-current-message + (geiser-eval-region (point-min) (point-max)))) (setq result - (if (equal (substring (current-message) 0 3) "=> ") - (replace-regexp-in-string "^=> " "" (current-message)) + (if (and (stringp result) (equal (substring result 0 3) "=> ")) + (replace-regexp-in-string "^=> " "" result) "\"An error occurred.\"")) (when (not repl) (save-current-buffer (set-buffer repl-buffer) @@ -156,7 +173,7 @@ is true; otherwise returns the last value." (setq result (if (or (string= result "#") (string= result "#")) nil - (read result))))) + result)))) result)) (defun org-babel-execute:scheme (body params) @@ -168,23 +185,23 @@ This function is called by `org-babel-execute-src-block'" (buffer-name source-buffer)))) (save-excursion (org-babel-reassemble-table - (let* ((result-type (cdr (assoc :result-type params))) - (impl (or (when (cdr (assoc :scheme params)) - (intern (cdr (assoc :scheme params)))) + (let* ((result-type (cdr (assq :result-type params))) + (impl (or (when (cdr (assq :scheme params)) + (intern (cdr (assq :scheme params)))) geiser-default-implementation (car geiser-active-implementations))) (session (org-babel-scheme-make-session-name - source-buffer-name (cdr (assoc :session params)) impl)) + source-buffer-name (cdr (assq :session params)) impl)) (full-body (org-babel-expand-body:scheme body params))) (org-babel-scheme-execute-with-geiser full-body ; code (string= result-type "output") ; output? impl ; implementation (and (not (string= session "none")) session))) ; session - (org-babel-pick-name (cdr (assoc :colname-names params)) - (cdr (assoc :colnames params))) - (org-babel-pick-name (cdr (assoc :rowname-names params)) - (cdr (assoc :rownames params))))))) + (org-babel-pick-name (cdr (assq :colname-names params)) + (cdr (assq :colnames params))) + (org-babel-pick-name (cdr (assq :rowname-names params)) + (cdr (assq :rownames params))))))) (provide 'ob-scheme) diff --git a/lisp/org/ob-screen.el b/lisp/org/ob-screen.el index a15f7f7bd8..554f8c4385 100644 --- a/lisp/org/ob-screen.el +++ b/lisp/org/ob-screen.el @@ -1,4 +1,4 @@ -;;; ob-screen.el --- org-babel support for interactive terminal +;;; ob-screen.el --- Babel Support for Interactive Terminal -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -48,18 +48,17 @@ In case you want to use a different screen than one selected by your $PATH") \"default\" session is used when none is specified." (message "Sending source code block to interactive terminal session...") (save-window-excursion - (let* ((session (cdr (assoc :session params))) + (let* ((session (cdr (assq :session params))) (socket (org-babel-screen-session-socketname session))) (unless socket (org-babel-prep-session:screen session params)) (org-babel-screen-session-execute-string session (org-babel-expand-body:generic body params))))) -(defun org-babel-prep-session:screen (session params) +(defun org-babel-prep-session:screen (_session params) "Prepare SESSION according to the header arguments specified in PARAMS." - (let* ((session (cdr (assoc :session params))) - (socket (org-babel-screen-session-socketname session)) - (cmd (cdr (assoc :cmd params))) - (terminal (cdr (assoc :terminal params))) + (let* ((session (cdr (assq :session params))) + (cmd (cdr (assq :cmd params))) + (terminal (cdr (assq :terminal params))) (process-name (concat "org-babel: terminal (" session ")"))) (apply 'start-process process-name "*Messages*" terminal `("-T" ,(concat "org-babel: " session) "-e" ,org-babel-screen-location @@ -104,7 +103,7 @@ In case you want to use a different screen than one selected by your $PATH") sockets))))) (when match-socket (car (split-string match-socket))))) -(defun org-babel-screen-session-write-temp-file (session body) +(defun org-babel-screen-session-write-temp-file (_session body) "Save BODY in a temp file that is named after SESSION." (let ((tmpfile (org-babel-temp-file "screen-"))) (with-temp-file tmpfile @@ -119,11 +118,10 @@ In case you want to use a different screen than one selected by your $PATH") "Test if the default setup works. The terminal should shortly flicker." (interactive) - (let* ((session "org-babel-testing") - (random-string (format "%s" (random 99999))) + (let* ((random-string (format "%s" (random 99999))) (tmpfile (org-babel-temp-file "ob-screen-test-")) (body (concat "echo '" random-string "' > " tmpfile "\nexit\n")) - process tmp-string) + tmp-string) (org-babel-execute:screen body org-babel-default-header-args:screen) ;; XXX: need to find a better way to do the following (while (not (file-readable-p tmpfile)) diff --git a/lisp/org/ob-sed.el b/lisp/org/ob-sed.el new file mode 100644 index 0000000000..733c7e19d3 --- /dev/null +++ b/lisp/org/ob-sed.el @@ -0,0 +1,107 @@ +;;; ob-sed.el --- Babel Functions for Sed Scripts -*- lexical-binding: t; -*- + +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. + +;; Author: Bjarte Johansen +;; Keywords: literate programming, reproducible research +;; Version: 0.1.0 + +;; This file is part of GNU Emacs. + +;;; License: + +;; 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, 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 GNU Emacs. If not, see . + +;;; Commentary: + +;; Provides a way to evaluate sed scripts in Org mode. + +;;; Usage: + +;; Add to your Emacs config: + +;; (org-babel-do-load-languages +;; 'org-babel-load-languages +;; '((sed . t))) + +;; In addition to the normal header arguments, ob-sed also provides +;; :cmd-line and :in-file. :cmd-line allows one to pass other flags to +;; the sed command like the "--in-place" flag which makes sed edit the +;; file pass to it instead of outputting to standard out or to a +;; different file. :in-file is a header arguments that allows one to +;; tell Org Babel which file the sed script to act on. + +;;; Code: +(require 'ob) + +(defvar org-babel-sed-command "sed" + "Name of the sed executable command.") + +(defvar org-babel-tangle-lang-exts) +(add-to-list 'org-babel-tangle-lang-exts '("sed" . "sed")) + +(defconst org-babel-header-args:sed + '((:cmd-line . :any) + (:in-file . :any)) + "Sed specific header arguments.") + +(defvar org-babel-default-header-args:sed '() + "Default arguments for evaluating a sed source block.") + +(defun org-babel-execute:sed (body params) + "Execute a block of sed code with Org Babel. +BODY is the source inside a sed source block and PARAMS is an +association list over the source block configurations. This +function is called by `org-babel-execute-src-block'." + (message "executing sed source code block") + (let* ((result-params (cdr (assq :result-params params))) + (cmd-line (cdr (assq :cmd-line params))) + (in-file (cdr (assq :in-file params))) + (code-file (let ((file (org-babel-temp-file "sed-"))) + (with-temp-file file + (insert body)) file)) + (stdin (let ((stdin (cdr (assq :stdin params)))) + (when stdin + (let ((tmp (org-babel-temp-file "sed-stdin-")) + (res (org-babel-ref-resolve stdin))) + (with-temp-file tmp + (insert res)) + tmp)))) + (cmd (mapconcat #'identity + (remq nil + (list org-babel-sed-command + (format "--file=\"%s\"" code-file) + cmd-line + in-file)) + " "))) + (org-babel-reassemble-table + (let ((results + (cond + (stdin (with-temp-buffer + (call-process-shell-command cmd stdin (current-buffer)) + (buffer-string))) + (t (org-babel-eval cmd ""))))) + (when results + (org-babel-result-cond result-params + results + (let ((tmp (org-babel-temp-file "sed-results-"))) + (with-temp-file tmp (insert results)) + (org-babel-import-elisp-from-file tmp))))) + (org-babel-pick-name + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) + (org-babel-pick-name + (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) + +(provide 'ob-sed) +;;; ob-sed.el ends here diff --git a/lisp/org/ob-sh.el b/lisp/org/ob-sh.el deleted file mode 100644 index 47dbab3f6d..0000000000 --- a/lisp/org/ob-sh.el +++ /dev/null @@ -1,217 +0,0 @@ -;;; ob-sh.el --- org-babel functions for shell evaluation - -;; Copyright (C) 2009-2017 Free Software Foundation, Inc. - -;; Author: Eric Schulte -;; Keywords: literate programming, reproducible research -;; Homepage: http://orgmode.org - -;; 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 . - -;;; Commentary: - -;; Org-Babel support for evaluating shell source code. - -;;; Code: -(require 'ob) -(require 'shell) -(eval-when-compile (require 'cl)) - -(declare-function org-babel-comint-wait-for-output "ob-comint" (buffer)) -(declare-function org-babel-comint-buffer-livep "ob-comint" (buffer)) -(declare-function orgtbl-to-generic "org-table" - (table params &optional backend)) - -(defvar org-babel-default-header-args:sh '()) - -(defvar org-babel-sh-command "sh" - "Command used to invoke a shell. -This will be passed to `shell-command-on-region'") - -(defcustom org-babel-sh-var-quote-fmt - "$(cat <<'BABEL_TABLE'\n%s\nBABEL_TABLE\n)" - "Format string used to escape variables when passed to shell scripts." - :group 'org-babel - :type 'string) - -(defun org-babel-execute:sh (body params) - "Execute a block of Shell commands with Babel. -This function is called by `org-babel-execute-src-block'." - (let* ((session (org-babel-sh-initiate-session - (cdr (assoc :session params)))) - (stdin (let ((stdin (cdr (assoc :stdin params)))) - (when stdin (org-babel-sh-var-to-string - (org-babel-ref-resolve stdin))))) - (full-body (org-babel-expand-body:generic - body params (org-babel-variable-assignments:sh params)))) - (org-babel-reassemble-table - (org-babel-sh-evaluate session full-body params stdin) - (org-babel-pick-name - (cdr (assoc :colname-names params)) (cdr (assoc :colnames params))) - (org-babel-pick-name - (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params)))))) - -(defun org-babel-prep-session:sh (session params) - "Prepare SESSION according to the header arguments specified in PARAMS." - (let* ((session (org-babel-sh-initiate-session session)) - (var-lines (org-babel-variable-assignments:sh params))) - (org-babel-comint-in-buffer session - (mapc (lambda (var) - (insert var) (comint-send-input nil t) - (org-babel-comint-wait-for-output session)) var-lines)) - session)) - -(defun org-babel-load-session:sh (session body params) - "Load BODY into SESSION." - (save-window-excursion - (let ((buffer (org-babel-prep-session:sh session params))) - (with-current-buffer buffer - (goto-char (process-mark (get-buffer-process (current-buffer)))) - (insert (org-babel-chomp body))) - buffer))) - -;; helper functions - -(defun org-babel-variable-assignments:sh (params) - "Return list of shell statements assigning the block's variables." - (let ((sep (cdr (assoc :separator params)))) - (mapcar - (lambda (pair) - (format "%s=%s" - (car pair) - (org-babel-sh-var-to-sh (cdr pair) sep))) - (mapcar #'cdr (org-babel-get-header params :var))))) - -(defun org-babel-sh-var-to-sh (var &optional sep) - "Convert an elisp value to a shell variable. -Convert an elisp var into a string of shell commands specifying a -var of the same value." - (format org-babel-sh-var-quote-fmt (org-babel-sh-var-to-string var sep))) - -(defun org-babel-sh-var-to-string (var &optional sep) - "Convert an elisp value to a string." - (let ((echo-var (lambda (v) (if (stringp v) v (format "%S" v))))) - (cond - ((and (listp var) (or (listp (car var)) (equal (car var) 'hline))) - (orgtbl-to-generic var (list :sep (or sep "\t") :fmt echo-var))) - ((listp var) - (mapconcat echo-var var "\n")) - (t (funcall echo-var var))))) - -(defun org-babel-sh-table-or-results (results) - "Convert RESULTS to an appropriate elisp value. -If the results look like a table, then convert them into an -Emacs-lisp table, otherwise return the results as a string." - (org-babel-script-escape results)) - -(defun org-babel-sh-initiate-session (&optional session params) - "Initiate a session named SESSION according to PARAMS." - (when (and session (not (string= session "none"))) - (save-window-excursion - (or (org-babel-comint-buffer-livep session) - (progn - (shell session) - ;; Needed for Emacs 23 since the marker is initially - ;; undefined and the filter functions try to use it without - ;; checking. - (set-marker comint-last-output-start (point)) - (get-buffer (current-buffer))))))) - -(defvar org-babel-sh-eoe-indicator "echo 'org_babel_sh_eoe'" - "String to indicate that evaluation has completed.") -(defvar org-babel-sh-eoe-output "org_babel_sh_eoe" - "String to indicate that evaluation has completed.") - -(defun org-babel-sh-evaluate (session body &optional params stdin) - "Pass BODY to the Shell process in BUFFER. -If RESULT-TYPE equals 'output then return a list of the outputs -of the statements in BODY, if RESULT-TYPE equals 'value then -return the value of the last statement in BODY." - (let ((results - (cond - (stdin ; external shell script w/STDIN - (let ((script-file (org-babel-temp-file "sh-script-")) - (stdin-file (org-babel-temp-file "sh-stdin-")) - (shebang (cdr (assoc :shebang params))) - (padline (not (string= "no" (cdr (assoc :padline params)))))) - (with-temp-file script-file - (when shebang (insert (concat shebang "\n"))) - (when padline (insert "\n")) - (insert body)) - (set-file-modes script-file #o755) - (with-temp-file stdin-file (insert stdin)) - (with-temp-buffer - (call-process-shell-command - (if shebang - script-file - (format "%s %s" org-babel-sh-command script-file)) - stdin-file - (current-buffer)) - (buffer-string)))) - (session ; session evaluation - (mapconcat - #'org-babel-sh-strip-weird-long-prompt - (mapcar - #'org-babel-trim - (butlast - (org-babel-comint-with-output - (session org-babel-sh-eoe-output t body) - (mapc - (lambda (line) - (insert line) - (comint-send-input nil t) - (while (save-excursion - (goto-char comint-last-input-end) - (not (re-search-forward - comint-prompt-regexp nil t))) - (accept-process-output - (get-buffer-process (current-buffer))))) - (append - (split-string (org-babel-trim body) "\n") - (list org-babel-sh-eoe-indicator)))) - 2)) "\n")) - ('otherwise ; external shell script - (if (and (cdr (assoc :shebang params)) - (> (length (cdr (assoc :shebang params))) 0)) - (let ((script-file (org-babel-temp-file "sh-script-")) - (shebang (cdr (assoc :shebang params))) - (padline (not (equal "no" (cdr (assoc :padline params)))))) - (with-temp-file script-file - (when shebang (insert (concat shebang "\n"))) - (when padline (insert "\n")) - (insert body)) - (set-file-modes script-file #o755) - (org-babel-eval script-file "")) - (org-babel-eval org-babel-sh-command (org-babel-trim body))))))) - (when results - (let ((result-params (cdr (assoc :result-params params)))) - (org-babel-result-cond result-params - results - (let ((tmp-file (org-babel-temp-file "sh-"))) - (with-temp-file tmp-file (insert results)) - (org-babel-import-elisp-from-file tmp-file))))))) - -(defun org-babel-sh-strip-weird-long-prompt (string) - "Remove prompt cruft from a string of shell output." - (while (string-match "^% +[\r\n$]+ *" string) - (setq string (substring string (match-end 0)))) - string) - -(provide 'ob-sh) - - - -;;; ob-sh.el ends here diff --git a/lisp/org/ob-shell.el b/lisp/org/ob-shell.el new file mode 100644 index 0000000000..3787c26a19 --- /dev/null +++ b/lisp/org/ob-shell.el @@ -0,0 +1,283 @@ +;;; ob-shell.el --- Babel Functions for Shell Evaluation -*- lexical-binding: t; -*- + +;; Copyright (C) 2009-2017 Free Software Foundation, Inc. + +;; Author: Eric Schulte +;; Keywords: literate programming, reproducible research +;; Homepage: http://orgmode.org + +;; 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 . + +;;; Commentary: + +;; Org-Babel support for evaluating shell source code. + +;;; Code: +(require 'ob) +(require 'shell) +(require 'cl-lib) + +(declare-function org-babel-comint-in-buffer "ob-comint" (buffer &rest body) + t) +(declare-function org-babel-comint-wait-for-output "ob-comint" (buffer)) +(declare-function org-babel-comint-buffer-livep "ob-comint" (buffer)) +(declare-function org-babel-comint-with-output "ob-comint" (meta &rest body) + t) +(declare-function org-trim "org" (s &optional keep-lead)) +(declare-function orgtbl-to-generic "org-table" (table params)) + +(defvar org-babel-default-header-args:shell '()) +(defvar org-babel-shell-names) + +(defun org-babel-shell-initialize () + "Define execution functions associated to shell names. +This function has to be called whenever `org-babel-shell-names' +is modified outside the Customize interface." + (interactive) + (dolist (name org-babel-shell-names) + (eval `(defun ,(intern (concat "org-babel-execute:" name)) + (body params) + ,(format "Execute a block of %s commands with Babel." name) + (let ((shell-file-name ,name)) + (org-babel-execute:shell body params)))) + (eval `(defalias ',(intern (concat "org-babel-variable-assignments:" name)) + 'org-babel-variable-assignments:shell + ,(format "Return list of %s statements assigning to the block's \ +variables." + name))))) + +(defcustom org-babel-shell-names + '("sh" "bash" "csh" "ash" "dash" "ksh" "mksh" "posh") + "List of names of shell supported by babel shell code blocks. +Call `org-babel-shell-initialize' when modifying this variable +outside the Customize interface." + :group 'org-babel + :type '(repeat (string :tag "Shell name: ")) + :set (lambda (symbol value) + (set-default symbol value) + (org-babel-shell-initialize))) + +(defun org-babel-execute:shell (body params) + "Execute a block of Shell commands with Babel. +This function is called by `org-babel-execute-src-block'." + (let* ((session (org-babel-sh-initiate-session + (cdr (assq :session params)))) + (stdin (let ((stdin (cdr (assq :stdin params)))) + (when stdin (org-babel-sh-var-to-string + (org-babel-ref-resolve stdin))))) + (cmdline (cdr (assq :cmdline params))) + (full-body (org-babel-expand-body:generic + body params (org-babel-variable-assignments:shell params)))) + (org-babel-reassemble-table + (org-babel-sh-evaluate session full-body params stdin cmdline) + (org-babel-pick-name + (cdr (assq :colname-names params)) (cdr (assq :colnames params))) + (org-babel-pick-name + (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) + +(defun org-babel-prep-session:shell (session params) + "Prepare SESSION according to the header arguments specified in PARAMS." + (let* ((session (org-babel-sh-initiate-session session)) + (var-lines (org-babel-variable-assignments:shell params))) + (org-babel-comint-in-buffer session + (mapc (lambda (var) + (insert var) (comint-send-input nil t) + (org-babel-comint-wait-for-output session)) var-lines)) + session)) + +(defun org-babel-load-session:shell (session body params) + "Load BODY into SESSION." + (save-window-excursion + (let ((buffer (org-babel-prep-session:shell session params))) + (with-current-buffer buffer + (goto-char (process-mark (get-buffer-process (current-buffer)))) + (insert (org-babel-chomp body))) + buffer))) + + +;;; Helper functions +(defun org-babel--variable-assignments:sh-generic + (varname values &optional sep hline) + "Returns a list of statements declaring the values as a generic variable." + (format "%s=%s" varname (org-babel-sh-var-to-sh values sep hline))) + +(defun org-babel--variable-assignments:bash_array + (varname values &optional sep hline) + "Returns a list of statements declaring the values as a bash array." + (format "unset %s\ndeclare -a %s=( %s )" + varname varname + (mapconcat + (lambda (value) (org-babel-sh-var-to-sh value sep hline)) + values + " "))) + +(defun org-babel--variable-assignments:bash_assoc + (varname values &optional sep hline) + "Returns a list of statements declaring the values as bash associative array." + (format "unset %s\ndeclare -A %s\n%s" + varname varname + (mapconcat + (lambda (items) + (format "%s[%s]=%s" + varname + (org-babel-sh-var-to-sh (car items) sep hline) + (org-babel-sh-var-to-sh (cdr items) sep hline))) + values + "\n"))) + +(defun org-babel--variable-assignments:bash (varname values &optional sep hline) + "Represents the parameters as useful Bash shell variables." + (pcase values + (`((,_ ,_ . ,_) . ,_) ;two-dimensional array + (org-babel--variable-assignments:bash_assoc varname values sep hline)) + (`(,_ . ,_) ;simple list + (org-babel--variable-assignments:bash_array varname values sep hline)) + (_ ;scalar value + (org-babel--variable-assignments:sh-generic varname values sep hline)))) + +(defun org-babel-variable-assignments:shell (params) + "Return list of shell statements assigning the block's variables." + (let ((sep (cdr (assq :separator params))) + (hline (when (string= "yes" (cdr (assq :hlines params))) + (or (cdr (assq :hline-string params)) + "hline")))) + (mapcar + (lambda (pair) + (if (string-suffix-p "bash" shell-file-name) + (org-babel--variable-assignments:bash + (car pair) (cdr pair) sep hline) + (org-babel--variable-assignments:sh-generic + (car pair) (cdr pair) sep hline))) + (org-babel--get-vars params)))) + +(defun org-babel-sh-var-to-sh (var &optional sep hline) + "Convert an elisp value to a shell variable. +Convert an elisp var into a string of shell commands specifying a +var of the same value." + (concat "'" (replace-regexp-in-string + "'" "'\"'\"'" + (org-babel-sh-var-to-string var sep hline)) + "'")) + +(defun org-babel-sh-var-to-string (var &optional sep hline) + "Convert an elisp value to a string." + (let ((echo-var (lambda (v) (if (stringp v) v (format "%S" v))))) + (cond + ((and (listp var) (or (listp (car var)) (eq (car var) 'hline))) + (orgtbl-to-generic var (list :sep (or sep "\t") :fmt echo-var + :hline hline))) + ((listp var) + (mapconcat echo-var var "\n")) + (t (funcall echo-var var))))) + +(defun org-babel-sh-initiate-session (&optional session _params) + "Initiate a session named SESSION according to PARAMS." + (when (and session (not (string= session "none"))) + (save-window-excursion + (or (org-babel-comint-buffer-livep session) + (progn + (shell session) + ;; Needed for Emacs 23 since the marker is initially + ;; undefined and the filter functions try to use it without + ;; checking. + (set-marker comint-last-output-start (point)) + (get-buffer (current-buffer))))))) + +(defvar org-babel-sh-eoe-indicator "echo 'org_babel_sh_eoe'" + "String to indicate that evaluation has completed.") +(defvar org-babel-sh-eoe-output "org_babel_sh_eoe" + "String to indicate that evaluation has completed.") + +(defun org-babel-sh-evaluate (session body &optional params stdin cmdline) + "Pass BODY to the Shell process in BUFFER. +If RESULT-TYPE equals `output' then return a list of the outputs +of the statements in BODY, if RESULT-TYPE equals `value' then +return the value of the last statement in BODY." + (let ((results + (cond + ((or stdin cmdline) ; external shell script w/STDIN + (let ((script-file (org-babel-temp-file "sh-script-")) + (stdin-file (org-babel-temp-file "sh-stdin-")) + (shebang (cdr (assq :shebang params))) + (padline (not (string= "no" (cdr (assq :padline params)))))) + (with-temp-file script-file + (when shebang (insert (concat shebang "\n"))) + (when padline (insert "\n")) + (insert body)) + (set-file-modes script-file #o755) + (with-temp-file stdin-file (insert (or stdin ""))) + (with-temp-buffer + (call-process-shell-command + (concat (if shebang script-file + (format "%s %s" shell-file-name script-file)) + (and cmdline (concat " " cmdline))) + stdin-file + (current-buffer)) + (buffer-string)))) + (session ; session evaluation + (mapconcat + #'org-babel-sh-strip-weird-long-prompt + (mapcar + #'org-trim + (butlast + (org-babel-comint-with-output + (session org-babel-sh-eoe-output t body) + (mapc + (lambda (line) + (insert line) + (comint-send-input nil t) + (while (save-excursion + (goto-char comint-last-input-end) + (not (re-search-forward + comint-prompt-regexp nil t))) + (accept-process-output + (get-buffer-process (current-buffer))))) + (append + (split-string (org-trim body) "\n") + (list org-babel-sh-eoe-indicator)))) + 2)) "\n")) + ('otherwise ; external shell script + (if (and (cdr (assq :shebang params)) + (> (length (cdr (assq :shebang params))) 0)) + (let ((script-file (org-babel-temp-file "sh-script-")) + (shebang (cdr (assq :shebang params))) + (padline (not (equal "no" (cdr (assq :padline params)))))) + (with-temp-file script-file + (when shebang (insert (concat shebang "\n"))) + (when padline (insert "\n")) + (insert body)) + (set-file-modes script-file #o755) + (org-babel-eval script-file "")) + (org-babel-eval shell-file-name (org-trim body))))))) + (when results + (let ((result-params (cdr (assq :result-params params)))) + (org-babel-result-cond result-params + results + (let ((tmp-file (org-babel-temp-file "sh-"))) + (with-temp-file tmp-file (insert results)) + (org-babel-import-elisp-from-file tmp-file))))))) + +(defun org-babel-sh-strip-weird-long-prompt (string) + "Remove prompt cruft from a string of shell output." + (while (string-match "^% +[\r\n$]+ *" string) + (setq string (substring string (match-end 0)))) + string) + +(provide 'ob-shell) + + + +;;; ob-shell.el ends here diff --git a/lisp/org/ob-shen.el b/lisp/org/ob-shen.el index d44a48a638..6bf36c6437 100644 --- a/lisp/org/ob-shen.el +++ b/lisp/org/ob-shen.el @@ -1,4 +1,4 @@ -;;; ob-shen.el --- org-babel functions for Shen +;;; ob-shen.el --- Babel Functions for Shen -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -43,7 +43,7 @@ (defun org-babel-expand-body:shen (body params) "Expand BODY according to PARAMS, return the expanded body." - (let ((vars (mapcar #'cdr (org-babel-get-header params :var)))) + (let ((vars (org-babel--get-vars params))) (if (> (length vars) 0) (concat "(let " (mapconcat (lambda (var) @@ -63,14 +63,13 @@ "Execute a block of Shen code with org-babel. This function is called by `org-babel-execute-src-block'" (require 'inf-shen) - (let* ((result-type (cdr (assoc :result-type params))) - (result-params (cdr (assoc :result-params params))) + (let* ((result-params (cdr (assq :result-params params))) (full-body (org-babel-expand-body:shen body params))) (let ((results (with-temp-buffer (insert full-body) (call-interactively #'shen-eval-defun)))) - (org-babel-result-cond result-params + (org-babel-result-cond result-params results (condition-case nil (org-babel-script-escape results) (error results)))))) diff --git a/lisp/org/ob-sql.el b/lisp/org/ob-sql.el index 17775829cb..06477d3846 100644 --- a/lisp/org/ob-sql.el +++ b/lisp/org/ob-sql.el @@ -1,4 +1,4 @@ -;;; ob-sql.el --- org-babel functions for sql evaluation +;;; ob-sql.el --- Babel Functions for SQL -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -36,6 +36,7 @@ ;; - engine ;; - cmdline ;; - dbhost +;; - dbport ;; - dbuser ;; - dbpassword ;; - database @@ -56,11 +57,11 @@ ;;; Code: (require 'ob) -(eval-when-compile (require 'cl)) (declare-function org-table-import "org-table" (file arg)) (declare-function orgtbl-to-csv "org-table" (table params)) (declare-function org-table-to-lisp "org-table" (&optional txt)) +(declare-function cygwin-convert-file-name-to-windows "cygw32.c" (file &optional absolute-p)) (defvar org-babel-default-header-args:sql '()) @@ -68,6 +69,7 @@ '((engine . :any) (out-file . :any) (dbhost . :any) + (dbport . :any) (dbuser . :any) (dbpassword . :any) (database . :any)) @@ -76,98 +78,167 @@ (defun org-babel-expand-body:sql (body params) "Expand BODY according to the values of PARAMS." (org-babel-sql-expand-vars - body (mapcar #'cdr (org-babel-get-header params :var)))) + body (org-babel--get-vars params))) -(defun dbstring-mysql (host user password database) +(defun org-babel-sql-dbstring-mysql (host port user password database) "Make MySQL cmd line args for database connection. Pass nil to omit that arg." (combine-and-quote-strings - (remq nil + (delq nil (list (when host (concat "-h" host)) + (when port (format "-P%d" port)) (when user (concat "-u" user)) (when password (concat "-p" password)) (when database (concat "-D" database)))))) +(defun org-babel-sql-dbstring-postgresql (host port user database) + "Make PostgreSQL command line args for database connection. +Pass nil to omit that arg." + (combine-and-quote-strings + (delq nil + (list (when host (concat "-h" host)) + (when port (format "-p%d" port)) + (when user (concat "-U" user)) + (when database (concat "-d" database)))))) + +(defun org-babel-sql-dbstring-oracle (host port user password database) + "Make Oracle command line args for database connection." + (format "%s/%s@%s:%s/%s" user password host port database)) + +(defun org-babel-sql-dbstring-mssql (host user password database) + "Make sqlcmd commmand line args for database connection. +`sqlcmd' is the preferred command line tool to access Microsoft +SQL Server on Windows and Linux platform." + (mapconcat #'identity + (delq nil + (list (when host (format "-S \"%s\"" host)) + (when user (format "-U \"%s\"" user)) + (when password (format "-P \"%s\"" password)) + (when database (format "-d \"%s\"" database)))) + " ")) + +(defun org-babel-sql-convert-standard-filename (file) + "Convert FILE to OS standard file name. +If in Cygwin environment, uses Cygwin specific function to +convert the file name. In a Windows-NT environment, do nothing. +Otherwise, use Emacs' standard conversion function." + (cond ((fboundp 'cygwin-convert-file-name-to-windows) + (format "%S" (cygwin-convert-file-name-to-windows file))) + ((string= "windows-nt" system-type) file) + (t (format "%S" (convert-standard-filename file))))) + (defun org-babel-execute:sql (body params) "Execute a block of Sql code with Babel. This function is called by `org-babel-execute-src-block'." - (let* ((result-params (cdr (assoc :result-params params))) - (cmdline (cdr (assoc :cmdline params))) - (dbhost (cdr (assoc :dbhost params))) - (dbuser (cdr (assoc :dbuser params))) - (dbpassword (cdr (assoc :dbpassword params))) - (database (cdr (assoc :database params))) - (engine (cdr (assoc :engine params))) - (colnames-p (not (equal "no" (cdr (assoc :colnames params))))) + (let* ((result-params (cdr (assq :result-params params))) + (cmdline (cdr (assq :cmdline params))) + (dbhost (cdr (assq :dbhost params))) + (dbport (cdr (assq :dbport params))) + (dbuser (cdr (assq :dbuser params))) + (dbpassword (cdr (assq :dbpassword params))) + (database (cdr (assq :database params))) + (engine (cdr (assq :engine params))) + (colnames-p (not (equal "no" (cdr (assq :colnames params))))) (in-file (org-babel-temp-file "sql-in-")) - (out-file (or (cdr (assoc :out-file params)) + (out-file (or (cdr (assq :out-file params)) (org-babel-temp-file "sql-out-"))) (header-delim "") - (command (case (intern engine) - ('dbi (format "dbish --batch %s < %s | sed '%s' > %s" + (command (pcase (intern engine) + (`dbi (format "dbish --batch %s < %s | sed '%s' > %s" (or cmdline "") (org-babel-process-file-name in-file) "/^+/d;s/^|//;s/(NULL)/ /g;$d" (org-babel-process-file-name out-file))) - ('monetdb (format "mclient -f tab %s < %s > %s" - (or cmdline "") - (org-babel-process-file-name in-file) - (org-babel-process-file-name out-file))) - ('msosql (format "osql %s -s \"\t\" -i %s -o %s" - (or cmdline "") - (org-babel-process-file-name in-file) - (org-babel-process-file-name out-file))) - ('mysql (format "mysql %s %s %s < %s > %s" - (dbstring-mysql dbhost dbuser dbpassword database) + (`monetdb (format "mclient -f tab %s < %s > %s" + (or cmdline "") + (org-babel-process-file-name in-file) + (org-babel-process-file-name out-file))) + (`mssql (format "sqlcmd %s -s \"\t\" %s -i %s -o %s" + (or cmdline "") + (org-babel-sql-dbstring-mssql + dbhost dbuser dbpassword database) + (org-babel-sql-convert-standard-filename + (org-babel-process-file-name in-file)) + (org-babel-sql-convert-standard-filename + (org-babel-process-file-name out-file)))) + (`mysql (format "mysql %s %s %s < %s > %s" + (org-babel-sql-dbstring-mysql + dbhost dbport dbuser dbpassword database) (if colnames-p "" "-N") - (or cmdline "") + (or cmdline "") (org-babel-process-file-name in-file) (org-babel-process-file-name out-file))) - ('postgresql (format - "psql -A -P footer=off -F \"\t\" -f %s -o %s %s" + (`postgresql (format + "%spsql --set=\"ON_ERROR_STOP=1\" %s -A -P \ +footer=off -F \"\t\" %s -f %s -o %s %s" + (if dbpassword + (format "PGPASSWORD=%s " dbpassword) + "") + (if colnames-p "" "-t") + (org-babel-sql-dbstring-postgresql + dbhost dbport dbuser database) (org-babel-process-file-name in-file) (org-babel-process-file-name out-file) (or cmdline ""))) - (t (error "No support for the %s SQL engine" engine))))) + (`oracle (format + "sqlplus -s %s < %s > %s" + (org-babel-sql-dbstring-oracle + dbhost dbport dbuser dbpassword database) + (org-babel-process-file-name in-file) + (org-babel-process-file-name out-file))) + (_ (error "No support for the %s SQL engine" engine))))) (with-temp-file in-file (insert - (case (intern engine) - ('dbi "/format partbox\n") - (t "")) + (pcase (intern engine) + (`dbi "/format partbox\n") + (`oracle "SET PAGESIZE 50000 +SET NEWPAGE 0 +SET TAB OFF +SET SPACE 0 +SET LINESIZE 9999 +SET ECHO OFF +SET FEEDBACK OFF +SET VERIFY OFF +SET HEADING ON +SET MARKUP HTML OFF SPOOL OFF +SET COLSEP '|' + +") + (`mssql "SET NOCOUNT ON + +") + (_ "")) (org-babel-expand-body:sql body params))) - (message command) (org-babel-eval command "") (org-babel-result-cond result-params (with-temp-buffer - (progn (insert-file-contents-literally out-file) (buffer-string))) + (progn (insert-file-contents-literally out-file) (buffer-string))) (with-temp-buffer (cond - ((or (eq (intern engine) 'mysql) - (eq (intern engine) 'dbi) - (eq (intern engine) 'postgresql)) - ;; Add header row delimiter after column-names header in first line - (cond - (colnames-p - (with-temp-buffer - (insert-file-contents out-file) - (goto-char (point-min)) - (forward-line 1) - (insert "-\n") - (setq header-delim "-") - (write-file out-file))))) - (t - ;; Need to figure out the delimiter for the header row - (with-temp-buffer - (insert-file-contents out-file) - (goto-char (point-min)) - (when (re-search-forward "^\\(-+\\)[^-]" nil t) - (setq header-delim (match-string-no-properties 1))) - (goto-char (point-max)) - (forward-char -1) - (while (looking-at "\n") - (delete-char 1) - (goto-char (point-max)) - (forward-char -1)) - (write-file out-file)))) + ((memq (intern engine) '(dbi mysql postgresql)) + ;; Add header row delimiter after column-names header in first line + (cond + (colnames-p + (with-temp-buffer + (insert-file-contents out-file) + (goto-char (point-min)) + (forward-line 1) + (insert "-\n") + (setq header-delim "-") + (write-file out-file))))) + (t + ;; Need to figure out the delimiter for the header row + (with-temp-buffer + (insert-file-contents out-file) + (goto-char (point-min)) + (when (re-search-forward "^\\(-+\\)[^-]" nil t) + (setq header-delim (match-string-no-properties 1))) + (goto-char (point-max)) + (forward-char -1) + (while (looking-at "\n") + (delete-char 1) + (goto-char (point-max)) + (forward-char -1)) + (write-file out-file)))) (org-table-import out-file '(16)) (org-babel-reassemble-table (mapcar (lambda (x) @@ -175,10 +246,10 @@ This function is called by `org-babel-execute-src-block'." 'hline x)) (org-table-to-lisp)) - (org-babel-pick-name (cdr (assoc :colname-names params)) - (cdr (assoc :colnames params))) - (org-babel-pick-name (cdr (assoc :rowname-names params)) - (cdr (assoc :rownames params)))))))) + (org-babel-pick-name (cdr (assq :colname-names params)) + (cdr (assq :colnames params))) + (org-babel-pick-name (cdr (assq :rowname-names params)) + (cdr (assq :rownames params)))))))) (defun org-babel-sql-expand-vars (body vars) "Expand the variables held in VARS in BODY." @@ -201,7 +272,7 @@ This function is called by `org-babel-execute-src-block'." vars) body) -(defun org-babel-prep-session:sql (session params) +(defun org-babel-prep-session:sql (_session _params) "Raise an error because Sql sessions aren't implemented." (error "SQL sessions not yet implemented")) diff --git a/lisp/org/ob-sqlite.el b/lisp/org/ob-sqlite.el index 4b165dc476..8094019d5e 100644 --- a/lisp/org/ob-sqlite.el +++ b/lisp/org/ob-sqlite.el @@ -1,4 +1,4 @@ -;;; ob-sqlite.el --- org-babel functions for sqlite database interaction +;;; ob-sqlite.el --- Babel Functions for SQLite Databases -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -53,23 +53,22 @@ (defun org-babel-expand-body:sqlite (body params) "Expand BODY according to the values of PARAMS." (org-babel-sqlite-expand-vars - body (mapcar #'cdr (org-babel-get-header params :var)))) + body (org-babel--get-vars params))) (defvar org-babel-sqlite3-command "sqlite3") (defun org-babel-execute:sqlite (body params) "Execute a block of Sqlite code with Babel. This function is called by `org-babel-execute-src-block'." - (let ((result-params (split-string (or (cdr (assoc :results params)) ""))) - (db (cdr (assoc :db params))) - (separator (cdr (assoc :separator params))) - (nullvalue (cdr (assoc :nullvalue params))) - (headers-p (equal "yes" (cdr (assoc :colnames params)))) + (let ((result-params (split-string (or (cdr (assq :results params)) ""))) + (db (cdr (assq :db params))) + (separator (cdr (assq :separator params))) + (nullvalue (cdr (assq :nullvalue params))) + (headers-p (equal "yes" (cdr (assq :colnames params)))) (others (delq nil (mapcar - (lambda (arg) (car (assoc arg params))) + (lambda (arg) (car (assq arg params))) (list :header :echo :bail :column - :csv :html :line :list)))) - exit-code) + :csv :html :line :list))))) (unless db (error "ob-sqlite: can't evaluate without a database")) (with-temp-buffer (insert @@ -140,7 +139,7 @@ This function is called by `org-babel-execute-src-block'." (equal 1 (length (car result)))) (org-babel-read (caar result)) (mapcar (lambda (row) - (if (equal 'hline row) + (if (eq 'hline row) 'hline (mapcar #'org-babel-string-read row))) result))) @@ -150,7 +149,7 @@ This function is called by `org-babel-execute-src-block'." (cons (car table) (cons 'hline (cdr table))) table)) -(defun org-babel-prep-session:sqlite (session params) +(defun org-babel-prep-session:sqlite (_session _params) "Raise an error because support for SQLite sessions isn't implemented. Prepare SESSION according to the header arguments specified in PARAMS." (error "SQLite sessions not yet implemented")) diff --git a/lisp/org/ob-stan.el b/lisp/org/ob-stan.el new file mode 100644 index 0000000000..40dd0efa38 --- /dev/null +++ b/lisp/org/ob-stan.el @@ -0,0 +1,84 @@ +;;; ob-stan.el --- Babel Functions for Stan -*- lexical-binding: t; -*- + +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. + +;; Author: Kyle Meyer +;; Keywords: literate programming, reproducible research +;; Homepage: http://orgmode.org + +;; 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 . + +;;; Commentary: + +;; Org-Babel support for evaluating Stan [1] source code. +;; +;; Evaluating a Stan block can produce two different results. +;; +;; 1) Dump the source code contents to a file. +;; +;; This file can then be used as a variable in other blocks, which +;; allows interfaces like RStan to use the model. +;; +;; 2) Compile the contents to a model file. +;; +;; This provides access to the CmdStan interface. To use this, set +;; `org-babel-stan-cmdstan-directory' and provide a :file argument +;; that does not end in ".stan". +;; +;; For more information and usage examples, visit +;; http://orgmode.org/worg/org-contrib/babel/languages/ob-doc-stan.html +;; +;; [1] http://mc-stan.org/ + +;;; Code: +(require 'ob) +(require 'org-compat) + +(defcustom org-babel-stan-cmdstan-directory nil + "CmdStan source directory. +'make' will be called from this directory to compile the Stan +block. When nil, executing Stan blocks dumps the content to a +plain text file." + :group 'org-babel + :type 'string) + +(defvar org-babel-default-header-args:stan + '((:results . "file"))) + +(defun org-babel-execute:stan (body params) + "Generate Stan file from BODY according to PARAMS. +A :file header argument must be given. If +`org-babel-stan-cmdstan-directory' is non-nil and the file name +does not have a \".stan\" extension, save an intermediate +\".stan\" file and compile the block to the named file. +Otherwise, write the Stan code directly to the named file." + (let ((file (expand-file-name + (or (cdr (assq :file params)) + (user-error "Set :file argument to execute Stan blocks"))))) + (if (or (not org-babel-stan-cmdstan-directory) + (string-match-p "\\.stan\\'" file)) + (with-temp-file file (insert body)) + (with-temp-file (concat file ".stan") (insert body)) + (let ((default-directory org-babel-stan-cmdstan-directory)) + (call-process-shell-command (concat "make " file)))) + nil)) ; Signal that output has been written to file. + +(defun org-babel-prep-session:stan (_session _params) + "Return an error because Stan does not support sessions." + (user-error "Stan does not support sessions")) + +(provide 'ob-stan) +;;; ob-stan.el ends here diff --git a/lisp/org/ob-table.el b/lisp/org/ob-table.el index 1fa9105ee2..4de8936df1 100644 --- a/lisp/org/ob-table.el +++ b/lisp/org/ob-table.el @@ -1,4 +1,4 @@ -;;; ob-table.el --- support for calling org-babel functions from tables +;;; ob-table.el --- Support for Calling Babel Functions from Tables -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -23,8 +23,8 @@ ;;; Commentary: -;; Should allow calling functions from org-mode tables using the -;; function `org-sbe' as so... +;; Should allow calling functions from Org tables using the function +;; `org-sbe' as so... ;; #+begin_src emacs-lisp :results silent ;; (defun fibbd (n) (if (< n 2) 1 (+ (fibbd (- n 1)) (fibbd (- n 2))))) @@ -47,38 +47,50 @@ ;; | 7 | | ;; | 8 | | ;; | 9 | | -;; #+TBLFM: $2='(org-sbe 'fibbd (n $1)) +;; #+TBLFM: $2='(org-sbe "fibbd" (n $1)) + +;; NOTE: The quotation marks around the function name, 'fibbd' here, +;; are optional. ;;; Code: (require 'ob-core) +(declare-function org-trim "org" (s &optional keep-lead)) + (defun org-babel-table-truncate-at-newline (string) "Replace newline character with ellipses. If STRING ends in a newline character, then remove the newline character and replace it with ellipses." (if (and (stringp string) (string-match "[\n\r]\\(.\\)?" string)) (concat (substring string 0 (match-beginning 0)) - (if (match-string 1 string) "...")) string)) + (when (match-string 1 string) "...")) string)) (defmacro org-sbe (source-block &rest variables) "Return the results of calling SOURCE-BLOCK with VARIABLES. -Each element of VARIABLES should be a two -element list, whose first element is the name of the variable and -second element is a string of its value. The following call to -`org-sbe' would be equivalent to the following source code block. - (org-sbe \\='source-block (n $2) (m 3)) +Each element of VARIABLES should be a list of two elements: the +first element is the name of the variable and second element is a +string of its value. + +So this `org-sbe' construct + + (org-sbe \"source-block\" (n $2) (m 3)) + +is the equivalent of the following source code block: + + #+begin_src emacs-lisp :var results=source-block(n=val_at_col_2, m=3) :results silent + results + #+end_src -#+begin_src emacs-lisp :var results=source-block(n=val_at_col_2, m=3) :results silent -results -#+end_src +NOTE: The quotation marks around the function name, +'source-block', are optional. -NOTE: by default string variable names are interpreted as +NOTE: By default, string variable names are interpreted as references to source-code blocks, to force interpretation of a cell's value as a string, prefix the identifier a \"$\" (e.g., \"$$2\" instead of \"$2\" or \"$@2$2\" instead of \"@2$2\"). -NOTE: it is also possible to pass header arguments to the code +NOTE: It is also possible to pass header arguments to the code block. In this case a table cell should hold the string value of the header argument which can then be passed before all variables as shown in the example below. @@ -132,7 +144,7 @@ as shown in the example below. nil (list "emacs-lisp" "results" params) '((:results . "silent")))) ""))) - (org-babel-trim (if (stringp result) result (format "%S" result))))))) + (org-trim (if (stringp result) result (format "%S" result))))))) (provide 'ob-table) diff --git a/lisp/org/ob-tangle.el b/lisp/org/ob-tangle.el index 437e0a296c..3b0533261c 100644 --- a/lisp/org/ob-tangle.el +++ b/lisp/org/ob-tangle.el @@ -1,4 +1,4 @@ -;;; ob-tangle.el --- extract source code from org-mode files +;;; ob-tangle.el --- Extract Source Code From Org Files -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -26,22 +26,35 @@ ;; Extract the code from source blocks out into raw source-code files. ;;; Code: + +(require 'cl-lib) (require 'org-src) -(eval-when-compile - (require 'cl)) +(declare-function make-directory "files" (dir &optional parents)) +(declare-function org-at-heading-p "org" (&optional ignored)) +(declare-function org-babel-update-block-body "ob-core" (new-body)) +(declare-function org-back-to-heading "org" (&optional invisible-ok)) +(declare-function org-before-first-heading-p "org" ()) (declare-function org-edit-special "org" (&optional arg)) +(declare-function org-element-at-point "org-element" ()) +(declare-function org-element-type "org-element" (element)) +(declare-function org-fill-template "org" (template alist)) +(declare-function org-heading-components "org" ()) +(declare-function org-in-commented-heading-p "org" (&optional no-inheritance)) (declare-function org-link-escape "org" (text &optional table merge)) -(declare-function org-store-link "org" (arg)) (declare-function org-open-link-from-string "org" (s &optional arg reference-buffer)) -(declare-function org-heading-components "org" ()) -(declare-function org-back-to-heading "org" (&optional invisible-ok)) -(declare-function org-fill-template "org" (template alist)) -(declare-function org-babel-update-block-body "ob-core" (new-body)) -(declare-function make-directory "files" (dir &optional parents)) +(declare-function org-remove-indentation "org" (code &optional n)) +(declare-function org-store-link "org" (arg)) +(declare-function org-string-nw-p "org-macs" (s)) +(declare-function org-trim "org" (s &optional keep-lead)) +(declare-function outline-previous-heading "outline" ()) +(declare-function org-id-find "org-id" (id &optional markerp)) + +(defvar org-link-types-re) (defcustom org-babel-tangle-lang-exts - '(("emacs-lisp" . "el")) + '(("emacs-lisp" . "el") + ("elisp" . "el")) "Alist mapping languages to their file extensions. The key is the language name, the value is the string that should be inserted as the extension commonly used to identify files @@ -54,6 +67,11 @@ then the name of the language is used." (string "Language name") (string "File Extension")))) +(defcustom org-babel-tangle-use-relative-file-links t + "Use relative path names in links from tangled source back the Org file." + :group 'org-babel-tangle + :type 'boolean) + (defcustom org-babel-post-tangle-hook nil "Hook run in code files tangled by `org-babel-tangle'." :group 'org-babel @@ -78,9 +96,14 @@ The following format strings can be used to insert special information into the output using `org-fill-template'. %start-line --- the line number at the start of the code block %file --------- the file from which the code block was tangled -%link --------- Org-mode style link to the code block +%link --------- Org style link to the code block %source-name -- name of the code block +Upon insertion the formatted comment will be commented out, and +followed by a newline. To inhibit this post-insertion processing +set the `org-babel-tangle-uncomment-comments' variable to a +non-nil value. + Whether or not comments are inserted during tangling is controlled by the :comments header argument." :group 'org-babel @@ -93,20 +116,33 @@ The following format strings can be used to insert special information into the output using `org-fill-template'. %start-line --- the line number at the start of the code block %file --------- the file from which the code block was tangled -%link --------- Org-mode style link to the code block +%link --------- Org style link to the code block %source-name -- name of the code block +Upon insertion the formatted comment will be commented out, and +followed by a newline. To inhibit this post-insertion processing +set the `org-babel-tangle-uncomment-comments' variable to a +non-nil value. + Whether or not comments are inserted during tangling is controlled by the :comments header argument." :group 'org-babel :version "24.1" :type 'string) -(defcustom org-babel-process-comment-text #'org-babel-trim - "Function called to process raw Org-mode text collected to be +(defcustom org-babel-tangle-uncomment-comments nil + "Inhibits automatic commenting and addition of trailing newline +of tangle comments. Use `org-babel-tangle-comment-format-beg' +and `org-babel-tangle-comment-format-end' to customize the format +of tangled comments." + :group 'org-babel + :type 'boolean) + +(defcustom org-babel-process-comment-text 'org-remove-indentation + "Function called to process raw Org text collected to be inserted as comments in tangled source-code files. The function should take a single string argument and return a string -result. The default value is `org-babel-trim'." +result. The default value is `org-remove-indentation'." :group 'org-babel :version "24.1" :type 'function) @@ -153,12 +189,14 @@ Return a list whose CAR is the tangled file name." (save-window-excursion (find-file file) (setq to-be-removed (current-buffer)) - (org-babel-tangle nil target-file lang)) + (mapcar #'expand-file-name (org-babel-tangle nil target-file lang))) (unless visited-p (kill-buffer to-be-removed))))) (defun org-babel-tangle-publish (_ filename pub-dir) "Tangle FILENAME and place the results in PUB-DIR." + (unless (file-exists-p pub-dir) + (make-directory pub-dir t)) (mapc (lambda (el) (copy-file el pub-dir t)) (org-babel-tangle-file filename))) ;;;###autoload @@ -176,12 +214,12 @@ used to limit the exported source code blocks by language." (run-hooks 'org-babel-pre-tangle-hook) ;; Possibly Restrict the buffer to the current code block (save-restriction - (when (equal arg '(4)) - (let ((head (org-babel-where-is-src-block-head))) + (save-excursion + (when (equal arg '(4)) + (let ((head (org-babel-where-is-src-block-head))) (if head (goto-char head) (user-error "Point is not in a source code block")))) - (save-excursion (let ((block-counter 0) (org-babel-default-header-args (if target-file @@ -190,7 +228,7 @@ used to limit the exported source code blocks by language." org-babel-default-header-args)) (tangle-file (when (equal arg '(16)) - (or (cdr (assoc :tangle (nth 2 (org-babel-get-src-block-info 'light)))) + (or (cdr (assq :tangle (nth 2 (org-babel-get-src-block-info 'light)))) (user-error "Point is not in a source code block")))) path-collector) (mapc ;; map over all languages @@ -216,7 +254,7 @@ used to limit the exported source code blocks by language." (base-name (cond ((string= "yes" tangle) (file-name-sans-extension - (buffer-file-name))) + (nth 1 spec))) ((string= "no" tangle) nil) ((> (length tangle) 0) tangle))) (file-name (when base-name @@ -243,9 +281,13 @@ used to limit the exported source code blocks by language." ;; We avoid append-to-file as it does not work with tramp. (let ((content (buffer-string))) (with-temp-buffer - (if (file-exists-p file-name) - (insert-file-contents file-name)) + (when (file-exists-p file-name) + (insert-file-contents file-name)) (goto-char (point-max)) + ;; Handle :padlines unless first line in file + (unless (or (string= "no" (cdr (assq :padline (nth 4 spec)))) + (= (point) (point-min))) + (insert "\n")) (insert content) (write-region nil nil file-name)))) ;; if files contain she-bangs, then make the executable @@ -253,10 +295,8 @@ used to limit the exported source code blocks by language." (unless tangle-mode (setq tangle-mode #o755))) ;; update counter (setq block-counter (+ 1 block-counter)) - (add-to-list 'path-collector - (cons file-name tangle-mode) - nil - (lambda (a b) (equal (car a) (car b)))))))) + (unless (assoc file-name path-collector) + (push (cons file-name tangle-mode) path-collector)))))) specs))) (if (equal arg '(4)) (org-babel-tangle-single-block 1 t) @@ -284,7 +324,7 @@ used to limit the exported source code blocks by language." Call this function inside of a source-code file generated by `org-babel-tangle' to remove all comments inserted automatically by `org-babel-tangle'. Warning, this comment removes any lines -containing constructs which resemble org-mode file links or noweb +containing constructs which resemble Org file links or noweb references." (interactive) (goto-char (point-min)) @@ -303,153 +343,134 @@ code file. This function uses `comment-region' which assumes that the appropriate major-mode is set. SPEC has the form: (start-line file link source-name params body comment)" - (let* ((start-line (nth 0 spec)) - (file (nth 1 spec)) - (link (nth 2 spec)) - (source-name (nth 3 spec)) - (body (nth 5 spec)) - (comment (nth 6 spec)) - (comments (cdr (assoc :comments (nth 4 spec)))) - (padline (not (string= "no" (cdr (assoc :padline (nth 4 spec)))))) - (link-p (or (string= comments "both") (string= comments "link") - (string= comments "yes") (string= comments "noweb"))) - (link-data (mapcar (lambda (el) - (cons (symbol-name el) - (let ((le (eval el))) - (if (stringp le) le (format "%S" le))))) - '(start-line file link source-name))) - (insert-comment (lambda (text) - (when (and comments (not (string= comments "no")) - (> (length text) 0)) - (when padline (insert "\n")) - (comment-region (point) (progn (insert text) (point))) - (end-of-line nil) (insert "\n"))))) + (pcase-let* + ((`(,start ,file ,link ,source ,info ,body ,comment) spec) + (comments (cdr (assq :comments info))) + (link? (or (string= comments "both") (string= comments "link") + (string= comments "yes") (string= comments "noweb"))) + (link-data `(("start-line" . ,(number-to-string start)) + ("file" . ,file) + ("link" . ,link) + ("source-name" . ,source))) + (insert-comment (lambda (text) + (when (and comments + (not (string= comments "no")) + (org-string-nw-p text)) + (if org-babel-tangle-uncomment-comments + ;; Plain comments: no processing. + (insert text) + ;; Ensure comments are made to be + ;; comments, and add a trailing newline. + ;; Also ignore invisible characters when + ;; commenting. + (comment-region + (point) + (progn (insert (org-no-properties text)) + (point))) + (end-of-line) + (insert "\n")))))) (when comment (funcall insert-comment comment)) - (when link-p - (funcall - insert-comment - (org-fill-template org-babel-tangle-comment-format-beg link-data))) - (when padline (insert "\n")) - (insert - (format - "%s\n" - (org-unescape-code-in-string - (org-babel-trim body (if org-src-preserve-indentation "[\f\n\r\v]"))))) - (when link-p - (funcall - insert-comment - (org-fill-template org-babel-tangle-comment-format-end link-data))))) - -(defvar org-comment-string) ;; Defined in org.el + (when link? + (funcall insert-comment + (org-fill-template + org-babel-tangle-comment-format-beg link-data))) + (insert body "\n") + (when link? + (funcall insert-comment + (org-fill-template + org-babel-tangle-comment-format-end link-data))))) + (defun org-babel-tangle-collect-blocks (&optional language tangle-file) - "Collect source blocks in the current Org-mode file. + "Collect source blocks in the current Org file. Return an association list of source-code block specifications of the form used by `org-babel-spec-to-string' grouped by language. Optional argument LANGUAGE can be used to limit the collected source code blocks by language. Optional argument TANGLE-FILE can be used to limit the collected code blocks by target file." - (let ((block-counter 1) (current-heading "") blocks by-lang) + (let ((counter 0) last-heading-pos blocks) (org-babel-map-src-blocks (buffer-file-name) - (lambda (new-heading) - (if (not (string= new-heading current-heading)) - (progn - (setq block-counter 1) - (setq current-heading new-heading)) - (setq block-counter (+ 1 block-counter)))) - (replace-regexp-in-string "[ \t]" "-" - (condition-case nil - (or (nth 4 (org-heading-components)) - "(dummy for heading without text)") - (error (buffer-file-name)))) - (let* ((info (org-babel-get-src-block-info 'light)) - (src-lang (nth 0 info)) - (src-tfile (cdr (assoc :tangle (nth 2 info))))) - (unless (or (string-match (concat "^" org-comment-string) current-heading) - (string= (cdr (assoc :tangle (nth 2 info))) "no") - (and tangle-file (not (equal tangle-file src-tfile)))) - (unless (and language (not (string= language src-lang))) - ;; Add the spec for this block to blocks under it's language - (setq by-lang (cdr (assoc src-lang blocks))) - (setq blocks (delq (assoc src-lang blocks) blocks)) - (setq blocks (cons - (cons src-lang - (cons - (org-babel-tangle-single-block - block-counter) - by-lang)) blocks)))))) - ;; Ensure blocks are in the correct order - (setq blocks - (mapcar - (lambda (by-lang) (cons (car by-lang) (reverse (cdr by-lang)))) - blocks)) - blocks)) - -(defun org-babel-tangle-single-block - (block-counter &optional only-this-block) + (let ((current-heading-pos + (org-with-wide-buffer + (org-with-limited-levels (outline-previous-heading))))) + (if (eq last-heading-pos current-heading-pos) (cl-incf counter) + (setq counter 1) + (setq last-heading-pos current-heading-pos))) + (unless (org-in-commented-heading-p) + (let* ((info (org-babel-get-src-block-info 'light)) + (src-lang (nth 0 info)) + (src-tfile (cdr (assq :tangle (nth 2 info))))) + (unless (or (string= src-tfile "no") + (and tangle-file (not (equal tangle-file src-tfile))) + (and language (not (string= language src-lang)))) + ;; Add the spec for this block to blocks under its + ;; language. + (let ((by-lang (assoc src-lang blocks)) + (block (org-babel-tangle-single-block counter))) + (if by-lang (setcdr by-lang (cons block (cdr by-lang))) + (push (cons src-lang (list block)) blocks))))))) + ;; Ensure blocks are in the correct order. + (mapcar (lambda (b) (cons (car b) (nreverse (cdr b)))) blocks))) + +(defun org-babel-tangle-single-block (block-counter &optional only-this-block) "Collect the tangled source for current block. Return the list of block attributes needed by -`org-babel-tangle-collect-blocks'. -When ONLY-THIS-BLOCK is non-nil, return the full association -list to be used by `org-babel-tangle' directly." +`org-babel-tangle-collect-blocks'. When ONLY-THIS-BLOCK is +non-nil, return the full association list to be used by +`org-babel-tangle' directly." (let* ((info (org-babel-get-src-block-info)) (start-line (save-restriction (widen) (+ 1 (line-number-at-pos (point))))) - (file (buffer-file-name)) + (file (buffer-file-name (buffer-base-buffer))) (src-lang (nth 0 info)) (params (nth 2 info)) (extra (nth 3 info)) (cref-fmt (or (and (string-match "-l \"\\(.+\\)\"" extra) (match-string 1 extra)) org-coderef-label-format)) - (link (let ((link (org-no-properties - (org-store-link nil)))) - (and (string-match org-bracket-link-regexp link) - (match-string 1 link)))) + (link (let ((l (org-no-properties (org-store-link nil)))) + (and (string-match org-bracket-link-regexp l) + (match-string 1 l)))) (source-name - (intern (or (nth 4 info) - (format "%s:%d" - (or (ignore-errors (nth 4 (org-heading-components))) - "No heading") - block-counter)))) - (expand-cmd - (intern (concat "org-babel-expand-body:" src-lang))) + (or (nth 4 info) + (format "%s:%d" + (or (ignore-errors (nth 4 (org-heading-components))) + "No heading") + block-counter))) + (expand-cmd (intern (concat "org-babel-expand-body:" src-lang))) (assignments-cmd (intern (concat "org-babel-variable-assignments:" src-lang))) (body ;; Run the tangle-body-hook. - (let* ((body ;; Expand the body in language specific manner. - (if (org-babel-noweb-p params :tangle) - (org-babel-expand-noweb-references info) - (nth 1 info))) - (body - (if (assoc :no-expand params) - body - (if (fboundp expand-cmd) - (funcall expand-cmd body params) - (org-babel-expand-body:generic - body params - (and (fboundp assignments-cmd) - (funcall assignments-cmd params))))))) - (with-temp-buffer - (insert body) - (when (string-match "-r" extra) - (goto-char (point-min)) - (while (re-search-forward - (replace-regexp-in-string "%s" ".+" cref-fmt) nil t) - (replace-match ""))) - (run-hooks 'org-babel-tangle-body-hook) - (buffer-string)))) + (let ((body (if (org-babel-noweb-p params :tangle) + (org-babel-expand-noweb-references info) + (nth 1 info)))) + (with-temp-buffer + (insert + ;; Expand body in language specific manner. + (cond ((assq :no-expand params) body) + ((fboundp expand-cmd) (funcall expand-cmd body params)) + (t + (org-babel-expand-body:generic + body params (and (fboundp assignments-cmd) + (funcall assignments-cmd params)))))) + (when (string-match "-r" extra) + (goto-char (point-min)) + (while (re-search-forward + (replace-regexp-in-string "%s" ".+" cref-fmt) nil t) + (replace-match ""))) + (run-hooks 'org-babel-tangle-body-hook) + (buffer-string)))) (comment - (when (or (string= "both" (cdr (assoc :comments params))) - (string= "org" (cdr (assoc :comments params)))) + (when (or (string= "both" (cdr (assq :comments params))) + (string= "org" (cdr (assq :comments params)))) ;; From the previous heading or code-block end (funcall org-babel-process-comment-text (buffer-substring (max (condition-case nil (save-excursion - (org-back-to-heading t) ; Sets match data + (org-back-to-heading t) ; Sets match data (match-end 0)) (error (point-min))) (save-excursion @@ -459,31 +480,48 @@ list to be used by `org-babel-tangle' directly." (point-min)))) (point))))) (result - (list start-line file link source-name params body comment))) + (list start-line + (if org-babel-tangle-use-relative-file-links + (file-relative-name file) + file) + (if (and org-babel-tangle-use-relative-file-links + (string-match org-link-types-re link) + (string= (match-string 0 link) "file")) + (concat "file:" + (file-relative-name (match-string 1 link) + (file-name-directory + (cdr (assq :tangle params))))) + link) + source-name + params + (org-unescape-code-in-string + (if org-src-preserve-indentation + (org-trim body t) + (org-trim (org-remove-indentation body)))) + comment))) (if only-this-block (list (cons src-lang (list result))) result))) -(defun org-babel-tangle-comment-links ( &optional info) +(defun org-babel-tangle-comment-links (&optional info) "Return a list of begin and end link comments for the code block at point." - (let* ((start-line (org-babel-where-is-src-block-head)) - (file (buffer-file-name)) - (link (org-link-escape (progn (call-interactively 'org-store-link) - (org-no-properties - (car (pop org-stored-links)))))) - (source-name (nth 4 (or info (org-babel-get-src-block-info 'light)))) - (link-data (mapcar (lambda (el) - (cons (symbol-name el) - (let ((le (eval el))) - (if (stringp le) le (format "%S" le))))) - '(start-line file link source-name)))) + (let ((link-data + `(("start-line" . ,(number-to-string + (org-babel-where-is-src-block-head))) + ("file" . ,(buffer-file-name)) + ("link" . ,(org-link-escape + (progn + (call-interactively #'org-store-link) + (org-no-properties (car (pop org-stored-links)))))) + ("source-name" . + ,(nth 4 (or info (org-babel-get-src-block-info 'light))))))) (list (org-fill-template org-babel-tangle-comment-format-beg link-data) (org-fill-template org-babel-tangle-comment-format-end link-data)))) ;; de-tangling functions (defvar org-bracket-link-analytic-regexp) (defun org-babel-detangle (&optional source-code-file) - "Propagate changes in source file back original to Org-mode file. + "Propagate changes in source file back original to Org file. This requires that code blocks were tangled with link comments which enable the original code blocks to be found." (interactive) @@ -504,18 +542,17 @@ which enable the original code blocks to be found." (prog1 counter (message "Detangled %d code blocks" counter))))) (defun org-babel-tangle-jump-to-org () - "Jump from a tangled code file to the related Org-mode file." + "Jump from a tangled code file to the related Org mode file." (interactive) (let ((mid (point)) - start body-start end done + start body-start end target-buffer target-char link path block-name body) (save-window-excursion (save-excursion (while (and (re-search-backward org-bracket-link-analytic-regexp nil t) (not ; ever wider searches until matching block comments - (and (setq start (point-at-eol)) - (setq body-start (save-excursion - (forward-line 2) (point-at-bol))) + (and (setq start (line-beginning-position)) + (setq body-start (line-beginning-position 2)) (setq link (match-string 0)) (setq path (match-string 3)) (setq block-name (match-string 5)) @@ -524,32 +561,37 @@ which enable the original code blocks to be found." (re-search-forward (concat " " (regexp-quote block-name) " ends here") nil t) - (setq end (point-at-bol)))))))) + (setq end (line-beginning-position)))))))) (unless (and start (< start mid) (< mid end)) (error "Not in tangled code")) - (setq body (org-babel-trim (buffer-substring start end)))) + (setq body (buffer-substring body-start end))) (when (string-match "::" path) (setq path (substring path 0 (match-beginning 0)))) - (find-file path) (setq target-buffer (current-buffer)) - (goto-char start) (org-open-link-from-string link) + (find-file (or (car (org-id-find path)) path)) + (setq target-buffer (current-buffer)) + ;; Go to the beginning of the relative block in Org file. + (org-open-link-from-string link) (if (string-match "[^ \t\n\r]:\\([[:digit:]]+\\)" block-name) - (org-babel-next-src-block - (string-to-number (match-string 1 block-name))) + (let ((n (string-to-number (match-string 1 block-name)))) + (if (org-before-first-heading-p) (goto-char (point-min)) + (org-back-to-heading t)) + ;; Do not skip the first block if it begins at point min. + (cond ((or (org-at-heading-p) + (not (eq (org-element-type (org-element-at-point)) + 'src-block))) + (org-babel-next-src-block n)) + ((= n 1)) + (t (org-babel-next-src-block (1- n))))) (org-babel-goto-named-src-block block-name)) - ;; position at the beginning of the code block body (goto-char (org-babel-where-is-src-block-head)) + ;; Preserve location of point within the source code in tangled + ;; code file. (forward-line 1) - ;; Use org-edit-special to isolate the code. - (org-edit-special) - ;; Then move forward the correct number of characters in the - ;; code buffer. (forward-char (- mid body-start)) - ;; And return to the Org-mode buffer with the point in the right - ;; place. - (org-edit-src-exit) (setq target-char (point))) (org-src-switch-to-buffer target-buffer t) - (prog1 body (goto-char target-char)))) + (goto-char target-char) + body)) (provide 'ob-tangle) diff --git a/lisp/org/ob.el b/lisp/org/ob.el index b0c3d521c5..736f58879b 100644 --- a/lisp/org/ob.el +++ b/lisp/org/ob.el @@ -1,4 +1,4 @@ -;;; ob.el --- working with code blocks in org-mode +;;; ob.el --- Working with Code Blocks in Org -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el index 153e3772b0..f90dd53bb0 100644 --- a/lisp/org/org-agenda.el +++ b/lisp/org/org-agenda.el @@ -24,7 +24,7 @@ ;; ;;; Commentary: -;; This file contains the code for creating and using the Agenda for Org-mode. +;; This file contains the code for creating and using the Agenda for Org. ;; ;; The functions `org-batch-agenda', `org-batch-agenda-csv', and ;; `org-batch-store-agenda-views' are implemented as macros to provide @@ -45,10 +45,9 @@ ;;; Code: +(require 'cl-lib) (require 'org) (require 'org-macs) -(eval-when-compile - (require 'cl)) (declare-function diary-add-to-list "diary-lib" (date string specifier &optional marker globcolor literal)) @@ -69,6 +68,7 @@ (declare-function calendar-persian-date-string "cal-persia" (&optional date)) (declare-function calendar-check-holidays "holidays" (date)) +(declare-function org-columns-remove-overlays "org-colview" ()) (declare-function org-datetree-find-date-create "org-datetree" (date &optional keep-restriction)) (declare-function org-columns-quit "org-colview" ()) @@ -79,16 +79,15 @@ (declare-function org-is-habit-p "org-habit" (&optional pom)) (declare-function org-habit-parse-todo "org-habit" (&optional pom)) (declare-function org-habit-get-priority "org-habit" (habit &optional moment)) -(declare-function org-pop-to-buffer-same-window "org-compat" - (&optional buffer-or-name norecord label)) (declare-function org-agenda-columns "org-colview" ()) (declare-function org-add-archive-files "org-archive" (files)) (declare-function org-capture "org-capture" (&optional goto keys)) -(defvar calendar-mode-map) ; defined in calendar.el -(defvar org-clock-current-task nil) ; defined in org-clock.el -(defvar org-mobile-force-id-on-agenda-items) ; defined in org-mobile.el -(defvar org-habit-show-habits) ; defined in org-habit.el +(defvar calendar-mode-map) +(defvar org-clock-current-task) +(defvar org-current-tag-alist) +(defvar org-mobile-force-id-on-agenda-items) +(defvar org-habit-show-habits) (defvar org-habit-show-habits-only-for-today) (defvar org-habit-show-all-today) @@ -96,8 +95,8 @@ (defvar org-agenda-buffer-name "*Org Agenda*") (defvar org-agenda-overriding-header nil) (defvar org-agenda-title-append nil) -(org-no-warnings (defvar entry)) ;; unprefixed, from calendar.el -(org-no-warnings (defvar date)) ;; unprefixed, from calendar.el +(with-no-warnings (defvar entry)) ;; unprefixed, from calendar.el +(with-no-warnings (defvar date)) ;; unprefixed, from calendar.el (defvar original-date) ; dynamically scoped, calendar.el does scope this (defvar org-agenda-undo-list nil @@ -135,7 +134,7 @@ addresses the separator between the current and the previous block." (string))) (defgroup org-agenda-export nil - "Options concerning exporting agenda views in Org-mode." + "Options concerning exporting agenda views in Org mode." :tag "Org Agenda Export" :group 'org-agenda) @@ -152,7 +151,7 @@ before assigned to the variables. So make sure to quote values you do *not* want evaluated, for example (setq org-agenda-exporter-settings - '((ps-print-color-p 'black-white)))" + \\='((ps-print-color-p \\='black-white)))" :group 'org-agenda-export :type '(repeat (list @@ -237,7 +236,7 @@ you can \"misuse\" it to also add other text to the header." :type 'boolean) (defgroup org-agenda-custom-commands nil - "Options concerning agenda views in Org-mode." + "Options concerning agenda views in Org mode." :tag "Org Agenda Custom Commands" :group 'org-agenda) @@ -261,8 +260,8 @@ you can \"misuse\" it to also add other text to the header." ;; Keep custom values for `org-agenda-filter-preset' compatible with ;; the new variable `org-agenda-tag-filter-preset'. -(org-defvaralias 'org-agenda-filter-preset 'org-agenda-tag-filter-preset) -(org-defvaralias 'org-agenda-filter 'org-agenda-tag-filter) +(defvaralias 'org-agenda-filter-preset 'org-agenda-tag-filter-preset) +(defvaralias 'org-agenda-filter 'org-agenda-tag-filter) (defvar org-agenda-entry-types '(:deadline :scheduled :timestamp :sexp) "List of types searched for when creating the daily/weekly agenda. @@ -360,6 +359,12 @@ the daily/weekly agenda, see `org-agenda-skip-function'.") (const :format "" quote) (repeat (string :tag "+tag or -tag")))) + (list :tag "Effort filter preset" + (const org-agenda-effort-filter-preset) + (list + (const :format "" quote) + (repeat + (string :tag "+=10 or -=10 or +<10 or ->10")))) (list :tag "Regexp filter preset" (const org-agenda-regexp-filter-preset) (list @@ -435,8 +440,9 @@ This will be spliced into the custom type of (defcustom org-agenda-custom-commands '(("n" "Agenda and all TODOs" ((agenda "") (alltodo "")))) "Custom commands for the agenda. +\\ These commands will be offered on the splash screen displayed by the -agenda dispatcher \\[org-agenda]. Each entry is a list like this: +agenda dispatcher `\\[org-agenda]'. Each entry is a list like this: (key desc type match settings files) @@ -463,8 +469,8 @@ match What to search for: settings A list of option settings, similar to that in a let form, so like this: ((opt1 val1) (opt2 val2) ...). The values will be evaluated at the moment of execution, so quote them when needed. -files A list of files file to write the produced agenda buffer to - with the command `org-store-agenda-views'. +files A list of files to write the produced agenda buffer to with + the command `org-store-agenda-views'. If a file name ends in \".html\", an HTML version of the buffer is written out. If it ends in \".ps\", a postscript version is produced. Otherwise, only the plain text is written to the file. @@ -601,23 +607,17 @@ subtree to see if any of the subtasks have project status. See also the variable `org-tags-match-list-sublevels' which applies to projects matched by this search as well. -After defining this variable, you may use \\[org-agenda-list-stuck-projects] -or `C-c a #' to produce the list." +After defining this variable, you may use `\\[org-agenda-list-stuck-projects]' +\(bound to `C-c a #') to produce the list." :group 'org-agenda-custom-commands :type '(list (string :tag "Tags/TODO match to identify a project") - (repeat :tag "Projects are *not* stuck if they have an entry with TODO keyword any of" (string)) - (repeat :tag "Projects are *not* stuck if they have an entry with TAG being any of" (string)) - (regexp :tag "Projects are *not* stuck if this regexp matches inside the subtree"))) - -(defcustom org-agenda-filter-effort-default-operator "<" - "The default operator for effort estimate filtering. -If you select an effort estimate limit without first pressing an operator, -this one will be used." - :group 'org-agenda-custom-commands - :type '(choice (const :tag "less or equal" "<") - (const :tag "greater or equal"">") - (const :tag "equal" "="))) + (repeat :tag "Projects are *not* stuck if they have an entry with \ +TODO keyword any of" (string)) + (repeat :tag "Projects are *not* stuck if they have an entry with \ +TAG being any of" (string)) + (regexp :tag "Projects are *not* stuck if this regexp matches inside \ +the subtree"))) (defgroup org-agenda-skip nil "Options concerning skipping parts of agenda files." @@ -769,10 +769,12 @@ to make his option also apply to the tags-todo list." (defcustom org-agenda-todo-ignore-deadlines nil "Non-nil means ignore some deadline TODO items when making TODO list. + There are different motivations for using different values, please think carefully when configuring this variable. -This applies when creating the global todo list. +This applies when creating the global TODO list. + Valid values are: near Don't show near deadline entries. A deadline is near when it is @@ -780,8 +782,8 @@ near Don't show near deadline entries. A deadline is near when it is is that such items will appear in the agenda anyway. far Don't show TODO entries where a deadline has been defined, but - the deadline is not near. This is useful if you don't want to - use the todo list to figure out what to do now. + is not going to happen anytime soon. This is useful if you want to use + the TODO list to figure out what to do now. past Don't show entries with a deadline timestamp for today or in the past. @@ -842,10 +844,9 @@ restricted to unfinished TODO entries only." (defcustom org-agenda-skip-scheduled-if-done nil "Non-nil means don't show scheduled items in agenda when they are done. -This is relevant for the daily/weekly agenda, not for the TODO list. And -it applies only to the actual date of the scheduling. Warnings about -an item with a past scheduling dates are always turned off when the item -is DONE." +This is relevant for the daily/weekly agenda, not for the TODO list. It +applies only to the actual date of the scheduling. Warnings about an item +with a past scheduling dates are always turned off when the item is DONE." :group 'org-agenda-skip :group 'org-agenda-daily/weekly :type 'boolean) @@ -894,8 +895,8 @@ several times." (defcustom org-agenda-skip-deadline-if-done nil "Non-nil means don't show deadlines when the corresponding item is done. When nil, the deadline is still shown and should give you a happy feeling. -This is relevant for the daily/weekly agenda. And it applied only to the -actually date of the deadline. Warnings about approaching and past-due +This is relevant for the daily/weekly agenda. It applies only to the +actual date of the deadline. Warnings about approaching and past-due deadlines are always turned off when the item is DONE." :group 'org-agenda-skip :group 'org-agenda-daily/weekly @@ -1001,8 +1002,6 @@ you want to use two-columns display (see `org-agenda-menu-two-columns')." :version "24.1" :type 'boolean) -(define-obsolete-variable-alias 'org-agenda-menu-two-column 'org-agenda-menu-two-columns "24.3") - (defcustom org-agenda-menu-two-columns nil "Non-nil means, use two columns to show custom commands in the dispatcher. If you use this, you probably want to set `org-agenda-menu-show-matcher' @@ -1011,7 +1010,6 @@ to nil." :version "24.1" :type 'boolean) -(define-obsolete-variable-alias 'org-finalize-agenda-hook 'org-agenda-finalize-hook "24.3") (defcustom org-agenda-finalize-hook nil "Hook run just before displaying an agenda buffer. The buffer is still writable when the hook is called. @@ -1024,8 +1022,8 @@ headlines as the agenda display heavily relies on them." (defcustom org-agenda-mouse-1-follows-link nil "Non-nil means mouse-1 on a link will follow the link in the agenda. -A longer mouse click will still set point. Does not work on XEmacs. -Needs to be set before org.el is loaded." +A longer mouse click will still set point. Needs to be set +before org.el is loaded." :group 'org-agenda-startup :type 'boolean) @@ -1054,9 +1052,9 @@ current item's tree, in an indirect buffer." (defcustom org-agenda-entry-text-maxlines 5 "Number of text lines to be added when `E' is pressed in the agenda. -Note that this variable only used during agenda display. Add add entry text +Note that this variable only used during agenda display. To add entry text when exporting the agenda, configure the variable -`org-agenda-add-entry-ext-maxlines'." +`org-agenda-add-entry-text-maxlines'." :group 'org-agenda :type 'integer) @@ -1097,6 +1095,7 @@ Possible values for this option are: current-window Show agenda in the current window, keeping all other windows. other-window Use `switch-to-buffer-other-window' to display agenda. +only-window Show agenda, deleting all other windows. reorganize-frame Show only two windows on the current frame, the current window and the agenda. other-frame Use `switch-to-buffer-other-frame' to display agenda. @@ -1107,6 +1106,7 @@ See also the variable `org-agenda-restore-windows-after-quit'." (const current-window) (const other-frame) (const other-window) + (const only-window) (const reorganize-frame))) (defcustom org-agenda-window-frame-fractions '(0.5 . 0.75) @@ -1126,16 +1126,6 @@ option will be ignored." :group 'org-agenda-windows :type 'boolean) -(defcustom org-agenda-ndays nil - "Number of days to include in overview display. -Should be 1 or 7. -Obsolete, see `org-agenda-span'." - :group 'org-agenda-daily/weekly - :type '(choice (const nil) - (integer))) - -(make-obsolete-variable 'org-agenda-ndays 'org-agenda-span "24.1") - (defcustom org-agenda-span 'week "Number of days to include in overview display. Can be day, week, month, year, or any number of days. @@ -1211,7 +1201,7 @@ For example, 9:30am would become 09:30 rather than 9:30." :type 'boolean) (defun org-agenda-time-of-day-to-ampm (time) - "Convert TIME of a string like `13:45' to an AM/PM style time string." + "Convert TIME of a string like \"13:45\" to an AM/PM style time string." (let* ((hour-number (string-to-number (substring time 0 -3))) (minute (substring time -2)) (ampm "am")) @@ -1284,20 +1274,22 @@ shown, either today or the nearest into the future." (defcustom org-scheduled-past-days 10000 "Number of days to continue listing scheduled items not marked DONE. -When an item is scheduled on a date, it shows up in the agenda on this -day and will be listed until it is marked done for the number of days -given here." +When an item is scheduled on a date, it shows up in the agenda on +this day and will be listed until it is marked done or for the +number of days given here." :group 'org-agenda-daily/weekly :type 'integer) (defcustom org-agenda-log-mode-items '(closed clock) "List of items that should be shown in agenda log mode. +\\\ This list may contain the following symbols: closed Show entries that have been closed on that day. clock Show entries that have received clocked time on that day. state Show all logged state changes. -Note that instead of changing this variable, you can also press `C-u l' in +Note that instead of changing this variable, you can also press \ +`\\[universal-argument] \\[org-agenda-log-mode]' in the agenda to display all available LOG items temporarily." :group 'org-agenda-daily/weekly :type '(set :greedy t (const closed) (const clock) (const state))) @@ -1413,7 +1405,7 @@ boolean search." :version "24.1" :type 'boolean) -(org-defvaralias 'org-agenda-search-view-search-words-only +(defvaralias 'org-agenda-search-view-search-words-only 'org-agenda-search-view-always-boolean) (defcustom org-agenda-search-view-force-full-words nil @@ -1434,7 +1426,7 @@ value, don't limit agenda view by outline level." :type 'integer) (defgroup org-agenda-time-grid nil - "Options concerning the time grid in the Org-mode Agenda." + "Options concerning the time grid in the Org Agenda." :tag "Org Agenda Time Grid" :group 'org-agenda) @@ -1506,7 +1498,7 @@ a grid line." :type 'string) (defgroup org-agenda-sorting nil - "Options concerning sorting in the Org-mode Agenda." + "Options concerning sorting in the Org Agenda." :tag "Org Agenda Sorting" :group 'org-agenda) @@ -1612,7 +1604,7 @@ When nil, such items are sorted as 0 minutes effort." :type 'boolean) (defgroup org-agenda-line-format nil - "Options concerning the entry prefix in the Org-mode agenda display." + "Options concerning the entry prefix in the Org agenda display." :tag "Org Agenda Line Format" :group 'org-agenda) @@ -1792,17 +1784,18 @@ When non-nil, this must be the number of minutes, e.g. 60 for one hour." (defcustom org-agenda-show-inherited-tags t "Non-nil means show inherited tags in each agenda line. -When this option is set to 'always, it take precedences over +When this option is set to `always', it takes precedence over `org-agenda-use-tag-inheritance' and inherited tags are shown in every agenda. When this option is set to t (the default), inherited tags are shown when they are available, i.e. when the value of -`org-agenda-use-tag-inheritance' has been taken into account. +`org-agenda-use-tag-inheritance' enables tag inheritance for the +given agenda type. This can be set to a list of agenda types in which the agenda -must display the inherited tags. Available types are 'todo, -'agenda, 'search and 'timeline. +must display the inherited tags. Available types are `todo', +`agenda', `search' and `timeline'. When set to nil, never show inherited tags in agenda lines." :group 'org-agenda-line-format @@ -1823,10 +1816,10 @@ controlled by `org-use-tag-inheritance'. In other agenda types, agenda entries. Still, you may want the agenda to be aware of the inherited tags anyway, e.g. for later tag filtering. -Allowed value are 'todo, 'search, 'timeline and 'agenda. +Allowed value are `todo', `search', `timeline' and `agenda'. This variable has no effect if `org-agenda-show-inherited-tags' -is set to 'always. In that case, the agenda is aware of those +is set to `always'. In that case, the agenda is aware of those tags. The default value sets tags in every agenda type. Setting this @@ -1858,10 +1851,10 @@ When this is the symbol `prefix', only remove tags when (const :tag "Never" nil) (const :tag "When prefix format contains %T" prefix))) -(org-defvaralias 'org-agenda-remove-tags-when-in-prefix +(defvaralias 'org-agenda-remove-tags-when-in-prefix 'org-agenda-remove-tags) -(defcustom org-agenda-tags-column (if (featurep 'xemacs) -79 -80) +(defcustom org-agenda-tags-column -80 "Shift tags in agenda items to this column. If this number is positive, it specifies the column. If it is negative, it means that the tags should be flushright to that column. For example, @@ -1869,7 +1862,7 @@ it means that the tags should be flushright to that column. For example, :group 'org-agenda-line-format :type 'integer) -(org-defvaralias 'org-agenda-align-tags-to-column 'org-agenda-tags-column) +(defvaralias 'org-agenda-align-tags-to-column 'org-agenda-tags-column) (defcustom org-agenda-fontify-priorities 'cookies "Non-nil means highlight low and high priorities in agenda. @@ -1948,6 +1941,14 @@ category, you can use: :tag "Org Agenda Column View" :group 'org-agenda) +(defcustom org-agenda-view-columns-initially nil + "When non-nil, switch to columns view right after creating the agenda." + :group 'org-agenda-column-view + :type 'boolean + :version "26.1" + :package-version '(Org . "9.0") + :safe #'booleanp) + (defcustom org-agenda-columns-show-summaries t "Non-nil means show summaries for columns displayed in the agenda view." :group 'org-agenda-column-view @@ -1975,7 +1976,8 @@ estimate." :type 'boolean) (defcustom org-agenda-auto-exclude-function nil - "A function called with a tag to decide if it is filtered on `/ RET'. + "A function called with a tag to decide if it is filtered on \ +\\`\\[org-agenda-filter-by-tag] RET'. The sole argument to the function, which is called once for each possible tag, is a string giving the name of the tag. The function should return either nil if the tag should be included @@ -1990,13 +1992,13 @@ the lower-case version of all tags." "Alist of characters and custom functions for bulk actions. For example, this value makes those two functions available: - ((?R set-category) - (?C bulk-cut)) + \\='((?R set-category) + (?C bulk-cut)) With selected entries in an agenda buffer, `B R' will call the custom function `set-category' on the selected entries. Note that functions in this alist don't need to be quoted." - :type 'alist + :type '(alist :key-type character :value-type (group function)) :version "24.1" :group 'org-agenda) @@ -2006,7 +2008,7 @@ If STRING is non-nil, the text property will be fetched from position 0 in that string. If STRING is nil, it will be fetched from the beginning of the current line." (org-with-gensyms (marker) - `(let ((,marker (get-text-property (if string 0 (point-at-bol)) + `(let ((,marker (get-text-property (if ,string 0 (point-at-bol)) 'org-hd-marker ,string))) (with-current-buffer (marker-buffer ,marker) (save-excursion @@ -2027,7 +2029,7 @@ works you probably want to add it to `org-agenda-custom-commands' for good." (defvar org-agenda-mode-map (make-sparse-keymap) "Keymap for `org-agenda-mode'.") -(org-defvaralias 'org-agenda-keymap 'org-agenda-mode-map) +(defvaralias 'org-agenda-keymap 'org-agenda-mode-map) (defvar org-agenda-menu) ; defined later in this file. (defvar org-agenda-restrict nil) ; defined later in this file. @@ -2044,6 +2046,8 @@ The buffer is still writable when this hook is called.") (defvar org-agenda-force-single-file nil) (defvar org-agenda-bulk-marked-entries nil "List of markers that refer to marked entries in the agenda.") +(defvar org-agenda-current-date nil + "Active date when building the agenda.") ;;; Multiple agenda buffers support @@ -2064,13 +2068,13 @@ When nil, `q' will kill the single agenda buffer." (> (prefix-numeric-value arg) 0) (not org-agenda-sticky)))) (if (equal new-value org-agenda-sticky) - (and (org-called-interactively-p 'interactive) + (and (called-interactively-p 'interactive) (message "Sticky agenda was already %s" (if org-agenda-sticky "enabled" "disabled"))) (setq org-agenda-sticky new-value) (org-agenda-kill-all-agenda-buffers) - (and (org-called-interactively-p 'interactive) - (message "Sticky agenda was %s" + (and (called-interactively-p 'interactive) + (message "Sticky agenda %s" (if org-agenda-sticky "enabled" "disabled")))))) (defvar org-agenda-buffer nil @@ -2080,6 +2084,8 @@ When nil, `q' will kill the single agenda buffer." (defvar org-agenda-this-buffer-name nil) (defvar org-agenda-doing-sticky-redo nil) (defvar org-agenda-this-buffer-is-sticky nil) +(defvar org-agenda-last-indirect-buffer nil + "Last buffer loaded by `org-agenda-tree-to-indirect-buffer'.") (defconst org-agenda-local-vars '(org-agenda-this-buffer-name @@ -2101,8 +2107,10 @@ When nil, `q' will kill the single agenda buffer." org-agenda-category-filter org-agenda-top-headline-filter org-agenda-regexp-filter + org-agenda-effort-filter org-agenda-markers org-agenda-last-search-view-search-was-boolean + org-agenda-last-indirect-buffer org-agenda-filtered-by-category org-agenda-filter-form org-agenda-cycle-counter @@ -2110,7 +2118,7 @@ When nil, `q' will kill the single agenda buffer." "Variables that must be local in agenda buffers to allow multiple buffers.") (defun org-agenda-mode () - "Mode for time-sorted view on action items in Org-mode files. + "Mode for time-sorted view on action items in Org files. The following commands are available: @@ -2123,42 +2131,41 @@ The following commands are available: ;; while letting `kill-all-local-variables' kill the rest (let ((save (buffer-local-variables))) (kill-all-local-variables) - (mapc 'make-local-variable org-agenda-local-vars) + (mapc #'make-local-variable org-agenda-local-vars) (dolist (elem save) - (let ((var (car elem)) - (val (cdr elem))) - (when (and val - (member var org-agenda-local-vars)) - (set var val))))) - (set (make-local-variable 'org-agenda-this-buffer-is-sticky) t)) + (pcase elem + (`(,var . ,val) ;ignore unbound variables + (when (and val (memq var org-agenda-local-vars)) + (set var val)))))) + (setq-local org-agenda-this-buffer-is-sticky t)) (org-agenda-sticky ;; Creating a sticky Agenda buffer for the first time (kill-all-local-variables) (mapc 'make-local-variable org-agenda-local-vars) - (set (make-local-variable 'org-agenda-this-buffer-is-sticky) t)) + (setq-local org-agenda-this-buffer-is-sticky t)) (t ;; Creating a non-sticky agenda buffer (kill-all-local-variables) - (set (make-local-variable 'org-agenda-this-buffer-is-sticky) nil))) + (setq-local org-agenda-this-buffer-is-sticky nil))) (setq org-agenda-undo-list nil org-agenda-pending-undo-list nil org-agenda-bulk-marked-entries nil) (setq major-mode 'org-agenda-mode) ;; Keep global-font-lock-mode from turning on font-lock-mode - (org-set-local 'font-lock-global-modes (list 'not major-mode)) + (setq-local font-lock-global-modes (list 'not major-mode)) (setq mode-name "Org-Agenda") (setq indent-tabs-mode nil) (use-local-map org-agenda-mode-map) (easy-menu-add org-agenda-menu) (if org-startup-truncated (setq truncate-lines t)) - (org-set-local 'line-move-visual nil) - (org-add-hook 'post-command-hook 'org-agenda-update-agenda-type nil 'local) - (org-add-hook 'pre-command-hook 'org-unhighlight nil 'local) + (setq-local line-move-visual nil) + (add-hook 'post-command-hook 'org-agenda-update-agenda-type nil 'local) + (add-hook 'pre-command-hook 'org-unhighlight nil 'local) ;; Make sure properties are removed when copying text - (org-add-hook 'filter-buffer-substring-functions - (lambda (fun start end delete) - (substring-no-properties (funcall fun start end delete))) - nil t) + (add-hook 'filter-buffer-substring-functions + (lambda (fun start end delete) + (substring-no-properties (funcall fun start end delete))) + nil t) (unless org-agenda-keep-modes (setq org-agenda-follow-mode org-agenda-start-with-follow-mode org-agenda-entry-text-mode org-agenda-start-with-entry-text-mode)) @@ -2309,9 +2316,9 @@ The following commands are available: (org-defkey org-agenda-mode-map "{" 'org-agenda-manipulate-query-add-re) (org-defkey org-agenda-mode-map "}" 'org-agenda-manipulate-query-subtract-re) (org-defkey org-agenda-mode-map "/" 'org-agenda-filter-by-tag) +(org-defkey org-agenda-mode-map "_" 'org-agenda-filter-by-effort) (org-defkey org-agenda-mode-map "=" 'org-agenda-filter-by-regexp) (org-defkey org-agenda-mode-map "|" 'org-agenda-filter-remove-all) -(org-defkey org-agenda-mode-map "\\" 'org-agenda-filter-by-tag-refine) (org-defkey org-agenda-mode-map "~" 'org-agenda-limit-interactively) (org-defkey org-agenda-mode-map "<" 'org-agenda-filter-by-category) (org-defkey org-agenda-mode-map "^" 'org-agenda-filter-by-top-headline) @@ -2322,6 +2329,10 @@ The following commands are available: (org-defkey org-agenda-mode-map [mouse-2] 'org-agenda-goto-mouse) (org-defkey org-agenda-mode-map [mouse-3] 'org-agenda-show-mouse) + +(define-key org-agenda-mode-map [remap forward-paragraph] 'org-agenda-forward-block) +(define-key org-agenda-mode-map [remap backward-paragraph] 'org-agenda-backward-block) + (when org-agenda-mouse-1-follows-link (org-defkey org-agenda-mode-map [follow-link] 'mouse-face)) (easy-menu-define org-agenda-menu org-agenda-mode-map "Agenda menu" @@ -2346,7 +2357,7 @@ The following commands are available: ["Fortnight View" org-agenda-fortnight-view :active (org-agenda-check-type nil 'agenda) :style radio :selected (eq org-agenda-current-span 'fortnight) - :keys "v f"] + :keys "v t"] ["Month View" org-agenda-month-view :active (org-agenda-check-type nil 'agenda) :style radio :selected (eq org-agenda-current-span 'month) @@ -2387,7 +2398,7 @@ The following commands are available: ["Remove Restriction" org-agenda-remove-restriction-lock org-agenda-restrict]) ["Write view to file" org-agenda-write t] ["Rebuild buffer" org-agenda-redo t] - ["Save all Org-mode Buffers" org-save-all-org-buffers t] + ["Save all Org buffers" org-save-all-org-buffers t] "--" ["Show original entry" org-agenda-show t] ["Go To (other window)" org-agenda-goto t] @@ -2538,7 +2549,7 @@ For example, if you have a custom agenda command \"p\" and you want this command to be accessible only from plain text files, use this: - \\='((\"p\" ((in-file . \"\\.txt\")))) + \\='((\"p\" ((in-file . \"\\\\.txt\\\\'\")))) Here are the available contexts definitions: @@ -2556,7 +2567,7 @@ accessible if there is at least one valid check. You can also bind a key to another agenda custom command depending on contextual rules. - \\='((\"p\" \"q\" ((in-file . \"\\.txt\")))) + \\='((\"p\" \"q\" ((in-file . \"\\\\.txt\\\\'\")))) Here it means: in .txt files, use \"p\" as the key for the agenda command otherwise associated with \"q\". (The command @@ -2656,6 +2667,7 @@ to limit entries to in this type." (const timeline)) (integer :tag "Max number of minutes"))))) +(defvar org-agenda-keep-restricted-file-list nil) (defvar org-keys nil) (defvar org-match nil) ;;;###autoload @@ -2688,9 +2700,9 @@ More commands can be added by configuring the variable `org-agenda-custom-commands'. In particular, specific tags and TODO keyword searches can be pre-defined in this way. -If the current buffer is in Org-mode and visiting a file, you can also +If the current buffer is in Org mode and visiting a file, you can also first press `<' once to indicate that the agenda should be temporarily -\(until the next use of \\[org-agenda]) restricted to the current file. +\(until the next use of `\\[org-agenda]') restricted to the current file. Pressing `<' twice means to restrict to the current subtree or region \(if active)." (interactive "P") @@ -2722,7 +2734,7 @@ Pressing `<' twice means to restrict to the current subtree or region entry key type org-match lprops ans) ;; Turn off restriction unless there is an overriding one, (unless org-agenda-overriding-restriction - (unless (org-bound-and-true-p org-agenda-keep-restricted-file-list) + (unless org-agenda-keep-restricted-file-list ;; There is a request to keep the file list in place (put 'org-agenda-files 'org-restrict nil)) (setq org-agenda-restrict nil) @@ -2819,7 +2831,7 @@ Pressing `<' twice means to restrict to the current subtree or region ((equal org-keys "M") (org-call-with-arg 'org-tags-view (or arg '(4)))) ((equal org-keys "e") (call-interactively 'org-store-agenda-views)) ((equal org-keys "?") (org-tags-view nil "+FLAGGED") - (org-add-hook + (add-hook 'post-command-hook (lambda () (unless (current-message) @@ -2836,7 +2848,7 @@ Pressing `<' twice means to restrict to the current subtree or region t t)) ((equal org-keys "L") (unless (derived-mode-p 'org-mode) - (user-error "This is not an Org-mode file")) + (user-error "This is not an Org file")) (unless restriction (put 'org-agenda-files 'org-restrict (list bfn)) (org-call-with-arg 'org-timeline arg))) @@ -2928,7 +2940,7 @@ L Timeline for current buffer # List stuck projects (!=configure) type (nth 2 entry) match (nth 3 entry)) (if (> (length key) 1) - (pushnew (string-to-char key) prefixes :test #'equal) + (cl-pushnew (string-to-char key) prefixes :test #'equal) (setq line (format "%-4s%-14s" @@ -3034,7 +3046,7 @@ L Timeline for current buffer # List stuck projects (!=configure) (call-interactively 'org-toggle-sticky-agenda) (sit-for 2)) ((and (not restrict-ok) (memq c '(?1 ?0 ?<))) - (message "Restriction is only possible in Org-mode buffers") + (message "Restriction is only possible in Org buffers") (ding) (sit-for 1)) ((eq c ?1) (org-agenda-remove-restriction-lock 'noupdate) @@ -3067,10 +3079,13 @@ L Timeline for current buffer # List stuck projects (!=configure) "Fit the window to the buffer size." (and (memq org-agenda-window-setup '(reorganize-frame)) (fboundp 'fit-window-to-buffer) - (org-fit-window-to-buffer - nil - (floor (* (frame-height) (cdr org-agenda-window-frame-fractions))) - (floor (* (frame-height) (car org-agenda-window-frame-fractions)))))) + (if (and (= (cdr org-agenda-window-frame-fractions) 1.0) + (= (car org-agenda-window-frame-fractions) 1.0)) + (delete-other-windows) + (org-fit-window-to-buffer + nil + (floor (* (frame-height) (cdr org-agenda-window-frame-fractions))) + (floor (* (frame-height) (car org-agenda-window-frame-fractions))))))) (defvar org-cmd nil) (defvar org-agenda-overriding-cmd nil) @@ -3089,9 +3104,9 @@ L Timeline for current buffer # List stuck projects (!=configure) match ;; The byte compiler incorrectly complains about this. Keep it! org-cmd type lprops) (while (setq org-cmd (pop cmds)) - (setq type (car org-cmd) - match (eval (nth 1 org-cmd)) - lprops (nth 2 org-cmd)) + (setq type (car org-cmd)) + (setq match (eval (nth 1 org-cmd))) + (setq lprops (nth 2 org-cmd)) (let ((org-agenda-overriding-arguments (if (eq org-agenda-overriding-cmd org-cmd) (or org-agenda-overriding-arguments @@ -3144,7 +3159,7 @@ Parameters are alternating variable names and values that will be bound before running the agenda command." (org-eval-in-environment (org-make-parameter-alist parameters) (let (org-agenda-sticky) - (if (> (length cmd-key) 2) + (if (> (length cmd-key) 1) (org-tags-view nil cmd-key) (org-agenda nil cmd-key)))) (set-buffer org-agenda-buffer-name) @@ -3232,7 +3247,7 @@ This ensures the export commands can easily use it." (setq tmp (replace-match "" t t tmp))) (when (and (setq re (plist-get props 'org-todo-regexp)) (setq re (concat "\\`\\.*" re " ?")) - (string-match re tmp)) + (let ((case-fold-search nil)) (string-match re tmp))) (plist-put props 'todo (match-string 1 tmp)) (setq tmp (replace-match "" t t tmp))) (plist-put props 'txt tmp))) @@ -3245,9 +3260,7 @@ This ensures the export commands can easily use it." ((not res) "") ((stringp res) res) (t (prin1-to-string res)))) - (while (string-match "," res) - (setq res (replace-match ";" t t res))) - (org-trim res))) + (org-trim (replace-regexp-in-string "," ";" res nil t)))) ;;;###autoload (defun org-store-agenda-views (&rest parameters) @@ -3306,39 +3319,42 @@ This ensures the export commands can easily use it." (defvar org-agenda-write-buffer-name "Agenda View") (defun org-agenda-write (file &optional open nosettings agenda-bufname) "Write the current buffer (an agenda view) as a file. + Depending on the extension of the file name, plain text (.txt), HTML (.html or .htm), PDF (.pdf) or Postscript (.ps) is produced. -If the extension is .ics, run icalendar export over all files used -to construct the agenda and limit the export to entries listed in the -agenda now. -If the extension is .org, collect all subtrees corresponding to the -agenda entries and add them in an .org file. -With prefix argument OPEN, open the new file immediately. -If NOSETTINGS is given, do not scope the settings of -`org-agenda-exporter-settings' into the export commands. This is used when -the settings have already been scoped and we do not wish to overrule other, -higher priority settings. -If AGENDA-BUFFER-NAME, use this as the buffer name for the agenda to write." +If the extension is .ics, translate visible agenda into iCalendar +format. If the extension is .org, collect all subtrees +corresponding to the agenda entries and add them in an .org file. + +With prefix argument OPEN, open the new file immediately. If +NOSETTINGS is given, do not scope the settings of +`org-agenda-exporter-settings' into the export commands. This is +used when the settings have already been scoped and we do not +wish to overrule other, higher priority settings. If +AGENDA-BUFFER-NAME is provided, use this as the buffer name for +the agenda to write." (interactive "FWrite agenda to file: \nP") (if (or (not (file-writable-p file)) (and (file-exists-p file) - (if (org-called-interactively-p 'any) + (if (called-interactively-p 'any) (not (y-or-n-p (format "Overwrite existing file %s? " file)))))) (user-error "Cannot write agenda to file %s" file)) (org-let (if nosettings nil org-agenda-exporter-settings) '(save-excursion (save-window-excursion - (let ((bs (copy-sequence (buffer-string))) beg content) + (let ((bs (copy-sequence (buffer-string))) + (extension (file-name-extension file)) + beg content) (with-temp-buffer (rename-buffer org-agenda-write-buffer-name t) (set-buffer-modified-p nil) (insert bs) - (org-agenda-remove-marked-text 'org-filtered) + (org-agenda-remove-marked-text 'invisible 'org-filtered) (run-hooks 'org-agenda-before-write-hook) (cond - ((org-bound-and-true-p org-mobile-creating-agendas) + ((bound-and-true-p org-mobile-creating-agendas) (org-mobile-write-agenda-for-mobile file)) - ((string-match "\\.org\\'" file) + ((string= "org" extension) (let (content p m message-log-max) (goto-char (point-min)) (while (setq p (next-single-property-change (point) 'org-hd-marker nil)) @@ -3357,7 +3373,7 @@ If AGENDA-BUFFER-NAME, use this as the buffer name for the agenda to write." (write-file file) (kill-buffer (current-buffer)) (message "Org file written to %s" file))) - ((string-match "\\.html?\\'" file) + ((member extension '("html" "htm")) (require 'htmlize) (set-buffer (htmlize-buffer (current-buffer))) (when org-agenda-export-html-style @@ -3369,11 +3385,11 @@ If AGENDA-BUFFER-NAME, use this as the buffer name for the agenda to write." (write-file file) (kill-buffer (current-buffer)) (message "HTML written to %s" file)) - ((string-match "\\.ps\\'" file) + ((string= "ps" extension) (require 'ps-print) (ps-print-buffer-with-faces file) (message "Postscript written to %s" file)) - ((string-match "\\.pdf\\'" file) + ((string= "pdf" extension) (require 'ps-print) (ps-print-buffer-with-faces (concat (file-name-sans-extension file) ".ps")) @@ -3383,7 +3399,7 @@ If AGENDA-BUFFER-NAME, use this as the buffer name for the agenda to write." (expand-file-name file)) (delete-file (concat (file-name-sans-extension file) ".ps")) (message "PDF written to %s" file)) - ((string-match "\\.ics\\'" file) + ((string= "ics" extension) (require 'ox-icalendar) (org-icalendar-export-current-agenda (expand-file-name file))) (t @@ -3395,7 +3411,7 @@ If AGENDA-BUFFER-NAME, use this as the buffer name for the agenda to write." (kill-buffer (current-buffer)) (message "Plain text written to %s" file)))))))) (set-buffer (or agenda-bufname - (and (org-called-interactively-p 'any) (buffer-name)) + (and (called-interactively-p 'any) (buffer-name)) org-agenda-buffer-name))) (when open (org-open-file file))) @@ -3416,7 +3432,7 @@ This will add a maximum of `org-agenda-add-entry-text-maxlines' lines of the entry text following headings shown in the agenda. Drawers will be excluded, also the line with scheduling/deadline info." (when (and (> org-agenda-add-entry-text-maxlines 0) - (not (org-bound-and-true-p org-mobile-creating-agendas))) + (not (bound-and-true-p org-mobile-creating-agendas))) (let (m txt) (goto-char (point-min)) (while (not (eobp)) @@ -3441,85 +3457,83 @@ removed from the entry content. Currently only `planning' is allowed here." (with-current-buffer (marker-buffer marker) (if (not (derived-mode-p 'org-mode)) (setq txt "") - (save-excursion - (save-restriction - (widen) - (goto-char marker) - (end-of-line 1) - (setq txt (buffer-substring - (min (1+ (point)) (point-max)) - (progn (outline-next-heading) (point))) - drawer-re org-drawer-regexp - kwd-time-re (concat "^[ \t]*" org-keyword-time-regexp - ".*\n?")) - (with-temp-buffer - (insert txt) - (when org-agenda-add-entry-text-descriptive-links - (goto-char (point-min)) - (while (org-activate-bracket-links (point-max)) - (add-text-properties (match-beginning 0) (match-end 0) - '(face org-link)))) - (goto-char (point-min)) - (while (re-search-forward org-bracket-link-regexp (point-max) t) - (set-text-properties (match-beginning 0) (match-end 0) - nil)) - (goto-char (point-min)) - (while (re-search-forward drawer-re nil t) - (delete-region - (match-beginning 0) - (progn (re-search-forward - "^[ \t]*:END:.*\n?" nil 'move) - (point)))) - (unless (member 'planning keep) - (goto-char (point-min)) - (while (re-search-forward kwd-time-re nil t) - (replace-match ""))) - (goto-char (point-min)) - (when org-agenda-entry-text-exclude-regexps - (let ((re-list org-agenda-entry-text-exclude-regexps) re) - (while (setq re (pop re-list)) - (goto-char (point-min)) - (while (re-search-forward re nil t) - (replace-match ""))))) - (goto-char (point-max)) - (skip-chars-backward " \t\n") - (if (looking-at "[ \t\n]+\\'") (replace-match "")) - - ;; find and remove min common indentation - (goto-char (point-min)) - (untabify (point-min) (point-max)) - (setq ind (org-get-indentation)) - (while (not (eobp)) - (unless (looking-at "[ \t]*$") - (setq ind (min ind (org-get-indentation)))) - (beginning-of-line 2)) - (goto-char (point-min)) - (while (not (eobp)) - (unless (looking-at "[ \t]*$") - (move-to-column ind) - (delete-region (point-at-bol) (point))) - (beginning-of-line 2)) - - (run-hooks 'org-agenda-entry-text-cleanup-hook) - - (goto-char (point-min)) - (when indent - (while (and (not (eobp)) (re-search-forward "^" nil t)) - (replace-match indent t t))) - (goto-char (point-min)) - (while (looking-at "[ \t]*\n") (replace-match "")) - (goto-char (point-max)) - (when (> (org-current-line) - n-lines) - (org-goto-line (1+ n-lines)) - (backward-char 1)) - (setq txt (buffer-substring (point-min) (point))))))))) + (org-with-wide-buffer + (goto-char marker) + (end-of-line 1) + (setq txt (buffer-substring + (min (1+ (point)) (point-max)) + (progn (outline-next-heading) (point))) + drawer-re org-drawer-regexp + kwd-time-re (concat "^[ \t]*" org-keyword-time-regexp + ".*\n?")) + (with-temp-buffer + (insert txt) + (when org-agenda-add-entry-text-descriptive-links + (goto-char (point-min)) + (while (org-activate-links (point-max)) + (add-text-properties (match-beginning 0) (match-end 0) + '(face org-link)))) + (goto-char (point-min)) + (while (re-search-forward org-bracket-link-regexp (point-max) t) + (set-text-properties (match-beginning 0) (match-end 0) + nil)) + (goto-char (point-min)) + (while (re-search-forward drawer-re nil t) + (delete-region + (match-beginning 0) + (progn (re-search-forward + "^[ \t]*:END:.*\n?" nil 'move) + (point)))) + (unless (member 'planning keep) + (goto-char (point-min)) + (while (re-search-forward kwd-time-re nil t) + (replace-match ""))) + (goto-char (point-min)) + (when org-agenda-entry-text-exclude-regexps + (let ((re-list org-agenda-entry-text-exclude-regexps) re) + (while (setq re (pop re-list)) + (goto-char (point-min)) + (while (re-search-forward re nil t) + (replace-match ""))))) + (goto-char (point-max)) + (skip-chars-backward " \t\n") + (if (looking-at "[ \t\n]+\\'") (replace-match "")) + + ;; find and remove min common indentation + (goto-char (point-min)) + (untabify (point-min) (point-max)) + (setq ind (org-get-indentation)) + (while (not (eobp)) + (unless (looking-at "[ \t]*$") + (setq ind (min ind (org-get-indentation)))) + (beginning-of-line 2)) + (goto-char (point-min)) + (while (not (eobp)) + (unless (looking-at "[ \t]*$") + (move-to-column ind) + (delete-region (point-at-bol) (point))) + (beginning-of-line 2)) + + (run-hooks 'org-agenda-entry-text-cleanup-hook) + + (goto-char (point-min)) + (when indent + (while (and (not (eobp)) (re-search-forward "^" nil t)) + (replace-match indent t t))) + (goto-char (point-min)) + (while (looking-at "[ \t]*\n") (replace-match "")) + (goto-char (point-max)) + (when (> (org-current-line) + n-lines) + (org-goto-line (1+ n-lines)) + (backward-char 1)) + (setq txt (buffer-substring (point-min) (point)))))))) txt)) (defun org-check-for-org-mode () "Make sure current buffer is in org-mode. Error if not." (or (derived-mode-p 'org-mode) - (error "Cannot execute org-mode agenda command on buffer in %s" + (error "Cannot execute Org agenda command on buffer in %s" major-mode))) ;;; Agenda prepare and finalize @@ -3531,6 +3545,7 @@ removed from the entry content. Currently only `planning' is allowed here." (defvar org-agenda-tag-filter nil) (defvar org-agenda-category-filter nil) (defvar org-agenda-regexp-filter nil) +(defvar org-agenda-effort-filter nil) (defvar org-agenda-top-headline-filter nil) (defvar org-agenda-tag-filter-preset nil "A preset of the tags filter used for secondary agenda filtering. @@ -3562,6 +3577,16 @@ the entire agenda view. In a block agenda, it will not work reliably to define a filter for one of the individual blocks. You need to set it in the global options and expect it to be applied to the entire view.") +(defvar org-agenda-effort-filter-preset nil + "A preset of the effort condition used for secondary agenda filtering. +This must be a list of strings, each string must be a single regexp +preceded by \"+\" or \"-\". +This variable should not be set directly, but agenda custom commands can +bind it in the options section. The preset filter is a global property of +the entire agenda view. In a block agenda, it will not work reliably to +define a filter for one of the individual blocks. You need to set it in +the global options and expect it to be applied to the entire view.") + (defun org-agenda-use-sticky-p () "Return non-nil if an agenda buffer named `org-agenda-buffer-name' exists and should be shown instead of @@ -3593,30 +3618,37 @@ FILTER-ALIST is an alist of filters we need to apply when ((equal (current-buffer) abuf) nil) (awin (select-window awin)) ((not (setq wconf (current-window-configuration)))) - ((equal org-agenda-window-setup 'current-window) - (org-pop-to-buffer-same-window abuf)) - ((equal org-agenda-window-setup 'other-window) + ((eq org-agenda-window-setup 'current-window) + (pop-to-buffer-same-window abuf)) + ((eq org-agenda-window-setup 'other-window) (org-switch-to-buffer-other-window abuf)) - ((equal org-agenda-window-setup 'other-frame) + ((eq org-agenda-window-setup 'other-frame) (switch-to-buffer-other-frame abuf)) - ((equal org-agenda-window-setup 'reorganize-frame) + ((eq org-agenda-window-setup 'only-window) + (delete-other-windows) + (pop-to-buffer-same-window abuf)) + ((eq org-agenda-window-setup 'reorganize-frame) (delete-other-windows) (org-switch-to-buffer-other-window abuf))) - (setq org-agenda-tag-filter (cdr (assoc 'tag filter-alist))) - (setq org-agenda-category-filter (cdr (assoc 'cat filter-alist))) - (setq org-agenda-regexp-filter (cdr (assoc 're filter-alist))) + (setq org-agenda-tag-filter (cdr (assq 'tag filter-alist))) + (setq org-agenda-category-filter (cdr (assq 'cat filter-alist))) + (setq org-agenda-effort-filter (cdr (assq 'effort filter-alist))) + (setq org-agenda-regexp-filter (cdr (assq 're filter-alist))) ;; Additional test in case agenda is invoked from within agenda ;; buffer via elisp link. (unless (equal (current-buffer) abuf) - (org-pop-to-buffer-same-window abuf)) + (pop-to-buffer-same-window abuf)) (setq org-agenda-pre-window-conf - (or org-agenda-pre-window-conf wconf)))) + (or wconf org-agenda-pre-window-conf)))) (defun org-agenda-prepare (&optional name) (let ((filter-alist (if org-agenda-persistent-filter - (list `(tag . ,org-agenda-tag-filter) - `(re . ,org-agenda-regexp-filter) - `(car . ,org-agenda-category-filter))))) + (with-current-buffer + (get-buffer-create org-agenda-buffer-name) + (list `(tag . ,org-agenda-tag-filter) + `(re . ,org-agenda-regexp-filter) + `(effort . ,org-agenda-effort-filter) + `(cat . ,org-agenda-category-filter)))))) (if (org-agenda-use-sticky-p) (progn (put 'org-agenda-tag-filter :preset-filter nil) @@ -3629,13 +3661,14 @@ FILTER-ALIST is an alist of filters we need to apply when (or org-agenda-multi (org-agenda-fit-window-to-buffer)) (throw 'exit "Sticky Agenda buffer, use `r' to refresh")) (setq org-todo-keywords-for-agenda nil) - (setq org-drawers-for-agenda nil) (put 'org-agenda-tag-filter :preset-filter org-agenda-tag-filter-preset) (put 'org-agenda-category-filter :preset-filter org-agenda-category-filter-preset) (put 'org-agenda-regexp-filter :preset-filter org-agenda-regexp-filter-preset) + (put 'org-agenda-effort-filter :preset-filter + org-agenda-effort-filter-preset) (if org-agenda-multi (progn (setq buffer-read-only nil) @@ -3649,7 +3682,6 @@ FILTER-ALIST is an alist of filters we need to apply when "\n")) (narrow-to-region (point) (point-max))) (setq org-done-keywords-for-agenda nil) - ;; Setting any org variables that are in org-agenda-local-vars ;; list need to be done after the prepare call (org-agenda-prepare-window @@ -3666,11 +3698,10 @@ FILTER-ALIST is an alist of filters we need to apply when (org-uniquify org-todo-keywords-for-agenda)) (setq org-done-keywords-for-agenda (org-uniquify org-done-keywords-for-agenda)) - (setq org-drawers-for-agenda (org-uniquify org-drawers-for-agenda)) (setq org-agenda-last-prefix-arg current-prefix-arg) (setq org-agenda-this-buffer-name org-agenda-buffer-name) (and name (not org-agenda-name) - (org-set-local 'org-agenda-name name))) + (setq-local org-agenda-name name))) (setq buffer-read-only nil)))) (defvar org-agenda-overriding-columns-format) ; From org-colview.el @@ -3681,11 +3712,7 @@ FILTER-ALIST is an alist of filters we need to apply when (let ((inhibit-read-only t)) (goto-char (point-min)) (save-excursion - (while (org-activate-bracket-links (point-max)) - (add-text-properties (match-beginning 0) (match-end 0) - '(face org-link)))) - (save-excursion - (while (org-activate-plain-links (point-max)) + (while (org-activate-links (point-max)) (add-text-properties (match-beginning 0) (match-end 0) '(face org-link)))) (unless (eq org-agenda-remove-tags t) @@ -3694,8 +3721,8 @@ FILTER-ALIST is an alist of filters we need to apply when (remove-text-properties (point-min) (point-max) '(face nil))) (if (and (boundp 'org-agenda-overriding-columns-format) org-agenda-overriding-columns-format) - (org-set-local 'org-agenda-overriding-columns-format - org-agenda-overriding-columns-format)) + (setq-local org-agenda-overriding-columns-format + org-agenda-overriding-columns-format)) (if (and (boundp 'org-agenda-view-columns-initially) org-agenda-view-columns-initially) (org-agenda-columns)) @@ -3733,10 +3760,10 @@ FILTER-ALIST is an alist of filters we need to apply when (org-agenda-filter-top-headline-apply org-agenda-top-headline-filter)) (when org-agenda-tag-filter - (org-agenda-filter-apply org-agenda-tag-filter 'tag)) + (org-agenda-filter-apply org-agenda-tag-filter 'tag t)) (when (get 'org-agenda-tag-filter :preset-filter) (org-agenda-filter-apply - (get 'org-agenda-tag-filter :preset-filter) 'tag)) + (get 'org-agenda-tag-filter :preset-filter) 'tag t)) (when org-agenda-category-filter (org-agenda-filter-apply org-agenda-category-filter 'category)) (when (get 'org-agenda-category-filter :preset-filter) @@ -3747,13 +3774,18 @@ FILTER-ALIST is an alist of filters we need to apply when (when (get 'org-agenda-regexp-filter :preset-filter) (org-agenda-filter-apply (get 'org-agenda-regexp-filter :preset-filter) 'regexp)) - (org-add-hook 'kill-buffer-hook 'org-agenda-reset-markers 'append 'local))))) + (when org-agenda-effort-filter + (org-agenda-filter-apply org-agenda-effort-filter 'effort)) + (when (get 'org-agenda-effort-filter :preset-filter) + (org-agenda-filter-apply + (get 'org-agenda-effort-filter :preset-filter) 'effort)) + (add-hook 'kill-buffer-hook 'org-agenda-reset-markers 'append 'local))))) (defun org-agenda-mark-clocking-task () "Mark the current clock entry in the agenda if it is present." ;; We need to widen when `org-agenda-finalize' is called from ;; `org-agenda-change-all-lines' (e.g. in `org-agenda-clock-in') - (when org-clock-current-task + (when (bound-and-true-p org-clock-current-task) (save-restriction (widen) (org-agenda-unmark-clocking-task) @@ -3782,7 +3814,7 @@ FILTER-ALIST is an alist of filters we need to apply when "Make highest priority lines bold, and lowest italic." (interactive) (mapc (lambda (o) (if (eq (overlay-get o 'org-type) 'org-priority) - (delete-overlay o))) + (delete-overlay o))) (overlays-in (point-min) (point-max))) (save-excursion (let (b e p ov h l) @@ -3800,16 +3832,17 @@ FILTER-ALIST is an alist of filters we need to apply when ov (make-overlay b e)) (overlay-put ov 'face - (cons (cond ((org-face-from-face-or-color - 'priority nil - (cdr (assoc p org-priority-faces)))) - ((and (listp org-agenda-fontify-priorities) - (org-face-from-face-or-color - 'priority nil - (cdr (assoc p org-agenda-fontify-priorities))))) - ((equal p l) 'italic) - ((equal p h) 'bold)) - 'org-priority)) + (let ((special-face + (cond ((org-face-from-face-or-color + 'priority 'org-priority + (cdr (assoc p org-priority-faces)))) + ((and (listp org-agenda-fontify-priorities) + (org-face-from-face-or-color + 'priority 'org-priority + (cdr (assoc p org-agenda-fontify-priorities))))) + ((equal p l) 'italic) + ((equal p h) 'bold)))) + (if special-face (list special-face 'org-priority) 'org-priority))) (overlay-put ov 'org-type 'org-priority))))) (defvar org-depend-tag-blocked) @@ -3819,39 +3852,39 @@ FILTER-ALIST is an alist of filters we need to apply when When INVISIBLE is non-nil, hide currently blocked TODO instead of dimming them." (interactive "P") - (when (org-called-interactively-p 'interactive) + (when (called-interactively-p 'interactive) (message "Dim or hide blocked tasks...")) - (mapc (lambda (o) (if (eq (overlay-get o 'org-type) 'org-blocked-todo) - (delete-overlay o))) - (overlays-in (point-min) (point-max))) + (dolist (o (overlays-in (point-min) (point-max))) + (when (eq (overlay-get o 'org-type) 'org-blocked-todo) + (delete-overlay o))) (save-excursion (let ((inhibit-read-only t) (org-depend-tag-blocked nil) - (invis (or (not (null invisible)) - (eq org-agenda-dim-blocked-tasks 'invisible))) - org-blocked-by-checkboxes - invis1 b e p ov h l) + org-blocked-by-checkboxes) (goto-char (point-min)) - (while (let ((pos (next-single-property-change (point) 'todo-state))) - (and pos (goto-char (1+ pos)))) - (setq org-blocked-by-checkboxes nil invis1 invis) + (while (let ((pos (text-property-not-all + (point) (point-max) 'todo-state nil))) + (when pos (goto-char pos))) + (setq org-blocked-by-checkboxes nil) (let ((marker (org-get-at-bol 'org-hd-marker))) - (when (and marker + (when (and (markerp marker) (with-current-buffer (marker-buffer marker) (save-excursion (goto-char marker) (org-entry-blocked-p)))) - (if org-blocked-by-checkboxes (setq invis1 nil)) - (setq b (if invis1 - (max (point-min) (1- (point-at-bol))) - (point-at-bol)) - e (point-at-eol) - ov (make-overlay b e)) - (if invis1 - (progn (overlay-put ov 'invisible t) - (overlay-put ov 'intangible t)) - (overlay-put ov 'face 'org-agenda-dimmed-todo-face)) - (overlay-put ov 'org-type 'org-blocked-todo)))))) - (when (org-called-interactively-p 'interactive) + ;; Entries blocked by checkboxes cannot be made invisible. + ;; See `org-agenda-dim-blocked-tasks' for details. + (let* ((really-invisible + (and (not org-blocked-by-checkboxes) + (or invisible (eq org-agenda-dim-blocked-tasks + 'invisible)))) + (ov (make-overlay (if really-invisible (line-end-position 0) + (line-beginning-position)) + (line-end-position)))) + (if really-invisible (overlay-put ov 'invisible t) + (overlay-put ov 'face 'org-agenda-dimmed-todo-face)) + (overlay-put ov 'org-type 'org-blocked-todo)))) + (forward-line)))) + (when (called-interactively-p 'interactive) (message "Dim or hide blocked tasks...done"))) (defvar org-agenda-skip-function nil @@ -3908,9 +3941,9 @@ functions do." (defun org-agenda-new-marker (&optional pos) "Return a new agenda marker. -Org-mode keeps a list of these markers and resets them when they are -no longer in use." - (let ((m (copy-marker (or pos (point))))) +Maker is at point, or at POS if non-nil. Org mode keeps a list of +these markers and resets them when they are no longer in use." + (let ((m (copy-marker (or pos (point)) t))) (setq org-agenda-last-marker-time (float-time)) (if org-agenda-buffer (with-current-buffer org-agenda-buffer @@ -3972,13 +4005,12 @@ This check for agenda markers in all agenda buffers currently active." (defun org-agenda-get-day-face (date) "Return the face DATE should be displayed with." - (or (and (functionp org-agenda-day-face-function) - (funcall org-agenda-day-face-function date)) - (cond ((org-agenda-todayp date) - 'org-agenda-date-today) - ((member (calendar-day-of-week date) org-agenda-weekend-days) - 'org-agenda-date-weekend) - (t 'org-agenda-date)))) + (cond ((and (functionp org-agenda-day-face-function) + (funcall org-agenda-day-face-function date))) + ((org-agenda-today-p date) 'org-agenda-date-today) + ((memq (calendar-day-of-week date) org-agenda-weekend-days) + 'org-agenda-date-weekend) + (t 'org-agenda-date))) ;;; Agenda timeline @@ -3986,12 +4018,16 @@ This check for agenda markers in all agenda buffers currently active." (defvar org-agenda-show-log-scoped) ;; dynamically scope in `org-timeline' or `org-agenda-list' (defun org-timeline (&optional dotodo) - "Show a time-sorted view of the entries in the current org file. -Only entries with a time stamp of today or later will be listed. With -\\[universal-argument] prefix, all unfinished TODO items will also be shown, + "Show a time-sorted view of the entries in the current Org file. + +Only entries with a time stamp of today or later will be listed. + +With `\\[universal-argument]' prefix, all unfinished TODO items will also be \ +shown, under the current date. -If the buffer contains an active region, only check the region for -dates." + +If the buffer contains an active region, only check the region +for dates." (interactive "P") (let* ((dopast t) (org-agenda-show-log-scoped org-agenda-show-log) @@ -4160,13 +4196,14 @@ items if they have an hour specification like [h]h:mm." (catch 'exit (setq org-agenda-buffer-name (or org-agenda-buffer-tmp-name + (and org-agenda-doing-sticky-redo org-agenda-buffer-name) (if org-agenda-sticky (cond ((and org-keys (stringp org-match)) (format "*Org Agenda(%s:%s)*" org-keys org-match)) (org-keys (format "*Org Agenda(%s)*" org-keys)) (t "*Org Agenda(a)*"))) - org-agenda-buffer-name)) + "*Org Agenda*")) (org-agenda-prepare "Day/Week") (setq start-day (or start-day org-agenda-start-day)) (if (stringp start-day) @@ -4174,8 +4211,7 @@ items if they have an hour specification like [h]h:mm." (setq start-day (time-to-days (org-read-date nil t start-day)))) (org-compile-prefix-format 'agenda) (org-set-sorting-strategy 'agenda) - (let* ((span (org-agenda-ndays-to-span - (or span org-agenda-ndays org-agenda-span))) + (let* ((span (org-agenda-ndays-to-span (or span org-agenda-span))) (today (org-today)) (sd (or start-day today)) (ndays (org-agenda-span-to-ndays span sd)) @@ -4205,9 +4241,9 @@ items if they have an hour specification like [h]h:mm." (setq day-numbers (nreverse day-numbers)) (setq clocktable-start (car day-numbers) clocktable-end (1+ (or (org-last day-numbers) 0))) - (org-set-local 'org-starting-day (car day-numbers)) - (org-set-local 'org-arg-loc arg) - (org-set-local 'org-agenda-current-span (org-agenda-ndays-to-span span)) + (setq-local org-starting-day (car day-numbers)) + (setq-local org-arg-loc arg) + (setq-local org-agenda-current-span (org-agenda-ndays-to-span span)) (unless org-agenda-compact-blocks (let* ((d1 (car day-numbers)) (d2 (org-last day-numbers)) @@ -4353,10 +4389,10 @@ START-DAY is an absolute time value." ((eq span 'fortnight) 14) ((eq span 'month) (let ((date (calendar-gregorian-from-absolute start-day))) - (calendar-last-day-of-month (car date) (caddr date)))) + (calendar-last-day-of-month (car date) (cl-caddr date)))) ((eq span 'year) (let ((date (calendar-gregorian-from-absolute start-day))) - (if (calendar-leap-year-p (caddr date)) 366 365))))) + (if (calendar-leap-year-p (cl-caddr date)) 366 365))))) (defun org-agenda-span-name (span) "Return a SPAN name." @@ -4371,7 +4407,7 @@ START-DAY is an absolute time value." (defvar org-agenda-search-history nil) (defvar org-search-syntax-table nil - "Special syntax table for org-mode search. + "Special syntax table for Org search. In this table, we have single quotes not as word constituents, to that when \"+Ameli\" is searched as a work, it will also match \"Ameli's\"") @@ -4444,7 +4480,7 @@ in `org-agenda-text-search-extra-files'." (full-words org-agenda-search-view-force-full-words) (org-agenda-text-search-extra-files org-agenda-text-search-extra-files) regexp rtn rtnall files file pos inherited-tags - marker category category-pos level tags c neg re boolean + marker category level tags c neg re boolean ee txt beg end words regexps+ regexps- hdl-only buffer beg1 str) (unless (and (not edit-at) (stringp string) @@ -4576,7 +4612,7 @@ in `org-agenda-text-search-extra-files'." (> (org-reduced-level (org-outline-level)) org-agenda-search-view-max-outline-level) (forward-line -1) - (outline-back-to-heading t))) + (org-back-to-heading t))) (skip-chars-forward "* ") (setq beg (point-at-bol) beg1 (point) @@ -4611,7 +4647,6 @@ in `org-agenda-text-search-extra-files'." (setq marker (org-agenda-new-marker (point)) category (org-get-category) level (make-string (org-reduced-level (org-outline-level)) ? ) - category-pos (get-text-property (point) 'org-category-position) inherited-tags (or (eq org-agenda-show-inherited-tags 'always) (and (listp org-agenda-show-inherited-tags) @@ -4630,8 +4665,7 @@ in `org-agenda-text-search-extra-files'." 'org-todo-regexp org-todo-regexp 'level level 'org-complex-heading-regexp org-complex-heading-regexp - 'priority 1000 'org-category category - 'org-category-position category-pos + 'priority 1000 'type "search") (push txt ee) (goto-char (1- end)))))))))) @@ -4648,8 +4682,12 @@ in `org-agenda-text-search-extra-files'." (add-text-properties pos (1- (point)) (list 'face 'org-warning)) (setq pos (point)) (unless org-agenda-multi - (insert (substitute-command-keys - "Press `[', `]' to add/sub word, `{', `}' to add/sub regexp, `C-u r' to edit\n")) + (insert (substitute-command-keys "\ +Press `\\[org-agenda-manipulate-query-add]', \ +`\\[org-agenda-manipulate-query-subtract]' to add/sub word, \ +`\\[org-agenda-manipulate-query-add-re]', \ +`\\[org-agenda-manipulate-query-subtract-re]' to add/sub regexp, \ +`\\[universal-argument] \\[org-agenda-redo]' to edit\n")) (add-text-properties pos (1- (point)) (list 'face 'org-agenda-structure)))) (org-agenda-mark-header-line (point-min)) @@ -4686,7 +4724,7 @@ in `org-agenda-text-search-extra-files'." (defun org-todo-list (&optional arg) "Show all (not done) TODO entries from all agenda file in a single list. The prefix arg can be used to select a specific TODO keyword and limit -the list to these. When using \\[universal-argument], you will be prompted +the list to these. When using `\\[universal-argument]', you will be prompted for a keyword. A numeric prefix directly selects the Nth keyword in `org-todo-keywords-1'." (interactive "P") @@ -4704,8 +4742,8 @@ for a keyword. A numeric prefix directly selects the Nth keyword in rtn rtnall files file pos) (when (equal arg '(4)) (setq org-select-this-todo-keyword - (org-icompleting-read "Keyword (or KWD1|K2D2|...): " - (mapcar 'list kwds) nil nil))) + (completing-read "Keyword (or KWD1|K2D2|...): " + (mapcar #'list kwds) nil nil))) (and (equal 0 arg) (setq org-select-this-todo-keyword nil)) (catch 'exit (if org-agenda-sticky @@ -4743,7 +4781,8 @@ for a keyword. A numeric prefix directly selects the Nth keyword in org-select-this-todo-keyword)) (setq pos (point)) (unless org-agenda-multi - (insert (substitute-command-keys "Available with `N r': (0)[ALL]")) + (insert (substitute-command-keys "Available with \ +`N \\[org-agenda-redo]': (0)[ALL]")) (let ((n 0) s) (mapc (lambda (x) (setq s (format "(%d)%s" (setq n (1+ n)) x)) @@ -4779,6 +4818,7 @@ The prefix arg TODO-ONLY limits the search to TODO entries." (let* ((org-tags-match-list-sublevels org-tags-match-list-sublevels) (completion-ignore-case t) + (org--matcher-tags-todo-only todo-only) rtn rtnall files file pos matcher buffer) (when (and (stringp match) (not (string-match "\\S-" match))) @@ -4794,13 +4834,15 @@ The prefix arg TODO-ONLY limits the search to TODO entries." ;; expanding tags within `org-make-tags-matcher' (org-agenda-prepare (concat "TAGS " match)) (setq matcher (org-make-tags-matcher match) - match (car matcher) matcher (cdr matcher)) + match (car matcher) + matcher (cdr matcher)) (org-compile-prefix-format 'tags) (org-set-sorting-strategy 'tags) (setq org-agenda-query-string match) (setq org-agenda-redo-command - (list 'org-tags-view `(quote ,todo-only) - (list 'if 'current-prefix-arg nil `(quote ,org-agenda-query-string)))) + (list 'org-tags-view + `(quote ,org--matcher-tags-todo-only) + `(if current-prefix-arg nil ,org-agenda-query-string))) (setq files (org-agenda-files nil 'ifmode) rtnall nil) (while (setq file (pop files)) @@ -4823,7 +4865,9 @@ The prefix arg TODO-ONLY limits the search to TODO entries." (narrow-to-region org-agenda-restrict-begin org-agenda-restrict-end) (widen)) - (setq rtn (org-scan-tags 'agenda matcher todo-only)) + (setq rtn (org-scan-tags 'agenda + matcher + org--matcher-tags-todo-only)) (setq rtnall (append rtnall rtn)))))))) (if org-agenda-overriding-header (insert (org-add-props (copy-sequence org-agenda-overriding-header) @@ -4839,18 +4883,21 @@ The prefix arg TODO-ONLY limits the search to TODO entries." (setq pos (point)) (unless org-agenda-multi (insert (substitute-command-keys - "Press `C-u r' to search again with new search string\n"))) - (add-text-properties pos (1- (point)) (list 'face 'org-agenda-structure))) + "Press `\\[universal-argument] \\[org-agenda-redo]' \ +to search again with new search string\n"))) + (add-text-properties pos (1- (point)) + (list 'face 'org-agenda-structure))) (org-agenda-mark-header-line (point-min)) (when rtnall (insert (org-agenda-finalize-entries rtnall 'tags) "\n")) (goto-char (point-min)) (or org-agenda-multi (org-agenda-fit-window-to-buffer)) - (add-text-properties (point-min) (point-max) - `(org-agenda-type tags - org-last-args (,todo-only ,match) - org-redo-cmd ,org-agenda-redo-command - org-series-cmd ,org-cmd)) + (add-text-properties + (point-min) (point-max) + `(org-agenda-type tags + org-last-args (,org--matcher-tags-todo-only ,match) + org-redo-cmd ,org-agenda-redo-command + org-series-cmd ,org-cmd)) (org-agenda-finalize) (setq buffer-read-only t)))) @@ -5038,50 +5085,53 @@ Stuck projects are project that have no next actions. For the definitions of what a project is and how to check if it stuck, customize the variable `org-stuck-projects'." (interactive) - (let* ((org-agenda-skip-function - 'org-agenda-skip-entry-when-regexp-matches-in-subtree) - ;; We could have used org-agenda-skip-if here. - (org-agenda-overriding-header + (let* ((org-agenda-overriding-header (or org-agenda-overriding-header "List of stuck projects: ")) (matcher (nth 0 org-stuck-projects)) (todo (nth 1 org-stuck-projects)) - (todo-wds (if (member "*" todo) - (progn - (org-agenda-prepare-buffers (org-agenda-files - nil 'ifmode)) - (org-delete-all - org-done-keywords-for-agenda - (copy-sequence org-todo-keywords-for-agenda))) - todo)) - (todo-re (concat "^\\*+[ \t]+\\(" - (mapconcat 'identity todo-wds "\\|") - "\\)\\>")) (tags (nth 2 org-stuck-projects)) - (tags-re (if (member "*" tags) - (concat org-outline-regexp-bol - (org-re ".*:[[:alnum:]_@#%]+:[ \t]*$")) - (if tags - (concat org-outline-regexp-bol - ".*:\\(" - (mapconcat 'identity tags "\\|") - (org-re "\\):[[:alnum:]_@#%:]*[ \t]*$"))))) - (gen-re (nth 3 org-stuck-projects)) - (re-list - (delq nil - (list - (if todo todo-re) - (if tags tags-re) - (and gen-re (stringp gen-re) (string-match "\\S-" gen-re) - gen-re))))) - (setq org-agenda-skip-regexp - (if re-list - (mapconcat 'identity re-list "\\|") - (error "No information how to identify unstuck projects"))) + (gen-re (org-string-nw-p (nth 3 org-stuck-projects))) + (todo-wds + (if (not (member "*" todo)) todo + (org-agenda-prepare-buffers (org-agenda-files nil 'ifmode)) + (org-delete-all org-done-keywords-for-agenda + (copy-sequence org-todo-keywords-for-agenda)))) + (todo-re (and todo + (format "^\\*+[ \t]+\\(%s\\)\\>" + (mapconcat #'identity todo-wds "\\|")))) + (tags-re (cond ((null tags) nil) + ((member "*" tags) + (eval-when-compile + (concat org-outline-regexp-bol + ".*:[[:alnum:]_@#%]+:[ \t]*$"))) + (tags (concat org-outline-regexp-bol + ".*:\\(" + (mapconcat #'identity tags "\\|") + "\\):[[:alnum:]_@#%:]*[ \t]*$")) + (t nil))) + (re-list (delq nil (list todo-re tags-re gen-re))) + (skip-re + (if (null re-list) + (error "Missing information to identify unstuck projects") + (mapconcat #'identity re-list "\\|"))) + (org-agenda-skip-function + ;; Skip entry if `org-agenda-skip-regexp' matches anywhere + ;; in the subtree. + `(lambda () + (and (save-excursion + (let ((case-fold-search nil)) + (re-search-forward + ,skip-re (save-excursion (org-end-of-subtree t)) t))) + (progn (outline-next-heading) (point)))))) (org-tags-view nil matcher) (setq org-agenda-buffer-name (buffer-name)) (with-current-buffer org-agenda-buffer-name (setq org-agenda-redo-command - `(org-agenda-list-stuck-projects ,current-prefix-arg))))) + `(org-agenda-list-stuck-projects ,current-prefix-arg)) + (let ((inhibit-read-only t)) + (add-text-properties + (point-min) (point-max) + `(org-redo-cmd ,org-agenda-redo-command)))))) ;;; Diary integration @@ -5159,7 +5209,7 @@ date. It also removes lines that contain only whitespace." (while (re-search-forward "^ +\n" nil t) (replace-match "")) (goto-char (point-min)) - (if (re-search-forward "^Org-mode dummy\n?" nil t) + (if (re-search-forward "^Org mode dummy\n?" nil t) (replace-match "")) (run-hooks 'org-agenda-cleanup-fancy-diary-hook)) @@ -5177,7 +5227,7 @@ date. It also removes lines that contain only whitespace." (setq string (org-modify-diary-entry-string string)))))) (defun org-modify-diary-entry-string (string) - "Add text properties to string, allowing org-mode to act on it." + "Add text properties to string, allowing Org to act on it." (org-add-props string nil 'mouse-face 'highlight 'help-echo (if buffer-file-name @@ -5193,9 +5243,9 @@ Needed to avoid empty dates which mess up holiday display." ;; Catch the error if dealing with the new add-to-diary-alist (when org-disable-agenda-to-diary (condition-case nil - (org-add-to-diary-list original-date "Org-mode dummy" "") + (org-add-to-diary-list original-date "Org mode dummy" "") (error - (org-add-to-diary-list original-date "Org-mode dummy" "" nil))))) + (org-add-to-diary-list original-date "Org mode dummy" "" nil))))) (defun org-add-to-diary-list (&rest args) (if (fboundp 'diary-add-to-list) @@ -5265,67 +5315,77 @@ function from a program - use `org-agenda-get-day-entries' instead." ;;; Agenda entry finders +(defun org-agenda--timestamp-to-absolute (&rest args) + "Call `org-time-string-to-absolute' with ARGS. +However, throw `:skip' whenever an error is raised." + (condition-case e + (apply #'org-time-string-to-absolute args) + (org-diary-sexp-no-match (throw :skip nil)) + (error + (message "%s; Skipping entry" (error-message-string e)) + (throw :skip nil)))) + (defun org-agenda-get-day-entries (file date &rest args) "Does the work for `org-diary' and `org-agenda'. FILE is the path to a file to be checked for entries. DATE is date like the one returned by `calendar-current-date'. ARGS are symbols indicating which kind of entries should be extracted. For details about these, see the documentation of `org-diary'." - (setq args (or args org-agenda-entry-types)) (let* ((org-startup-folded nil) (org-startup-align-all-tables nil) - (buffer (if (file-exists-p file) - (org-get-agenda-file-buffer file) - (error "No such file %s" file))) - arg results rtn deadline-results) + (buffer (if (file-exists-p file) (org-get-agenda-file-buffer file) + (error "No such file %s" file)))) (if (not buffer) - ;; If file does not exist, make sure an error message ends up in diary + ;; If file does not exist, signal it in diary nonetheless. (list (format "ORG-AGENDA-ERROR: No such org-file %s" file)) (with-current-buffer buffer (unless (derived-mode-p 'org-mode) (error "Agenda file %s is not in `org-mode'" file)) (setq org-agenda-buffer (or org-agenda-buffer buffer)) - (let ((case-fold-search nil)) - (save-excursion - (save-restriction - (if (eq buffer org-agenda-restrict) - (narrow-to-region org-agenda-restrict-begin - org-agenda-restrict-end) - (widen)) - ;; The way we repeatedly append to `results' makes it O(n^2) :-( - (while (setq arg (pop args)) - (cond - ((and (eq arg :todo) - (equal date (calendar-gregorian-from-absolute - (org-today)))) - (setq rtn (org-agenda-get-todos)) - (setq results (append results rtn))) - ((eq arg :timestamp) - (setq rtn (org-agenda-get-blocks)) - (setq results (append results rtn)) - (setq rtn (org-agenda-get-timestamps deadline-results)) - (setq results (append results rtn))) - ((eq arg :sexp) - (setq rtn (org-agenda-get-sexps)) - (setq results (append results rtn))) - ((eq arg :scheduled) - (setq rtn (org-agenda-get-scheduled deadline-results)) - (setq results (append results rtn))) - ((eq arg :scheduled*) - (setq rtn (org-agenda-get-scheduled deadline-results t)) - (setq results (append results rtn))) - ((eq arg :closed) - (setq rtn (org-agenda-get-progress)) - (setq results (append results rtn))) - ((eq arg :deadline) - (setq rtn (org-agenda-get-deadlines)) - (setq deadline-results (copy-sequence rtn)) - (setq results (append results rtn))) - ((eq arg :deadline*) - (setq rtn (org-agenda-get-deadlines t)) - (setq deadline-results (copy-sequence rtn)) - (setq results (append results rtn)))))))) - results)))) + (setf org-agenda-current-date date) + (save-excursion + (save-restriction + (if (eq buffer org-agenda-restrict) + (narrow-to-region org-agenda-restrict-begin + org-agenda-restrict-end) + (widen)) + ;; Rationalize ARGS. Also make sure `:deadline' comes + ;; first in order to populate DEADLINES before passing it. + ;; + ;; We use `delq' since `org-uniquify' duplicates ARGS, + ;; guarding us from modifying `org-agenda-entry-types'. + (setf args (org-uniquify (or args org-agenda-entry-types))) + (when (and (memq :scheduled args) (memq :scheduled* args)) + (setf args (delq :scheduled* args))) + (cond + ((memq :deadline args) + (setf args (cons :deadline + (delq :deadline (delq :deadline* args))))) + ((memq :deadline* args) + (setf args (cons :deadline* (delq :deadline* args))))) + ;; Collect list of headlines. Return them flattened. + (let ((case-fold-search nil) results deadlines) + (dolist (arg args (apply #'nconc (nreverse results))) + (pcase arg + ((and :todo (guard (org-agenda-today-p date))) + (push (org-agenda-get-todos) results)) + (:timestamp + (push (org-agenda-get-blocks) results) + (push (org-agenda-get-timestamps deadlines) results)) + (:sexp + (push (org-agenda-get-sexps) results)) + (:scheduled + (push (org-agenda-get-scheduled deadlines) results)) + (:scheduled* + (push (org-agenda-get-scheduled deadlines t) results)) + (:closed + (push (org-agenda-get-progress) results)) + (:deadline + (setf deadlines (org-agenda-get-deadlines)) + (push deadlines results)) + (:deadline* + (setf deadlines (org-agenda-get-deadlines t)) + (push deadlines results))))))))))) (defsubst org-em (x y list) "Is X or Y a member of LIST?" @@ -5334,6 +5394,40 @@ the documentation of `org-diary'." (defvar org-heading-keyword-regexp-format) ; defined in org.el (defvar org-agenda-sorting-strategy-selected nil) +(defun org-agenda-entry-get-agenda-timestamp (pom) + "Retrieve timestamp information for sorting agenda views. +Given a point or marker POM, returns a cons cell of the timestamp +and the timestamp type relevant for the sorting strategy in +`org-agenda-sorting-strategy-selected'." + (let (ts ts-date-type) + (save-match-data + (cond ((org-em 'scheduled-up 'scheduled-down + org-agenda-sorting-strategy-selected) + (setq ts (org-entry-get pom "SCHEDULED") + ts-date-type " scheduled")) + ((org-em 'deadline-up 'deadline-down + org-agenda-sorting-strategy-selected) + (setq ts (org-entry-get pom "DEADLINE") + ts-date-type " deadline")) + ((org-em 'ts-up 'ts-down + org-agenda-sorting-strategy-selected) + (setq ts (org-entry-get pom "TIMESTAMP") + ts-date-type " timestamp")) + ((org-em 'tsia-up 'tsia-down + org-agenda-sorting-strategy-selected) + (setq ts (org-entry-get pom "TIMESTAMP_IA") + ts-date-type " timestamp_ia")) + ((org-em 'timestamp-up 'timestamp-down + org-agenda-sorting-strategy-selected) + (setq ts (or (org-entry-get pom "SCHEDULED") + (org-entry-get pom "DEADLINE") + (org-entry-get pom "TIMESTAMP") + (org-entry-get pom "TIMESTAMP_IA")) + ts-date-type "")) + (t (setq ts-date-type ""))) + (cons (when ts (ignore-errors (org-time-string-to-absolute ts))) + ts-date-type)))) + (defun org-agenda-get-todos () "Return the TODO information for agenda display." (let* ((props (list 'face nil @@ -5345,6 +5439,7 @@ the documentation of `org-diary'." 'help-echo (format "mouse-2 or RET jump to org file %s" (abbreviate-file-name buffer-file-name)))) + (case-fold-search nil) (regexp (format org-heading-keyword-regexp-format (cond ((and org-select-this-todo-keyword @@ -5358,7 +5453,8 @@ the documentation of `org-diary'." "|") "\\|") "\\)")) (t org-not-done-regexp)))) - marker priority category category-pos level tags todo-state ts-date ts-date-type + marker priority category level tags todo-state + ts-date ts-date-type ts-date-pair ee txt beg end inherited-tags todo-state-end-pos) (goto-char (point-min)) (while (re-search-forward regexp nil t) @@ -5378,36 +5474,10 @@ the documentation of `org-diary'." (goto-char (match-beginning 2)) (setq marker (org-agenda-new-marker (match-beginning 0)) category (org-get-category) - ts-date (let (ts) - (save-match-data - (cond ((org-em 'scheduled-up 'scheduled-down - org-agenda-sorting-strategy-selected) - (setq ts (org-entry-get (point) "SCHEDULED") - ts-date-type " scheduled")) - ((org-em 'deadline-up 'deadline-down - org-agenda-sorting-strategy-selected) - (setq ts (org-entry-get (point) "DEADLINE") - ts-date-type " deadline")) - ((org-em 'ts-up 'ts-down - org-agenda-sorting-strategy-selected) - (setq ts (org-entry-get (point) "TIMESTAMP") - ts-date-type " timestamp")) - ((org-em 'tsia-up 'tsia-down - org-agenda-sorting-strategy-selected) - (setq ts (org-entry-get (point) "TIMESTAMP_IA") - ts-date-type " timestamp_ia")) - ((org-em 'timestamp-up 'timestamp-down - org-agenda-sorting-strategy-selected) - (setq ts (or (org-entry-get (point) "SCHEDULED") - (org-entry-get (point) "DEADLINE") - (org-entry-get (point) "TIMESTAMP") - (org-entry-get (point) "TIMESTAMP_IA")) - ts-date-type "")) - (t (setq ts-date-type ""))) - (when ts (ignore-errors (org-time-string-to-absolute ts))))) - category-pos (get-text-property (point) 'org-category-position) - txt (org-trim - (buffer-substring (match-beginning 2) (match-end 0))) + ts-date-pair (org-agenda-entry-get-agenda-timestamp (point)) + ts-date (car ts-date-pair) + ts-date-type (cdr ts-date-pair) + txt (org-trim (buffer-substring (match-beginning 2) (match-end 0))) inherited-tags (or (eq org-agenda-show-inherited-tags 'always) (and (listp org-agenda-show-inherited-tags) @@ -5421,10 +5491,9 @@ the documentation of `org-diary'." priority (1+ (org-get-priority txt))) (org-add-props txt props 'org-marker marker 'org-hd-marker marker - 'priority priority 'org-category category + 'priority priority 'level level 'ts-date ts-date - 'org-category-position category-pos 'type (concat "todo" ts-date-type) 'todo-state todo-state) (push txt ee) (if org-agenda-todo-list-sublevels @@ -5473,7 +5542,7 @@ This function is invoked if `org-agenda-todo-ignore-deadlines', (cond ((memq org-agenda-todo-ignore-deadlines '(t all)) t) ((eq org-agenda-todo-ignore-deadlines 'far) - (not (org-deadline-close (match-string 1)))) + (not (org-deadline-close-p (match-string 1)))) ((eq org-agenda-todo-ignore-deadlines 'future) (> (org-time-stamp-to-now (match-string 1) org-agenda-todo-ignore-time-comparison-use-seconds) 0)) @@ -5483,7 +5552,7 @@ This function is invoked if `org-agenda-todo-ignore-deadlines', ((numberp org-agenda-todo-ignore-deadlines) (org-agenda-todo-custom-ignore-p (match-string 1) org-agenda-todo-ignore-deadlines)) - (t (org-deadline-close (match-string 1))))) + (t (org-deadline-close-p (match-string 1))))) (and org-agenda-todo-ignore-timestamp (let ((buffer (current-buffer)) (regexp @@ -5512,24 +5581,27 @@ This function is invoked if `org-agenda-todo-ignore-deadlines', (match-string 1) org-agenda-todo-ignore-timestamp)) (t)))))))))) -(defun org-agenda-get-timestamps (&optional deadline-results) - "Return the date stamp information for agenda display." +(defun org-agenda-get-timestamps (&optional deadlines) + "Return the date stamp information for agenda display. +Optional argument DEADLINES is a list of deadline items to be +displayed in agenda view." (let* ((props (list 'face 'org-agenda-calendar-event 'org-not-done-regexp org-not-done-regexp 'org-todo-regexp org-todo-regexp 'org-complex-heading-regexp org-complex-heading-regexp 'mouse-face 'highlight 'help-echo - (format "mouse-2 or RET jump to org file %s" + (format "mouse-2 or RET jump to Org file %s" (abbreviate-file-name buffer-file-name)))) - (d1 (calendar-absolute-from-gregorian date)) - mm + (current (calendar-absolute-from-gregorian date)) + (today (org-today)) (deadline-position-alist - (mapcar (lambda (a) (and (setq mm (get-text-property - 0 'org-hd-marker a)) - (cons (marker-position mm) a))) - deadline-results)) - (remove-re org-ts-regexp) + (mapcar (lambda (d) + (let ((m (get-text-property 0 'org-hd-marker d))) + (and m (marker-position m)))) + deadlines)) + ;; Match time-stamps set to current date, time-stamps with + ;; a repeater, and S-exp time-stamps. (regexp (concat (if org-agenda-include-inactive-timestamps "[[<]" "<") @@ -5537,97 +5609,106 @@ This function is invoked if `org-agenda-todo-ignore-deadlines', (substring (format-time-string (car org-time-stamp-formats) - (apply 'encode-time ; DATE bound by calendar + (apply #'encode-time ; DATE bound by calendar (list 0 0 0 (nth 1 date) (car date) (nth 2 date)))) 1 11)) "\\|\\(<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[hdwmy]>\\)" "\\|\\(<%%\\(([^>\n]+)\\)>\\)")) - marker hdmarker deadlinep scheduledp clockp closedp inactivep - donep tmp priority category category-pos level ee txt timestr tags - b0 b3 e3 head todo-state end-of-match show-all warntime habitp - inherited-tags ts-date) + timestamp-items) (goto-char (point-min)) - (while (setq end-of-match (re-search-forward regexp nil t)) - (setq b0 (match-beginning 0) - b3 (match-beginning 3) e3 (match-end 3) - todo-state (save-match-data (ignore-errors (org-get-todo-state))) - habitp (and (functionp 'org-is-habit-p) (save-match-data (org-is-habit-p))) - show-all (or (eq org-agenda-repeating-timestamp-show-all t) - (member todo-state - org-agenda-repeating-timestamp-show-all))) + (while (re-search-forward regexp nil t) + ;; Skip date ranges, scheduled and deadlines, which are handled + ;; specially. Also skip time-stamps before first headline as + ;; there would be no entry to add to the agenda. Eventually, + ;; ignore clock entries. (catch :skip - (and (org-at-date-range-p) (throw :skip nil)) - (org-agenda-skip) - (if (and (match-end 1) - (not (= d1 (org-time-string-to-absolute - (match-string 1) d1 nil show-all - (current-buffer) b0)))) - (throw :skip nil)) - (if (and e3 - (not (org-diary-sexp-entry (buffer-substring b3 e3) "" date))) + (save-match-data + (when (or (org-at-date-range-p) + (org-at-planning-p) + (org-before-first-heading-p) + (and org-agenda-include-inactive-timestamps + (org-at-clock-log-p))) (throw :skip nil)) - (setq tmp (buffer-substring (max (point-min) - (- b0 org-ds-keyword-length)) - b0) - timestr (if b3 "" (buffer-substring b0 (point-at-eol))) - inactivep (= (char-after b0) ?\[) - deadlinep (string-match org-deadline-regexp tmp) - scheduledp (string-match org-scheduled-regexp tmp) - closedp (and org-agenda-include-inactive-timestamps - (string-match org-closed-string tmp)) - clockp (and org-agenda-include-inactive-timestamps - (or (string-match org-clock-string tmp) - (string-match "]-+\\'" tmp))) - warntime (get-text-property (point) 'org-appt-warntime) - donep (member todo-state org-done-keywords)) - (if (or scheduledp deadlinep closedp clockp - (and donep org-agenda-skip-timestamp-if-done)) + (org-agenda-skip)) + (let* ((pos (match-beginning 0)) + (repeat (match-string 1)) + (sexp-entry (match-string 3)) + (time-stamp (if (or repeat sexp-entry) (match-string 0) + (save-excursion + (goto-char pos) + (looking-at org-ts-regexp-both) + (match-string 0)))) + (todo-state (org-get-todo-state)) + (show-all (or (eq org-agenda-repeating-timestamp-show-all t) + (member todo-state + org-agenda-repeating-timestamp-show-all))) + (warntime (get-text-property (point) 'org-appt-warntime)) + (done? (member todo-state org-done-keywords))) + ;; Possibly skip done tasks. + (when (and done? org-agenda-skip-timestamp-if-done) (throw :skip t)) - (if (string-match ">" timestr) - ;; substring should only run to end of time stamp - (setq timestr (substring timestr 0 (match-end 0)))) - (setq marker (org-agenda-new-marker b0) - category (org-get-category b0) - category-pos (get-text-property b0 'org-category-position)) - (save-excursion - (if (not (re-search-backward org-outline-regexp-bol nil t)) - (throw :skip nil) - (goto-char (match-beginning 0)) - (if (and (eq t org-agenda-skip-timestamp-if-deadline-is-shown) - (assoc (point) deadline-position-alist)) - (throw :skip nil)) - (setq hdmarker (org-agenda-new-marker) - inherited-tags - (or (eq org-agenda-show-inherited-tags 'always) - (and (listp org-agenda-show-inherited-tags) - (memq 'agenda org-agenda-show-inherited-tags)) - (and (eq org-agenda-show-inherited-tags t) - (or (eq org-agenda-use-tag-inheritance t) - (memq 'agenda org-agenda-use-tag-inheritance)))) - tags (org-get-tags-at nil (not inherited-tags)) - level (make-string (org-reduced-level (org-outline-level)) ? )) - (looking-at "\\*+[ \t]+\\([^\r\n]+\\)") - (setq head (or (match-string 1) "")) - (setq txt (org-agenda-format-item - (if inactivep org-agenda-inactive-leader nil) - head level category tags timestr - remove-re habitp))) - (setq priority (org-get-priority txt)) - (org-add-props txt props 'priority priority - 'org-marker marker 'org-hd-marker hdmarker - 'org-category category 'date date - 'level level - 'ts-date - (ignore-errors (org-time-string-to-absolute timestr)) - 'org-category-position category-pos - 'todo-state todo-state - 'warntime warntime - 'type "timestamp") - (push txt ee)) - (if org-agenda-skip-additional-timestamps-same-entry - (outline-next-heading) - (goto-char end-of-match)))) - (nreverse ee))) + ;; S-exp entry doesn't match current day: skip it. + (when (and sexp-entry (not (org-diary-sexp-entry sexp-entry "" date))) + (throw :skip nil)) + ;; When time-stamp doesn't match CURRENT but has a repeater, + ;; make sure it repeats on CURRENT. Furthermore, if + ;; SHOW-ALL is nil, ensure that repeats are only the first + ;; before and the first after today. + (when (and repeat + (if show-all + (/= current + (org-agenda--timestamp-to-absolute + repeat current 'future (current-buffer) pos)) + (and (/= current + (org-agenda--timestamp-to-absolute + repeat today 'past (current-buffer) pos)) + (/= current + (org-agenda--timestamp-to-absolute + repeat today 'future (current-buffer) pos))))) + (throw :skip nil)) + (save-excursion + (re-search-backward org-outline-regexp-bol nil t) + ;; Possibly skip time-stamp when a deadline is set. + (when (and org-agenda-skip-timestamp-if-deadline-is-shown + (assq (point) deadline-position-alist)) + (throw :skip nil)) + (let* ((category (org-get-category pos)) + (inherited-tags + (or (eq org-agenda-show-inherited-tags 'always) + (and (consp org-agenda-show-inherited-tags) + (memq 'agenda org-agenda-show-inherited-tags)) + (and (eq org-agenda-show-inherited-tags t) + (or (eq org-agenda-use-tag-inheritance t) + (memq 'agenda + org-agenda-use-tag-inheritance))))) + (tags (org-get-tags-at nil (not inherited-tags))) + (level (make-string (org-reduced-level (org-outline-level)) + ?\s)) + (head (and (looking-at "\\*+[ \t]+\\(.*\\)") + (match-string 1))) + (inactive? (= (char-after pos) ?\[)) + (habit? (and (fboundp 'org-is-habit-p) (org-is-habit-p))) + (item + (org-agenda-format-item + (and inactive? org-agenda-inactive-leader) + head level category tags time-stamp org-ts-regexp habit?))) + (org-add-props item props + 'priority (if habit? + (org-habit-get-priority (org-habit-parse-todo)) + (org-get-priority item)) + 'org-marker (org-agenda-new-marker pos) + 'org-hd-marker (org-agenda-new-marker) + 'date date + 'level level + 'ts-date (if repeat (org-agenda--timestamp-to-absolute repeat) + current) + 'todo-state todo-state + 'warntime warntime + 'type "timestamp") + (push item timestamp-items)))) + (when org-agenda-skip-additional-timestamps-same-entry + (outline-next-heading)))) + (nreverse timestamp-items))) (defun org-agenda-get-sexps () "Return the sexp information for agenda display." @@ -5638,7 +5719,7 @@ This function is invoked if `org-agenda-todo-ignore-deadlines', (format "mouse-2 or RET jump to org file %s" (abbreviate-file-name buffer-file-name)))) (regexp "^&?%%(") - marker category extra category-pos level ee txt tags entry + marker category extra level ee txt tags entry result beg b sexp sexp-entry todo-state warntime inherited-tags) (goto-char (point-min)) (while (re-search-forward regexp nil t) @@ -5657,7 +5738,6 @@ This function is invoked if `org-agenda-todo-ignore-deadlines', (setq marker (org-agenda-new-marker beg) level (make-string (org-reduced-level (org-outline-level)) ? ) category (org-get-category beg) - category-pos (get-text-property beg 'org-category-position) inherited-tags (or (eq org-agenda-show-inherited-tags 'always) (and (listp org-agenda-show-inherited-tags) @@ -5682,38 +5762,33 @@ This function is invoked if `org-agenda-todo-ignore-deadlines', (setq txt "SEXP entry returned empty string")) (setq txt (org-agenda-format-item extra txt level category tags 'time)) (org-add-props txt props 'org-marker marker - 'org-category category 'date date 'todo-state todo-state - 'org-category-position category-pos - 'level level - 'type "sexp" 'warntime warntime) + 'date date 'todo-state todo-state + 'level level 'type "sexp" 'warntime warntime) (push txt ee))))) (nreverse ee))) ;; Calendar sanity: define some functions that are independent of ;; `calendar-date-style'. -;; Normally I would like to use ISO format when calling the diary functions, -;; but to make sure we still have Emacs 22 compatibility we bind -;; also `european-calendar-style' and use european format (defun org-anniversary (year month day &optional mark) "Like `diary-anniversary', but with fixed (ISO) order of arguments." - (org-no-warnings - (let ((calendar-date-style 'european) (european-calendar-style t)) - (diary-anniversary day month year mark)))) + (with-no-warnings + (let ((calendar-date-style 'iso)) + (diary-anniversary year month day mark)))) (defun org-cyclic (N year month day &optional mark) "Like `diary-cyclic', but with fixed (ISO) order of arguments." - (org-no-warnings - (let ((calendar-date-style 'european) (european-calendar-style t)) - (diary-cyclic N day month year mark)))) + (with-no-warnings + (let ((calendar-date-style 'iso)) + (diary-cyclic N year month day mark)))) (defun org-block (Y1 M1 D1 Y2 M2 D2 &optional mark) "Like `diary-block', but with fixed (ISO) order of arguments." - (org-no-warnings - (let ((calendar-date-style 'european) (european-calendar-style t)) - (diary-block D1 M1 Y1 D2 M2 Y2 mark)))) + (with-no-warnings + (let ((calendar-date-style 'iso)) + (diary-block Y1 M1 D1 Y2 M2 D2 mark)))) (defun org-date (year month day &optional mark) "Like `diary-date', but with fixed (ISO) order of arguments." - (org-no-warnings - (let ((calendar-date-style 'european) (european-calendar-style t)) - (diary-date day month year mark)))) + (with-no-warnings + (let ((calendar-date-style 'iso)) + (diary-date year month day mark)))) ;; Define the `org-class' function (defun org-class (y1 m1 d1 y2 m2 d2 dayname &rest skip-weeks) @@ -5740,26 +5815,6 @@ then those holidays will be skipped." (delq nil (mapcar (lambda(g) (member g skip-weeks)) h)))) entry))) -(defun org-diary-class (m1 d1 y1 m2 d2 y2 dayname &rest skip-weeks) - "Like `org-class', but honor `calendar-date-style'. -The order of the first 2 times 3 arguments depends on the variable -`calendar-date-style' or, if that is not defined, on `european-calendar-style'. -So for American calendars, give this as MONTH DAY YEAR, for European as -DAY MONTH YEAR, and for ISO as YEAR MONTH DAY. -DAYNAME is a number between 0 (Sunday) and 6 (Saturday). SKIP-WEEKS -is any number of ISO weeks in the block period for which the item should -be skipped. - -This function is here only for backward compatibility and it is deprecated, -please use `org-class' instead." - (let* ((date1 (org-order-calendar-date-args m1 d1 y1)) - (date2 (org-order-calendar-date-args m2 d2 y2))) - (org-class - (nth 2 date1) (car date1) (nth 1 date1) - (nth 2 date2) (car date2) (nth 1 date2) - dayname skip-weeks))) -(make-obsolete 'org-diary-class 'org-class "") - (defalias 'org-get-closed 'org-agenda-get-progress) (defun org-agenda-get-progress () "Return the logged TODO entries for agenda display." @@ -5794,7 +5849,7 @@ please use `org-class' instead." (list 0 0 0 (nth 1 date) (car date) (nth 2 date)))) 1 11)))) (org-agenda-search-headline-for-time nil) - marker hdmarker priority category category-pos level tags closedp + marker hdmarker priority category level tags closedp statep clockp state ee txt extra timestr rest clocked inherited-tags) (goto-char (point-min)) (while (re-search-forward regexp nil t) @@ -5806,7 +5861,6 @@ please use `org-class' instead." clockp (not (or closedp statep)) state (and statep (match-string 2)) category (org-get-category (match-beginning 0)) - category-pos (get-text-property (match-beginning 0) 'org-category-position) timestr (buffer-substring (match-beginning 0) (point-at-eol))) (when (string-match "\\]" timestr) ;; substring should only run to end of time stamp @@ -5858,9 +5912,7 @@ please use `org-class' instead." (setq priority 100000) (org-add-props txt props 'org-marker marker 'org-hd-marker hdmarker 'face 'org-agenda-done - 'priority priority 'org-category category - 'org-category-position category-pos - 'level level + 'priority priority 'level level 'type "closed" 'date date 'undone-face 'org-warning 'done-face 'org-agenda-done) (push txt ee)) @@ -5876,7 +5928,7 @@ See also the user option `org-agenda-clock-consistency-checks'." (re (concat "^[ \t]*" org-clock-string "[ \t]+" - "\\(\\[.*?\\]\\)" ; group 1 is first stamp + "\\(\\[.*?\\]\\)" ; group 1 is first stamp "\\(-\\{1,3\\}\\(\\[.*?\\]\\)\\)?")) ; group 3 is second (tlstart 0.) (tlend 0.) @@ -5913,9 +5965,9 @@ See also the user option `org-agenda-clock-consistency-checks'." (setq ts (match-string 1) te (match-string 3) ts (float-time - (apply 'encode-time (org-parse-time-string ts))) + (apply #'encode-time (org-parse-time-string ts))) te (float-time - (apply 'encode-time (org-parse-time-string te))) + (apply #'encode-time (org-parse-time-string te))) dt (- te ts)))) (cond ((> dt (* 60 maxtime)) @@ -6001,312 +6053,348 @@ specification like [h]h:mm." (regexp (if with-hour org-deadline-time-hour-regexp org-deadline-time-regexp)) - (todayp (org-agenda-todayp date)) ; DATE bound by calendar - (d1 (calendar-absolute-from-gregorian date)) ; DATE bound by calendar - (dl0 (car org-agenda-deadline-leaders)) - (dl1 (nth 1 org-agenda-deadline-leaders)) - (dl2 (or (nth 2 org-agenda-deadline-leaders) dl1)) - d2 diff dfrac wdays pos pos1 category category-pos level - tags suppress-prewarning ee txt head face s todo-state - show-all upcomingp donep timestr warntime inherited-tags ts-date) + (today (org-today)) + (today? (org-agenda-today-p date)) ; DATE bound by calendar. + (current (calendar-absolute-from-gregorian date)) + deadline-items) (goto-char (point-min)) (while (re-search-forward regexp nil t) (catch :skip + (unless (save-match-data (org-at-planning-p)) (throw :skip nil)) (org-agenda-skip) - (setq s (match-string 1) - txt nil - pos (1- (match-beginning 1)) - todo-state (save-match-data (org-get-todo-state)) - show-all (or (eq org-agenda-repeating-timestamp-show-all t) - (member todo-state - org-agenda-repeating-timestamp-show-all)) - d2 (org-time-string-to-absolute - s d1 'past show-all (current-buffer) pos) - diff (- d2 d1)) - (setq suppress-prewarning - (let ((ds (and org-agenda-skip-deadline-prewarning-if-scheduled - (let ((item (buffer-substring (point-at-bol) - (point-at-eol)))) - (save-match-data - (and (string-match - org-scheduled-time-regexp item) - (match-string 1 item))))))) - (cond - ((not ds) nil) - ;; The current item has a scheduled date (in ds), so - ;; evaluate its prewarning lead time. - ((integerp org-agenda-skip-deadline-prewarning-if-scheduled) - ;; Use global prewarning-restart lead time. - org-agenda-skip-deadline-prewarning-if-scheduled) - ((eq org-agenda-skip-deadline-prewarning-if-scheduled - 'pre-scheduled) - ;; Set prewarning to no earlier than scheduled. - (min (- d2 (org-time-string-to-absolute - ds d1 'past show-all (current-buffer) pos)) - org-deadline-warning-days)) - ;; Set prewarning to deadline. - (t 0)))) - (setq wdays (if suppress-prewarning - (let ((org-deadline-warning-days suppress-prewarning)) - (org-get-wdays s)) - (org-get-wdays s)) - dfrac (- 1 (/ (* 1.0 diff) (max wdays 1))) - upcomingp (and todayp (> diff 0))) - ;; When to show a deadline in the calendar: - ;; If the expiration is within wdays warning time. - ;; Past-due deadlines are only shown on the current date - (if (and (or (and (<= diff wdays) - (and todayp (not org-agenda-only-exact-dates))) - (= diff 0))) - (save-excursion - ;; (setq todo-state (org-get-todo-state)) - (setq donep (member todo-state org-done-keywords)) - (if (and donep - (or org-agenda-skip-deadline-if-done - (not (= diff 0)))) - (setq txt nil) - (setq category (org-get-category) - warntime (get-text-property (point) 'org-appt-warntime) - category-pos (get-text-property (point) 'org-category-position)) - (if (not (re-search-backward "^\\*+[ \t]+" nil t)) - (throw :skip nil) - (goto-char (match-end 0)) - (setq pos1 (match-beginning 0)) - (setq level (make-string (org-reduced-level (org-outline-level)) ? )) - (setq inherited-tags - (or (eq org-agenda-show-inherited-tags 'always) - (and (listp org-agenda-show-inherited-tags) - (memq 'agenda org-agenda-show-inherited-tags)) - (and (eq org-agenda-show-inherited-tags t) - (or (eq org-agenda-use-tag-inheritance t) - (memq 'agenda org-agenda-use-tag-inheritance)))) - tags (org-get-tags-at pos1 (not inherited-tags))) - (setq head (buffer-substring - (point) - (progn (skip-chars-forward "^\r\n") - (point)))) - (if (string-match " \\([012]?[0-9]:[0-9][0-9]\\)" s) - (setq timestr - (concat (substring s (match-beginning 1)) " ")) - (setq timestr 'time)) - (setq txt (org-agenda-format-item - (cond ((= diff 0) dl0) - ((> diff 0) - (if (functionp dl1) - (funcall dl1 diff date) - (format dl1 diff))) - (t - (if (functionp dl2) - (funcall dl2 diff date) - (format dl2 (if (string= dl2 dl1) - diff (abs diff)))))) - head level category tags - (if (not (= diff 0)) nil timestr))))) - (when txt - (setq face (org-agenda-deadline-face dfrac)) - (org-add-props txt props - 'org-marker (org-agenda-new-marker pos) - 'warntime warntime - 'level level - 'ts-date d2 - 'org-hd-marker (org-agenda-new-marker pos1) - 'priority (+ (- diff) - (org-get-priority txt)) - 'org-category category - 'org-category-position category-pos - 'todo-state todo-state - 'type (if upcomingp "upcoming-deadline" "deadline") - 'date (if upcomingp date d2) - 'face (if donep 'org-agenda-done face) - 'undone-face face 'done-face 'org-agenda-done) - (push txt ee)))))) - (nreverse ee))) + (let* ((s (match-string 1)) + (pos (1- (match-beginning 1))) + (todo-state (save-match-data (org-get-todo-state))) + (done? (member todo-state org-done-keywords)) + (show-all (or (eq org-agenda-repeating-timestamp-show-all t) + (member todo-state + org-agenda-repeating-timestamp-show-all))) + (sexp? (string-prefix-p "%%" s)) + ;; DEADLINE is the bare deadline date, i.e., without + ;; any repeater, or the last repeat if SHOW-ALL is + ;; non-nil. REPEAT is closest repeat after CURRENT, if + ;; all repeated time stamps are to be shown, or after + ;; TODAY otherwise. REPEAT only applies to future + ;; dates. + (deadline (cond + (sexp? (org-agenda--timestamp-to-absolute s current)) + (show-all (org-agenda--timestamp-to-absolute s)) + (t (org-agenda--timestamp-to-absolute + s today 'past (current-buffer) pos)))) + (repeat (cond (sexp? deadline) + ((< current today) deadline) + (t + (org-agenda--timestamp-to-absolute + s (if show-all current today) 'future + (current-buffer) pos)))) + (diff (- deadline current)) + (suppress-prewarning + (let ((scheduled + (and org-agenda-skip-deadline-prewarning-if-scheduled + (org-entry-get nil "SCHEDULED")))) + (cond + ((not scheduled) nil) + ;; The current item has a scheduled date, so + ;; evaluate its prewarning lead time. + ((integerp org-agenda-skip-deadline-prewarning-if-scheduled) + ;; Use global prewarning-restart lead time. + org-agenda-skip-deadline-prewarning-if-scheduled) + ((eq org-agenda-skip-deadline-prewarning-if-scheduled + 'pre-scheduled) + ;; Set pre-warning to no earlier than SCHEDULED. + (min (- deadline + (org-agenda--timestamp-to-absolute scheduled)) + org-deadline-warning-days)) + ;; Set pre-warning to deadline. + (t 0)))) + (wdays (if suppress-prewarning + (let ((org-deadline-warning-days suppress-prewarning)) + (org-get-wdays s)) + (org-get-wdays s)))) + ;; When to show a deadline in the calendar: if the + ;; expiration is within WDAYS warning time. Past-due + ;; deadlines are only shown on today agenda. + (when (cond ((= current deadline) nil) + ((< deadline today) + (and (not today?) + (or (< current today) (/= repeat current)))) + ((> deadline current) + (or (not today?) (> diff wdays))) + (t (/= repeat current))) + (throw :skip nil)) + ;; Possibly skip done tasks. + (when (and done? + (or org-agenda-skip-deadline-if-done + (/= deadline current))) + (throw :skip nil)) + (save-excursion + (re-search-backward "^\\*+[ \t]+" nil t) + (goto-char (match-end 0)) + (let* ((category (org-get-category)) + (level + (make-string (org-reduced-level (org-outline-level)) ?\s)) + (head (buffer-substring (point) (line-end-position))) + (inherited-tags + (or (eq org-agenda-show-inherited-tags 'always) + (and (listp org-agenda-show-inherited-tags) + (memq 'agenda org-agenda-show-inherited-tags)) + (and (eq org-agenda-show-inherited-tags t) + (or (eq org-agenda-use-tag-inheritance t) + (memq 'agenda + org-agenda-use-tag-inheritance))))) + (tags (org-get-tags-at nil (not inherited-tags))) + (time + (cond + ;; No time of day designation if it is only + ;; a reminder. + ((and (/= current deadline) (/= current repeat)) nil) + ((string-match " \\([012]?[0-9]:[0-9][0-9]\\)" s) + (concat (substring s (match-beginning 1)) " ")) + (t 'time))) + (item + (org-agenda-format-item + ;; Insert appropriate suffixes before deadlines. + (pcase-let ((`(,now ,future ,past) + org-agenda-deadline-leaders)) + (cond + ;; Future (i.e., repeated) deadlines are + ;; displayed as new headlines. + ((> current today) now) + ;; When SHOW-ALL is nil, prefer repeated + ;; deadlines over reminders of past deadlines. + ((and (not show-all) (= repeat today)) now) + ((= deadline current) now) + ((< deadline current) (format past (- diff))) + (t (format future diff)))) + head level category tags + (and (or (= repeat current) (= deadline current)) + time))) + (face (org-agenda-deadline-face + (- 1 (/ (float (- deadline current)) (max wdays 1))))) + (upcoming? (and today? (> deadline today))) + (warntime (get-text-property (point) 'org-appt-warntime))) + (org-add-props item props + 'org-marker (org-agenda-new-marker pos) + 'org-hd-marker (org-agenda-new-marker (line-beginning-position)) + 'warntime warntime + 'level level + 'ts-date deadline + 'priority + ;; Adjust priority to today reminders about deadlines. + ;; Overdue deadlines get the highest priority + ;; increase, then imminent deadlines and eventually + ;; more distant deadlines. + (let ((adjust (cond ((not today?) 0) + ((and (not show-all) (= repeat current)) 0) + (t (- diff))))) + (+ adjust (org-get-priority item))) + 'todo-state todo-state + 'type (if upcoming? "upcoming-deadline" "deadline") + 'date (if upcoming? date deadline) + 'face (if done? 'org-agenda-done face) + 'undone-face face + 'done-face 'org-agenda-done) + (push item deadline-items)))))) + (nreverse deadline-items))) (defun org-agenda-deadline-face (fraction) "Return the face to displaying a deadline item. FRACTION is what fraction of the head-warning time has passed." - (let ((faces org-agenda-deadline-faces) f) - (catch 'exit - (while (setq f (pop faces)) - (if (>= fraction (car f)) (throw 'exit (cdr f))))))) + (assoc-default fraction org-agenda-deadline-faces #'<=)) -(defun org-agenda-get-scheduled (&optional deadline-results with-hour) +(defun org-agenda-get-scheduled (&optional deadlines with-hour) "Return the scheduled information for agenda display. -When WITH-HOUR is non-nil, only return scheduled items with -an hour specification like [h]h:mm." +Optional argument DEADLINES is a list of deadline items to be +displayed in agenda view. When WITH-HOUR is non-nil, only return +scheduled items with an hour specification like [h]h:mm." (let* ((props (list 'org-not-done-regexp org-not-done-regexp 'org-todo-regexp org-todo-regexp 'org-complex-heading-regexp org-complex-heading-regexp 'done-face 'org-agenda-done 'mouse-face 'highlight 'help-echo - (format "mouse-2 or RET jump to org file %s" + (format "mouse-2 or RET jump to Org file %s" (abbreviate-file-name buffer-file-name)))) (regexp (if with-hour org-scheduled-time-hour-regexp org-scheduled-time-regexp)) - (todayp (org-agenda-todayp date)) ; DATE bound by calendar - (d1 (calendar-absolute-from-gregorian date)) ; DATE bound by calendar - mm - (deadline-position-alist - (mapcar (lambda (a) (and (setq mm (get-text-property - 0 'org-hd-marker a)) - (cons (marker-position mm) a))) - deadline-results)) - d2 diff pos pos1 category category-pos level tags donep - ee txt head pastschedp todo-state face timestr s habitp show-all - did-habit-check-p warntime inherited-tags ts-date suppress-delay - ddays) + (today (org-today)) + (todayp (org-agenda-today-p date)) ; DATE bound by calendar. + (current (calendar-absolute-from-gregorian date)) + (deadline-pos + (mapcar (lambda (d) + (let ((m (get-text-property 0 'org-hd-marker d))) + (and m (marker-position m)))) + deadlines)) + scheduled-items) (goto-char (point-min)) (while (re-search-forward regexp nil t) (catch :skip + (unless (save-match-data (org-at-planning-p)) (throw :skip nil)) (org-agenda-skip) - (setq s (match-string 1) - txt nil - pos (1- (match-beginning 1)) - todo-state (save-match-data (org-get-todo-state)) - show-all (or (eq org-agenda-repeating-timestamp-show-all t) - (member todo-state - org-agenda-repeating-timestamp-show-all)) - d2 (org-time-string-to-absolute - s d1 'past show-all (current-buffer) pos) - diff (- d2 d1) - warntime (get-text-property (point) 'org-appt-warntime)) - (setq pastschedp (and todayp (< diff 0))) - (setq did-habit-check-p nil) - (setq suppress-delay - (let ((ds (and org-agenda-skip-scheduled-delay-if-deadline - (let ((item (buffer-substring (point-at-bol) (point-at-eol)))) - (save-match-data - (and (string-match - org-deadline-time-regexp item) - (match-string 1 item))))))) + (let* ((s (match-string 1)) + (pos (1- (match-beginning 1))) + (todo-state (save-match-data (org-get-todo-state))) + (donep (member todo-state org-done-keywords)) + (show-all (or (eq org-agenda-repeating-timestamp-show-all t) + (member todo-state + org-agenda-repeating-timestamp-show-all))) + (sexp? (string-prefix-p "%%" s)) + ;; SCHEDULE is the bare scheduled date, i.e., without + ;; any repeater if non-nil, or last repeat if SHOW-ALL + ;; is nil. REPEAT is the closest repeat after CURRENT, + ;; if all repeated time stamps are to be shown, or + ;; after TODAY otherwise. REPEAT only applies to + ;; future dates. + (schedule (cond + (sexp? (org-agenda--timestamp-to-absolute s current)) + (show-all (org-agenda--timestamp-to-absolute s)) + (t (org-agenda--timestamp-to-absolute + s today 'past (current-buffer) pos)))) + (repeat (cond + (sexp? schedule) + ((< current today) schedule) + (t + (org-agenda--timestamp-to-absolute + s (if show-all current today) 'future + (current-buffer) pos)))) + (diff (- current schedule)) + (warntime (get-text-property (point) 'org-appt-warntime)) + (pastschedp (< schedule today)) + (habitp (and (fboundp 'org-is-habit-p) (org-is-habit-p))) + (suppress-delay + (let ((deadline (and org-agenda-skip-scheduled-delay-if-deadline + (org-entry-get nil "DEADLINE")))) + (cond + ((not deadline) nil) + ;; The current item has a deadline date, so + ;; evaluate its delay time. + ((integerp org-agenda-skip-scheduled-delay-if-deadline) + ;; Use global delay time. + (- org-agenda-skip-scheduled-delay-if-deadline)) + ((eq org-agenda-skip-scheduled-delay-if-deadline + 'post-deadline) + ;; Set delay to no later than DEADLINE. + (min (- schedule + (org-agenda--timestamp-to-absolute deadline)) + org-scheduled-delay-days)) + (t 0)))) + (ddays (cond - ((not ds) nil) - ;; The current item has a deadline date (in ds), so - ;; evaluate its delay time. - ((integerp org-agenda-skip-scheduled-delay-if-deadline) - ;; Use global delay time. - (- org-agenda-skip-scheduled-delay-if-deadline)) - ((eq org-agenda-skip-scheduled-delay-if-deadline - 'post-deadline) - ;; Set delay to no later than deadline. - (min (- d2 (org-time-string-to-absolute - ds d1 'past show-all (current-buffer) pos)) - org-scheduled-delay-days)) - (t 0)))) - (setq ddays (if suppress-delay - (let ((org-scheduled-delay-days suppress-delay)) - (org-get-wdays s t t)) - (org-get-wdays s t))) - ;; Use a delay of 0 when there is a repeater and the delay is - ;; of the form --3d - (when (and (save-match-data (string-match "--[0-9]+[hdwmy]" s)) - (< (org-time-string-to-absolute s) - (org-time-string-to-absolute - s d2 'past nil (current-buffer) pos))) - (setq ddays 0)) - ;; When to show a scheduled item in the calendar: - ;; If it is on or past the date. - (when (or (and (> ddays 0) (= diff (- ddays))) - (and (zerop ddays) (= diff 0)) - (and (< (+ diff ddays) 0) - (< (abs diff) org-scheduled-past-days) - (and todayp (not org-agenda-only-exact-dates))) - ;; org-is-habit-p uses org-entry-get, which is expansive - ;; so we go extra mile to only call it once - (and todayp - (boundp 'org-habit-show-all-today) - org-habit-show-all-today - (setq did-habit-check-p t) - (setq habitp (and (functionp 'org-is-habit-p) - (org-is-habit-p))))) - (save-excursion - (setq donep (member todo-state org-done-keywords)) - (if (and donep + ;; Nullify delay when a repeater triggered already + ;; and the delay is of the form --Xd. + ((and (string-match-p "--[0-9]+[hdwmy]" s) + (> current schedule)) + 0) + (suppress-delay + (let ((org-scheduled-delay-days suppress-delay)) + (org-get-wdays s t t))) + (t (org-get-wdays s t))))) + ;; Display scheduled items at base date (SCHEDULE), today if + ;; scheduled before the current date, and at any repeat past + ;; today. However, skip delayed items and items that have + ;; been displayed for more than `org-scheduled-past-days'. + (unless (and todayp + habitp + (bound-and-true-p org-habit-show-all-today)) + (when (or (and (> ddays 0) (< diff ddays)) + (> diff org-scheduled-past-days) + (> schedule current) + (and (< schedule current) + (not todayp) + (/= repeat current))) + (throw :skip nil))) + ;; Possibly skip done tasks. + (when (and donep (or org-agenda-skip-scheduled-if-done - (not (= diff 0)) - (and (functionp 'org-is-habit-p) - (org-is-habit-p)))) - (setq txt nil) - (setq habitp (if did-habit-check-p habitp - (and (functionp 'org-is-habit-p) - (org-is-habit-p)))) - (setq category (org-get-category) - category-pos (get-text-property (point) 'org-category-position)) - (if (and (eq org-agenda-skip-scheduled-if-deadline-is-shown - 'repeated-after-deadline) - (org-get-deadline-time (point)) - (<= 0 (- d2 (time-to-days (org-get-deadline-time (point)))))) - (throw :skip nil)) - (if (not (re-search-backward "^\\*+[ \t]+" nil t)) - (throw :skip nil) - (goto-char (match-end 0)) - (setq pos1 (match-beginning 0)) - (if habitp - (if (or (not org-habit-show-habits) - (and (not todayp) - (boundp 'org-habit-show-habits-only-for-today) - org-habit-show-habits-only-for-today)) - (throw :skip nil)) - (if (and - (or (eq t org-agenda-skip-scheduled-if-deadline-is-shown) - (and (eq org-agenda-skip-scheduled-if-deadline-is-shown 'not-today) - pastschedp)) - (setq mm (assoc pos1 deadline-position-alist))) - (throw :skip nil))) - (setq inherited-tags - (or (eq org-agenda-show-inherited-tags 'always) - (and (listp org-agenda-show-inherited-tags) - (memq 'agenda org-agenda-show-inherited-tags)) - (and (eq org-agenda-show-inherited-tags t) - (or (eq org-agenda-use-tag-inheritance t) - (memq 'agenda org-agenda-use-tag-inheritance)))) - - tags (org-get-tags-at nil (not inherited-tags))) - (setq level (make-string (org-reduced-level (org-outline-level)) ? )) - (setq head (buffer-substring - (point) - (progn (skip-chars-forward "^\r\n") (point)))) - (if (string-match " \\([012]?[0-9]:[0-9][0-9]\\)" s) - (setq timestr - (concat (substring s (match-beginning 1)) " ")) - (setq timestr 'time)) - (setq txt (org-agenda-format-item - (if (= diff 0) - (car org-agenda-scheduled-leaders) - (format (nth 1 org-agenda-scheduled-leaders) - (- 1 diff))) - head level category tags - (if (not (= diff 0)) nil timestr) - nil habitp)))) - (when txt - (setq face + (/= schedule current))) + (throw :skip nil)) + ;; Skip entry if it already appears as a deadline, per + ;; `org-agenda-skip-scheduled-if-deadline-is-shown'. This + ;; doesn't apply to habits. + (when (pcase org-agenda-skip-scheduled-if-deadline-is-shown + ((guard + (or (not (memq (line-beginning-position 0) deadline-pos)) + habitp)) + nil) + (`repeated-after-deadline + (>= repeat (time-to-days (org-get-deadline-time (point))))) + (`not-today pastschedp) + (`t t) + (_ nil)) + (throw :skip nil)) + ;; Skip habits if `org-habit-show-habits' is nil, or if we + ;; only show them for today. Also skip done habits. + (when (and habitp + (or donep + (not (bound-and-true-p org-habit-show-habits)) + (and (not todayp) + (bound-and-true-p + org-habit-show-habits-only-for-today)))) + (throw :skip nil)) + (save-excursion + (re-search-backward "^\\*+[ \t]+" nil t) + (goto-char (match-end 0)) + (let* ((category (org-get-category)) + (inherited-tags + (or (eq org-agenda-show-inherited-tags 'always) + (and (listp org-agenda-show-inherited-tags) + (memq 'agenda org-agenda-show-inherited-tags)) + (and (eq org-agenda-show-inherited-tags t) + (or (eq org-agenda-use-tag-inheritance t) + (memq 'agenda + org-agenda-use-tag-inheritance))))) + (tags (org-get-tags-at nil (not inherited-tags))) + (level + (make-string (org-reduced-level (org-outline-level)) ?\s)) + (head (buffer-substring (point) (line-end-position))) + (time (cond - ((and (not habitp) pastschedp) - 'org-scheduled-previously) - (todayp 'org-scheduled-today) - (t 'org-scheduled)) - habitp (and habitp (org-habit-parse-todo))) - (org-add-props txt props + ;; No time of day designation if it is only + ;; a reminder. + ((and (/= current schedule) (/= current repeat)) nil) + ((string-match " \\([012]?[0-9]:[0-9][0-9]\\)" s) + (concat (substring s (match-beginning 1)) " ")) + (t 'time))) + (item + (org-agenda-format-item + (pcase-let ((`(,first ,next) org-agenda-scheduled-leaders)) + (cond + ;; If CURRENT is in the future, don't use past + ;; scheduled prefix. + ((> current today) first) + ;; SHOW-ALL focuses on future repeats. If one + ;; such repeat happens today, ignore late + ;; schedule reminder. However, still report + ;; such reminders when repeat happens later. + ((and (not show-all) (= repeat today)) first) + ;; Initial report. + ((= schedule current) first) + ;; Subsequent reminders. Count from base + ;; schedule. + (t (format next diff)))) + head level category tags time nil habitp)) + (face (cond ((and (not habitp) pastschedp) + 'org-scheduled-previously) + (todayp 'org-scheduled-today) + (t 'org-scheduled))) + (habitp (and habitp (org-habit-parse-todo)))) + (org-add-props item props 'undone-face face 'face (if donep 'org-agenda-done face) 'org-marker (org-agenda-new-marker pos) - 'org-hd-marker (org-agenda-new-marker pos1) + 'org-hd-marker (org-agenda-new-marker (line-beginning-position)) 'type (if pastschedp "past-scheduled" "scheduled") - 'date (if pastschedp d2 date) - 'ts-date d2 + 'date (if pastschedp schedule date) + 'ts-date schedule 'warntime warntime 'level level - 'priority (if habitp - (org-habit-get-priority habitp) - (+ 94 (- 5 diff) (org-get-priority txt))) - 'org-category category - 'category-position category-pos + 'priority (if habitp (org-habit-get-priority habitp) + (+ 99 diff (org-get-priority item))) 'org-habit-p habitp 'todo-state todo-state) - (push txt ee)))))) - (nreverse ee))) + (push item scheduled-items)))))) + (nreverse scheduled-items))) (defun org-agenda-get-blocks () "Return the date-range information for agenda display." @@ -6320,7 +6408,7 @@ an hour specification like [h]h:mm." (abbreviate-file-name buffer-file-name)))) (regexp org-tr-regexp) (d0 (calendar-absolute-from-gregorian date)) - marker hdmarker ee txt d1 d2 s1 s2 category category-pos + marker hdmarker ee txt d1 d2 s1 s2 category level todo-state tags pos head donep inherited-tags) (goto-char (point-min)) (while (re-search-forward regexp nil t) @@ -6341,9 +6429,8 @@ an hour specification like [h]h:mm." (setq donep (member todo-state org-done-keywords)) (if (and donep org-agenda-skip-timestamp-if-done) (throw :skip t)) - (setq marker (org-agenda-new-marker (point))) - (setq category (org-get-category) - category-pos (get-text-property (point) 'org-category-position)) + (setq marker (org-agenda-new-marker (point)) + category (org-get-category)) (if (not (re-search-backward org-outline-regexp-bol nil t)) (throw :skip nil) (goto-char (match-beginning 0)) @@ -6358,7 +6445,7 @@ an hour specification like [h]h:mm." tags (org-get-tags-at nil (not inherited-tags))) (setq level (make-string (org-reduced-level (org-outline-level)) ? )) - (looking-at "\\*+[ \t]+\\([^\r\n]+\\)") + (looking-at "\\*+[ \t]+\\(.*\\)") (setq head (match-string 1)) (let ((remove-re (if org-agenda-remove-timeranges-from-blocks @@ -6385,8 +6472,7 @@ an hour specification like [h]h:mm." 'type "block" 'date date 'level level 'todo-state todo-state - 'priority (org-get-priority txt) 'org-category category - 'org-category-position category-pos) + 'priority (org-get-priority txt)) (push txt ee)))) (goto-char pos))) ;; Sort the entries by expiration date. @@ -6413,11 +6499,11 @@ The flag is set if the currently compiled format contains a `%b'.") (defun org-agenda-get-category-icon (category) "Return an image for CATEGORY according to `org-agenda-category-icon-alist'." - (dolist (entry org-agenda-category-icon-alist) - (when (org-string-match-p (car entry) category) + (cl-dolist (entry org-agenda-category-icon-alist) + (when (string-match-p (car entry) category) (if (listp (cadr entry)) - (return (cadr entry)) - (return (apply 'create-image (cdr entry))))))) + (cl-return (cadr entry)) + (cl-return (apply #'create-image (cdr entry))))))) (defun org-agenda-format-item (extra txt &optional level category tags dotime remove-re habitp) @@ -6444,8 +6530,8 @@ Any match of REMOVE-RE will be removed from TXT." ;; buffer (let* ((bindings (car org-prefix-format-compiled)) (formatter (cadr org-prefix-format-compiled))) - (loop for (var value) in bindings - do (set var value)) + (cl-loop for (var value) in bindings + do (set var value)) (save-match-data ;; Diary entries sometimes have extra whitespace at the beginning (setq txt (org-trim txt)) @@ -6457,9 +6543,6 @@ Any match of REMOVE-RE will be removed from TXT." org-agenda-hide-tags-regexp)) (let* ((category (or category - (if (stringp org-category) - org-category - (and org-category (symbol-name org-category))) (if buffer-file-name (file-name-sans-extension (file-name-nondirectory buffer-file-name)) @@ -6468,15 +6551,17 @@ Any match of REMOVE-RE will be removed from TXT." (category-icon (if category-icon (propertize " " 'display category-icon) "")) + (effort (and (not (string= txt "")) + (get-text-property 1 'effort txt))) ;; time, tag, effort are needed for the eval of the prefix format (tag (if tags (nth (1- (length tags)) tags) "")) - time effort neffort + time (ts (if dotime (concat (if (stringp dotime) dotime "") (and org-agenda-search-headline-for-time txt)))) (time-of-day (and dotime (org-get-time-of-day ts))) stamp plain s0 s1 s2 rtn srp l - duration thecategory breadcrumbs) + duration breadcrumbs) (and (derived-mode-p 'org-mode) buffer-file-name (add-to-list 'org-agenda-contributing-files buffer-file-name)) (when (and dotime time-of-day) @@ -6516,8 +6601,7 @@ Any match of REMOVE-RE will be removed from TXT." (setq duration (- (org-hh:mm-string-to-minutes s2) (org-hh:mm-string-to-minutes s1))))) - (when (string-match (org-re "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$") - txt) + (when (string-match "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" txt) ;; Tags are in the string (if (or (eq org-agenda-remove-tags t) (and org-agenda-remove-tags @@ -6527,16 +6611,6 @@ Any match of REMOVE-RE will be removed from TXT." (concat (make-string (max (- 50 (length txt)) 1) ?\ ) (match-string 2 txt)) t t txt)))) - (when (derived-mode-p 'org-mode) - (setq effort (ignore-errors (get-text-property 0 'org-effort txt)))) - - ;; org-agenda-add-time-grid-maybe calls us with *Agenda* as - ;; current buffer, so move this check outside of above - (if effort - (setq neffort (org-duration-string-to-minutes effort) - effort (setq effort (concat "[" effort "]"))) - ;; prevent erroring out with %e format when there is no effort - (setq effort "")) (when remove-re (while (string-match remove-re txt) @@ -6563,7 +6637,6 @@ Any match of REMOVE-RE will be removed from TXT." (t "")) extra (or (and (not habitp) extra) "") category (if (symbolp category) (symbol-name category) category) - thecategory (copy-sequence category) level (or level "")) (if (string-match org-bracket-link-regexp category) (progn @@ -6584,14 +6657,12 @@ Any match of REMOVE-RE will be removed from TXT." ;; And finally add the text properties (remove-text-properties 0 (length rtn) '(line-prefix t wrap-prefix t) rtn) (org-add-props rtn nil - 'org-category (if thecategory (downcase thecategory) category) + 'org-category category 'tags (mapcar 'org-downcase-keep-props tags) 'org-highest-priority org-highest-priority 'org-lowest-priority org-lowest-priority 'time-of-day time-of-day 'duration duration - 'effort effort - 'effort-minutes neffort 'breadcrumbs breadcrumbs 'txt txt 'level level @@ -6605,7 +6676,7 @@ Any match of REMOVE-RE will be removed from TXT." The modified list may contain inherited tags, and tags matched by `org-agenda-hide-tags-regexp' will be removed." (when (or add-inherited hide-re) - (if (string-match (org-re "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$") txt) + (if (string-match "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" txt) (setq txt (substring txt 0 (match-beginning 0)))) (setq tags (delq nil @@ -6710,12 +6781,12 @@ and stored in the variable `org-prefix-format-compiled'." c (or (match-string 3 s) "") opt (match-beginning 1) start (1+ (match-beginning 0))) - (if (equal var 'time) (setq org-prefix-has-time t)) - (if (equal var 'tag) (setq org-prefix-has-tag t)) - (if (equal var 'effort) (setq org-prefix-has-effort t)) - (if (equal var 'breadcrumbs) (setq org-prefix-has-breadcrumbs t)) + (if (eq var 'time) (setq org-prefix-has-time t)) + (if (eq var 'tag) (setq org-prefix-has-tag t)) + (if (eq var 'effort) (setq org-prefix-has-effort t)) + (if (eq var 'breadcrumbs) (setq org-prefix-has-breadcrumbs t)) (setq f (concat "%" (match-string 2 s) "s")) - (when (equal var 'category) + (when (eq var 'category) (setq org-prefix-category-length (floor (abs (string-to-number (match-string 2 s))))) (setq org-prefix-category-max-length @@ -6727,10 +6798,13 @@ and stored in the variable `org-prefix-format-compiled'." (setq varform `(format ,f (org-eval ,(read (match-string 4 s))))) (if opt (setq varform - `(if (equal "" ,var) + `(if (or (equal "" ,var) (equal nil ,var)) "" - (format ,f (if (equal "" ,var) "" (concat ,var ,c))))) - (setq varform `(format ,f (if (equal ,var "") "" (concat ,var ,c (get-text-property 0 'extra-space ,var))))))) + (format ,f (concat ,var ,c)))) + (setq varform + `(format ,f (if (or (equal ,var "") + (equal ,var nil)) "" + (concat ,var ,c (get-text-property 0 'extra-space ,var))))))) (setq s (replace-match "%s" t nil s)) (push varform vars)) (setq vars (nreverse vars)) @@ -6817,7 +6891,7 @@ The optional argument TYPE tells the agenda type." (t org-agenda-max-tags))) (max-entries (cond ((listp org-agenda-max-entries) (cdr (assoc type org-agenda-max-entries))) - (t org-agenda-max-entries))) l) + (t org-agenda-max-entries)))) (when org-agenda-before-sorting-filter-function (setq list (delq nil @@ -6827,7 +6901,9 @@ The optional argument TYPE tells the agenda type." list (mapcar 'identity (sort list 'org-entries-lessp))) (when max-effort (setq list (org-agenda-limit-entries - list 'effort-minutes max-effort 'identity))) + list 'effort-minutes max-effort + (lambda (e) (or e (if org-sort-agenda-noeffort-is-high + 32767 -1)))))) (when max-todo (setq list (org-agenda-limit-entries list 'todo-state max-todo))) (when max-tags @@ -6845,26 +6921,39 @@ The optional argument TYPE tells the agenda type." (delq nil (mapcar (lambda (e) - (let ((pval (funcall fun (get-text-property 1 prop e)))) + (let ((pval (funcall + fun (get-text-property (1- (length e)) + prop e)))) (if pval (setq lim (+ lim pval))) (cond ((and pval (<= lim (abs limit))) e) ((and include (not pval)) e)))) list))) list))) -(defun org-agenda-limit-interactively () +(defun org-agenda-limit-interactively (remove) "In agenda, interactively limit entries to various maximums." - (interactive) - (let* ((max (read-char "Number of [e]ntries [t]odos [T]ags [E]ffort? ")) - (num (string-to-number (read-from-minibuffer "How many? ")))) - (cond ((equal max ?e) - (let ((org-agenda-max-entries num)) (org-agenda-redo))) - ((equal max ?t) - (let ((org-agenda-max-todos num)) (org-agenda-redo))) - ((equal max ?T) - (let ((org-agenda-max-tags num)) (org-agenda-redo))) - ((equal max ?E) - (let ((org-agenda-max-effort num)) (org-agenda-redo))))) + (interactive "P") + (if remove + (progn (setq org-agenda-max-entries nil + org-agenda-max-todos nil + org-agenda-max-tags nil + org-agenda-max-effort nil) + (org-agenda-redo)) + (let* ((max (read-char "Number of [e]ntries [t]odos [T]ags [E]ffort? ")) + (msg (cond ((= max ?E) "How many minutes? ") + ((= max ?e) "How many entries? ") + ((= max ?t) "How many TODO entries? ") + ((= max ?T) "How many tagged entries? ") + (t (user-error "Wrong input")))) + (num (string-to-number (read-from-minibuffer msg)))) + (cond ((equal max ?e) + (let ((org-agenda-max-entries num)) (org-agenda-redo))) + ((equal max ?t) + (let ((org-agenda-max-todos num)) (org-agenda-redo))) + ((equal max ?T) + (let ((org-agenda-max-tags num)) (org-agenda-redo))) + ((equal max ?E) + (let ((org-agenda-max-effort num)) (org-agenda-redo)))))) (org-agenda-fit-window-to-buffer)) (defun org-agenda-highlight-todo (x) @@ -6910,25 +6999,31 @@ The optional argument TYPE tells the agenda type." (substring x (match-end 3))))))) x))) -(defsubst org-cmp-priority (a b) - "Compare the priorities of string A and B." - (let ((pa (or (get-text-property 1 'priority a) 0)) - (pb (or (get-text-property 1 'priority b) 0))) +(defsubst org-cmp-values (a b property) + "Compare the numeric value of text PROPERTY for string A and B." + (let ((pa (or (get-text-property (1- (length a)) property a) 0)) + (pb (or (get-text-property (1- (length b)) property b) 0))) (cond ((> pa pb) +1) ((< pa pb) -1)))) (defsubst org-cmp-effort (a b) "Compare the effort values of string A and B." (let* ((def (if org-sort-agenda-noeffort-is-high 32767 -1)) - (ea (or (get-text-property 1 'effort-minutes a) def)) - (eb (or (get-text-property 1 'effort-minutes b) def))) + ;; `effort-minutes' property is not directly accessible from + ;; the strings, but is stored as a property in `txt'. + (ea (or (get-text-property + 0 'effort-minutes (get-text-property 0 'txt a)) + def)) + (eb (or (get-text-property + 0 'effort-minutes (get-text-property 0 'txt b)) + def))) (cond ((> ea eb) +1) ((< ea eb) -1)))) (defsubst org-cmp-category (a b) "Compare the string values of categories of strings A and B." - (let ((ca (or (get-text-property 1 'org-category a) "")) - (cb (or (get-text-property 1 'org-category b) ""))) + (let ((ca (or (get-text-property (1- (length a)) 'org-category a) "")) + (cb (or (get-text-property (1- (length b)) 'org-category b) ""))) (cond ((string-lessp ca cb) -1) ((string-lessp cb ca) +1)))) @@ -6959,7 +7054,8 @@ The optional argument TYPE tells the agenda type." (let* ((pla (text-property-any 0 (length a) 'org-heading t a)) (plb (text-property-any 0 (length b) 'org-heading t b)) (ta (and pla (substring a pla))) - (tb (and plb (substring b plb)))) + (tb (and plb (substring b plb))) + (case-fold-search nil)) (when pla (if (string-match (concat "\\`[ \t]*" (or (get-text-property 0 'org-todo-regexp a) "") "\\([ \t]*\\[[a-zA-Z0-9]\\]\\)? *") ta) @@ -7038,8 +7134,11 @@ their type." (time-up (and (org-em 'time-up 'time-down ss) (org-cmp-time a b))) (time-down (if time-up (- time-up) nil)) + (stats-up (and (org-em 'stats-up 'stats-down ss) + (org-cmp-values a b 'org-stats))) + (stats-down (if stats-up (- stats-up) nil)) (priority-up (and (org-em 'priority-up 'priority-down ss) - (org-cmp-priority a b))) + (org-cmp-values a b 'priority))) (priority-down (if priority-up (- priority-up) nil)) (effort-up (and (org-em 'effort-up 'effort-down ss) (org-cmp-effort a b))) @@ -7080,15 +7179,16 @@ their type." 'face 'org-agenda-restriction-lock) (overlay-put org-agenda-restriction-lock-overlay 'help-echo "Agendas are currently limited to this subtree.") -(org-detach-overlay org-agenda-restriction-lock-overlay) +(delete-overlay org-agenda-restriction-lock-overlay) ;;;###autoload (defun org-agenda-set-restriction-lock (&optional type) "Set restriction lock for agenda, to current subtree or file. -Restriction will be the file if TYPE is `file', or if TYPE is the -universal prefix `(4)', or if the cursor is before the first headline +Restriction will be the file if TYPE is `file', or if type is the +universal prefix \\='(4), or if the cursor is before the first headline in the file. Otherwise, restriction will be to the current subtree." (interactive "P") + (org-agenda-remove-restriction-lock 'noupdate) (and (equal type '(4)) (setq type 'file)) (setq type (cond (type type) @@ -7125,8 +7225,8 @@ in the file. Otherwise, restriction will be to the current subtree." (defun org-agenda-remove-restriction-lock (&optional noupdate) "Remove the agenda restriction lock." (interactive "P") - (org-detach-overlay org-agenda-restriction-lock-overlay) - (org-detach-overlay org-speedbar-restriction-lock-overlay) + (delete-overlay org-agenda-restriction-lock-overlay) + (delete-overlay org-speedbar-restriction-lock-overlay) (setq org-agenda-overriding-restriction nil) (setq org-agenda-restrict nil) (put 'org-agenda-files 'org-restrict nil) @@ -7138,7 +7238,9 @@ in the file. Otherwise, restriction will be to the current subtree." (defun org-agenda-maybe-redo () "If there is any window showing the agenda view, update it." - (let ((w (get-buffer-window org-agenda-buffer-name t)) + (let ((w (get-buffer-window (or org-agenda-this-buffer-name + org-agenda-buffer-name) + t)) (w0 (selected-window))) (when w (select-window w) @@ -7154,7 +7256,7 @@ in the file. Otherwise, restriction will be to the current subtree." (defun org-agenda-check-type (error &rest types) "Check if agenda buffer is of allowed type. If ERROR is non-nil, throw an error, otherwise just return nil. -Allowed types are 'agenda 'timeline 'todo 'tags 'search." +Allowed types are `agenda' `timeline' `todo' `tags' `search'." (if (not org-agenda-type) (error "No Org agenda currently displayed") (if (memq org-agenda-type types) @@ -7164,77 +7266,76 @@ Allowed types are 'agenda 'timeline 'todo 'tags 'search." nil)))) (defun org-agenda-Quit () - "Exit the agenda and kill buffers loaded by `org-agenda'. -Also restore the window configuration." + "Exit the agenda, killing the agenda buffer. +Like `org-agenda-quit', but kill the buffer even when +`org-agenda-sticky' is non-nil." (interactive) - (if org-agenda-columns-active - (org-columns-quit) - (let ((buf (current-buffer))) - (if (eq org-agenda-window-setup 'other-frame) - (progn - (org-agenda-reset-markers) - (kill-buffer buf) - (org-columns-remove-overlays) - (setq org-agenda-archives-mode nil) - (delete-frame)) - (and (not (eq org-agenda-window-setup 'current-window)) - (not (one-window-p)) - (delete-window)) - (org-agenda-reset-markers) - (kill-buffer buf) - (org-columns-remove-overlays) - (setq org-agenda-archives-mode nil))) - (setq org-agenda-buffer nil) - ;; Maybe restore the pre-agenda window configuration. - (and org-agenda-restore-windows-after-quit - (not (eq org-agenda-window-setup 'other-frame)) - org-agenda-pre-window-conf - (set-window-configuration org-agenda-pre-window-conf) - (setq org-agenda-pre-window-conf nil)))) + (org-agenda--quit)) (defun org-agenda-quit () - "Exit the agenda and restore the window configuration. -When `org-agenda-sticky' is non-nil, only bury the agenda." + "Exit the agenda. + +When `org-agenda-sticky' is non-nil, bury the agenda buffer +instead of killing it. + +When `org-agenda-restore-windows-after-quit' is non-nil, restore +the pre-agenda window configuration. + +When column view is active, exit column view instead of the +agenda." (interactive) - (if (and (eq org-indirect-buffer-display 'other-window) - org-last-indirect-buffer) - (let ((org-last-indirect-window - (get-buffer-window org-last-indirect-buffer))) - (if org-last-indirect-window - (delete-window org-last-indirect-window)))) + (org-agenda--quit org-agenda-sticky)) + +(defun org-agenda--quit (&optional bury) (if org-agenda-columns-active (org-columns-quit) - (if org-agenda-sticky - (let ((buf (current-buffer))) - (if (eq org-agenda-window-setup 'other-frame) - (progn - (delete-frame)) - (and (not (eq org-agenda-window-setup 'current-window)) - (not (one-window-p)) - (delete-window))) + (let ((wconf org-agenda-pre-window-conf) + (buf (current-buffer)) + (org-agenda-last-indirect-window + (and (eq org-indirect-buffer-display 'other-window) + org-agenda-last-indirect-buffer + (get-buffer-window org-agenda-last-indirect-buffer)))) + (cond + ((eq org-agenda-window-setup 'other-frame) + (delete-frame)) + ((and org-agenda-restore-windows-after-quit + wconf) + ;; Maybe restore the pre-agenda window configuration. Reset + ;; `org-agenda-pre-window-conf' before running + ;; `set-window-configuration', which loses the current buffer. + (setq org-agenda-pre-window-conf nil) + (set-window-configuration wconf)) + (t + (when org-agenda-last-indirect-window + (delete-window org-agenda-last-indirect-window)) + (and (not (eq org-agenda-window-setup 'current-window)) + (not (one-window-p)) + (delete-window)))) + (if bury + ;; Set the agenda buffer as the current buffer instead of + ;; passing it as an argument to `bury-buffer' so that + ;; `bury-buffer' removes it from the window. (with-current-buffer buf - (bury-buffer) - ;; Maybe restore the pre-agenda window configuration. - (and org-agenda-restore-windows-after-quit - (not (eq org-agenda-window-setup 'other-frame)) - org-agenda-pre-window-conf - (set-window-configuration org-agenda-pre-window-conf) - (setq org-agenda-pre-window-conf nil)))) - (org-agenda-Quit)))) + (bury-buffer)) + (kill-buffer buf) + (setq org-agenda-archives-mode nil + org-agenda-buffer nil))))) (defun org-agenda-exit () - "Exit the agenda and restore the window configuration. -Also kill Org-mode buffers loaded by `org-agenda'. Org-mode -buffers visited directly by the user will not be touched." + "Exit the agenda, killing Org buffers loaded by the agenda. +Like `org-agenda-Quit', but kill any buffers that were created by +the agenda. Org buffers visited directly by the user will not be +touched. Also, exit the agenda even if it is in column view." (interactive) + (when org-agenda-columns-active + (org-columns-quit)) (org-release-buffers org-agenda-new-buffers) (setq org-agenda-new-buffers nil) (org-agenda-Quit)) (defun org-agenda-kill-all-agenda-buffers () "Kill all buffers in `org-agenda-mode'. -This is used when toggling sticky agendas. -You can also explicitly invoke it with `C-c a C-k'." +This is used when toggling sticky agendas." (interactive) (let (blist) (dolist (buf (buffer-list)) @@ -7267,6 +7368,9 @@ in the agenda." (cat-preset (get 'org-agenda-category-filter :preset-filter)) (re-filter org-agenda-regexp-filter) (re-preset (get 'org-agenda-regexp-filter :preset-filter)) + (effort-filter org-agenda-effort-filter) + (effort-preset (get 'org-agenda-effort-filter :preset-filter)) + (org-agenda-tag-filter-while-redo (or tag-filter tag-preset)) (cols org-agenda-columns-active) (line (org-current-line)) (window-line (- line (org-current-line (window-start)))) @@ -7284,6 +7388,7 @@ in the agenda." (put 'org-agenda-tag-filter :preset-filter nil) (put 'org-agenda-category-filter :preset-filter nil) (put 'org-agenda-regexp-filter :preset-filter nil) + (put 'org-agenda-effort-filter :preset-filter nil) (and cols (org-columns-quit)) (message "Rebuilding agenda buffer...") (if series-redo-cmd @@ -7294,19 +7399,23 @@ in the agenda." org-agenda-tag-filter tag-filter org-agenda-category-filter cat-filter org-agenda-regexp-filter re-filter + org-agenda-effort-filter effort-filter org-agenda-top-headline-filter top-hl-filter) (message "Rebuilding agenda buffer...done") (put 'org-agenda-tag-filter :preset-filter tag-preset) (put 'org-agenda-category-filter :preset-filter cat-preset) (put 'org-agenda-regexp-filter :preset-filter re-preset) + (put 'org-agenda-effort-filter :preset-filter effort-preset) (let ((tag (or tag-filter tag-preset)) (cat (or cat-filter cat-preset)) - (re (or re-filter re-preset))) - (when tag (org-agenda-filter-apply tag 'tag)) + (effort (or effort-filter effort-preset)) + (re (or re-filter re-preset))) + (when tag (org-agenda-filter-apply tag 'tag t)) (when cat (org-agenda-filter-apply cat 'category)) + (when effort (org-agenda-filter-apply effort 'effort)) (when re (org-agenda-filter-apply re 'regexp))) (and top-hl-filter (org-agenda-filter-top-headline-apply top-hl-filter)) - (and cols (org-called-interactively-p 'any) (org-agenda-columns)) + (and cols (called-interactively-p 'any) (org-agenda-columns)) (org-goto-line line) (recenter window-line))) @@ -7315,32 +7424,36 @@ in the agenda." (defvar org-agenda-filtered-by-category nil) (defun org-agenda-filter-by-category (strip) - "Keep only those lines in the agenda buffer that have a specific category. -The category is that of the current line." + "Filter lines in the agenda buffer that have a specific category. +The category is that of the current line. +Without prefix argument, keep only the lines of that category. +With a prefix argument, exclude the lines of that category. +" (interactive "P") (if (and org-agenda-filtered-by-category org-agenda-category-filter) (org-agenda-filter-show-all-cat) - (let ((cat (org-no-properties (get-text-property (point) 'org-category)))) + (let ((cat (org-no-properties (org-get-at-eol 'org-category 1)))) (cond ((and cat strip) (org-agenda-filter-apply (push (concat "-" cat) org-agenda-category-filter) 'category)) - ((and cat) + (cat (org-agenda-filter-apply (setq org-agenda-category-filter (list (concat "+" cat))) 'category)) (t (error "No category at point")))))) (defun org-find-top-headline (&optional pos) - "Find the topmost parent headline and return it." + "Find the topmost parent headline and return it. +POS when non-nil is the marker or buffer position to start the +search from." (save-excursion - (with-current-buffer (if pos (marker-buffer pos) (current-buffer)) - (if pos (goto-char pos)) - ;; Skip up to the topmost parent - (while (ignore-errors (outline-up-heading 1) t)) - (ignore-errors - (nth 4 (org-heading-components)))))) + (with-current-buffer (if (markerp pos) (marker-buffer pos) (current-buffer)) + (when pos (goto-char pos)) + ;; Skip up to the topmost parent. + (while (org-up-heading-safe)) + (ignore-errors (nth 4 (org-heading-components)))))) (defvar org-agenda-filtered-by-top-headline nil) (defun org-agenda-filter-by-top-headline (strip) @@ -7375,6 +7488,49 @@ With two prefix arguments, remove the regexp filters." (org-agenda-filter-show-all-re) (message "Regexp filter removed"))) +(defvar org-agenda-effort-filter nil) +(defun org-agenda-filter-by-effort (strip) + "Filter agenda entries by effort. +With no prefix argument, keep entries matching the effort condition. +With one prefix argument, filter out entries matching the condition. +With two prefix arguments, remove the effort filters." + (interactive "P") + (cond + ((member strip '(nil 4)) + (let* ((efforts (split-string + (or (cdr (assoc (concat org-effort-property "_ALL") + org-global-properties)) + "0 0:10 0:30 1:00 2:00 3:00 4:00 5:00 6:00 7:00"))) + ;; XXX: the following handles only up to 10 different + ;; effort values. + (allowed-keys (if (null efforts) nil + (mapcar (lambda (n) (mod n 10)) ;turn 10 into 0 + (number-sequence 1 (length efforts))))) + (op nil)) + (while (not (memq op '(?< ?> ?=))) + (setq op (read-char-exclusive "Effort operator? (> = or <)"))) + ;; Select appropriate duration. Ignore non-digit characters. + (let ((prompt + (apply #'format + (concat "Effort %c " + (mapconcat (lambda (s) (concat "[%d]" s)) + efforts + " ")) + op allowed-keys)) + (eff -1)) + (while (not (memq eff allowed-keys)) + (message prompt) + (setq eff (- (read-char-exclusive) 48))) + (setq org-agenda-effort-filter + (list (concat (if strip "-" "+") + (char-to-string op) + ;; Numbering is 1 2 3 ... 9 0, but we want + ;; 0 1 2 ... 8 9. + (nth (mod (1- eff) 10) efforts))))) + (org-agenda-filter-apply org-agenda-effort-filter 'effort))) + (t (org-agenda-filter-show-all-effort) + (message "Effort filter removed")))) + (defun org-agenda-filter-remove-all () "Remove all filters from the current agenda buffer." (interactive) @@ -7386,15 +7542,24 @@ With two prefix arguments, remove the regexp filters." (org-agenda-filter-show-all-re)) (when org-agenda-top-headline-filter (org-agenda-filter-show-all-top-filter)) + (when org-agenda-effort-filter + (org-agenda-filter-show-all-effort)) (org-agenda-finalize)) -(defun org-agenda-filter-by-tag (strip &optional char narrow) +(defun org-agenda-filter-by-tag (arg &optional char exclude) "Keep only those lines in the agenda buffer that have a specific tag. + The tag is selected with its fast selection letter, as configured. -With prefix argument STRIP, remove all lines that do have the tag. -A lisp caller can specify CHAR. NARROW means that the new tag should be -used to narrow the search - the interactive user can also press `-' or `+' -to switch to narrowing." + +With a `\\[universal-argument]' prefix, exclude the agenda search. + +With a `\\[universal-argument] \\[universal-argument]' prefix, filter the literal tag, \ +i.e. don't +filter on all its group members. + +A lisp caller can specify CHAR. EXCLUDE means that the new tag +should be used to exclude the search - the interactive user can +also press `-' or `+' to switch between filtering and excluding." (interactive "P") (let* ((alist org-tag-alist-for-agenda) (tag-chars (mapconcat @@ -7402,54 +7567,34 @@ to switch to narrowing." (cdr x)) (char-to-string (cdr x)) "")) - alist "")) - (efforts (org-split-string - (or (cdr (assoc (concat org-effort-property "_ALL") - org-global-properties)) - "0 0:10 0:30 1:00 2:00 3:00 4:00 5:00 6:00 7:00 8:00" - ""))) - (effort-op org-agenda-filter-effort-default-operator) - (effort-prompt "") + org-tag-alist-for-agenda "")) + (valid-char-list (append '(?\t ?\r ?/ ?. ?\s ?q) + (string-to-list tag-chars))) + (exclude (or exclude (equal arg '(4)))) + (expand (not (equal arg '(16)))) (inhibit-read-only t) (current org-agenda-tag-filter) - maybe-refresh a n tag) + a n tag) (unless char - (message - "%s by tag [%s ], [TAB], %s[/]:off, [+-]:narrow, [>==<]:effort: " tag-chars) - (setq char (read-char-exclusive))) - (when (member char '(?< ?> ?= ??)) - ;; An effort operator - (setq effort-op (char-to-string char)) - (setq alist nil) ; to make sure it will be interpreted as effort. - (unless (equal char ??) - (loop for i from 0 to 9 do - (setq effort-prompt - (concat - effort-prompt " [" - (if (= i 9) "0" (int-to-string (1+ i))) - "]" (nth i efforts)))) - (message "Effort%s: %s " effort-op effort-prompt) + (while (not (memq char valid-char-list)) + (message + "%s by tag [%s ], [TAB], %s[/]:off, [+/-]:filter/exclude%s, [q]:quit" + (if exclude "Exclude" "Filter") tag-chars + (if org-agenda-auto-exclude-function "[RET], " "") + (if expand "" ", no grouptag expand")) (setq char (read-char-exclusive)) - (when (or (< char ?0) (> char ?9)) - (error "Need 1-9,0 to select effort")))) - (when (equal char ?\t) + ;; Excluding or filtering down + (cond ((eq char ?-) (setq exclude t)) + ((eq char ?+) (setq exclude nil))))) + (when (eq char ?\t) (unless (local-variable-p 'org-global-tags-completion-table (current-buffer)) - (org-set-local 'org-global-tags-completion-table - (org-global-tags-completion-table))) + (setq-local org-global-tags-completion-table + (org-global-tags-completion-table))) (let ((completion-ignore-case t)) - (setq tag (org-icompleting-read + (setq tag (completing-read "Tag: " org-global-tags-completion-table)))) (cond - ((equal char ?\r) + ((eq char ?\r) (org-agenda-filter-show-all-tag) (when org-agenda-auto-exclude-function (setq org-agenda-tag-filter nil) @@ -7458,39 +7603,27 @@ to switch to narrowing." (if modifier (push modifier org-agenda-tag-filter)))) (if (not (null org-agenda-tag-filter)) - (org-agenda-filter-apply org-agenda-tag-filter 'tag))) - (setq maybe-refresh t)) - ((equal char ?/) + (org-agenda-filter-apply org-agenda-tag-filter 'tag expand)))) + ((eq char ?/) (org-agenda-filter-show-all-tag) (when (get 'org-agenda-tag-filter :preset-filter) - (org-agenda-filter-apply org-agenda-tag-filter 'tag)) - (setq maybe-refresh t)) - ((equal char ?. ) + (org-agenda-filter-apply org-agenda-tag-filter 'tag expand))) + ((eq char ?.) (setq org-agenda-tag-filter (mapcar (lambda(tag) (concat "+" tag)) (org-get-at-bol 'tags))) - (org-agenda-filter-apply org-agenda-tag-filter 'tag) - (setq maybe-refresh t)) - ((or (equal char ?\ ) + (org-agenda-filter-apply org-agenda-tag-filter 'tag expand)) + ((eq char ?q)) ;If q, abort (even if there is a q-key for a tag...) + ((or (eq char ?\s) (setq a (rassoc char alist)) - (and (>= char ?0) (<= char ?9) - (setq n (if (= char ?0) 9 (- char ?0 1)) - tag (concat effort-op (nth n efforts)) - a (cons tag nil))) - (and (= char ??) - (setq tag "?eff") - a (cons tag nil)) (and tag (setq a (cons tag nil)))) (org-agenda-filter-show-all-tag) (setq tag (car a)) (setq org-agenda-tag-filter - (cons (concat (if strip "-" "+") tag) - (if narrow current nil))) - (org-agenda-filter-apply org-agenda-tag-filter 'tag) - (setq maybe-refresh t)) - (t (error "Invalid tag selection character %c" char))) - (when maybe-refresh - (org-agenda-redo)))) + (cons (concat (if exclude "-" "+") tag) + current)) + (org-agenda-filter-apply org-agenda-tag-filter 'tag expand)) + (t (error "Invalid tag selection character %c" char))))) (defun org-agenda-get-represented-tags () "Get a list of all tags currently represented in the agenda." @@ -7503,13 +7636,11 @@ to switch to narrowing." (get-text-property (point) 'tags)))) tags)) -(defun org-agenda-filter-by-tag-refine (strip &optional char) - "Refine the current filter. See `org-agenda-filter-by-tag'." - (interactive "P") - (org-agenda-filter-by-tag strip char 'refine)) -(defun org-agenda-filter-make-matcher (filter type) - "Create the form that tests a line for agenda filter." +(defun org-agenda-filter-make-matcher (filter type &optional expand) + "Create the form that tests a line for agenda filter. Optional +argument EXPAND can be used for the TYPE tag and will expand the +tags in the FILTER if any of the tags in FILTER are grouptags." (let (f f1) (cond ;; Tag filter @@ -7519,28 +7650,11 @@ to switch to narrowing." (append (get 'org-agenda-tag-filter :preset-filter) filter))) (dolist (x filter) - (let ((nfilter (org-agenda-filter-expand-tags filter)) nf nf1 - (ffunc - (lambda (nf0 nf01 fltr notgroup op) - (dolist (x fltr) - (if (member x '("-" "+")) - (setq nf01 (if (equal x "-") 'tags '(not tags))) - (if (string-match "[<=>?]" x) - (setq nf01 (org-agenda-filter-effort-form x)) - (setq nf01 (list 'member (downcase (substring x 1)) - 'tags))) - (when (equal (string-to-char x) ?-) - (setq nf01 (list 'not nf01)) - (when (not notgroup) (setq op 'and)))) - (push nf01 nf0)) - (if notgroup - (push (cons 'and nf0) f) - (push (cons (or op 'or) nf0) f))))) - (cond ((equal filter '("+")) - (setq f (list (list 'not 'tags)))) - ((equal nfilter filter) - (funcall ffunc f1 f filter t nil)) - (t (funcall ffunc nf1 nf nfilter nil nil)))))) + (let ((op (string-to-char x))) + (if expand (setq x (org-agenda-filter-expand-tags (list x) t)) + (setq x (list x))) + (setq f1 (org-agenda-filter-make-matcher-tag-exp x op)) + (push f1 f)))) ;; Category filter ((eq type 'category) (setq filter @@ -7562,9 +7676,35 @@ to switch to narrowing." (if (equal "-" (substring x 0 1)) (setq f1 (list 'not (list 'string-match (substring x 1) 'txt))) (setq f1 (list 'string-match (substring x 1) 'txt))) - (push f1 f)))) + (push f1 f))) + ;; Effort filter + ((eq type 'effort) + (setq filter + (delete-dups + (append (get 'org-agenda-effort-filter :preset-filter) + filter))) + (dolist (x filter) + (push (org-agenda-filter-effort-form x) f)))) (cons 'and (nreverse f)))) +(defun org-agenda-filter-make-matcher-tag-exp (tags op) + "Return a form associated to tag-expression TAGS. +Build a form testing a line for agenda filter for +tag-expressions. OP is an operator of type CHAR that allows the +function to set the right switches in the returned form." + (let (form) + ;; Any of the expressions can match if OP is +, all must match if + ;; the operator is -. + (dolist (x tags (cons (if (eq op ?-) 'and 'or) form)) + (let* ((tag (substring x 1)) + (f (cond + ((string= "" tag) '(not tags)) + ((and (string-match-p "\\`{" tag) (string-match-p "}\\'" tag)) + ;; TAG is a regexp. + (list 'org-match-any-p (substring tag 1 -1) 'tags)) + (t (list 'member (downcase tag) 'tags))))) + (push (if (eq op ?-) (list 'not f) f) form))))) + (defun org-agenda-filter-effort-form (e) "Return the form to compare the effort of the current line with what E says. E looks like \"+<2:25\"." @@ -7581,11 +7721,12 @@ E looks like \"+<2:25\"." (defun org-agenda-compare-effort (op value) "Compare the effort of the current line with VALUE, using OP. If the line does not have an effort defined, return nil." - (let ((eff (org-get-at-bol 'effort-minutes))) - (if (equal op ??) - (not eff) - (funcall op (or eff (if org-sort-agenda-noeffort-is-high 32767 0)) - value)))) + ;; `effort-minutes' property cannot be extracted directly from + ;; current line but is stored as a property in `txt'. + (let ((effort (get-text-property 0 'effort-minutes (org-get-at-bol 'txt)))) + (funcall op + (or effort (if org-sort-agenda-noeffort-is-high 32767 -1)) + value))) (defun org-agenda-filter-expand-tags (filter &optional no-operator) "Expand group tags in FILTER for the agenda. @@ -7605,12 +7746,14 @@ When NO-OPERATOR is non-nil, do not add the + operator to returned tags." (reverse rtn)) filter)) -(defun org-agenda-filter-apply (filter type) - "Set FILTER as the new agenda filter and apply it." +(defun org-agenda-filter-apply (filter type &optional expand) + "Set FILTER as the new agenda filter and apply it. Optional +argument EXPAND can be used for the TYPE tag and will expand the +tags in the FILTER if any of the tags in FILTER are grouptags." ;; Deactivate `org-agenda-entry-text-mode' when filtering (if org-agenda-entry-text-mode (org-agenda-entry-text-mode)) (let (tags cat txt) - (setq org-agenda-filter-form (org-agenda-filter-make-matcher filter type)) + (setq org-agenda-filter-form (org-agenda-filter-make-matcher filter type expand)) ;; Only set `org-agenda-filtered-by-category' to t when a unique ;; category is used as the filter: (setq org-agenda-filtered-by-category @@ -7622,13 +7765,9 @@ When NO-OPERATOR is non-nil, do not add the + operator to returned tags." (while (not (eobp)) (if (org-get-at-bol 'org-marker) (progn - (setq tags ; used in eval - (apply 'append - (mapcar (lambda (f) - (org-agenda-filter-expand-tags (list f) t)) - (org-get-at-bol 'tags))) - cat (get-text-property (point) 'org-category) - txt (get-text-property (point) 'txt)) + (setq tags (org-get-at-bol 'tags) + cat (org-get-at-eol 'org-category 1) + txt (org-get-at-bol 'txt)) (if (not (eval org-agenda-filter-form)) (org-agenda-filter-hide-line type)) (beginning-of-line 2)) @@ -7681,6 +7820,8 @@ When NO-OPERATOR is non-nil, do not add the + operator to returned tags." (org-agenda-remove-filter 'tag)) (defun org-agenda-filter-show-all-re nil (org-agenda-remove-filter 'regexp)) +(defun org-agenda-filter-show-all-effort nil + (org-agenda-remove-filter 'effort)) (defun org-agenda-filter-show-all-cat nil (org-agenda-remove-filter 'category)) (defun org-agenda-filter-show-all-top-filter nil @@ -7779,7 +7920,7 @@ Negative selection means regexp must not match for selection of an entry." (tdpos (goto-char tdpos)) ((eq org-agenda-type 'agenda) (let* ((sd (org-agenda-compute-starting-span - (org-today) (or curspan org-agenda-ndays org-agenda-span))) + (org-today) (or curspan org-agenda-span))) (org-agenda-overriding-arguments args)) (setf (nth 1 org-agenda-overriding-arguments) sd) (org-agenda-redo) @@ -7792,27 +7933,40 @@ Negative selection means regexp must not match for selection of an entry." (text-property-any (point-min) (point-max) 'org-today t) (text-property-any (point-min) (point-max) 'org-agenda-type 'agenda) (and (get-text-property (min (1- (point-max)) (point)) 'org-series) - (org-agenda-goto-block-beginning)) + (org-agenda-backward-block)) (point-min)))) -(defun org-agenda-goto-block-beginning () - "Go the agenda block beginning." +(defun org-agenda-backward-block () + "Move backward by one agenda block." (interactive) - (if (not (derived-mode-p 'org-agenda-mode)) - (error "Cannot execute this command outside of org-agenda-mode buffers") - (let (dest) - (save-excursion - (unless (looking-at "\\'") - (forward-char)) - (let* ((prop 'org-agenda-structural-header) - (p (previous-single-property-change (point) prop)) - (n (next-single-property-change (or (and (looking-at "\\`") 1) - (1- (point))) prop))) - (setq dest (cond ((eq n (point-at-eol)) (1- n)) (p (1- p)))))) - (if (not dest) - (error "Cannot find the beginning of the blog") - (goto-char dest) - (move-beginning-of-line 1))))) + (org-agenda-forward-block 'backward)) + +(defun org-agenda-forward-block (&optional backward) + "Move forward by one agenda block. +When optional argument BACKWARD is set, go backward" + (interactive) + (cond ((not (derived-mode-p 'org-agenda-mode)) + (user-error + "Cannot execute this command outside of org-agenda-mode buffers")) + ((looking-at (if backward "\\`" "\\'")) + (message "Already at the %s block" (if backward "first" "last"))) + (t (let ((pos (prog1 (point) + (ignore-errors (if backward (backward-char 1) + (move-end-of-line 1))))) + (f (if backward + 'previous-single-property-change + 'next-single-property-change)) + moved dest) + (while (and (setq dest (funcall + f (point) 'org-agenda-structural-header)) + (not (get-text-property + (point) 'org-agenda-structural-header))) + (setq moved t) + (goto-char dest)) + (if moved (move-beginning-of-line 1) + (goto-char (if backward (point-min) (point-max))) + (move-beginning-of-line 1) + (message "No %s block" (if backward "previous" "further"))))))) (defun org-agenda-later (arg) "Go forward in time by the current span. @@ -7866,71 +8020,77 @@ With prefix ARG, go backward that many times the current span." (message "View: [d]ay [w]eek for[t]night [m]onth [y]ear [SPC]reset [q]uit/abort time[G]rid [[]inactive [f]ollow [l]og [L]og-all [c]lockcheck [a]rch-trees [A]rch-files clock[R]eport include[D]iary [E]ntryText") - (let ((a (read-char-exclusive))) - (case a - (?\ (call-interactively 'org-agenda-reset-view)) - (?d (call-interactively 'org-agenda-day-view)) - (?w (call-interactively 'org-agenda-week-view)) - (?t (call-interactively 'org-agenda-fortnight-view)) - (?m (call-interactively 'org-agenda-month-view)) - (?y (call-interactively 'org-agenda-year-view)) - (?l (call-interactively 'org-agenda-log-mode)) - (?L (org-agenda-log-mode '(4))) - (?c (org-agenda-log-mode 'clockcheck)) - ((?F ?f) (call-interactively 'org-agenda-follow-mode)) - (?a (call-interactively 'org-agenda-archives-mode)) - (?A (org-agenda-archives-mode 'files)) - ((?R ?r) (call-interactively 'org-agenda-clockreport-mode)) - ((?E ?e) (call-interactively 'org-agenda-entry-text-mode)) - (?G (call-interactively 'org-agenda-toggle-time-grid)) - (?D (call-interactively 'org-agenda-toggle-diary)) - (?\! (call-interactively 'org-agenda-toggle-deadlines)) - (?\[ (let ((org-agenda-include-inactive-timestamps t)) - (org-agenda-check-type t 'timeline 'agenda) - (org-agenda-redo)) - (message "Display now includes inactive timestamps as well")) - (?q (message "Abort")) - (otherwise (error "Invalid key" ))))) + (pcase (read-char-exclusive) + (?\ (call-interactively 'org-agenda-reset-view)) + (?d (call-interactively 'org-agenda-day-view)) + (?w (call-interactively 'org-agenda-week-view)) + (?t (call-interactively 'org-agenda-fortnight-view)) + (?m (call-interactively 'org-agenda-month-view)) + (?y (call-interactively 'org-agenda-year-view)) + (?l (call-interactively 'org-agenda-log-mode)) + (?L (org-agenda-log-mode '(4))) + (?c (org-agenda-log-mode 'clockcheck)) + ((or ?F ?f) (call-interactively 'org-agenda-follow-mode)) + (?a (call-interactively 'org-agenda-archives-mode)) + (?A (org-agenda-archives-mode 'files)) + ((or ?R ?r) (call-interactively 'org-agenda-clockreport-mode)) + ((or ?E ?e) (call-interactively 'org-agenda-entry-text-mode)) + (?G (call-interactively 'org-agenda-toggle-time-grid)) + (?D (call-interactively 'org-agenda-toggle-diary)) + (?\! (call-interactively 'org-agenda-toggle-deadlines)) + (?\[ (let ((org-agenda-include-inactive-timestamps t)) + (org-agenda-check-type t 'timeline 'agenda) + (org-agenda-redo)) + (message "Display now includes inactive timestamps as well")) + (?q (message "Abort")) + (key (user-error "Invalid key: %s" key)))) (defun org-agenda-reset-view () "Switch to default view for agenda." (interactive) - (org-agenda-change-time-span (or org-agenda-ndays org-agenda-span))) + (org-agenda-change-time-span org-agenda-span)) + (defun org-agenda-day-view (&optional day-of-month) "Switch to daily view for agenda. With argument DAY-OF-MONTH, switch to that day of the month." (interactive "P") (org-agenda-change-time-span 'day day-of-month)) + (defun org-agenda-week-view (&optional iso-week) - "Switch to daily view for agenda. + "Switch to weekly view for agenda. With argument ISO-WEEK, switch to the corresponding ISO week. -If ISO-WEEK has more then 2 digits, only the last two encode the -week. Any digits before this encode a year. So 200712 means -week 12 of year 2007. Years in the range 1938-2037 can also be -written as 2-digit years." +If ISO-WEEK has more then 2 digits, only the last two encode +the week. Any digits before this encode a year. So 200712 +means week 12 of year 2007. Years ranging from 70 years ago +to 30 years in the future can also be written as 2-digit years." (interactive "P") (org-agenda-change-time-span 'week iso-week)) + (defun org-agenda-fortnight-view (&optional iso-week) - "Switch to daily view for agenda. + "Switch to fortnightly view for agenda. With argument ISO-WEEK, switch to the corresponding ISO week. -If ISO-WEEK has more then 2 digits, only the last two encode the -week. Any digits before this encode a year. So 200712 means -week 12 of year 2007. Years in the range 1938-2037 can also be -written as 2-digit years." +If ISO-WEEK has more then 2 digits, only the last two encode +the week. Any digits before this encode a year. So 200712 +means week 12 of year 2007. Years ranging from 70 years ago +to 30 years in the future can also be written as 2-digit years." (interactive "P") (org-agenda-change-time-span 'fortnight iso-week)) + (defun org-agenda-month-view (&optional month) "Switch to monthly view for agenda. -With argument MONTH, switch to that month." +With argument MONTH, switch to that month. If MONTH has more +then 2 digits, only the last two encode the month. Any digits +before this encode a year. So 200712 means December year 2007. +Years ranging from 70 years ago to 30 years in the future can +also be written as 2-digit years." (interactive "P") (org-agenda-change-time-span 'month month)) + (defun org-agenda-year-view (&optional year) "Switch to yearly view for agenda. -With argument YEAR, switch to that year. -If MONTH has more then 2 digits, only the last two encode the -month. Any digits before this encode a year. So 200712 means -December year 2007. Years in the range 1938-2037 can also be -written as 2-digit years." +With argument YEAR, switch to that year. Years ranging from 70 +years ago to 30 years in the future can also be written as +2-digit years." (interactive "P") (when year (setq year (org-small-year-to-year year))) @@ -7988,7 +8148,7 @@ so that the date SD will be in that range." (setq y1 (org-small-year-to-year (/ n 100)) n (mod n 100))) (setq sd - (calendar-absolute-from-iso + (calendar-iso-to-absolute (list n 1 (or y1 (nth 2 (calendar-iso-from-absolute sd))))))))) ((eq span 'month) @@ -8034,7 +8194,7 @@ so that the date SD will be in that range." (defun org-unhighlight () "Detach overlay INDEX." - (org-detach-overlay org-hl)) + (delete-overlay org-hl)) (defun org-unhighlight-once () "Remove the highlight from its position, and this function from the hook." @@ -8091,9 +8251,12 @@ so that the date SD will be in that range." (defun org-agenda-log-mode (&optional special) "Toggle log mode in an agenda buffer. + With argument SPECIAL, show all possible log items, not only the ones configured in `org-agenda-log-mode-items'. -With a double `C-u' prefix arg, show *only* log items, nothing else." + +With a `\\[universal-argument] \\[universal-argument]' prefix, show *only* \ +log items, nothing else." (interactive "P") (org-agenda-check-type t 'agenda 'timeline) (setq org-agenda-show-log @@ -8107,8 +8270,7 @@ With a double `C-u' prefix arg, show *only* log items, nothing else." (setq org-agenda-start-with-log-mode org-agenda-show-log) (org-agenda-set-mode-name) (org-agenda-redo) - (message "Log mode is %s" - (if org-agenda-show-log "on" "off"))) + (message "Log mode is %s" (if org-agenda-show-log "on" "off"))) (defun org-agenda-archives-mode (&optional with-files) "Toggle inclusion of items in trees marked with :ARCHIVE:. @@ -8180,7 +8342,7 @@ When called with a prefix argument, include all archive files as well." (t "")) (if (or org-agenda-category-filter (get 'org-agenda-category-filter :preset-filter)) - '(:eval (org-propertize + '(:eval (propertize (concat " <" (mapconcat 'identity @@ -8193,7 +8355,7 @@ When called with a prefix argument, include all archive files as well." 'help-echo "Category used in filtering")) "") (if (or org-agenda-tag-filter (get 'org-agenda-tag-filter :preset-filter)) - '(:eval (org-propertize + '(:eval (propertize (concat " {" (mapconcat 'identity @@ -8204,9 +8366,22 @@ When called with a prefix argument, include all archive files as well." "}") 'face 'org-agenda-filter-tags 'help-echo "Tags used in filtering")) "") + (if (or org-agenda-effort-filter + (get 'org-agenda-effort-filter :preset-filter)) + '(:eval (propertize + (concat " {" + (mapconcat + 'identity + (append + (get 'org-agenda-effort-filter :preset-filter) + org-agenda-effort-filter) + "") + "}") + 'face 'org-agenda-filter-effort + 'help-echo "Effort conditions used in filtering")) "") (if (or org-agenda-regexp-filter (get 'org-agenda-regexp-filter :preset-filter)) - '(:eval (org-propertize + '(:eval (propertize (concat " [" (mapconcat 'identity @@ -8225,9 +8400,6 @@ When called with a prefix argument, include all archive files as well." (if org-agenda-clockreport-mode " Clock" ""))) (force-mode-line-update)) -(define-obsolete-function-alias - 'org-agenda-post-command-hook 'org-agenda-update-agenda-type "24.3") - (defun org-agenda-update-agenda-type () "Update the agenda type after each command." (setq org-agenda-type @@ -8290,7 +8462,7 @@ When called with a prefix argument, include all archive files as well." (message "No tags associated with this line")))) (defun org-agenda-goto (&optional highlight) - "Go to the Org-mode file which contains the item at point." + "Go to the entry at point in the corresponding Org file." (interactive) (let* ((marker (or (org-get-at-bol 'org-marker) (org-agenda-error))) @@ -8302,12 +8474,11 @@ When called with a prefix argument, include all archive files as well." (goto-char pos) (when (derived-mode-p 'org-mode) (org-show-context 'agenda) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil)))) ; show the next heading - (when (outline-invisible-p) - (show-entry)) ; display invisible text - (recenter (/ (window-height) 2)) + (recenter (/ (window-height) 2)) + (org-back-to-heading t) + (let ((case-fold-search nil)) + (when (re-search-forward org-complex-heading-regexp nil t) + (goto-char (match-beginning 4))))) (run-hooks 'org-agenda-after-show-hook) (and highlight (org-highlight (point-at-bol) (point-at-eol))))) @@ -8394,7 +8565,7 @@ Point is in the buffer where the item originated.") (org-remove-subtree-entries-from-agenda)) (org-back-to-heading t) (funcall cmd))) - (error "Archiving works only in Org-mode files")))))) + (error "Archiving works only in Org files")))))) (defun org-remove-subtree-entries-from-agenda (&optional buf beg end) "Remove all lines in the agenda that correspond to a given subtree. @@ -8424,9 +8595,16 @@ If this information is not given, the function uses the tree at point." (defun org-agenda-refile (&optional goto rfloc no-update) "Refile the item at point. -When GOTO is 0 or '(64), clear the refile cache. -When GOTO is '(16), go to the location of the last refiled item. +When called with `\\[universal-argument] \\[universal-argument]', \ +go to the location of the last +refiled item. + +When called with `\\[universal-argument] \\[universal-argument] \ +\\[universal-argument]' prefix or when GOTO is 0, clear +the refile cache. + RFLOC can be a refile location obtained in a different way. + When NO-UPDATE is non-nil, don't redo the agenda buffer." (interactive "P") (cond @@ -8445,13 +8623,11 @@ When NO-UPDATE is non-nil, don't redo the agenda buffer." (if goto "Goto" "Refile to") buffer org-refile-allow-creating-parent-nodes)))) (with-current-buffer buffer - (save-excursion - (save-restriction - (widen) - (goto-char marker) - (let ((org-agenda-buffer-name buffer-orig)) - (org-remove-subtree-entries-from-agenda)) - (org-refile goto buffer rfloc))))) + (org-with-wide-buffer + (goto-char marker) + (let ((org-agenda-buffer-name buffer-orig)) + (org-remove-subtree-entries-from-agenda)) + (org-refile goto buffer rfloc)))) (unless no-update (org-agenda-redo))))) (defun org-agenda-open-link (&optional arg) @@ -8476,13 +8652,11 @@ It also looks at the text of the entry itself." (setq trg (and (string-match org-bracket-link-regexp l) (match-string 1 l))) (if (or (not trg) (string-match org-any-link-re trg)) - (save-excursion - (save-restriction - (widen) - (goto-char marker) - (when (search-forward l nil lkend) - (goto-char (match-beginning 0)) - (org-open-at-point)))) + (org-with-wide-buffer + (goto-char marker) + (when (search-forward l nil lkend) + (goto-char (match-beginning 0)) + (org-open-at-point))) ;; This is an internal link, widen the buffer (switch-to-buffer-other-window buffer) (widen) @@ -8502,11 +8676,14 @@ It also looks at the text of the entry itself." "Get a variable from a referenced buffer and install it here." (let ((m (org-get-at-bol 'org-marker))) (when (and m (buffer-live-p (marker-buffer m))) - (org-set-local var (with-current-buffer (marker-buffer m) - (symbol-value var)))))) + (set (make-local-variable var) + (with-current-buffer (marker-buffer m) + (symbol-value var)))))) (defun org-agenda-switch-to (&optional delete-other-windows) - "Go to the Org-mode file which contains the item at point." + "Go to the Org mode file which contains the item at point. +When optional argument DELETE-OTHER-WINDOWS is non-nil, the +displayed Org file fills the frame." (interactive) (if (and org-return-follows-link (not (org-get-at-bol 'org-marker)) @@ -8516,44 +8693,40 @@ It also looks at the text of the entry itself." (org-agenda-error))) (buffer (marker-buffer marker)) (pos (marker-position marker))) - (org-pop-to-buffer-same-window buffer) - (and delete-other-windows (delete-other-windows)) + (unless buffer (user-error "Trying to switch to non-existent buffer")) + (pop-to-buffer-same-window buffer) + (when delete-other-windows (delete-other-windows)) (widen) (goto-char pos) (when (derived-mode-p 'org-mode) (org-show-context 'agenda) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil))) ; show the next heading - (when (outline-invisible-p) - (show-entry)) ; display invisible text (run-hooks 'org-agenda-after-show-hook))))) (defun org-agenda-goto-mouse (ev) - "Go to the Org-mode file which contains the item at the mouse click." + "Go to the Org file which contains the item at the mouse click." (interactive "e") (mouse-set-point ev) (org-agenda-goto)) (defun org-agenda-show (&optional full-entry) - "Display the Org-mode file which contains the item at point. + "Display the Org file which contains the item at point. With prefix argument FULL-ENTRY, make the entire entry visible if it was hidden in the outline." (interactive "P") (let ((win (selected-window))) - (if full-entry - (let ((org-show-entry-below t)) - (org-agenda-goto t)) - (org-agenda-goto t)) + (org-agenda-goto t) + (when full-entry (org-show-entry)) (select-window win))) (defvar org-agenda-show-window nil) (defun org-agenda-show-and-scroll-up (&optional arg) - "Display the Org-mode file which contains the item at point. + "Display the Org file which contains the item at point. + When called repeatedly, scroll the window that is displaying the buffer. -With a \\[universal-argument] prefix, use `org-show-entry' instead of -`show-subtree' to display the item, so that drawers and logbooks stay -folded." + +With a `\\[universal-argument]' prefix, use `org-show-entry' instead of \ +`outline-show-subtree' +to display the item, so that drawers and logbooks stay folded." (interactive "P") (let ((win (selected-window))) (if (and (window-live-p org-agenda-show-window) @@ -8562,7 +8735,7 @@ folded." (select-window org-agenda-show-window) (ignore-errors (scroll-up))) (org-agenda-goto t) - (if arg (org-show-entry) (show-subtree)) + (if arg (org-show-entry) (outline-show-subtree)) (setq org-agenda-show-window (selected-window))) (select-window win))) @@ -8576,7 +8749,7 @@ folded." (select-window win)))) (defun org-agenda-show-1 (&optional more) - "Display the Org-mode file which contains the item at point. + "Display the Org file which contains the item at point. The prefix arg selects the amount of information to display: 0 hide the subtree @@ -8594,50 +8767,46 @@ if it was hidden in the outline." (set-window-start (selected-window) (point-at-bol)) (cond ((= more 0) - (hide-subtree) + (outline-hide-subtree) (save-excursion (org-back-to-heading) (run-hook-with-args 'org-cycle-hook 'folded)) (message "Remote: FOLDED")) - ((and (org-called-interactively-p 'any) (= more 1)) + ((and (called-interactively-p 'any) (= more 1)) (message "Remote: show with default settings")) ((= more 2) - (show-entry) - (show-children) + (outline-show-entry) + (org-show-children) (save-excursion (org-back-to-heading) (run-hook-with-args 'org-cycle-hook 'children)) (message "Remote: CHILDREN")) ((= more 3) - (show-subtree) + (outline-show-subtree) (save-excursion (org-back-to-heading) (run-hook-with-args 'org-cycle-hook 'subtree)) (message "Remote: SUBTREE")) ((= more 4) - (let* ((org-drawers (delete "LOGBOOK" (copy-sequence org-drawers))) - (org-drawer-regexp - (concat "^[ \t]*:\\(" - (mapconcat 'regexp-quote org-drawers "\\|") - "\\):[ \t]*$"))) - (show-subtree) - (save-excursion - (org-back-to-heading) - (org-cycle-hide-drawers 'subtree))) + (outline-show-subtree) + (save-excursion + (org-back-to-heading) + (org-cycle-hide-drawers 'subtree '("LOGBOOK"))) (message "Remote: SUBTREE AND LOGBOOK")) ((> more 4) - (show-subtree) + (outline-show-subtree) (message "Remote: SUBTREE AND ALL DRAWERS"))) (select-window win))) (defvar org-agenda-cycle-counter nil) (defun org-agenda-cycle-show (&optional n) "Show the current entry in another window, with default settings. -Default settings are taken from `org-show-hierarchy-above' and siblings. -When use repeatedly in immediate succession, the remote entry will cycle -through visibility -children -> subtree -> folded +Default settings are taken from `org-show-context-detail'. When +use repeatedly in immediate succession, the remote entry will +cycle through visibility + + children -> subtree -> folded When called with a numeric prefix arg, that arg will be passed through to `org-agenda-show-1'. For the interpretation of that argument, see the @@ -8655,7 +8824,7 @@ docstring of `org-agenda-show-1'." (org-agenda-show-1 org-agenda-cycle-counter)) (defun org-agenda-recenter (arg) - "Display the Org-mode file which contains the item at point and recenter." + "Display the Org file which contains the item at point and recenter." (interactive "P") (let ((win (selected-window))) (org-agenda-goto t) @@ -8663,7 +8832,7 @@ docstring of `org-agenda-show-1'." (select-window win))) (defun org-agenda-show-mouse (ev) - "Display the Org-mode file which contains the item at the mouse click." + "Display the Org file which contains the item at the mouse click." (interactive "e") (mouse-set-point ev) (org-agenda-show)) @@ -8674,7 +8843,8 @@ docstring of `org-agenda-show-1'." (org-agenda-error))) (defun org-agenda-error () - (error "Command not allowed in this line")) + "Throw an error when a command is not allowed in the agenda." + (user-error "Command not allowed in this line")) (defun org-agenda-tree-to-indirect-buffer (arg) "Show the subtree corresponding to the current entry in an indirect buffer. @@ -8682,8 +8852,10 @@ This calls the command `org-tree-to-indirect-buffer' from the original buffer. With a numerical prefix ARG, go up to this level and then take that tree. With a negative numeric ARG, go up by this number of levels. -With a \\[universal-argument] prefix, make a separate frame for this tree (i.e. don't -use the dedicated frame)." + +With a `\\[universal-argument]' prefix, make a separate frame for this tree, \ +i.e. don't use +the dedicated frame." (interactive "P") (if current-prefix-arg (org-agenda-do-tree-to-indirect-buffer arg) @@ -8701,7 +8873,8 @@ use the dedicated frame)." (and indirect-window (select-window indirect-window)) (switch-to-buffer org-last-indirect-buffer :norecord) (fit-window-to-buffer indirect-window))) - (select-window (get-buffer-window agenda-buffer))))) + (select-window (get-buffer-window agenda-buffer)) + (setq org-agenda-last-indirect-buffer org-last-indirect-buffer)))) (defun org-agenda-do-tree-to-indirect-buffer (arg) "Same as `org-agenda-tree-to-indirect-buffer' without saving window." @@ -8730,9 +8903,9 @@ by a remote command from the agenda.") (org-agenda-todo 'previousset)) (defun org-agenda-todo (&optional arg) - "Cycle TODO state of line at point, also in Org-mode file. + "Cycle TODO state of line at point, also in Org file. This changes the line at point, all other lines in the agenda referring to -the same tree node, and the headline of the tree node in the Org-mode file." +the same tree node, and the headline of the tree node in the Org file." (interactive "P") (org-agenda-check-no-diary) (let* ((col (current-column)) @@ -8741,7 +8914,7 @@ the same tree node, and the headline of the tree node in the Org-mode file." (buffer (marker-buffer marker)) (pos (marker-position marker)) (hdmarker (org-get-at-bol 'org-hd-marker)) - (todayp (org-agenda-todayp (org-get-at-bol 'day))) + (todayp (org-agenda-today-p (org-get-at-bol 'day))) (inhibit-read-only t) org-agenda-headline-snapshot-before-repeat newhead just-one) (org-with-remote-undo buffer @@ -8749,14 +8922,11 @@ the same tree node, and the headline of the tree node in the Org-mode file." (widen) (goto-char pos) (org-show-context 'agenda) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil))) ; show the next heading (let ((current-prefix-arg arg)) (call-interactively 'org-todo)) (and (bolp) (forward-char 1)) (setq newhead (org-get-heading)) - (when (and (org-bound-and-true-p + (when (and (bound-and-true-p org-agenda-headline-snapshot-before-repeat) (not (equal org-agenda-headline-snapshot-before-repeat newhead)) @@ -8769,11 +8939,12 @@ the same tree node, and the headline of the tree node in the Org-mode file." (beginning-of-line 1) (save-window-excursion (org-agenda-change-all-lines newhead hdmarker 'fixface just-one)) - (when (org-bound-and-true-p org-clock-out-when-done) + (when (bound-and-true-p org-clock-out-when-done) (string-match (concat "^" (regexp-opt org-done-keywords-for-agenda)) newhead) (org-agenda-unmark-clocking-task)) - (org-move-to-column col)))) + (org-move-to-column col) + (org-agenda-mark-clocking-task)))) (defun org-agenda-add-note (&optional arg) "Add a time-stamped note to the entry at point." @@ -8789,9 +8960,6 @@ the same tree node, and the headline of the tree node in the Org-mode file." (widen) (goto-char pos) (org-show-context 'agenda) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil))) ; show the next heading (org-add-note)))) (defun org-agenda-change-all-lines (newhead hdmarker @@ -8808,9 +8976,9 @@ If FORCE-TAGS is non nil, the car of it returns the new tags." (line (org-current-line)) (org-agenda-buffer (current-buffer)) (thetags (with-current-buffer (marker-buffer hdmarker) - (save-excursion (save-restriction (widen) - (goto-char hdmarker) - (org-get-tags-at))))) + (org-with-wide-buffer + (goto-char hdmarker) + (org-get-tags-at)))) props m pl undone-face done-face finish new dotime level cat tags) (save-excursion (goto-char (point-max)) @@ -8822,7 +8990,7 @@ If FORCE-TAGS is non nil, the car of it returns the new tags." (equal m hdmarker)) (setq props (text-properties-at (point)) dotime (org-get-at-bol 'dotime) - cat (org-get-at-bol 'org-category) + cat (org-get-at-eol 'org-category 1) level (org-get-at-bol 'level) tags thetags new @@ -8831,20 +8999,25 @@ If FORCE-TAGS is non nil, the car of it returns the new tags." org-prefix-format-compiled)) (extra (org-get-at-bol 'extra))) (with-current-buffer (marker-buffer hdmarker) - (save-excursion - (save-restriction - (widen) - (org-agenda-format-item extra newhead level cat tags dotime))))) + (org-with-wide-buffer + (org-agenda-format-item extra newhead level cat tags dotime)))) pl (text-property-any (point-at-bol) (point-at-eol) 'org-heading t) undone-face (org-get-at-bol 'undone-face) done-face (org-get-at-bol 'done-face)) (beginning-of-line 1) (cond - ((equal new "") - (and (looking-at ".*\n?") (replace-match ""))) + ((equal new "") (delete-region (point) (line-beginning-position 2))) ((looking-at ".*") - (replace-match new t t) - (beginning-of-line 1) + ;; When replacing the whole line, preserve bulk mark + ;; overlay, if any. + (let ((mark (catch :overlay + (dolist (o (overlays-in (point) (+ 2 (point)))) + (when (eq (overlay-get o 'type) + 'org-marked-entry-overlay) + (throw :overlay o)))))) + (replace-match new t t) + (beginning-of-line) + (when mark (move-overlay mark (point) (+ 2 (point))))) (add-text-properties (point-at-bol) (point-at-eol) props) (when fixface (add-text-properties @@ -8865,7 +9038,7 @@ If FORCE-TAGS is non nil, the car of it returns the new tags." (let ((inhibit-read-only t) l c) (save-excursion (goto-char (if line (point-at-bol) (point-min))) - (while (re-search-forward (org-re "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$") + (while (re-search-forward "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" (if line (point-at-eol) nil) t) (add-text-properties (match-beginning 2) (match-end 2) @@ -8889,19 +9062,19 @@ If FORCE-TAGS is non nil, the car of it returns the new tags." (org-font-lock-add-tag-faces (point-max))))) (defun org-agenda-priority-up () - "Increase the priority of line at point, also in Org-mode file." + "Increase the priority of line at point, also in Org file." (interactive) (org-agenda-priority 'up)) (defun org-agenda-priority-down () - "Decrease the priority of line at point, also in Org-mode file." + "Decrease the priority of line at point, also in Org file." (interactive) (org-agenda-priority 'down)) (defun org-agenda-priority (&optional force-direction) - "Set the priority of line at point, also in Org-mode file. + "Set the priority of line at point, also in Org file. This changes the line at point, all other lines in the agenda referring to -the same tree node, and the headline of the tree node in the Org-mode file. +the same tree node, and the headline of the tree node in the Org file. Called with a universal prefix arg, show the priority instead of setting it." (interactive "P") (if (equal force-direction '(4)) @@ -8922,9 +9095,6 @@ Called with a universal prefix arg, show the priority instead of setting it." (widen) (goto-char pos) (org-show-context 'agenda) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil))) ; show the next heading (funcall 'org-priority force-direction) (end-of-line 1) (setq newhead (org-get-heading))) @@ -8936,7 +9106,7 @@ Called with a universal prefix arg, show the priority instead of setting it." "Set tags for the current headline." (interactive) (org-agenda-check-no-diary) - (if (and (org-region-active-p) (org-called-interactively-p 'any)) + (if (and (org-region-active-p) (called-interactively-p 'any)) (call-interactively 'org-change-tag-in-region) (let* ((hdmarker (or (org-get-at-bol 'org-hd-marker) (org-agenda-error))) @@ -8948,12 +9118,7 @@ Called with a universal prefix arg, show the priority instead of setting it." (with-current-buffer buffer (widen) (goto-char pos) - (save-excursion - (org-show-context 'agenda)) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil))) ; show the next heading - (goto-char pos) + (org-show-context 'agenda) (if tag (org-toggle-tag tag onoff) (call-interactively 'org-set-tags)) @@ -8976,12 +9141,7 @@ Called with a universal prefix arg, show the priority instead of setting it." (with-current-buffer buffer (widen) (goto-char pos) - (save-excursion - (org-show-context 'agenda)) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil))) ; show the next heading - (goto-char pos) + (org-show-context 'agenda) (call-interactively 'org-set-property))))) (defun org-agenda-set-effort () @@ -8998,12 +9158,7 @@ Called with a universal prefix arg, show the priority instead of setting it." (with-current-buffer buffer (widen) (goto-char pos) - (save-excursion - (org-show-context 'agenda)) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil))) ; show the next heading - (goto-char pos) + (org-show-context 'agenda) (call-interactively 'org-set-effort) (end-of-line 1) (setq newhead (org-get-heading))) @@ -9024,9 +9179,6 @@ Called with a universal prefix arg, show the priority instead of setting it." (widen) (goto-char pos) (org-show-context 'agenda) - (save-excursion - (and (outline-next-heading) - (org-flag-heading nil))) ; show the next heading (call-interactively 'org-toggle-archive-tag) (end-of-line 1) (setq newhead (org-get-heading))) @@ -9140,18 +9292,10 @@ Called with a universal prefix arg, show the priority instead of setting it." (when (equal marker (org-get-at-bol 'org-marker)) (remove-text-properties (point-at-bol) (point-at-eol) '(display)) (org-move-to-column (- (window-width) (length stamp)) t) - (if (featurep 'xemacs) - ;; Use `duplicable' property to trigger undo recording - (let ((ex (make-extent nil nil)) - (gl (make-glyph stamp))) - (set-glyph-face gl 'secondary-selection) - (set-extent-properties - ex (list 'invisible t 'end-glyph gl 'duplicable t)) - (insert-extent ex (1- (point)) (point-at-eol))) - (add-text-properties - (1- (point)) (point-at-eol) - (list 'display (org-add-props stamp nil - 'face '(secondary-selection default))))) + (add-text-properties + (1- (point)) (point-at-eol) + (list 'display (org-add-props stamp nil + 'face '(secondary-selection default)))) (beginning-of-line 1)) (beginning-of-line 0))))) @@ -9187,7 +9331,6 @@ ARG is passed through to `org-schedule'." (type (marker-insertion-type marker)) (buffer (marker-buffer marker)) (pos (marker-position marker)) - (org-insert-labeled-timestamps-at-point nil) ts) (set-marker-insertion-type marker t) (org-with-remote-undo buffer @@ -9208,7 +9351,6 @@ ARG is passed through to `org-deadline'." (org-agenda-error))) (buffer (marker-buffer marker)) (pos (marker-position marker)) - (org-insert-labeled-timestamps-at-point nil) ts) (org-with-remote-undo buffer (with-current-buffer buffer @@ -9235,7 +9377,6 @@ ARG is passed through to `org-deadline'." (widen) (goto-char pos) (org-show-context 'agenda) - (org-show-entry) (org-cycle-hide-drawers 'children) (org-clock-in arg) (setq newhead (org-get-heading))) @@ -9250,14 +9391,12 @@ ARG is passed through to `org-deadline'." (let ((marker (make-marker)) (col (current-column)) newhead) (org-with-remote-undo (marker-buffer org-clock-marker) (with-current-buffer (marker-buffer org-clock-marker) - (save-excursion - (save-restriction - (widen) - (goto-char org-clock-marker) - (org-back-to-heading t) - (move-marker marker (point)) - (org-clock-out) - (setq newhead (org-get-heading)))))) + (org-with-wide-buffer + (goto-char org-clock-marker) + (org-back-to-heading t) + (move-marker marker (point)) + (org-clock-out) + (setq newhead (org-get-heading))))) (org-agenda-change-all-lines newhead marker) (move-marker marker nil) (org-move-to-column col) @@ -9284,7 +9423,7 @@ buffer, display it in another window." (cond (pos (goto-char pos)) ;; If the currently clocked entry is not in the agenda ;; buffer, we visit it in another window: - (org-clock-current-task + ((bound-and-true-p org-clock-current-task) (org-switch-to-buffer-other-window (org-clock-goto))) (t (message "No running clock, use `C-c C-x C-j' to jump to the most recent one"))))) @@ -9334,11 +9473,13 @@ buffer, display it in another window." "Where in `org-agenda-diary-file' should new entries be added? Valid values: -date-tree in the date tree, as child of the date -top-level as top-level entries at the end of the file." +date-tree in the date tree, as first child of the date +date-tree-last in the date tree, as last child of the date +top-level as top-level entries at the end of the file." :group 'org-agenda :type '(choice - (const :tag "in a date tree" date-tree) + (const :tag "first in a date tree" date-tree) + (const :tag "last in a date tree" date-tree-last) (const :tag "as top level at end of file" top-level))) (defcustom org-agenda-insert-diary-extract-time nil @@ -9434,40 +9575,43 @@ Add TEXT as headline, and position the cursor in the second line so that a timestamp can be added there." (widen) (goto-char (point-max)) - (or (bolp) (insert "\n")) - (insert "* " text "\n") - (if org-adapt-indentation (org-indent-to-column 2))) + (unless (bolp) (insert "\n")) + (org-insert-heading nil t t) + (insert text) + (org-end-of-meta-data) + (unless (bolp) (insert "\n")) + (when org-adapt-indentation (indent-to-column 2))) (defun org-agenda-insert-diary-make-new-entry (text) - "Make a new entry with TEXT as the first child of the current subtree. -Position the point in the line right after the new heading so -that a timestamp can be added there." - (let ((org-show-following-heading t) - (org-show-siblings t) - (org-show-hierarchy-above t) - (org-show-entry-below t) - col) + "Make a new entry with TEXT as a child of the current subtree. +Position the point in the heading's first body line so that +a timestamp can be added there." + (cond + ((eq org-agenda-insert-diary-strategy 'date-tree-last) + (end-of-line) + (org-insert-heading '(4) t) + (org-do-demote)) + (t (outline-next-heading) (org-back-over-empty-lines) - (or (looking-at "[ \t]*$") - (progn (insert "\n") (backward-char 1))) + (unless (looking-at "[ \t]*$") (save-excursion (insert "\n"))) (org-insert-heading nil t) - (org-do-demote) - (setq col (current-column)) - (insert text "\n") - (if org-adapt-indentation (org-indent-to-column col)) - (let ((org-show-following-heading t) - (org-show-siblings t) - (org-show-hierarchy-above t) - (org-show-entry-below t)) - (org-show-context)))) + (org-do-demote))) + (let ((col (current-column))) + (insert text) + (org-end-of-meta-data) + ;; Ensure point is left on a blank line, at proper indentation. + (unless (bolp) (insert "\n")) + (unless (looking-at-p "^[ \t]*$") (save-excursion (insert "\n"))) + (when org-adapt-indentation (indent-to-column col))) + (org-show-set-visibility 'lineage)) (defun org-agenda-diary-entry () "Make a diary entry, like the `i' command from the calendar. All the standard commands work: block, weekly etc. When `org-agenda-diary-file' points to a file, `org-agenda-diary-entry-in-org-file' is called instead to create -entries in that Org-mode file." +entries in that Org file." (interactive) (if (not (eq org-agenda-diary-file 'diary-file)) (org-agenda-diary-entry-in-org-file) @@ -9476,13 +9620,13 @@ entries in that Org-mode file." (message "Diary entry: [d]ay [w]eekly [m]onthly [y]early [a]nniversary [b]lock [c]yclic") (read-char-exclusive))) (cmd (cdr (assoc char - '((?d . insert-diary-entry) - (?w . insert-weekly-diary-entry) - (?m . insert-monthly-diary-entry) - (?y . insert-yearly-diary-entry) - (?a . insert-anniversary-diary-entry) - (?b . insert-block-diary-entry) - (?c . insert-cyclic-diary-entry))))) + '((?d . diary-insert-entry) + (?w . diary-insert-weekly-entry) + (?m . diary-insert-monthly-entry) + (?y . diary-insert-yearly-entry) + (?a . diary-insert-anniversary-entry) + (?b . diary-insert-block-entry) + (?c . diary-insert-cyclic-entry))))) (oldf (symbol-function 'calendar-cursor-to-date)) ;; (buf (get-file-buffer (substitute-in-file-name diary-file))) (point (point)) @@ -9538,7 +9682,7 @@ entries in that Org-mode file." (defun org-agenda-holidays () "Display the holidays for the 3 months around the cursor date." (interactive) - (org-agenda-execute-calendar-command 'list-calendar-holidays)) + (org-agenda-execute-calendar-command 'calendar-list-holidays)) (defvar calendar-longitude) ; defined in calendar.el (defvar calendar-latitude) ; defined in calendar.el @@ -9572,12 +9716,16 @@ argument, latitude and longitude will be prompted for." ;;;###autoload (defun org-calendar-goto-agenda () - "Compute the Org-mode agenda for the calendar date displayed at the cursor. + "Compute the Org agenda for the calendar date displayed at the cursor. This is a command that has to be installed in `calendar-mode-map'." (interactive) - (org-agenda-list nil (calendar-absolute-from-gregorian - (calendar-cursor-to-date)) - nil)) + ;; Temporarily disable sticky agenda since user clearly wants to + ;; refresh view anyway. + (let ((org-agenda-buffer-tmp-name "*Org Agenda(a)*") + (org-agenda-sticky nil)) + (org-agenda-list nil (calendar-absolute-from-gregorian + (calendar-cursor-to-date)) + nil))) (defun org-agenda-convert-date () (interactive) @@ -9610,6 +9758,7 @@ This is a command that has to be installed in `calendar-mode-map'." ;;; Bulk commands (defun org-agenda-bulk-marked-p () + "Non-nil when current entry is marked for bulk action." (eq (get-char-property (point-at-bol) 'type) 'org-marked-entry-overlay)) @@ -9651,9 +9800,12 @@ This is a command that has to be installed in `calendar-mode-map'." (goto-char (next-single-property-change (point) 'org-hd-marker)) (while (and (re-search-forward regexp nil t) (setq txt-at-point (get-text-property (point) 'txt))) - (when (string-match regexp txt-at-point) - (setq entries-marked (1+ entries-marked)) - (call-interactively 'org-agenda-bulk-mark)))) + (if (get-char-property (point) 'invisible) + (beginning-of-line 2) + (when (string-match regexp txt-at-point) + (setq entries-marked (1+ entries-marked)) + (call-interactively 'org-agenda-bulk-mark))))) + (if (not entries-marked) (message "No entry matching this regexp.")))) @@ -9712,7 +9864,6 @@ This will remove the markers and the overlays." (interactive) (if (null org-agenda-bulk-marked-entries) (message "No entry to unmark") - (mapc (lambda (m) (move-marker m nil)) org-agenda-bulk-marked-entries) (setq org-agenda-bulk-marked-entries nil) (org-agenda-bulk-remove-overlays (point-min) (point-max)))) @@ -9786,21 +9937,21 @@ The prefix arg is passed through to the command if possible." redo-at-end t)) ((equal action ?t) - (setq state (org-icompleting-read + (setq state (completing-read "Todo state: " (with-current-buffer (marker-buffer (car entries)) - (mapcar 'list org-todo-keywords-1)))) + (mapcar #'list org-todo-keywords-1)))) (setq cmd `(let ((org-inhibit-blocking t) (org-inhibit-logging 'note)) (org-agenda-todo ,state)))) ((memq action '(?- ?+)) - (setq tag (org-icompleting-read + (setq tag (completing-read (format "Tag to %s: " (if (eq action ?+) "add" "remove")) (with-current-buffer (marker-buffer (car entries)) (delq nil - (mapcar (lambda (x) - (if (stringp (car x)) x)) org-tag-alist))))) + (mapcar (lambda (x) (and (stringp (car x)) x)) + org-current-tag-alist))))) (setq cmd `(org-agenda-set-tags ,tag ,(if (eq action ?+) ''on ''off)))) ((memq action '(?s ?d)) @@ -9810,8 +9961,17 @@ The prefix arg is passed through to the command if possible." nil nil nil (if (eq action ?s) "(Re)Schedule to" "(Re)Set Deadline to") org-overriding-default-time))) - (c1 (if (eq action ?s) 'org-agenda-schedule 'org-agenda-deadline))) - (setq cmd `(eval '(,c1 arg ,time))))) + (c1 (if (eq action ?s) 'org-agenda-schedule + 'org-agenda-deadline))) + ;; Make sure to not prompt for a note when bulk + ;; rescheduling as Org cannot cope with simultaneous + ;; notes. Besides, it could be annoying depending on the + ;; number of items re-scheduled. + (setq cmd `(eval '(let ((org-log-reschedule + (and org-log-reschedule 'time)) + (org-log-redeadline + (and org-log-redeadline 'time))) + (,c1 arg ,time)))))) ((equal action ?S) (if (not (org-agenda-check-type nil 'agenda 'timeline 'todo)) @@ -9828,13 +9988,13 @@ The prefix arg is passed through to the command if possible." (calendar-gregorian-from-absolute (org-today))))) (dotimes (i (1+ dist)) (while (member day-of-week org-agenda-weekend-days) - (incf distance) - (incf day-of-week) - (if (= day-of-week 7) - (setq day-of-week 0))) - (incf day-of-week) - (if (= day-of-week 7) - (setq day-of-week 0))))) + (cl-incf distance) + (cl-incf day-of-week) + (when (= day-of-week 7) + (setq day-of-week 0))) + (cl-incf day-of-week) + (when (= day-of-week 7) + (setq day-of-week 0))))) ;; silently fail when try to replan a sexp entry (condition-case nil (let* ((date (calendar-gregorian-from-absolute @@ -9850,8 +10010,8 @@ The prefix arg is passed through to the command if possible." ((equal action ?f) (setq cmd (list (intern - (org-icompleting-read "Function: " - obarray 'fboundp t nil nil))))) + (completing-read "Function: " + obarray 'fboundp t nil nil))))) (t (user-error "Invalid bulk action"))) @@ -9874,6 +10034,11 @@ The prefix arg is passed through to the command if possible." (goto-char pos) (let (org-loop-over-headlines-in-active-region) (eval cmd)) + ;; `post-command-hook' is not run yet. We make sure any + ;; pending log note is processed. + (when (or (memq 'org-add-log-note (default-value 'post-command-hook)) + (memq 'org-add-log-note post-command-hook)) + (org-add-log-note)) (setq cnt (1+ cnt)))) (when redo-at-end (org-agenda-redo)) (unless org-agenda-persistent-marks @@ -9903,12 +10068,14 @@ current HH:MM time." (defun org-agenda-reapply-filters () "Re-apply all agenda filters." (mapcar - (lambda(f) (when (car f) (org-agenda-filter-apply (car f) (cadr f)))) + (lambda(f) (when (car f) (org-agenda-filter-apply (car f) (cadr f) t))) `((,org-agenda-tag-filter tag) (,org-agenda-category-filter category) (,org-agenda-regexp-filter regexp) + (,org-agenda-effort-filter effort) (,(get 'org-agenda-tag-filter :preset-filter) tag) (,(get 'org-agenda-category-filter :preset-filter) category) + (,(get 'org-agenda-effort-filter :preset-filter) effort) (,(get 'org-agenda-regexp-filter :preset-filter) regexp)))) (defun org-agenda-drag-line-forward (arg &optional backward) @@ -9969,7 +10136,9 @@ tag and (if present) the flagging note." (replace-match "\n" t t)) (goto-char (point-min)) (select-window win) - (message "Flagging note pushed to kill ring. Press [?] again to remove tag and note")))) + (message "%s" (substitute-command-keys "Flagging note pushed to \ +kill ring. Press `\\[org-agenda-show-the-flagging-note]' again to remove \ +tag and note"))))) (defun org-agenda-remove-flag (marker) "Remove the FLAGGED tag and any flagging note in the entry." @@ -9992,7 +10161,8 @@ tag and (if present) the flagging note." ;;;###autoload (defun org-agenda-to-appt (&optional refresh filter &rest args) "Activate appointments found in `org-agenda-files'. -With a \\[universal-argument] prefix, refresh the list of + +With a `\\[universal-argument]' prefix, refresh the list of \ appointments. If FILTER is t, interactively prompt the user for a regular @@ -10008,8 +10178,8 @@ argument: an entry from `org-agenda-get-day-entries'. FILTER can also be an alist with the car of each cell being either `headline' or `category'. For example: - ((headline \"IMPORTANT\") - (category \"Work\")) + \\='((headline \"IMPORTANT\") + (category \"Work\")) will only add headlines containing IMPORTANT or headlines belonging to the \"Work\" category. @@ -10026,75 +10196,78 @@ to override `appt-message-warning-time'." (if refresh (setq appt-time-msg-list nil)) (if (eq filter t) (setq filter (read-from-minibuffer "Regexp filter: "))) - (let* ((cnt 0) ; count added events - (scope (or args '(:deadline* :scheduled* :timestamp))) - (org-agenda-new-buffers nil) - (org-deadline-warning-days 0) - ;; Do not use `org-today' here because appt only takes - ;; time and without date as argument, so it may pass wrong - ;; information otherwise - (today (org-date-to-gregorian - (time-to-days (current-time)))) - (org-agenda-restrict nil) - (files (org-agenda-files 'unrestricted)) entries file - (org-agenda-buffer nil)) + (let* ((cnt 0) ; count added events + (scope (or args '(:deadline* :scheduled* :timestamp))) + (org-agenda-new-buffers nil) + (org-deadline-warning-days 0) + ;; Do not use `org-today' here because appt only takes + ;; time and without date as argument, so it may pass wrong + ;; information otherwise + (today (org-date-to-gregorian + (time-to-days (current-time)))) + (org-agenda-restrict nil) + (files (org-agenda-files 'unrestricted)) entries file + (org-agenda-buffer nil)) ;; Get all entries which may contain an appt (org-agenda-prepare-buffers files) (while (setq file (pop files)) (setq entries - (delq nil - (append entries - (apply 'org-agenda-get-day-entries - file today scope))))) + (delq nil + (append entries + (apply 'org-agenda-get-day-entries + file today scope))))) ;; Map thru entries and find if we should filter them out (mapc - (lambda(x) + (lambda (x) (let* ((evt (org-trim - (replace-regexp-in-string - org-bracket-link-regexp "\\3" - (or (get-text-property 1 'txt x) "")))) - (cat (get-text-property 1 'org-category x)) - (tod (get-text-property 1 'time-of-day x)) - (ok (or (null filter) - (and (stringp filter) (string-match filter evt)) - (and (functionp filter) (funcall filter x)) - (and (listp filter) - (let ((cat-filter (cadr (assoc 'category filter))) - (evt-filter (cadr (assoc 'headline filter)))) - (or (and (stringp cat-filter) - (string-match cat-filter cat)) - (and (stringp evt-filter) - (string-match evt-filter evt))))))) - (wrn (get-text-property 1 'warntime x))) - ;; FIXME: Shall we remove text-properties for the appt text? - ;; (setq evt (set-text-properties 0 (length evt) nil evt)) - (when (and ok tod) - (setq tod (concat "00" (number-to-string tod)) - tod (when (string-match - "\\([0-9]\\{1,2\\}\\)\\([0-9]\\{2\\}\\)\\'" tod) - (concat (match-string 1 tod) ":" - (match-string 2 tod)))) - (if (version< emacs-version "23.3") - (appt-add tod evt) - (appt-add tod evt wrn)) - (setq cnt (1+ cnt))))) entries) + (replace-regexp-in-string + org-bracket-link-regexp "\\3" + (or (get-text-property 1 'txt x) "")))) + (cat (get-text-property (1- (length x)) 'org-category x)) + (tod (get-text-property 1 'time-of-day x)) + (ok (or (null filter) + (and (stringp filter) (string-match filter evt)) + (and (functionp filter) (funcall filter x)) + (and (listp filter) + (let ((cat-filter (cadr (assq 'category filter))) + (evt-filter (cadr (assq 'headline filter)))) + (or (and (stringp cat-filter) + (string-match cat-filter cat)) + (and (stringp evt-filter) + (string-match evt-filter evt))))))) + (wrn (get-text-property 1 'warntime x))) + ;; FIXME: Shall we remove text-properties for the appt text? + ;; (setq evt (set-text-properties 0 (length evt) nil evt)) + (when (and ok tod (not (string-match "\\`DONE\\|CANCELLED" evt))) + (setq tod (concat "00" (number-to-string tod))) + (setq tod (when (string-match + "\\([0-9]\\{1,2\\}\\)\\([0-9]\\{2\\}\\)\\'" tod) + (concat (match-string 1 tod) ":" + (match-string 2 tod)))) + (when (if (version< emacs-version "23.3") + (appt-add tod evt) + (appt-add tod evt wrn)) + (setq cnt (1+ cnt)))))) + entries) (org-release-buffers org-agenda-new-buffers) (if (eq cnt 0) - (message "No event to add") + (message "No event to add") (message "Added %d event%s for today" cnt (if (> cnt 1) "s" ""))))) -(defun org-agenda-todayp (date) - "Does DATE mean today, when considering `org-extend-today-until'?" - (let ((today (org-today)) - (date (if (and date (listp date)) (calendar-absolute-from-gregorian date) - date))) - (eq date today))) +(defun org-agenda-today-p (date) + "Non nil when DATE means today. +DATE is either a list of the form (month day year) or a number of +days as returned by `calendar-absolute-from-gregorian' or +`org-today'. This function considers `org-extend-today-until' +when defining today." + (eq (org-today) + (if (consp date) (calendar-absolute-from-gregorian date) date))) (defun org-agenda-todo-yesterday (&optional arg) "Like `org-agenda-todo' but the time of change will be 23:59 of yesterday." (interactive "P") - (let* ((hour (third (decode-time - (org-current-time)))) + (let* ((org-use-effective-time t) + (hour (nth 2 (decode-time (org-current-time)))) (org-extend-today-until (1+ hour))) (org-agenda-todo arg))) diff --git a/lisp/org/org-archive.el b/lisp/org/org-archive.el index 39a6581046..ce1f35df36 100644 --- a/lisp/org/org-archive.el +++ b/lisp/org/org-archive.el @@ -1,4 +1,4 @@ -;;; org-archive.el --- Archiving for Org-mode +;;; org-archive.el --- Archiving for Org -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -29,10 +29,10 @@ ;;; Code: (require 'org) -(eval-when-compile (require 'cl)) -(declare-function org-inlinetask-remove-END-maybe "org-inlinetask" ()) +(declare-function org-element-type "org-element" (element)) (declare-function org-datetree-find-date-create "org-datetree" (date &optional keep-restriction)) +(declare-function org-inlinetask-remove-END-maybe "org-inlinetask" ()) (defcustom org-archive-default-command 'org-archive-subtree "The default archiving command." @@ -57,7 +57,7 @@ See `org-archive-to-archive-sibling' for more information." (defcustom org-archive-mark-done nil "Non-nil means mark entries as DONE when they are moved to the archive file. -This can be a string to set the keyword to use. When t, Org-mode will +This can be a string to set the keyword to use. When non-nil, Org will use the first keyword in its list that means done." :group 'org-archive :type '(choice @@ -120,9 +120,15 @@ information." (const :tag "Outline path" olpath) (const :tag "Local tags" ltags))) +(defvar org-archive-hook nil + "Hook run after successfully archiving a subtree. +Hook functions are called with point on the subtree in the +original file. At this stage, the subtree has been added to the +archive location, but not yet deleted from the original file.") + (defun org-get-local-archive-location () "Get the archive location applicable at point." - (let ((re "^#\\+ARCHIVE:[ \t]+\\(\\S-.*\\S-\\)[ \t]*$") + (let ((re "^[ \t]*#\\+ARCHIVE:[ \t]+\\(\\S-.*\\S-\\)[ \t]*$") prop) (save-excursion (save-restriction @@ -154,21 +160,24 @@ archive file is." (defun org-all-archive-files () "Get a list of all archive files used in the current buffer." - (let (file files) - (save-excursion - (save-restriction - (goto-char (point-min)) - (while (re-search-forward - "^\\(#\\+\\|[ \t]*:\\)ARCHIVE:[ \t]+\\(.*\\)" - nil t) - (setq file (org-extract-archive-file - (org-match-string-no-properties 2))) - (and file (> (length file) 0) (file-exists-p file) - (pushnew file files :test #'equal))))) + (let ((case-fold-search t) + files) + (org-with-wide-buffer + (goto-char (point-min)) + (while (re-search-forward + "^[ \t]*\\(#\\+\\|:\\)ARCHIVE:[ \t]+\\(.*\\)" + nil t) + (when (save-match-data + (if (eq (match-string 1) ":") (org-at-property-p) + (eq (org-element-type (org-element-at-point)) 'keyword))) + (let ((file (org-extract-archive-file + (match-string-no-properties 2)))) + (when (and (org-string-nw-p file) (file-exists-p file)) + (push file files)))))) (setq files (nreverse files)) - (setq file (org-extract-archive-file)) - (and file (> (length file) 0) (file-exists-p file) - (pushnew file files :test #'equal)) + (let ((file (org-extract-archive-file))) + (when (and (org-string-nw-p file) (file-exists-p file)) + (push file files))) files)) (defun org-extract-archive-file (&optional location) @@ -195,15 +204,19 @@ if LOCATION is not given, the value of `org-archive-location' is used." ;;;###autoload (defun org-archive-subtree (&optional find-done) "Move the current subtree to the archive. -The archive can be a certain top-level heading in the current file, or in -a different file. The tree will be moved to that location, the subtree -heading be marked DONE, and the current time will be added. - -When called with prefix argument FIND-DONE, find whole trees without any -open TODO items and archive them (after getting confirmation from the user). -If the cursor is not at a headline when this command is called, try all level -1 trees. If the cursor is on a headline, only try the direct children of -this heading." +The archive can be a certain top-level heading in the current +file, or in a different file. The tree will be moved to that +location, the subtree heading be marked DONE, and the current +time will be added. + +When called with a single prefix argument FIND-DONE, find whole +trees without any open TODO items and archive them (after getting +confirmation from the user). When called with a double prefix +argument, find whole trees with timestamps before today and +archive them (after getting confirmation from the user). If the +cursor is not at a headline when these commands are called, try +all level 1 trees. If the cursor is on a headline, only try the +direct children of this heading." (interactive "P") (if (and (org-region-active-p) org-loop-over-headlines-in-active-region) (let ((cl (if (eq org-loop-over-headlines-in-active-region 'start-level) @@ -213,46 +226,36 @@ this heading." `(progn (setq org-map-continue-from (progn (org-back-to-heading) (point))) (org-archive-subtree ,find-done)) org-loop-over-headlines-in-active-region - cl (if (outline-invisible-p) (org-end-of-subtree nil t)))) - (if find-done - (org-archive-all-done) + cl (if (org-invisible-p) (org-end-of-subtree nil t)))) + (cond + ((equal find-done '(4)) (org-archive-all-done)) + ((equal find-done '(16)) (org-archive-all-old)) + (t ;; Save all relevant TODO keyword-relatex variables - (let ((tr-org-todo-line-regexp org-todo-line-regexp) ; keep despite compiler - (tr-org-todo-keywords-1 org-todo-keywords-1) - (tr-org-todo-kwd-alist org-todo-kwd-alist) - (tr-org-done-keywords org-done-keywords) - (tr-org-todo-regexp org-todo-regexp) - (tr-org-todo-line-regexp org-todo-line-regexp) - (tr-org-odd-levels-only org-odd-levels-only) - (this-buffer (current-buffer)) - ;; start of variables that will be used for saving context - ;; The compiler complains about them - keep them anyway! - (file (abbreviate-file-name - (or (buffer-file-name (buffer-base-buffer)) - (error "No file associated to buffer")))) - (olpath (mapconcat 'identity (org-get-outline-path) "/")) - (time (format-time-string - (substring (cdr org-time-stamp-formats) 1 -1))) - category todo priority ltags itags atags - ;; end of variables that will be used for saving context - location afile heading buffer level newfile-p infile-p visiting - datetree-date datetree-subheading-p) - - ;; Find the local archive location - (setq location (org-get-local-archive-location) - afile (org-extract-archive-file location) - heading (org-extract-archive-heading location) - infile-p (equal file (abbreviate-file-name (or afile "")))) - (unless afile - (error "Invalid `org-archive-location'")) - - (if (> (length afile) 0) - (setq newfile-p (not (file-exists-p afile)) - visiting (find-buffer-visiting afile) - buffer (or visiting (find-file-noselect afile))) - (setq buffer (current-buffer))) - (unless buffer - (error "Cannot access file \"%s\"" afile)) + (let* ((tr-org-todo-keywords-1 org-todo-keywords-1) + (tr-org-todo-kwd-alist org-todo-kwd-alist) + (tr-org-done-keywords org-done-keywords) + (tr-org-todo-regexp org-todo-regexp) + (tr-org-todo-line-regexp org-todo-line-regexp) + (tr-org-odd-levels-only org-odd-levels-only) + (this-buffer (current-buffer)) + (time (format-time-string + (substring (cdr org-time-stamp-formats) 1 -1))) + (file (abbreviate-file-name + (or (buffer-file-name (buffer-base-buffer)) + (error "No file associated to buffer")))) + (location (org-get-local-archive-location)) + (afile (or (org-extract-archive-file location) + (error "Invalid `org-archive-location'"))) + (heading (org-extract-archive-heading location)) + (infile-p (equal file (abbreviate-file-name (or afile "")))) + (newfile-p (and (org-string-nw-p afile) + (not (file-exists-p afile)))) + (buffer (cond ((not (org-string-nw-p afile)) this-buffer) + ((find-buffer-visiting afile)) + ((find-file-noselect afile)) + (t (error "Cannot access file \"%s\"" afile)))) + level datetree-date datetree-subheading-p) (when (string-match "\\`datetree/" heading) ;; Replace with ***, to represent the 3 levels of headings the ;; datetree has. @@ -266,108 +269,120 @@ this heading." (setq heading nil level 0)) (save-excursion (org-back-to-heading t) - ;; Get context information that will be lost by moving the tree - (setq category (org-get-category nil 'force-refresh) - todo (and (looking-at org-todo-line-regexp) - (match-string 2)) - priority (org-get-priority - (if (match-end 3) (match-string 3) "")) - ltags (org-get-tags) - itags (org-delete-all ltags (org-get-tags-at)) - atags (org-get-tags-at)) - (setq ltags (mapconcat 'identity ltags " ") - itags (mapconcat 'identity itags " ")) - ;; We first only copy, in case something goes wrong - ;; we need to protect `this-command', to avoid kill-region sets it, - ;; which would lead to duplication of subtrees - (let (this-command) (org-copy-subtree 1 nil t)) - (set-buffer buffer) - ;; Enforce org-mode for the archive buffer - (if (not (derived-mode-p 'org-mode)) - ;; Force the mode for future visits. - (let ((org-insert-mode-line-in-empty-file t) - (org-inhibit-startup t)) - (call-interactively 'org-mode))) - (when (and newfile-p org-archive-file-header-format) - (goto-char (point-max)) - (insert (format org-archive-file-header-format - (buffer-file-name this-buffer)))) - (when datetree-date - (require 'org-datetree) - (org-datetree-find-date-create datetree-date) - (org-narrow-to-subtree)) - ;; Force the TODO keywords of the original buffer - (let ((org-todo-line-regexp tr-org-todo-line-regexp) - (org-todo-keywords-1 tr-org-todo-keywords-1) - (org-todo-kwd-alist tr-org-todo-kwd-alist) - (org-done-keywords tr-org-done-keywords) - (org-todo-regexp tr-org-todo-regexp) - (org-todo-line-regexp tr-org-todo-line-regexp) - (org-odd-levels-only - (if (local-variable-p 'org-odd-levels-only (current-buffer)) - org-odd-levels-only - tr-org-odd-levels-only))) - (goto-char (point-min)) - (show-all) - (if (and heading (not (and datetree-date (not datetree-subheading-p)))) - (progn - (if (re-search-forward - (concat "^" (regexp-quote heading) - (org-re "[ \t]*\\(:[[:alnum:]_@#%:]+:\\)?[ \t]*\\($\\|\r\\)")) - nil t) - (goto-char (match-end 0)) - ;; Heading not found, just insert it at the end - (goto-char (point-max)) - (or (bolp) (insert "\n")) - ;; datetrees don't need too much spacing - (insert (if datetree-date "" "\n") heading "\n") - (end-of-line 0)) - ;; Make the subtree visible - (show-subtree) - (if org-archive-reversed-order - (progn - (org-back-to-heading t) - (outline-next-heading)) - (org-end-of-subtree t)) - (skip-chars-backward " \t\r\n") - (and (looking-at "[ \t\r\n]*") - ;; datetree archives don't need so much spacing. - (replace-match (if datetree-date "\n" "\n\n")))) - ;; No specific heading, just go to end of file. - (goto-char (point-max)) (unless datetree-date (insert "\n"))) - ;; Paste - (org-paste-subtree (org-get-valid-level level (and heading 1))) - ;; Shall we append inherited tags? - (and itags - (or (and (eq org-archive-subtree-add-inherited-tags 'infile) - infile-p) - (eq org-archive-subtree-add-inherited-tags t)) - (org-set-tags-to atags)) - ;; Mark the entry as done - (when (and org-archive-mark-done - (looking-at org-todo-line-regexp) - (or (not (match-end 2)) - (not (member (match-string 2) org-done-keywords)))) - (let (org-log-done org-todo-log-states) - (org-todo - (car (or (member org-archive-mark-done org-done-keywords) - org-done-keywords))))) - - ;; Add the context info - (when org-archive-save-context-info - (let ((l org-archive-save-context-info) e n v) - (while (setq e (pop l)) - (when (and (setq v (symbol-value e)) - (stringp v) (string-match "\\S-" v)) - (setq n (concat "ARCHIVE_" (upcase (symbol-name e)))) - (org-entry-put (point) n v))))) - - (widen) - ;; Save and kill the buffer, if it is not the same buffer. - (when (not (eq this-buffer buffer)) - (save-buffer)))) - ;; Here we are back in the original buffer. Everything seems to have - ;; worked. So now cut the tree and finish up. + ;; Get context information that will be lost by moving the + ;; tree. See `org-archive-save-context-info'. + (let* ((all-tags (org-get-tags-at)) + (local-tags (org-get-tags)) + (inherited-tags (org-delete-all local-tags all-tags)) + (context + `((category . ,(org-get-category nil 'force-refresh)) + (file . ,file) + (itags . ,(mapconcat #'identity inherited-tags " ")) + (ltags . ,(mapconcat #'identity local-tags " ")) + (olpath . ,(mapconcat #'identity + (org-get-outline-path) + "/")) + (time . ,time) + (todo . ,(org-entry-get (point) "TODO"))))) + ;; We first only copy, in case something goes wrong + ;; we need to protect `this-command', to avoid kill-region sets it, + ;; which would lead to duplication of subtrees + (let (this-command) (org-copy-subtree 1 nil t)) + (set-buffer buffer) + ;; Enforce Org mode for the archive buffer + (if (not (derived-mode-p 'org-mode)) + ;; Force the mode for future visits. + (let ((org-insert-mode-line-in-empty-file t) + (org-inhibit-startup t)) + (call-interactively 'org-mode))) + (when (and newfile-p org-archive-file-header-format) + (goto-char (point-max)) + (insert (format org-archive-file-header-format + (buffer-file-name this-buffer)))) + (when datetree-date + (require 'org-datetree) + (org-datetree-find-date-create datetree-date) + (org-narrow-to-subtree)) + ;; Force the TODO keywords of the original buffer + (let ((org-todo-line-regexp tr-org-todo-line-regexp) + (org-todo-keywords-1 tr-org-todo-keywords-1) + (org-todo-kwd-alist tr-org-todo-kwd-alist) + (org-done-keywords tr-org-done-keywords) + (org-todo-regexp tr-org-todo-regexp) + (org-todo-line-regexp tr-org-todo-line-regexp) + (org-odd-levels-only + (if (local-variable-p 'org-odd-levels-only (current-buffer)) + org-odd-levels-only + tr-org-odd-levels-only))) + (goto-char (point-min)) + (outline-show-all) + (if (and heading (not (and datetree-date (not datetree-subheading-p)))) + (progn + (if (re-search-forward + (concat "^" (regexp-quote heading) + "[ \t]*\\(:[[:alnum:]_@#%:]+:\\)?[ \t]*\\($\\|\r\\)") + nil t) + (goto-char (match-end 0)) + ;; Heading not found, just insert it at the end + (goto-char (point-max)) + (or (bolp) (insert "\n")) + ;; datetrees don't need too much spacing + (insert (if datetree-date "" "\n") heading "\n") + (end-of-line 0)) + ;; Make the subtree visible + (outline-show-subtree) + (if org-archive-reversed-order + (progn + (org-back-to-heading t) + (outline-next-heading)) + (org-end-of-subtree t)) + (skip-chars-backward " \t\r\n") + (and (looking-at "[ \t\r\n]*") + ;; datetree archives don't need so much spacing. + (replace-match (if datetree-date "\n" "\n\n")))) + ;; No specific heading, just go to end of file. + (goto-char (point-max)) + ;; Subtree narrowing can let the buffer end on + ;; a headline. `org-paste-subtree' then deletes it. + ;; To prevent this, make sure visible part of buffer + ;; always terminates on a new line, while limiting + ;; number of blank lines in a date tree. + (unless (and datetree-date (bolp)) (insert "\n"))) + ;; Paste + (org-paste-subtree (org-get-valid-level level (and heading 1))) + ;; Shall we append inherited tags? + (and inherited-tags + (or (and (eq org-archive-subtree-add-inherited-tags 'infile) + infile-p) + (eq org-archive-subtree-add-inherited-tags t)) + (org-set-tags-to all-tags)) + ;; Mark the entry as done + (when (and org-archive-mark-done + (let ((case-fold-search nil)) + (looking-at org-todo-line-regexp)) + (or (not (match-end 2)) + (not (member (match-string 2) org-done-keywords)))) + (let (org-log-done org-todo-log-states) + (org-todo + (car (or (member org-archive-mark-done org-done-keywords) + org-done-keywords))))) + + ;; Add the context info. + (dolist (item org-archive-save-context-info) + (let ((value (cdr (assq item context)))) + (when (org-string-nw-p value) + (org-entry-put + (point) + (concat "ARCHIVE_" (upcase (symbol-name item))) + value)))) + (widen) + ;; Save and kill the buffer, if it is not the same + ;; buffer. + (unless (eq this-buffer buffer) (save-buffer))))) + ;; Here we are back in the original buffer. Everything seems + ;; to have worked. So now run hooks, cut the tree and finish + ;; up. + (run-hooks 'org-archive-hook) (let (this-command) (org-cut-subtree)) (when (featurep 'org-inlinetask) (org-inlinetask-remove-END-maybe)) @@ -375,7 +390,7 @@ this heading." (message "Subtree archived %s" (if (eq this-buffer buffer) (concat "under heading: " heading) - (concat "in file: " (abbreviate-file-name afile)))))) + (concat "in file: " (abbreviate-file-name afile))))))) (org-reveal) (if (looking-at "^[ \t]*$") (outline-next-visible-heading 1)))) @@ -383,9 +398,12 @@ this heading." ;;;###autoload (defun org-archive-to-archive-sibling () "Archive the current heading by moving it under the archive sibling. + The archive sibling is a sibling of the heading with the heading name `org-archive-sibling-heading' and an `org-archive-tag' tag. If this -sibling does not exist, it will be created at the end of the subtree." +sibling does not exist, it will be created at the end of the subtree. + +Archiving time is retained in the ARCHIVE_TIME node property." (interactive) (if (and (org-region-active-p) org-loop-over-headlines-in-active-region) (let ((cl (when (eq org-loop-over-headlines-in-active-region 'start-level) @@ -400,7 +418,7 @@ sibling does not exist, it will be created at the end of the subtree." (when (org-at-heading-p) (org-archive-to-archive-sibling))) org-loop-over-headlines-in-active-region - cl (if (outline-invisible-p) (org-end-of-subtree nil t)))) + cl (if (org-invisible-p) (org-end-of-subtree nil t)))) (save-restriction (widen) (let (b e pos leader level) @@ -443,7 +461,7 @@ sibling does not exist, it will be created at the end of the subtree." (format-time-string (substring (cdr org-time-stamp-formats) 1 -1))) (outline-up-heading 1 t) - (hide-subtree) + (outline-hide-subtree) (org-cycle-show-empty-lines 'folded) (goto-char pos))) (org-reveal) @@ -455,13 +473,51 @@ sibling does not exist, it will be created at the end of the subtree." If the cursor is not on a headline, try all level 1 trees. If it is on a headline, try all direct children. When TAG is non-nil, don't move trees, but mark them with the ARCHIVE tag." - (let ((re org-not-done-heading-regexp) re1 - (rea (concat ".*:" org-archive-tag ":")) + (org-archive-all-matches + (lambda (_beg end) + (let ((case-fold-search nil)) + (unless (re-search-forward org-not-done-heading-regexp end t) + "no open TODO items"))) + tag)) + +(defun org-archive-all-old (&optional tag) + "Archive sublevels of the current tree with timestamps prior to today. +If the cursor is not on a headline, try all level 1 trees. If +it is on a headline, try all direct children. +When TAG is non-nil, don't move trees, but mark them with the ARCHIVE tag." + (org-archive-all-matches + (lambda (_beg end) + (let (ts) + (and (re-search-forward org-ts-regexp end t) + (setq ts (match-string 0)) + (< (org-time-stamp-to-now ts) 0) + (if (not (looking-at + (concat "--\\(" org-ts-regexp "\\)"))) + (concat "old timestamp " ts) + (setq ts (concat "old timestamp " ts (match-string 0))) + (and (< (org-time-stamp-to-now (match-string 1)) 0) + ts))))) + tag)) + +(defun org-archive-all-matches (predicate &optional tag) + "Archive sublevels of the current tree that match PREDICATE. + +PREDICATE is a function of two arguments, BEG and END, which +specify the beginning and end of the headline being considered. +It is called with point positioned at BEG. The headline will be +archived if PREDICATE returns non-nil. If the return value of +PREDICATE is a string, it should describe the reason for +archiving the heading. + +If the cursor is not on a headline, try all level 1 trees. If it +is on a headline, try all direct children. When TAG is non-nil, +don't move trees, but mark them with the ARCHIVE tag." + (let ((rea (concat ".*:" org-archive-tag ":")) re1 (begm (make-marker)) (endm (make-marker)) - (question (if tag "Set ARCHIVE tag (no open TODO items)? " - "Move subtree to archive (no open TODO items)? ")) - beg end (cntarch 0)) + (question (if tag "Set ARCHIVE tag? " + "Move subtree to archive? ")) + reason beg end (cntarch 0)) (if (org-at-heading-p) (progn (setq re1 (concat "^" (regexp-quote @@ -481,11 +537,14 @@ When TAG is non-nil, don't move trees, but mark them with the ARCHIVE tag." (setq beg (match-beginning 0) end (save-excursion (org-end-of-subtree t) (point))) (goto-char beg) - (if (re-search-forward re end t) + (if (not (setq reason (funcall predicate beg end))) (goto-char end) (goto-char beg) (if (and (or (not tag) (not (looking-at rea))) - (y-or-n-p question)) + (y-or-n-p + (if (stringp reason) + (concat question "(" reason ")") + question))) (progn (if tag (org-toggle-tag org-archive-tag 'on) @@ -507,14 +566,14 @@ the children that do not contain any open TODO items." (org-map-entries `(org-toggle-archive-tag ,find-done) org-loop-over-headlines-in-active-region - cl (if (outline-invisible-p) (org-end-of-subtree nil t)))) + cl (if (org-invisible-p) (org-end-of-subtree nil t)))) (if find-done (org-archive-all-done 'tag) (let (set) (save-excursion (org-back-to-heading t) (setq set (org-toggle-tag org-archive-tag)) - (when set (hide-subtree))) + (when set (org-flag-subtree t))) (and set (beginning-of-line 1)) (message "Subtree %s" (if set "archived" "unarchived")))))) @@ -528,7 +587,7 @@ the children that do not contain any open TODO items." (org-map-entries 'org-archive-set-tag org-loop-over-headlines-in-active-region - cl (if (outline-invisible-p) (org-end-of-subtree nil t)))) + cl (if (org-invisible-p) (org-end-of-subtree nil t)))) (org-toggle-tag org-archive-tag 'on))) ;;;###autoload diff --git a/lisp/org/org-attach.el b/lisp/org/org-attach.el index 7d25437d9f..a026eee4f1 100644 --- a/lisp/org/org-attach.el +++ b/lisp/org/org-attach.el @@ -1,4 +1,4 @@ -;;; org-attach.el --- Manage file attachments to org-mode tasks +;;; org-attach.el --- Manage file attachments to Org tasks -*- lexical-binding: t; -*- ;; Copyright (C) 2008-2017 Free Software Foundation, Inc. @@ -22,7 +22,7 @@ ;;; Commentary: -;; See the Org-mode manual for information on how to use it. +;; See the Org manual for information on how to use it. ;; ;; Attachments are managed in a special directory called "data", which ;; lives in the same directory as the org file itself. If this data @@ -37,14 +37,13 @@ ;;; Code: -(eval-when-compile - (require 'cl)) -(require 'org-id) +(require 'cl-lib) (require 'org) +(require 'org-id) (require 'vc-git) (defgroup org-attach nil - "Options concerning entry attachments in Org-mode." + "Options concerning entry attachments in Org mode." :tag "Org Attach" :group 'org) @@ -55,6 +54,14 @@ where the Org file lives." :group 'org-attach :type 'directory) +(defcustom org-attach-commit t + "If non-nil commit attachments with git. +This is only done if the Org file is in a git repository." + :group 'org-attach + :type 'boolean + :version "26.1" + :package-version '(Org . "9.0")) + (defcustom org-attach-git-annex-cutoff (* 32 1024) "If non-nil, files larger than this will be annexed instead of stored." :group 'org-attach @@ -120,6 +127,28 @@ lns create a symbol link. Note that this is not supported (const :tag "Link to origin location" t) (const :tag "Link to the attach-dir location" attached))) +(defcustom org-attach-archive-delete nil + "Non-nil means attachments are deleted upon archiving a subtree. +When set to `query', ask the user instead." + :group 'org-attach + :version "26.1" + :package-version '(Org . "8.3") + :type '(choice + (const :tag "Never delete attachments" nil) + (const :tag "Always delete attachments" t) + (const :tag "Query the user" query))) + +(defcustom org-attach-annex-auto-get 'ask + "Confirmation preference for automatically getting annex files. +If \\='ask, prompt using `y-or-n-p'. If t, always get. If nil, never get." + :group 'org-attach + :package-version '(Org . "9") + :version "26.1" + :type '(choice + (const :tag "confirm with `y-or-n-p'" ask) + (const :tag "always get from annex if necessary" t) + (const :tag "never get from annex" nil))) + ;;;###autoload (defun org-attach () "The dispatcher for attachment commands. @@ -197,25 +226,23 @@ using the entry ID will be invoked to access the unique directory for the current entry. If the directory does not exist and CREATE-IF-NOT-EXISTS-P is non-nil, the directory and (if necessary) the corresponding ID will be created." - (let (attach-dir uuid inherit) + (let (attach-dir uuid) (setq org-attach-inherited (org-entry-get nil "ATTACH_DIR_INHERIT")) (cond ((setq attach-dir (org-entry-get nil "ATTACH_DIR")) (org-attach-check-absolute-path attach-dir)) ((and org-attach-allow-inheritance - (setq inherit (org-entry-get nil "ATTACH_DIR_INHERIT" t))) + (org-entry-get nil "ATTACH_DIR_INHERIT" t)) (setq attach-dir - (save-excursion - (save-restriction - (widen) - (if (marker-position org-entry-property-inherited-from) - (goto-char org-entry-property-inherited-from) - (org-back-to-heading t)) - (let (org-attach-allow-inheritance) - (org-attach-dir create-if-not-exists-p))))) + (org-with-wide-buffer + (if (marker-position org-entry-property-inherited-from) + (goto-char org-entry-property-inherited-from) + (org-back-to-heading t)) + (let (org-attach-allow-inheritance) + (org-attach-dir create-if-not-exists-p)))) (org-attach-check-absolute-path attach-dir) (setq org-attach-inherited t)) - (t ; use the ID + (t ; use the ID (org-attach-check-absolute-path nil) (setq uuid (org-id-get (point) create-if-not-exists-p)) (when (or uuid create-if-not-exists-p) @@ -261,33 +288,59 @@ the ATTACH_DIR property) their own attachment directory." (org-entry-put nil "ATTACH_DIR_INHERIT" "t") (message "Children will inherit attachment directory")) +(defun org-attach-use-annex () + "Return non-nil if git annex can be used." + (let ((git-dir (vc-git-root (expand-file-name org-attach-directory)))) + (and org-attach-git-annex-cutoff + (or (file-exists-p (expand-file-name "annex" git-dir)) + (file-exists-p (expand-file-name ".git/annex" git-dir)))))) + +(defun org-attach-annex-get-maybe (path) + "Call git annex get PATH (via shell) if using git annex. +Signals an error if the file content is not available and it was not retrieved." + (let ((path-relative (file-relative-name path))) + (when (and (org-attach-use-annex) + (not + (string-equal + "found" + (shell-command-to-string + (format "git annex find --format=found --in=here %s" + (shell-quote-argument path-relative)))))) + (let ((should-get + (if (eq org-attach-annex-auto-get 'ask) + (y-or-n-p (format "Run git annex get %s? " path-relative)) + org-attach-annex-auto-get))) + (if should-get + (progn (message "Running git annex get \"%s\"." path-relative) + (call-process "git" nil nil nil "annex" "get" path-relative)) + (error "File %s stored in git annex but it is not available, and was not retrieved" + path)))))) + (defun org-attach-commit () "Commit changes to git if `org-attach-directory' is properly initialized. This checks for the existence of a \".git\" directory in that directory." (let* ((dir (expand-file-name org-attach-directory)) (git-dir (vc-git-root dir)) + (use-annex (org-attach-use-annex)) (changes 0)) (when (and git-dir (executable-find "git")) (with-temp-buffer (cd dir) - (let ((have-annex - (and org-attach-git-annex-cutoff - (file-exists-p (expand-file-name "annex" git-dir))))) - (dolist (new-or-modified - (split-string - (shell-command-to-string - "git ls-files -zmo --exclude-standard") "\0" t)) - (if (and have-annex - (>= (nth 7 (file-attributes new-or-modified)) - org-attach-git-annex-cutoff)) - (call-process "git" nil nil nil "annex" "add" new-or-modified) - (call-process "git" nil nil nil "add" new-or-modified)) - (incf changes))) + (dolist (new-or-modified + (split-string + (shell-command-to-string + "git ls-files -zmo --exclude-standard") "\0" t)) + (if (and use-annex + (>= (nth 7 (file-attributes new-or-modified)) + org-attach-git-annex-cutoff)) + (call-process "git" nil nil nil "annex" "add" new-or-modified) + (call-process "git" nil nil nil "add" new-or-modified)) + (cl-incf changes)) (dolist (deleted (split-string (shell-command-to-string "git ls-files -z --deleted") "\0" t)) (call-process "git" nil nil nil "rm" deleted) - (incf changes)) + (cl-incf changes)) (when (> changes 0) (shell-command "git commit -m 'Synchronized attachments'")))))) @@ -328,7 +381,8 @@ METHOD may be `cp', `mv', `ln', or `lns' default taken from ((eq method 'cp) (copy-file file fname)) ((eq method 'ln) (add-name-to-file file fname)) ((eq method 'lns) (make-symbolic-link file fname))) - (org-attach-commit) + (when org-attach-commit + (org-attach-commit)) (org-attach-tag) (cond ((eq org-attach-store-link-p 'attached) (org-attach-store-link fname)) @@ -378,7 +432,7 @@ The attachment is created as an Emacs buffer." (let* ((attach-dir (org-attach-dir t)) (files (org-attach-file-list attach-dir)) (file (or file - (org-icompleting-read + (completing-read "Delete attachment: " (mapcar (lambda (f) (list (file-name-nondirectory f))) @@ -387,7 +441,8 @@ The attachment is created as an Emacs buffer." (unless (file-exists-p file) (error "No such attachment: %s" file)) (delete-file file) - (org-attach-commit))) + (when org-attach-commit + (org-attach-commit)))) (defun org-attach-delete-all (&optional force) "Delete all attachments from the current task. @@ -403,14 +458,16 @@ A safer way is to open the directory in dired and delete from there." (y-or-n-p "Are you sure you want to remove all attachments of this entry? "))) (shell-command (format "rm -fr %s" attach-dir)) (message "Attachment directory removed") - (org-attach-commit) + (when org-attach-commit + (org-attach-commit)) (org-attach-untag)))) (defun org-attach-sync () "Synchronize the current tasks with its attachments. This can be used after files have been added externally." (interactive) - (org-attach-commit) + (when org-attach-commit + (org-attach-commit)) (when (and org-attach-file-list-property (not org-attach-inherited)) (org-entry-delete (point) org-attach-file-list-property)) (let ((attach-dir (org-attach-dir))) @@ -419,15 +476,15 @@ This can be used after files have been added externally." (and files (org-attach-tag)) (when org-attach-file-list-property (dolist (file files) - (unless (string-match "^\\." file) + (unless (string-match "^\\.\\.?\\'" file) (org-entry-add-to-multivalued-property (point) org-attach-file-list-property file)))))))) (defun org-attach-file-list (dir) "Return a list of files in the attachment directory. -This ignores files starting with a \".\", and files ending in \"~\"." +This ignores files ending in \"~\"." (delq nil - (mapcar (lambda (x) (if (string-match "^\\." x) nil x)) + (mapcar (lambda (x) (if (string-match "^\\.\\.?\\'" x) nil x)) (directory-files dir nil "[^~]\\'")))) (defun org-attach-reveal (&optional if-exists) @@ -454,9 +511,11 @@ If IN-EMACS is non-nil, force opening in Emacs." (files (org-attach-file-list attach-dir)) (file (if (= (length files) 1) (car files) - (org-icompleting-read "Open attachment: " - (mapcar 'list files) nil t)))) - (org-open-file (expand-file-name file attach-dir) in-emacs))) + (completing-read "Open attachment: " + (mapcar #'list files) nil t))) + (path (expand-file-name file attach-dir))) + (org-attach-annex-get-maybe path) + (org-open-file path in-emacs))) (defun org-attach-open-in-emacs () "Open attachment, force opening in Emacs. @@ -475,6 +534,17 @@ Basically, this adds the path to the attachment directory, and a \"file:\" prefix." (concat "file:" (org-attach-expand file))) +(defun org-attach-archive-delete-maybe () + "Maybe delete subtree attachments when archiving. +This function is called by `org-archive-hook'. The option +`org-attach-archive-delete' controls its behavior." + (when (if (eq org-attach-archive-delete 'query) + (yes-or-no-p "Delete all attachments? ") + org-attach-archive-delete) + (org-attach-delete-all t))) + +(add-hook 'org-archive-hook 'org-attach-archive-delete-maybe) + (provide 'org-attach) ;; Local variables: diff --git a/lisp/org/org-bbdb.el b/lisp/org/org-bbdb.el index e41bda47db..f851668157 100644 --- a/lisp/org/org-bbdb.el +++ b/lisp/org/org-bbdb.el @@ -1,4 +1,4 @@ -;;; org-bbdb.el --- Support for links to BBDB entries from within Org-mode +;;; org-bbdb.el --- Support for links to BBDB entries -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -25,12 +25,12 @@ ;; ;;; Commentary: -;; This file implements links to BBDB database entries from within Org-mode. -;; Org-mode loads this module by default - if this is not what you want, +;; This file implements links to BBDB database entries from within Org. +;; Org mode loads this module by default - if this is not what you want, ;; configure the variable `org-modules'. ;; It also implements an interface (based on Ivar Rummelhoff's -;; bbdb-anniv.el) for those org-mode users, who do not use the diary +;; bbdb-anniv.el) for those Org users, who do not use the diary ;; but who do want to include the anniversaries stored in the BBDB ;; into the org-agenda. If you already include the `diary' into the ;; agenda, you might want to prefer to include the anniversaries in @@ -94,8 +94,7 @@ ;;; Code: (require 'org) -(eval-when-compile - (require 'cl)) +(require 'cl-lib) ;; Declare external functions and variables @@ -106,6 +105,7 @@ (declare-function bbdb-name "ext:bbdb-com" (string elidep)) (declare-function bbdb-completing-read-record "ext:bbdb-com" (prompt &optional omit-records)) +(declare-function bbdb-record-field "ext:bbdb" (recond field)) (declare-function bbdb-record-getprop "ext:bbdb" (record property)) (declare-function bbdb-record-name "ext:bbdb" (record)) (declare-function bbdb-records "ext:bbdb" @@ -124,7 +124,7 @@ (declare-function calendar-leap-year-p "calendar" (year)) (declare-function diary-ordinal-suffix "diary-lib" (n)) -(org-no-warnings (defvar date)) ;; unprefixed, from calendar.el +(with-no-warnings (defvar date)) ;; unprefixed, from calendar.el ;; Customization @@ -194,10 +194,12 @@ date year)." :group 'org-bbdb-anniversaries :require 'bbdb) - ;; Install the link type -(org-add-link-type "bbdb" 'org-bbdb-open 'org-bbdb-export) -(add-hook 'org-store-link-functions 'org-bbdb-store-link) +(org-link-set-parameters "bbdb" + :follow #'org-bbdb-open + :export #'org-bbdb-export + :complete #'org-bbdb-complete-link + :store #'org-bbdb-store-link) ;; Implementation (defun org-bbdb-store-link () @@ -208,7 +210,7 @@ date year)." (name (bbdb-record-name rec)) (company (if (fboundp 'bbdb-record-getprop) (bbdb-record-getprop rec 'company) - (car (bbdb-record-get-field rec 'organization)))) + (car (bbdb-record-field rec 'organization)))) (link (concat "bbdb:" name))) (org-store-link-props :type "bbdb" :name name :company company :link link :description name) @@ -230,10 +232,9 @@ italicized, in all other cases it is left unchanged." (defun org-bbdb-open (name) "Follow a BBDB link to NAME." (require 'bbdb-com) - (let ((inhibit-redisplay (not debug-on-error)) - (bbdb-electric-p nil)) + (let ((inhibit-redisplay (not debug-on-error))) (if (fboundp 'bbdb-name) - (org-bbdb-open-old name) + (org-bbdb-open-old name) (org-bbdb-open-new name)))) (defun org-bbdb-open-old (name) @@ -280,14 +281,11 @@ italicized, in all other cases it is left unchanged." "Convert YYYY-MM-DD to (month date year). Argument TIME-STR is the value retrieved from BBDB. If YYYY- is omitted it will be considered unknown." - (multiple-value-bind (a b c) (values-list (org-split-string time-str "-")) - (if (eq c nil) - (list (string-to-number a) - (string-to-number b) - nil) - (list (string-to-number b) - (string-to-number c) - (string-to-number a))))) + (pcase (org-split-string time-str "-") + (`(,a ,b nil) (list (string-to-number a) (string-to-number b) nil)) + (`(,a ,b ,c) (list (string-to-number b) + (string-to-number c) + (string-to-number a))))) (defun org-bbdb-anniv-split (str) "Split multiple entries in the BBDB anniversary field. @@ -325,9 +323,9 @@ The anniversaries are assumed to be stored `org-bbdb-anniversary-field'." (bbdb-split "\n" annivs))) (while annivs (setq split (org-bbdb-anniv-split (pop annivs))) - (multiple-value-bind (m d y) - (values-list (funcall org-bbdb-extract-date-fun (car split))) - (setq tmp (gethash (list m d) org-bbdb-anniv-hash)) + (pcase-let ((`(,m ,d ,y) (funcall org-bbdb-extract-date-fun + (car split)))) + (setq tmp (gethash (list m d) org-bbdb-anniv-hash)) (puthash (list m d) (cons (list y (bbdb-record-name rec) (cadr split)) @@ -335,7 +333,7 @@ The anniversaries are assumed to be stored `org-bbdb-anniversary-field'." org-bbdb-anniv-hash)))))) (setq org-bbdb-updated-p nil)) -(defun org-bbdb-updated (rec) +(defun org-bbdb-updated (_rec) "Record the fact that BBDB has been updated. This is used by Org to re-create the anniversary hash table." (setq org-bbdb-updated-p t)) @@ -397,6 +395,66 @@ This is used by Org to re-create the anniversary hash table." )) text)) +;;; Return list of anniversaries for today and the next n-1 (default: n=7) days. +;;; This is meant to be used in an org file instead of org-bbdb-anniversaries: +;;; +;;; %%(org-bbdb-anniversaries-future) +;;; +;;; or +;;; +;;; %%(org-bbdb-anniversaries-future 3) +;;; +;;; to override the 7-day default. + +(defun org-bbdb-date-list (d n) + "Return a list of dates in (m d y) format from the given date D to n-1 days hence." + (let ((abs (calendar-absolute-from-gregorian d))) + (mapcar (lambda (i) (calendar-gregorian-from-absolute (+ abs i))) + (number-sequence 0 (1- n))))) + +;;;###autoload +(defun org-bbdb-anniversaries-future (&optional n) + "Return list of anniversaries for today and the next n-1 days (default n=7)." + (let ((n (or n 7))) + (when (<= n 0) + (error "The (optional) argument of `org-bbdb-anniversaries-future' \ +must be positive")) + (let ( + ;; List of relevant dates. + (dates (org-bbdb-date-list date n)) + ;; Function to annotate text of each element of l with the + ;; anniversary date d. + (annotate-descriptions + (lambda (d l) + (mapcar (lambda (x) + ;; The assumption here is that x is a bbdb link + ;; of the form [[bbdb:name][description]]. + ;; This function rather arbitrarily modifies + ;; the description by adding the date to it in + ;; a fixed format. + (string-match "]]" x) + (replace-match (format " -- %d-%02d-%02d\\&" + (nth 2 d) + (nth 0 d) + (nth 1 d)) + nil nil x)) + l)))) + ;; Map a function that generates anniversaries for each date + ;; over the dates and nconc the results into a single list. When + ;; it is no longer necessary to support older versions of Emacs, + ;; this can be done with a cl-mapcan; for now, we use the (apply + ;; #'nconc ...) method for compatibility. + (apply #'nconc + (mapcar + (lambda (d) + (let ((date d)) + ;; Rebind 'date' so that org-bbdb-anniversaries will + ;; be fooled into giving us the list for the given + ;; date and then annotate the descriptions for that + ;; date. + (funcall annotate-descriptions d (org-bbdb-anniversaries)))) + dates))))) + (defun org-bbdb-complete-link () "Read a bbdb link with name completion." (require 'bbdb-com) diff --git a/lisp/org/org-bibtex.el b/lisp/org/org-bibtex.el index f8b376daa1..d81c9f1898 100644 --- a/lisp/org/org-bibtex.el +++ b/lisp/org/org-bibtex.el @@ -1,4 +1,4 @@ -;;; org-bibtex.el --- Org links to BibTeX entries +;;; org-bibtex.el --- Org links to BibTeX entries -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2007-2017 Free Software Foundation, Inc. ;; @@ -73,7 +73,7 @@ ;; ===================================================================== ;; ;; Additionally, the following functions are now available for storing -;; bibtex entries within Org-mode documents. +;; bibtex entries within Org documents. ;; ;; - Run `org-bibtex' to export the current file to a .bib. ;; @@ -92,27 +92,28 @@ ;; ;;; History: ;; -;; The link creation part has been part of Org-mode for a long time. +;; The link creation part has been part of Org for a long time. ;; ;; Creating better capture template information was inspired by a request ;; of Austin Frank: http://article.gmane.org/gmane.emacs.orgmode/4112 ;; and then implemented by Bastien Guerry. ;; ;; Eric Schulte eventually added the functions for translating between -;; Org-mode headlines and Bibtex entries, and for fleshing out the Bibtex -;; fields of existing Org-mode headlines. +;; Org headlines and Bibtex entries, and for fleshing out the Bibtex +;; fields of existing Org headlines. ;; -;; Org-mode loads this module by default - if this is not what you want, +;; Org mode loads this module by default - if this is not what you want, ;; configure the variable `org-modules'. ;;; Code: (require 'org) (require 'bibtex) -(eval-when-compile - (require 'cl)) +(require 'cl-lib) (require 'org-compat) +(defvar org-agenda-overriding-header) +(defvar org-agenda-search-view-always-boolean) (defvar org-bibtex-description nil) ; dynamically scoped from org.el (defvar org-id-locations) @@ -120,7 +121,6 @@ (declare-function bibtex-generate-autokey "bibtex" ()) (declare-function bibtex-parse-entry "bibtex" (&optional content)) (declare-function bibtex-url "bibtex" (&optional pos no-browse)) -(declare-function org-babel-trim "ob-core" (string &optional regexp)) ;;; Bibtex data @@ -264,26 +264,39 @@ IDs must be unique." (defcustom org-bibtex-tags-are-keywords nil "Convert the value of the keywords field to tags and vice versa. -If set to t, comma-separated entries in a bibtex entry's keywords -field will be converted to org tags. Note: spaces will be escaped -with underscores, and characters that are not permitted in org + +When non-nil, comma-separated entries in a bibtex entry's keywords +field will be converted to Org tags. Note: spaces will be escaped +with underscores, and characters that are not permitted in Org tags will be removed. -If t, local tags in an org entry will be exported as a -comma-separated string of keywords when exported to bibtex. Tags -defined in `org-bibtex-tags' or `org-bibtex-no-export-tags' will -not be exported." +When non-nil, local tags in an Org entry will be exported as +a comma-separated string of keywords when exported to bibtex. +If `org-bibtex-inherit-tags' is non-nil, inherited tags will also +be exported as keywords. Tags defined in `org-bibtex-tags' or +`org-bibtex-no-export-tags' will not be exported." :group 'org-bibtex :version "24.1" :type 'boolean) (defcustom org-bibtex-no-export-tags nil "List of tag(s) that should not be converted to keywords. -This variable is relevant only if `org-bibtex-tags-are-keywords' is t." +This variable is relevant only if `org-bibtex-tags-are-keywords' +is non-nil." :group 'org-bibtex :version "24.1" :type '(repeat :tag "Tag" (string))) +(defcustom org-bibtex-inherit-tags nil + "Controls whether inherited tags are converted to bibtex keywords. +It is relevant only if `org-bibtex-tags-are-keywords' is non-nil. +Tag inheritence itself is controlled by `org-use-tag-inheritence' +and `org-exclude-tags-from-inheritence'." + :group 'org-bibtex + :version "26.1" + :package-version '(Org . "8.3") + :type 'boolean) + (defcustom org-bibtex-type-property-name "btype" "Property in which to store bibtex entry type (e.g., article)." :group 'org-bibtex @@ -299,7 +312,7 @@ This variable is relevant only if `org-bibtex-tags-are-keywords' is t." (org-entry-get (point) (upcase property)) (org-entry-get (point) (concat org-bibtex-prefix (upcase property))))))) - (when it (org-babel-trim it)))) + (when it (org-trim it)))) (defun org-bibtex-put (property value) (let ((prop (upcase (if (keywordp property) @@ -312,27 +325,27 @@ This variable is relevant only if `org-bibtex-tags-are-keywords' is t." (defun org-bibtex-headline () "Return a bibtex entry of the given headline as a string." - (let* ((val (lambda (key lst) (cdr (assoc key lst)))) - (to (lambda (string) (intern (concat ":" string)))) - (from (lambda (key) (substring (symbol-name key) 1))) - flatten ; silent compiler warning - (flatten (lambda (&rest lsts) - (apply #'append (mapcar - (lambda (e) - (if (listp e) (apply flatten e) (list e))) - lsts)))) - (notes (buffer-string)) - (id (org-bibtex-get org-bibtex-key-property)) - (type (org-bibtex-get org-bibtex-type-property-name)) - (tags (when org-bibtex-tags-are-keywords - (delq nil - (mapcar - (lambda (tag) - (unless (member tag - (append org-bibtex-tags - org-bibtex-no-export-tags)) - tag)) - (org-get-local-tags-at)))))) + (letrec ((val (lambda (key lst) (cdr (assoc key lst)))) + (to (lambda (string) (intern (concat ":" string)))) + (from (lambda (key) (substring (symbol-name key) 1))) + (flatten (lambda (&rest lsts) + (apply #'append (mapcar + (lambda (e) + (if (listp e) (apply flatten e) (list e))) + lsts)))) + (id (org-bibtex-get org-bibtex-key-property)) + (type (org-bibtex-get org-bibtex-type-property-name)) + (tags (when org-bibtex-tags-are-keywords + (delq nil + (mapcar + (lambda (tag) + (unless (member tag + (append org-bibtex-tags + org-bibtex-no-export-tags)) + tag)) + (if org-bibtex-inherit-tags + (org-get-tags-at) + (org-get-local-tags-at))))))) (when type (let ((entry (format "@%s{%s,\n%s\n}\n" type id @@ -358,7 +371,7 @@ This variable is relevant only if `org-bibtex-tags-are-keywords' is t." (mapcar (lambda (field) (let ((value (or (org-bibtex-get (funcall from field)) - (and (equal :title field) + (and (eq :title field) (nth 4 (org-heading-components)))))) (when value (cons (funcall from field) value)))) (funcall flatten @@ -421,13 +434,14 @@ With optional argument OPTIONAL, also prompt for optional fields." (funcall val :required (funcall val type org-bibtex-types))) (when optional (funcall val :optional (funcall val type org-bibtex-types))))) (when (consp field) ; or'd pair of fields e.g., (:editor :author) - (let ((present (first (remove + (let ((present (nth 0 (remove nil (mapcar - (lambda (f) (when (org-bibtex-get (funcall name f)) f)) + (lambda (f) + (when (org-bibtex-get (funcall name f)) f)) field))))) (setf field (or present (funcall keyword - (org-icompleting-read + (completing-read "Field: " (mapcar name field))))))) (let ((name (funcall name field))) (unless (org-bibtex-get name) @@ -439,8 +453,9 @@ With optional argument OPTIONAL, also prompt for optional fields." ;;; Bibtex link functions -(org-add-link-type "bibtex" 'org-bibtex-open) -(add-hook 'org-store-link-functions 'org-bibtex-store-link) +(org-link-set-parameters "bibtex" + :follow #'org-bibtex-open + :store #'org-bibtex-store-link) (defun org-bibtex-open (path) "Visit the bibliography entry on PATH." @@ -533,21 +548,23 @@ With optional argument OPTIONAL, also prompt for optional fields." (add-hook 'org-execute-file-search-functions 'org-execute-file-search-in-bibtex) -;;; Bibtex <-> Org-mode headline translation functions -(defun org-bibtex (&optional filename) +;;; Bibtex <-> Org headline translation functions +(defun org-bibtex (filename) "Export each headline in the current file to a bibtex entry. Headlines are exported using `org-bibtex-headline'." (interactive (list (read-file-name "Bibtex file: " nil nil nil - (file-name-nondirectory - (concat (file-name-sans-extension (buffer-file-name)) ".bib"))))) + (let ((file (buffer-file-name (buffer-base-buffer)))) + (and file + (file-name-nondirectory + (concat (file-name-sans-extension file) ".bib"))))))) (let ((error-point (catch 'bib (let ((bibtex-entries (remove nil (org-map-entries (lambda () - (condition-case foo + (condition-case nil (org-bibtex-headline) (error (throw 'bib (point))))))))) (with-temp-file filename @@ -578,7 +595,7 @@ With prefix argument OPTIONAL also prompt for optional fields." With a prefix arg, query for optional fields as well. If nonew is t, add data to the headline of the entry at point." (interactive "P") - (let* ((type (org-icompleting-read + (let* ((type (completing-read "Type: " (mapcar (lambda (type) (substring (symbol-name (car type)) 1)) org-bibtex-types) @@ -597,7 +614,7 @@ If nonew is t, add data to the headline of the entry at point." (org-bibtex-put org-bibtex-type-property-name (substring (symbol-name type) 1)) (org-bibtex-fleshout type arg) - (mapc (lambda (tag) (org-toggle-tag tag 'on)) org-bibtex-tags))) + (dolist (tag org-bibtex-tags) (org-toggle-tag tag 'on)))) (defun org-bibtex-create-in-current-entry (&optional arg) "Add bibliographical data to the current entry. @@ -611,10 +628,10 @@ This uses `bibtex-parse-entry'." (interactive) (let ((keyword (lambda (str) (intern (concat ":" (downcase str))))) (clean-space (lambda (str) (replace-regexp-in-string - "[[:space:]\n\r]+" " " str))) + "[[:space:]\n\r]+" " " str))) (strip-delim - (lambda (str) ; strip enclosing "..." and {...} - (dolist (pair '((34 . 34) (123 . 125) (123 . 125))) + (lambda (str) ; strip enclosing "..." and {...} + (dolist (pair '((34 . 34) (123 . 125))) (when (and (> (length str) 1) (= (aref str 0) (car pair)) (= (aref str (1- (length str))) (cdr pair))) @@ -622,10 +639,10 @@ This uses `bibtex-parse-entry'." (push (mapcar (lambda (pair) (cons (let ((field (funcall keyword (car pair)))) - (case field + (pcase field (:=type= :type) (:=key= :key) - (otherwise field))) + (_ field))) (funcall clean-space (funcall strip-delim (cdr pair))))) (save-excursion (bibtex-beginning-of-entry) (bibtex-parse-entry))) org-bibtex-entries))) @@ -633,7 +650,7 @@ This uses `bibtex-parse-entry'." (defun org-bibtex-read-buffer (buffer) "Read all bibtex entries in BUFFER and save to `org-bibtex-entries'. Return the number of saved entries." - (interactive "bbuffer: ") + (interactive "bBuffer: ") (let ((start-length (length org-bibtex-entries))) (with-current-buffer buffer (save-excursion @@ -643,12 +660,12 @@ Return the number of saved entries." (org-bibtex-read) (bibtex-beginning-of-entry)))) (let ((added (- (length org-bibtex-entries) start-length))) - (message "parsed %d entries" added) + (message "Parsed %d entries" added) added))) (defun org-bibtex-read-file (file) "Read FILE with `org-bibtex-read-buffer'." - (interactive "ffile: ") + (interactive "fFile: ") (org-bibtex-read-buffer (find-file-noselect file 'nowarn 'rawfile))) (defun org-bibtex-write () @@ -666,25 +683,23 @@ Return the number of saved entries." (org-bibtex-put org-bibtex-type-property-name (downcase (funcall val :type))) (dolist (pair entry) - (case (car pair) + (pcase (car pair) (:title nil) (:type nil) (:key (org-bibtex-put org-bibtex-key-property (cdr pair))) (:keywords (if org-bibtex-tags-are-keywords - (mapc - (lambda (kw) - (funcall - togtag - (replace-regexp-in-string - "[^[:alnum:]_@#%]" "" - (replace-regexp-in-string "[ \t]+" "_" kw)))) - (split-string (cdr pair) ", *")) + (dolist (kw (split-string (cdr pair) ", *")) + (funcall + togtag + (replace-regexp-in-string + "[^[:alnum:]_@#%]" "" + (replace-regexp-in-string "[ \t]+" "_" kw)))) (org-bibtex-put (car pair) (cdr pair)))) - (otherwise (org-bibtex-put (car pair) (cdr pair))))) + (_ (org-bibtex-put (car pair) (cdr pair))))) (mapc togtag org-bibtex-tags))) (defun org-bibtex-yank () - "If kill ring holds a bibtex entry yank it as an Org-mode headline." + "If kill ring holds a bibtex entry yank it as an Org headline." (interactive) (let (entry) (with-temp-buffer (yank 1) (setf entry (org-bibtex-read))) @@ -693,8 +708,8 @@ Return the number of saved entries." (error "Yanked text does not appear to contain a BibTeX entry")))) (defun org-bibtex-import-from-file (file) - "Read bibtex entries from FILE and insert as Org-mode headlines after point." - (interactive "ffile: ") + "Read bibtex entries from FILE and insert as Org headlines after point." + (interactive "fFile: ") (dotimes (_ (org-bibtex-read-file file)) (save-excursion (org-bibtex-write)) (re-search-forward org-property-end-re) diff --git a/lisp/org/org-capture.el b/lisp/org/org-capture.el index b302113f3e..63e23cc118 100644 --- a/lisp/org/org-capture.el +++ b/lisp/org/org-capture.el @@ -1,4 +1,4 @@ -;;; org-capture.el --- Fast note taking in Org-mode +;;; org-capture.el --- Fast note taking in Org -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -47,23 +47,22 @@ ;;; Code: -(eval-when-compile - (require 'cl)) +(require 'cl-lib) (require 'org) +(declare-function org-at-encrypted-entry-p "org-crypt" ()) (declare-function org-datetree-find-date-create "org-datetree" (date &optional keep-restriction)) -(declare-function org-table-get-specials "org-table" ()) -(declare-function org-table-goto-line "org-table" (N)) -(declare-function org-pop-to-buffer-same-window "org-compat" - (&optional buffer-or-name norecord label)) -(declare-function org-at-encrypted-entry-p "org-crypt" ()) -(declare-function org-encrypt-entry "org-crypt" ()) (declare-function org-decrypt-entry "org-crypt" ()) +(declare-function org-encrypt-entry "org-crypt" ()) +(declare-function org-table-analyze "org-table" ()) +(declare-function org-table-goto-line "org-table" (N)) +(defvar org-end-time-was-given) (defvar org-remember-default-headline) (defvar org-remember-templates) (defvar org-table-hlines) +(defvar org-table-current-begin-pos) (defvar dired-buffers) (defvar org-capture-clock-was-started nil @@ -76,6 +75,9 @@ ;; to indicate that the link properties have already been stored (defvar org-capture-link-is-already-stored nil) +(defvar org-capture-is-refiling nil + "Non-nil when capture process is refiling an entry.") + (defgroup org-capture nil "Options concerning capturing new entries." :tag "Org Capture" @@ -103,9 +105,9 @@ description A short string describing the template, will be shown during selection. type The type of entry. Valid types are: - entry an Org-mode node, with a headline. Will be - filed as the child of the target entry or as - a top-level entry. + entry an Org node, with a headline. Will be filed + as the child of the target entry or as a + top-level entry. item a plain list item, will be placed in the first plain list at the target location. @@ -116,21 +118,22 @@ type The type of entry. Valid types are: plain text to be inserted as it is. target Specification of where the captured item should be placed. - In Org-mode files, targets usually define a node. Entries will + In Org files, targets usually define a node. Entries will become children of this node, other types will be added to the table or list in the body of this node. Most target specifications contain a file name. If that file name is the empty string, it defaults to `org-default-notes-file'. A file can also be given as a variable, function, or Emacs Lisp - form. + form. When an absolute path is not specified for a + target, it is taken as relative to `org-directory'. Valid values are: (file \"path/to/file\") Text will be placed at the beginning or end of that file - (id \"id of existing org entry\") + (id \"id of existing Org entry\") File as child of this entry, or in the body of the entry (file+headline \"path/to/file\" \"node headline\") @@ -148,6 +151,12 @@ target Specification of where the captured item should be placed. (file+datetree+prompt \"path/to/file\") Will create a heading in a date tree, prompts for date + (file+weektree \"path/to/file\") + Will create a heading in a week tree for today's date + + (file+weektree+prompt \"path/to/file\") + Will create a heading in a week tree, prompts for date + (file+function \"path/to/file\" function-finding-location) A function to find the right location in the file @@ -155,8 +164,8 @@ target Specification of where the captured item should be placed. File to the entry that is currently being clocked (function function-finding-location) - Most general way, write your own function to find both - file and location + Most general way: write your own function which both visits + the file and moves point to the right location template The template for creating the capture item. If you leave this empty, an appropriate default template will be used. See below @@ -218,15 +227,20 @@ properties are: is finalized. The template defines the text to be inserted. Often this is an -org-mode entry (so the first line should start with a star) that +Org mode entry (so the first line should start with a star) that will be filed as a child of the target headline. It can also be freely formatted text. Furthermore, the following %-escapes will -be replaced with content and expanded in this order: +be replaced with content and expanded: - %[pathname] Insert the contents of the file given by `pathname'. + %[pathname] Insert the contents of the file given by + `pathname'. These placeholders are expanded at the very + beginning of the process so they can be used to extend the + current template. %(sexp) Evaluate elisp `(sexp)' and replace it with the results. - For convenience, %:keyword (see below) placeholders within - the expression will be expanded prior to this. + Only placeholders pre-existing within the template, or + introduced with %[pathname] are expanded this way. Since this + happens after expanding non-interactive %-escapes, those can + be used to fill the expression. %<...> The result of format-time-string on the ... format specification. %t Time stamp, date only. %T Time stamp with date and time. @@ -255,8 +269,8 @@ be replaced with content and expanded in this order: A default value and a completion table ca be specified like this: %^{prompt|default|completion2|completion3|...}. %? After completing the template, position cursor here. - %\\n Insert the text entered at the nth %^{prompt}, where `n' is - a number, starting from 1. + %\\1 ... %\\N Insert the text entered at the nth %^{prompt}, where N + is a number, starting from 1. Apart from these general escapes, you can access information specific to the link type that is created. For example, calling `org-capture' in emails @@ -274,13 +288,21 @@ gnus | %:from %:fromname %:fromaddress | %:date %:date-timestamp (as active timestamp) | %:date-timestamp-inactive (as inactive timestamp) gnus | %:group, for messages also all email fields -w3, w3m | %:type %:url +eww, w3, w3m | %:type %:url info | %:type %:file %:node -calendar | %:type %:date" +calendar | %:type %:date + +When you need to insert a literal percent sign in the template, +you can escape ambiguous cases with a backward slash, e.g., \\%i." :group 'org-capture :version "24.1" :type - '(repeat + (let ((file-variants '(choice :tag "Filename " + (file :tag "Literal") + (function :tag "Function") + (variable :tag "Variable") + (sexp :tag "Form")))) + `(repeat (choice :value ("" "" entry (file "~/org/notes.org") "") (list :tag "Multikey description" (string :tag "Keys ") @@ -297,39 +319,45 @@ calendar | %:type %:date" (choice :tag "Target location" (list :tag "File" (const :format "" file) - (file :tag " File")) + ,file-variants) (list :tag "ID" (const :format "" id) (string :tag " ID")) (list :tag "File & Headline" (const :format "" file+headline) - (file :tag " File ") + ,file-variants (string :tag " Headline")) (list :tag "File & Outline path" (const :format "" file+olp) - (file :tag " File ") + ,file-variants (repeat :tag "Outline path" :inline t (string :tag "Headline"))) (list :tag "File & Regexp" (const :format "" file+regexp) - (file :tag " File ") + ,file-variants (regexp :tag " Regexp")) (list :tag "File & Date tree" (const :format "" file+datetree) - (file :tag " File")) + ,file-variants) (list :tag "File & Date tree, prompt for date" (const :format "" file+datetree+prompt) - (file :tag " File")) + ,file-variants) + (list :tag "File & Week tree" + (const :format "" file+weektree) + ,file-variants) + (list :tag "File & Week tree, prompt for date" + (const :format "" file+weektree+prompt) + ,file-variants) (list :tag "File & function" (const :format "" file+function) - (file :tag " File ") + ,file-variants (sexp :tag " Function")) (list :tag "Current clocking task" (const :format "" clock)) (list :tag "Function" (const :format "" function) (sexp :tag " Function"))) - (choice :tag "Template" + (choice :tag "Template " (string) (list :tag "File" (const :format "" file) @@ -350,7 +378,7 @@ calendar | %:type %:date" ((const :format "%v " :clock-resume) (const t)) ((const :format "%v " :unnarrowed) (const t)) ((const :format "%v " :table-line-pos) (const t)) - ((const :format "%v " :kill-buffer) (const t)))))))) + ((const :format "%v " :kill-buffer) (const t))))))))) (defcustom org-capture-before-finalize-hook nil "Hook that is run right before a capture process is finalized. @@ -421,7 +449,7 @@ to avoid conflicts with other active capture processes." (defvar org-capture-mode-map (make-sparse-keymap) "Keymap for `org-capture-mode', a minor mode. -Use this map to set additional keybindings for when Org-mode is used +Use this map to set additional keybindings for when Org mode is used for a capture buffer.") (defvar org-capture-mode-hook nil @@ -432,10 +460,12 @@ for a capture buffer.") Turning on this mode runs the normal hook `org-capture-mode-hook'." nil " Rem" org-capture-mode-map - (org-set-local - 'header-line-format + (setq-local + header-line-format (substitute-command-keys - "Capture buffer. Finish `C-c C-c', refile `C-c C-w', abort `C-c C-k'."))) + "\\Capture buffer. Finish \ +`\\[org-capture-finalize]', refile `\\[org-capture-refile]', \ +abort `\\[org-capture-kill]'."))) (define-key org-capture-mode-map "\C-c\C-c" 'org-capture-finalize) (define-key org-capture-mode-map "\C-c\C-k" 'org-capture-kill) (define-key org-capture-mode-map "\C-c\C-w" 'org-capture-refile) @@ -460,7 +490,7 @@ For example, if you have a capture template \"c\" and you want this template to be accessible only from `message-mode' buffers, use this: - ((\"c\" ((in-mode . \"message-mode\")))) + \\='((\"c\" ((in-mode . \"message-mode\")))) Here are the available contexts definitions: @@ -478,7 +508,7 @@ accessible if there is at least one valid check. You can also bind a key to another agenda custom command depending on contextual rules. - ((\"c\" \"d\" ((in-mode . \"message-mode\")))) + \\='((\"c\" \"d\" ((in-mode . \"message-mode\")))) Here it means: in `message-mode buffers', use \"c\" as the key for the capture template otherwise associated with \"d\". @@ -504,7 +534,8 @@ to avoid duplicates.)" (defcustom org-capture-use-agenda-date nil "Non-nil means use the date at point when capturing from agendas. -When nil, you can still capture using the date at point with \\[org-agenda-capture]." +When nil, you can still capture using the date at point with +`\\[org-agenda-capture]'." :group 'org-capture :version "24.3" :type 'boolean) @@ -513,17 +544,20 @@ When nil, you can still capture using the date at point with \\[org-agenda-captu (defun org-capture (&optional goto keys) "Capture something. \\ -This will let you select a template from `org-capture-templates', and then -file the newly captured information. The text is immediately inserted -at the target location, and an indirect buffer is shown where you can -edit it. Pressing \\[org-capture-finalize] brings you back to the previous state -of Emacs, so that you can continue your work. - -When called interactively with a \\[universal-argument] prefix argument GOTO, don't capture -anything, just go to the file/headline where the selected template -stores its notes. With a double prefix argument \ -\\[universal-argument] \\[universal-argument], go to the last note -stored. +This will let you select a template from `org-capture-templates', and +then file the newly captured information. The text is immediately +inserted at the target location, and an indirect buffer is shown where +you can edit it. Pressing `\\[org-capture-finalize]' brings you back to the \ +previous +state of Emacs, so that you can continue your work. + +When called interactively with a `\\[universal-argument]' prefix argument \ +GOTO, don't +capture anything, just go to the file/headline where the selected +template stores its notes. + +With a `\\[universal-argument] \\[universal-argument]' prefix argument, go to \ +the last note stored. When called with a `C-0' (zero) prefix, insert a template at point. @@ -564,7 +598,7 @@ of the day at point (if any) or the current HH:MM time." ((equal entry "C") (customize-variable 'org-capture-templates)) ((equal entry "q") - (error "Abort")) + (user-error "Abort")) (t (org-capture-set-plist entry) (org-capture-get-template) @@ -596,10 +630,10 @@ of the day at point (if any) or the current HH:MM time." (org-capture-insert-template-here) (condition-case error (org-capture-place-template - (equal (car (org-capture-get :target)) 'function)) + (eq (car (org-capture-get :target)) 'function)) ((error quit) (if (and (buffer-base-buffer (current-buffer)) - (string-match "\\`CAPTURE-" (buffer-name))) + (string-prefix-p "CAPTURE-" (buffer-name))) (kill-buffer (current-buffer))) (set-window-configuration (org-capture-get :return-to-wconf)) (error "Capture template `%s': %s" @@ -613,7 +647,7 @@ of the day at point (if any) or the current HH:MM time." (org-capture-put :interrupted-clock (copy-marker org-clock-marker))) (org-clock-in) - (org-set-local 'org-capture-clock-was-started t)) + (setq-local org-capture-clock-was-started t)) (error "Could not start the clock in this capture buffer"))) (if (org-capture-get :immediate-finish) @@ -646,7 +680,7 @@ captured item after finalizing." (setq stay-with-capture t)) (unless (and org-capture-mode (buffer-base-buffer (current-buffer))) - (error "This does not seem to be a capture buffer for Org-mode")) + (error "This does not seem to be a capture buffer for Org mode")) (run-hooks 'org-capture-prepare-finalize-hook) @@ -682,23 +716,13 @@ captured item after finalizing." (m2 (org-capture-get :end-marker 'local))) (if (and m1 m2 (= m1 beg) (= m2 end)) (progn - (setq m2 (if (cdr (assoc 'heading org-blank-before-new-entry)) + (setq m2 (if (cdr (assq 'heading org-blank-before-new-entry)) m2 (1+ m2)) m2 (if (< (point-max) m2) (point-max) m2)) (setq abort-note 'clean) (kill-region m1 m2)) (setq abort-note 'dirty))) - ;; Make sure that the empty lines after are correct - (when (and (> (point-max) end) ; indeed, the buffer was still narrowed - (member (org-capture-get :type 'local) - '(entry item checkitem plain))) - (save-excursion - (goto-char end) - (or (bolp) (newline)) - (org-capture-empty-lines-after - (or (org-capture-get :empty-lines-after 'local) - (org-capture-get :empty-lines 'local) 0)))) ;; Postprocessing: Update Statistics cookies, do the sorting (when (derived-mode-p 'org-mode) (save-excursion @@ -715,8 +739,7 @@ captured item after finalizing." ;; Store this place as the last one where we stored something ;; Do the marking in the base buffer, so that it makes sense after ;; the indirect buffer has been killed. - (when org-capture-bookmark - (org-capture-bookmark-last-stored-position)) + (org-capture-store-last-position) ;; Run the hook (run-hooks 'org-capture-before-finalize-hook)) @@ -770,11 +793,12 @@ captured item after finalizing." ;; Special cases (cond (abort-note - (cond - ((equal abort-note 'clean) - (message "Capture process aborted and target buffer cleaned up")) - ((equal abort-note 'dirty) - (error "Capture process aborted, but target buffer could not be cleaned up correctly")))) + (cl-case abort-note + (clean + (message "Capture process aborted and target buffer cleaned up")) + (dirty + (error "Capture process aborted, but target buffer could not be \ +cleaned up correctly")))) (stay-with-capture (org-capture-goto-last-stored))) ;; Return if we did store something @@ -786,19 +810,28 @@ Refiling is done from the base buffer, because the indirect buffer is then already gone. Any prefix argument will be passed to the refile command." (interactive) (unless (eq (org-capture-get :type 'local) 'entry) - (error - "Refiling from a capture buffer makes only sense for `entry'-type templates")) - (let ((pos (point)) - (base (buffer-base-buffer (current-buffer))) - (org-refile-for-capture t)) - (save-window-excursion - (with-current-buffer (or base (current-buffer)) - (save-excursion - (save-restriction - (widen) - (goto-char pos) - (call-interactively 'org-refile))))) - (org-capture-finalize))) + (user-error "Refiling from a capture buffer makes only sense \ +for `entry'-type templates")) + (let* ((base (or (buffer-base-buffer) (current-buffer))) + (pos (make-marker)) + (org-capture-is-refiling t) + (kill-buffer (org-capture-get :kill-buffer 'local))) + ;; Since `org-capture-finalize' may alter buffer contents (e.g., + ;; empty lines) around entry, use a marker to refer to the + ;; headline to be refiled. Place the marker in the base buffer, + ;; as the current indirect one is going to be killed. + (set-marker pos (save-excursion (org-back-to-heading t) (point)) base) + (org-capture-put :kill-buffer nil) + (unwind-protect + (progn + (org-capture-finalize) + (save-window-excursion + (with-current-buffer base + (org-with-wide-buffer + (goto-char pos) + (call-interactively 'org-refile)))) + (when kill-buffer (kill-buffer base))) + (set-marker pos nil)))) (defun org-capture-kill () "Abort the current capture process." @@ -813,7 +846,8 @@ already gone. Any prefix argument will be passed to the refile command." "Go to the location where the last capture note was stored." (interactive) (org-goto-marker-or-bmk org-capture-last-stored-marker - "org-capture-last-stored") + (plist-get org-bookmark-names-plist + :last-capture)) (message "This is the last note stored by a capture process")) ;;; Supporting functions for handling the process @@ -823,7 +857,7 @@ already gone. Any prefix argument will be passed to the refile command." (org-capture-put :initial-target-region ;; Check if the buffer is currently narrowed - (when (/= (buffer-size) (- (point-max) (point-min))) + (when (org-buffer-narrowed-p) (cons (point-min) (point-max)))) ;; store the current point (org-capture-put :initial-target-position (point))) @@ -853,14 +887,14 @@ Store them in the capture property list." ((eq (car target) 'file+headline) (set-buffer (org-capture-target-buffer (nth 1 target))) + (unless (derived-mode-p 'org-mode) + (error + "Target buffer \"%s\" for file+headline should be in Org mode" + (current-buffer))) (org-capture-put-target-region-and-position) (widen) (let ((hd (nth 2 target))) (goto-char (point-min)) - (unless (derived-mode-p 'org-mode) - (error - "Target buffer \"%s\" for file+headline should be in Org mode" - (current-buffer))) (if (re-search-forward (format org-complex-heading-regexp-format (regexp-quote hd)) nil t) @@ -892,21 +926,29 @@ Store them in the capture property list." (setq target-entry-p (and (derived-mode-p 'org-mode) (org-at-heading-p)))) (error "No match for target regexp in file %s" (nth 1 target)))) - ((memq (car target) '(file+datetree file+datetree+prompt)) + ((memq (car target) '(file+datetree file+datetree+prompt file+weektree file+weektree+prompt)) (require 'org-datetree) (set-buffer (org-capture-target-buffer (nth 1 target))) + (unless (derived-mode-p 'org-mode) + (error "Target buffer \"%s\" for %s should be in Org mode" + (current-buffer) + (car target))) (org-capture-put-target-region-and-position) (widen) - ;; Make a date tree entry, with the current date (or yesterday, - ;; if we are extending dates for a couple of hours) - (org-datetree-find-date-create + ;; Make a date/week tree entry, with the current date (or + ;; yesterday, if we are extending dates for a couple of hours) + (funcall + (cond + ((memq (car target) '(file+weektree file+weektree+prompt)) + #'org-datetree-find-iso-week-create) + (t #'org-datetree-find-date-create)) (calendar-gregorian-from-absolute (cond (org-overriding-default-time ;; use the overriding default time (time-to-days org-overriding-default-time)) - ((eq (car target) 'file+datetree+prompt) + ((memq (car target) '(file+datetree+prompt file+weektree+prompt)) ;; prompt for date (let ((prompt-time (org-read-date nil t nil "Date for tree entry:" @@ -917,7 +959,9 @@ Store them in the capture property list." (not org-time-was-given)) (not (= (time-to-days prompt-time) (org-today)))) ;; Use 00:00 when no time is given for another date than today? - (apply 'encode-time (append '(0 0 0) (cdddr (decode-time prompt-time))))) + (apply #'encode-time + (append '(0 0 0) + (cl-cdddr (decode-time prompt-time))))) ((string-match "\\([^ ]+\\)--?[^ ]+[ ]+\\(.*\\)" org-read-date-final-answer) ;; Replace any time range by its start (apply 'encode-time @@ -964,31 +1008,31 @@ Store them in the capture property list." :decrypted decrypted-hl-pos)))) (defun org-capture-expand-file (file) - "Expand functions and symbols for FILE. + "Expand functions, symbols and file names for FILE. When FILE is a function, call it. When it is a form, evaluate -it. When it is a variable, retrieve the value. Return whatever we get." +it. When it is a variable, retrieve the value. When it is +a string, treat it as a file name, possibly expanding it +according to `org-directory', and return it. If it is the empty +string, however, return `org-default-notes-file'. In any other +case, raise an error." (cond - ((org-string-nw-p file) file) + ((equal file "") org-default-notes-file) + ((stringp file) (expand-file-name file org-directory)) ((functionp file) (funcall file)) ((and (symbolp file) (boundp file)) (symbol-value file)) - ((and file (consp file)) (eval file)) + ((consp file) (eval file)) (t file))) (defun org-capture-target-buffer (file) - "Get a buffer for FILE." - (setq file (org-capture-expand-file file)) - (setq file (or (org-string-nw-p file) - org-default-notes-file - (error "No notes file specified, and no default available"))) - (or (org-find-base-buffer-visiting file) - (progn (org-capture-put :new-buffer t) - (find-file-noselect (expand-file-name file org-directory))))) - -(defun org-capture-steal-local-variables (buffer) - "Install Org-mode local variables of BUFFER." - (mapc (lambda (v) - (ignore-errors (org-set-local (car v) (cdr v)))) - (buffer-local-variables buffer))) + "Get a buffer for FILE. +FILE is a generalized file location, as handled by +`org-capture-expand-file'." + (let ((file (or (org-string-nw-p (org-capture-expand-file file)) + org-default-notes-file + (error "No notes file specified, and no default available")))) + (or (org-find-base-buffer-visiting file) + (progn (org-capture-put :new-buffer t) + (find-file-noselect file))))) (defun org-capture-place-template (&optional inhibit-wconf-store) "Insert the template at the target location, and display the buffer. @@ -1000,65 +1044,52 @@ may have been stored before." (org-switch-to-buffer-other-window (org-capture-get-indirect-buffer (org-capture-get :buffer) "CAPTURE")) (widen) - (show-all) + (outline-show-all) (goto-char (org-capture-get :pos)) - (org-set-local 'org-capture-target-marker - (point-marker)) - (org-set-local 'outline-level 'org-outline-level) - (let* ((template (org-capture-get :template)) - (type (org-capture-get :type))) - (case type - ((nil entry) (org-capture-place-entry)) - (table-line (org-capture-place-table-line)) - (plain (org-capture-place-plain-text)) - (item (org-capture-place-item)) - (checkitem (org-capture-place-item)))) + (setq-local outline-level 'org-outline-level) + (pcase (org-capture-get :type) + ((or `nil `entry) (org-capture-place-entry)) + (`table-line (org-capture-place-table-line)) + (`plain (org-capture-place-plain-text)) + (`item (org-capture-place-item)) + (`checkitem (org-capture-place-item))) (org-capture-mode 1) - (org-set-local 'org-capture-current-plist org-capture-plist)) + (setq-local org-capture-current-plist org-capture-plist)) (defun org-capture-place-entry () "Place the template as a new Org entry." - (let* ((txt (org-capture-get :template)) - (reversed (org-capture-get :prepend)) - (target-entry-p (org-capture-get :target-entry-p)) - level beg end file) - - (cond - ((org-capture-get :exact-position) + (let ((reversed? (org-capture-get :prepend)) + level) + (when (org-capture-get :exact-position) (goto-char (org-capture-get :exact-position))) - ((not target-entry-p) - ;; Insert as top-level entry, either at beginning or at end of file - (setq level 1) - (if reversed - (progn (goto-char (point-min)) - (or (org-at-heading-p) - (outline-next-heading))) - (goto-char (point-max)) - (or (bolp) (insert "\n")))) - (t - ;; Insert as a child of the current entry - (and (looking-at "\\*+") - (setq level (- (match-end 0) (match-beginning 0)))) - (setq level (org-get-valid-level (or level 1) 1)) - (if reversed - (progn - (outline-next-heading) - (or (bolp) (insert "\n"))) - (org-end-of-subtree t nil) - (or (bolp) (insert "\n"))))) + (cond + ;; Insert as a child of the current entry. + ((org-capture-get :target-entry-p) + (setq level (org-get-valid-level + (if (org-at-heading-p) (org-outline-level) 1) + 1)) + (if reversed? (outline-next-heading) (org-end-of-subtree t))) + ;; Insert as a top-level entry at the beginning of the file. + (reversed? + (goto-char (point-min)) + (unless (org-at-heading-p) (outline-next-heading))) + ;; Otherwise, insert as a top-level entry at the end of the file. + (t (goto-char (point-max)))) + (unless (bolp) (insert "\n")) (org-capture-empty-lines-before) - (setq beg (point)) - (org-capture-verify-tree txt) - (org-paste-subtree level txt 'for-yank) - (org-capture-empty-lines-after 1) - (org-capture-position-for-last-stored beg) - (outline-next-heading) - (setq end (point)) - (org-capture-mark-kill-region beg (1- end)) - (org-capture-narrow beg (1- end)) - (if (or (re-search-backward "%\\?" beg t) - (re-search-forward "%\\?" end t)) - (replace-match "")))) + (let ((beg (point)) + (template (org-capture-get :template))) + (org-capture-verify-tree template) + (org-paste-subtree level template 'for-yank) + (org-capture-empty-lines-after) + (org-capture-position-for-last-stored beg) + (unless (org-at-heading-p) (outline-next-heading)) + (let ((end (point))) + (org-capture-mark-kill-region beg end) + (org-capture-narrow beg end) + (when (or (re-search-backward "%\\?" beg t) + (re-search-forward "%\\?" end t)) + (replace-match "")))))) (defun org-capture-place-item () "Place the template as a new plain list item." @@ -1075,21 +1106,18 @@ may have been stored before." (t (setq beg (1+ (point-at-eol)) end (save-excursion (outline-next-heading) (point))))) + (setq ind nil) (if (org-capture-get :prepend) (progn (goto-char beg) - (if (org-list-search-forward (org-item-beginning-re) end t) - (progn - (goto-char (match-beginning 0)) - (setq ind (org-get-indentation))) - (goto-char end) - (setq ind 0))) + (when (org-list-search-forward (org-item-beginning-re) end t) + (goto-char (match-beginning 0)) + (setq ind (org-get-indentation)))) (goto-char end) - (if (org-list-search-backward (org-item-beginning-re) beg t) - (progn - (setq ind (org-get-indentation)) - (org-end-of-item)) - (setq ind 0)))) + (when (org-list-search-backward (org-item-beginning-re) beg t) + (setq ind (org-get-indentation)) + (org-end-of-item))) + (unless ind (goto-char end))) ;; Remove common indentation (setq txt (org-remove-indentation txt)) ;; Make sure this is indeed an item @@ -1097,18 +1125,23 @@ may have been stored before." (setq txt (concat "- " (mapconcat 'identity (split-string txt "\n") "\n ")))) + ;; Prepare surrounding empty lines. + (org-capture-empty-lines-before) + (setq beg (point)) + (unless (eolp) (save-excursion (insert "\n"))) + (unless ind + (org-indent-line) + (setq ind (org-get-indentation)) + (delete-region beg (point))) ;; Set the correct indentation, depending on context (setq ind (make-string ind ?\ )) (setq txt (concat ind (mapconcat 'identity (split-string txt "\n") (concat "\n" ind)) "\n")) - ;; Insert, with surrounding empty lines - (org-capture-empty-lines-before) - (setq beg (point)) + ;; Insert item. (insert txt) - (or (bolp) (insert "\n")) - (org-capture-empty-lines-after 1) + (org-capture-empty-lines-after) (org-capture-position-for-last-stored beg) (forward-char 1) (setq end (point)) @@ -1124,7 +1157,7 @@ may have been stored before." (let* ((txt (org-capture-get :template)) (target-entry-p (org-capture-get :target-entry-p)) (table-line-pos (org-capture-get :table-line-pos)) - ind beg end) + beg end) (cond ((org-capture-get :exact-position) (goto-char (org-capture-get :exact-position))) @@ -1149,21 +1182,24 @@ may have been stored before." ;; Check if the template is good (if (not (string-match org-table-dataline-regexp txt)) (setq txt "| %?Bad template |\n")) + (if (functionp table-line-pos) + (setq table-line-pos (funcall table-line-pos)) + (setq table-line-pos (eval table-line-pos))) (cond ((and table-line-pos (string-match "\\(I+\\)\\([-+][0-9]\\)" table-line-pos)) - ;; we have a complex line specification (goto-char (point-min)) - (let ((nh (- (match-end 1) (match-beginning 1))) - (delta (string-to-number (match-string 2 table-line-pos))) - ll) + ;; we have a complex line specification + (let ((ll (ignore-errors + (save-match-data (org-table-analyze)) + (aref org-table-hlines + (- (match-end 1) (match-beginning 1))))) + (delta (string-to-number (match-string 2 table-line-pos)))) ;; The user wants a special position in the table - (org-table-get-specials) - (setq ll (ignore-errors (aref org-table-hlines nh))) - (unless ll (error "Invalid table line specification \"%s\"" - table-line-pos)) - (setq ll (+ ll delta (if (< delta 0) 0 -1))) - (org-goto-line ll) + (unless ll + (error "Invalid table line specification \"%s\"" table-line-pos)) + (goto-char org-table-current-begin-pos) + (forward-line (+ ll delta (if (< delta 0) 0 -1))) (org-table-insert-row 'below) (beginning-of-line 1) (delete-region (point) (1+ (point-at-eol))) @@ -1216,7 +1252,7 @@ Of course, if exact position has been required, just put it there." ;; we should place the text into this entry (if (org-capture-get :prepend) ;; Skip meta data and drawers - (org-end-of-meta-data-and-drawers) + (org-end-of-meta-data t) ;; go to ent of the entry text, before the next headline (outline-next-heading))) (t @@ -1226,7 +1262,7 @@ Of course, if exact position has been required, just put it there." (org-capture-empty-lines-before) (setq beg (point)) (insert txt) - (org-capture-empty-lines-after 1) + (org-capture-empty-lines-after) (org-capture-position-for-last-stored beg) (setq end (point)) (org-capture-mark-kill-region beg (1- end)) @@ -1256,8 +1292,8 @@ Of course, if exact position has been required, just put it there." (org-table-current-dline)))) (t (error "This should not happen")))) -(defun org-capture-bookmark-last-stored-position () - "Bookmark the last-captured position." +(defun org-capture-store-last-position () + "Store the last-captured position." (let* ((where (org-capture-get :position-for-last-stored 'local)) (pos (cond ((markerp where) @@ -1270,16 +1306,11 @@ Of course, if exact position has been required, just put it there." (point-at-bol)) (point)))))) (with-current-buffer (buffer-base-buffer (current-buffer)) - (save-excursion - (save-restriction - (widen) - (goto-char pos) - (let ((bookmark-name (plist-get org-bookmark-names-plist - :last-capture))) - (when bookmark-name - (with-demoted-errors - (bookmark-set bookmark-name)))) - (move-marker org-capture-last-stored-marker (point))))))) + (org-with-point-at pos + (when org-capture-bookmark + (let ((bookmark (plist-get org-bookmark-names-plist :last-capture))) + (when bookmark (with-demoted-errors (bookmark-set bookmark))))) + (move-marker org-capture-last-stored-marker (point)))))) (defun org-capture-narrow (beg end) "Narrow, unless configuration says not to narrow." @@ -1315,7 +1346,7 @@ Point will remain at the first line after the inserted text." (let* ((template (org-capture-get :template)) (type (org-capture-get :type)) beg end pp) - (or (bolp) (newline)) + (unless (bolp) (insert "\n")) (setq beg (point)) (cond ((and (eq type 'entry) (derived-mode-p 'org-mode)) @@ -1337,13 +1368,16 @@ Point will remain at the first line after the inserted text." (org-capture-empty-lines-after) (goto-char beg) (org-list-repair) - (org-end-of-item) - (setq end (point))) - (t (insert template))) + (org-end-of-item)) + (t + (insert template) + (org-capture-empty-lines-after) + (skip-chars-forward " \t\n") + (unless (eobp) (beginning-of-line)))) (setq end (point)) (goto-char beg) - (if (re-search-forward "%\\?" end t) - (replace-match "")))) + (when (re-search-forward "%\\?" end t) + (replace-match "")))) (defun org-capture-set-plist (entry) "Initialize the property list from the template definition." @@ -1365,13 +1399,11 @@ Point will remain at the first line after the inserted text." "Go to the target location of a capture template. The user is queried for the template." (interactive) - (let* (org-select-template-temp-major-mode - (entry (org-capture-select-template template-key))) - (unless entry - (error "No capture template selected")) + (let ((entry (org-capture-select-template template-key))) + (unless entry (error "No capture template selected")) (org-capture-set-plist entry) (org-capture-set-target-location) - (org-pop-to-buffer-same-window (org-capture-get :buffer)) + (pop-to-buffer-same-window (org-capture-get :buffer)) (goto-char (org-capture-get :pos)))) (defun org-capture-get-indirect-buffer (&optional buffer prefix) @@ -1381,7 +1413,7 @@ Use PREFIX as a prefix for the name of the indirect buffer." (let ((n 1) (base (buffer-name buffer)) bname) (setq bname (concat prefix "-" base)) (while (buffer-live-p (get-buffer bname)) - (setq bname (concat prefix "-" (number-to-string (incf n)) "-" base))) + (setq bname (concat prefix "-" (number-to-string (cl-incf n)) "-" base))) (condition-case nil (make-indirect-buffer buffer bname 'clone) (error @@ -1396,6 +1428,7 @@ Use PREFIX as a prefix for the name of the indirect buffer." (defun org-mks (table title &optional prompt specials) "Select a member of an alist with multiple keys. + TABLE is the alist which should contain entries where the car is a string. There should be two types of entries. @@ -1403,7 +1436,7 @@ There should be two types of entries. This indicates that `a' is a prefix key for multi-letter selection, and that there are entries following with keys like \"ab\", \"ax\"... -2. Selectable members must have more than two elements, with the first +2. Select-able members must have more than two elements, with the first being the string of keys that lead to selecting it, and the second a short description string of the item. @@ -1414,84 +1447,72 @@ When you press a prefix key, the commands (and maybe further prefixes) under this key will be shown and offered for selection. TITLE will be placed over the selection in the temporary buffer, -PROMPT will be used when prompting for a key. SPECIAL is an alist with -also (\"key\" \"description\") entries. When one of these is selection, -only the bare key is returned." - (setq prompt (or prompt "Select: ")) - (let (tbl orig-table dkey ddesc des-keys allowed-keys - current prefix rtn re pressed buffer (inhibit-quit t)) - (save-window-excursion - (setq buffer (org-switch-to-buffer-other-window "*Org Select*")) - (setq orig-table table) - (catch 'exit - (while t - (erase-buffer) - (insert title "\n\n") - (setq tbl table - des-keys nil - allowed-keys nil - cursor-type nil) - (setq prefix (if current (concat current " ") "")) - (while tbl - (cond - ((and (= 2 (length (car tbl))) (= (length (caar tbl)) 1)) - ;; This is a description on this level - (setq dkey (caar tbl) ddesc (cadar tbl)) - (pop tbl) - (push dkey des-keys) - (push dkey allowed-keys) - (insert prefix "[" dkey "]" "..." " " ddesc "..." "\n") - ;; Skip keys which are below this prefix - (setq re (concat "\\`" (regexp-quote dkey))) - (let (case-fold-search) - (while (and tbl (string-match re (caar tbl))) (pop tbl)))) - ((= 2 (length (car tbl))) - ;; Not yet a usable description, skip it - ) - (t - ;; usable entry on this level - (insert prefix "[" (caar tbl) "]" " " (nth 1 (car tbl)) "\n") - (push (caar tbl) allowed-keys) - (pop tbl)))) - (when specials - (insert "-------------------------------------------------------------------------------\n") - (let ((sp specials)) - (while sp - (insert (format "[%s] %s\n" - (caar sp) (nth 1 (car sp)))) - (push (caar sp) allowed-keys) - (pop sp)))) - (push "\C-g" allowed-keys) - (goto-char (point-min)) - (if (not (pos-visible-in-window-p (point-max))) - (org-fit-window-to-buffer)) - (message prompt) - (setq pressed (char-to-string (read-char-exclusive))) - (while (not (member pressed allowed-keys)) - (message "Invalid key `%s'" pressed) (sit-for 1) - (message prompt) - (setq pressed (char-to-string (read-char-exclusive)))) - (when (equal pressed "\C-g") - (kill-buffer buffer) - (error "Abort")) - (when (and (not (assoc pressed table)) - (not (member pressed des-keys)) - (assoc pressed specials)) - (throw 'exit (setq rtn pressed))) - (unless (member pressed des-keys) - (throw 'exit (setq rtn (rassoc (cdr (assoc pressed table)) - orig-table)))) - (setq current (concat current pressed)) - (setq table (mapcar - (lambda (x) - (if (and (> (length (car x)) 1) - (equal (substring (car x) 0 1) pressed)) - (cons (substring (car x) 1) (cdr x)) - nil)) - table)) - (setq table (remove nil table))))) - (when buffer (kill-buffer buffer)) - rtn)) +PROMPT will be used when prompting for a key. SPECIAL is an +alist with (\"key\" \"description\") entries. When one of these +is selected, only the bare key is returned." + (save-window-excursion + (let ((inhibit-quit t) + (buffer (org-switch-to-buffer-other-window "*Org Select*")) + (prompt (or prompt "Select: ")) + current) + (unwind-protect + (catch 'exit + (while t + (erase-buffer) + (insert title "\n\n") + (let ((des-keys nil) + (allowed-keys '("\C-g")) + (cursor-type nil)) + ;; Populate allowed keys and descriptions keys + ;; available with CURRENT selector. + (let ((re (format "\\`%s\\(.\\)\\'" + (if current (regexp-quote current) ""))) + (prefix (if current (concat current " ") ""))) + (dolist (entry table) + (pcase entry + ;; Description. + (`(,(and key (pred (string-match re))) ,desc) + (let ((k (match-string 1 key))) + (push k des-keys) + (push k allowed-keys) + (insert prefix "[" k "]" "..." " " desc "..." "\n"))) + ;; Usable entry. + (`(,(and key (pred (string-match re))) ,desc . ,_) + (let ((k (match-string 1 key))) + (insert prefix "[" k "]" " " desc "\n") + (push k allowed-keys))) + (_ nil)))) + ;; Insert special entries, if any. + (when specials + (insert "----------------------------------------------------\ +---------------------------\n") + (pcase-dolist (`(,key ,description) specials) + (insert (format "[%s] %s\n" key description)) + (push key allowed-keys))) + ;; Display UI and let user select an entry or + ;; a sub-level prefix. + (goto-char (point-min)) + (unless (pos-visible-in-window-p (point-max)) + (org-fit-window-to-buffer)) + (message prompt) + (let ((pressed (char-to-string (read-char-exclusive)))) + (while (not (member pressed allowed-keys)) + (message "Invalid key `%s'" pressed) (sit-for 1) + (message prompt) + (setq pressed (char-to-string (read-char-exclusive)))) + (setq current (concat current pressed)) + (cond + ((equal pressed "\C-g") (user-error "Abort")) + ;; Selection is a prefix: open a new menu. + ((member pressed des-keys)) + ;; Selection matches an association: return it. + ((let ((entry (assoc current table))) + (and entry (throw 'exit entry)))) + ;; Selection matches a special entry: return the + ;; selection prefix. + ((assoc current specials) (throw 'exit current)) + (t (error "No entry available"))))))) + (when buffer (kill-buffer buffer)))))) ;;; The template code (defun org-capture-select-template (&optional keys) @@ -1511,46 +1532,41 @@ Lisp programs can force the template by setting KEYS to a string." '(("C" "Customize org-capture-templates") ("q" "Abort")))))) +(defvar org-capture--clipboards nil + "List various clipboards values.") + (defun org-capture-fill-template (&optional template initial annotation) "Fill a template and return the filled template as a string. The template may still contain \"%?\" for cursor positioning." - (setq template (or template (org-capture-get :template))) - (when (stringp initial) - (setq initial (org-no-properties initial))) - (let* ((buffer (org-capture-get :buffer)) + (let* ((template (or template (org-capture-get :template))) + (buffer (org-capture-get :buffer)) (file (buffer-file-name (or (buffer-base-buffer buffer) buffer))) - (ct (org-capture-get :default-time)) - (dct (decode-time ct)) - (ct1 - (if (< (nth 2 dct) org-extend-today-until) - (encode-time 0 59 23 (1- (nth 3 dct)) (nth 4 dct) (nth 5 dct)) - ct)) - (plist-p (if org-store-link-plist t nil)) - (v-c (and (> (length kill-ring) 0) (current-kill 0))) + (time (let* ((c (or (org-capture-get :default-time) (current-time))) + (d (decode-time c))) + (if (< (nth 2 d) org-extend-today-until) + (encode-time 0 59 23 (1- (nth 3 d)) (nth 4 d) (nth 5 d)) + c))) + (v-t (format-time-string (org-time-stamp-format nil) time)) + (v-T (format-time-string (org-time-stamp-format t) time)) + (v-u (format-time-string (org-time-stamp-format nil t) time)) + (v-U (format-time-string (org-time-stamp-format t t) time)) + (v-c (and kill-ring (current-kill 0))) (v-x (or (org-get-x-clipboard 'PRIMARY) (org-get-x-clipboard 'CLIPBOARD) (org-get-x-clipboard 'SECONDARY))) - (v-t (format-time-string (car org-time-stamp-formats) ct1)) - (v-T (format-time-string (cdr org-time-stamp-formats) ct1)) - (v-u (concat "[" (substring v-t 1 -1) "]")) - (v-U (concat "[" (substring v-T 1 -1) "]")) - ;; `initial' and `annotation' might habe been passed. - ;; But if the property list has them, we prefer those values + ;; `initial' and `annotation' might have been passed. But if + ;; the property list has them, we prefer those values. (v-i (or (plist-get org-store-link-plist :initial) - initial + (and (stringp initial) (org-no-properties initial)) (org-capture-get :initial) "")) - (v-a (or (plist-get org-store-link-plist :annotation) - annotation - (org-capture-get :annotation) - "")) - ;; Is the link empty? Then we do not want it... - (v-a (if (equal v-a "[[]]") "" v-a)) - (clipboards (remove nil (list v-i - (org-get-x-clipboard 'PRIMARY) - (org-get-x-clipboard 'CLIPBOARD) - (org-get-x-clipboard 'SECONDARY) - v-c))) + (v-a + (let ((a (or (plist-get org-store-link-plist :annotation) + annotation + (org-capture-get :annotation) + ""))) + ;; Is the link empty? Then we do not want it... + (if (equal a "[[]]") "" a))) (l-re "\\[\\[\\(.*?\\)\\]\\(\\[.*?\\]\\)?\\]") (v-A (if (and v-a (string-match l-re v-a)) (replace-match "[[\\1][%^{Link description}]]" nil nil v-a) @@ -1559,202 +1575,260 @@ The template may still contain \"%?\" for cursor positioning." (replace-match "\\1" nil nil v-a) v-a)) (v-n user-full-name) - (v-k (if (marker-buffer org-clock-marker) - (org-no-properties org-clock-heading))) + (v-k (and (marker-buffer org-clock-marker) + (org-no-properties org-clock-heading))) (v-K (if (marker-buffer org-clock-marker) (org-make-link-string (buffer-file-name (marker-buffer org-clock-marker)) org-clock-heading))) (v-f (or (org-capture-get :original-file-nondirectory) "")) (v-F (or (org-capture-get :original-file) "")) - v-I - (org-startup-folded nil) - (org-inhibit-startup t) - org-time-was-given org-end-time-was-given x - prompt completions char time pos default histvar strings) - - (setq org-store-link-plist - (plist-put org-store-link-plist :annotation v-a) - org-store-link-plist - (plist-put org-store-link-plist :initial v-i)) - (setq initial v-i) - - (unless template (setq template "") (message "No template") (ding) - (sit-for 1)) + (org-capture--clipboards + (delq nil + (list v-i + (org-get-x-clipboard 'PRIMARY) + (org-get-x-clipboard 'CLIPBOARD) + (org-get-x-clipboard 'SECONDARY) + v-c)))) + + (setq org-store-link-plist (plist-put org-store-link-plist :annotation v-a)) + (setq org-store-link-plist (plist-put org-store-link-plist :initial v-i)) + + (unless template + (setq template "") + (message "no template") (ding) + (sit-for 1)) (save-window-excursion - (delete-other-windows) - (org-pop-to-buffer-same-window (get-buffer-create "*Capture*")) + (org-switch-to-buffer-other-window (get-buffer-create "*Capture*")) (erase-buffer) + (setq buffer-file-name nil) + (setq mark-active nil) (insert template) (goto-char (point-min)) - (org-capture-steal-local-variables buffer) - (setq buffer-file-name nil mark-active nil) - ;; %[] Insert contents of a file. - (goto-char (point-min)) - (while (re-search-forward "%\\[\\(.+\\)\\]" nil t) - (unless (org-capture-escaped-%) - (let ((start (match-beginning 0)) - (end (match-end 0)) - (filename (expand-file-name (match-string 1)))) - (goto-char start) - (delete-region start end) - (condition-case error - (insert-file-contents filename) - (error (insert (format "%%![Could not insert %s: %s]" - filename error))))))) - ;; %() embedded elisp - (org-capture-expand-embedded-elisp) + ;; %[] insert contents of a file. + (save-excursion + (while (re-search-forward "%\\[\\(.+\\)\\]" nil t) + (let ((filename (expand-file-name (match-string 1))) + (beg (copy-marker (match-beginning 0))) + (end (copy-marker (match-end 0)))) + (unless (org-capture-escaped-%) + (delete-region beg end) + (set-marker beg nil) + (set-marker end nil) + (condition-case error + (insert-file-contents filename) + (error + (insert (format "%%![couldn not insert %s: %s]" + filename + error)))))))) - ;; The current time - (goto-char (point-min)) - (while (re-search-forward "%<\\([^>\n]+\\)>" nil t) - (replace-match (format-time-string (match-string 1)) t t)) + ;; Mark %() embedded elisp for later evaluation. + (org-capture-expand-embedded-elisp 'mark) - ;; Simple %-escapes - (goto-char (point-min)) - (while (re-search-forward "%\\([tTuUaliAcxkKInfF]\\)" nil t) - (unless (org-capture-escaped-%) - (when (and initial (equal (match-string 0) "%i")) - (save-match-data - (let* ((lead (buffer-substring - (point-at-bol) (match-beginning 0)))) - (setq v-i (mapconcat 'identity - (org-split-string initial "\n") - (concat "\n" lead)))))) - (replace-match (or (eval (intern (concat "v-" (match-string 1)))) "") - t t))) - - ;; From the property list - (when plist-p - (goto-char (point-min)) - (while (re-search-forward "%\\(:[-a-zA-Z]+\\)" nil t) - (unless (org-capture-escaped-%) - (and (setq x (or (plist-get org-store-link-plist - (intern (match-string 1))) "")) - (replace-match x t t))))) - - ;; Turn on org-mode in temp buffer, set local variables - ;; This is to support completion in interactive prompts + ;; Expand non-interactive templates. + (let ((regexp "%\\(:[-a-za-z]+\\|<\\([^>\n]+\\)>\\|[aAcfFikKlntTuUx]\\)")) + (save-excursion + (while (re-search-forward regexp nil t) + ;; `org-capture-escaped-%' may modify buffer and cripple + ;; match-data. Use markers instead. Ditto for other + ;; templates. + (let ((pos (copy-marker (match-beginning 0))) + (end (copy-marker (match-end 0))) + (value (match-string 1)) + (time-string (match-string 2))) + (unless (org-capture-escaped-%) + (delete-region pos end) + (set-marker pos nil) + (set-marker end nil) + (let* ((inside-sexp? (org-capture-inside-embedded-elisp-p)) + (replacement + (pcase (string-to-char value) + (?< (format-time-string time-string)) + (?: + (or (plist-get org-store-link-plist (intern value)) + "")) + (?i + (if inside-sexp? v-i + ;; Outside embedded Lisp, repeat leading + ;; characters before initial place holder + ;; every line. + (let ((lead (buffer-substring-no-properties + (line-beginning-position) (point)))) + (replace-regexp-in-string "\n\\(.\\)" + (concat lead "\\1") + v-i nil nil 1)))) + (?a v-a) + (?A v-A) + (?c v-c) + (?f v-f) + (?F v-F) + (?k v-k) + (?K v-K) + (?l v-l) + (?n v-n) + (?t v-t) + (?T v-T) + (?u v-u) + (?U v-U) + (?x v-x)))) + (insert + (if inside-sexp? + ;; Escape sensitive characters. + (replace-regexp-in-string "[\\\"]" "\\\\\\&" replacement) + replacement)))))))) + + ;; Expand %() embedded Elisp. Limit to Sexp originally marked. + (org-capture-expand-embedded-elisp) + + ;; Expand interactive templates. This is the last step so that + ;; template is mostly expanded when prompting happens. Turn on + ;; Org mode and set local variables. This is to support + ;; completion in interactive prompts. (let ((org-inhibit-startup t)) (org-mode)) - ;; Interactive template entries - (goto-char (point-min)) - (while (re-search-forward "%^\\({\\([^}]*\\)}\\)?\\([gGtTuUCLp]\\)?" nil t) - (unless (org-capture-escaped-%) - (setq char (if (match-end 3) (match-string-no-properties 3)) - prompt (if (match-end 2) (match-string-no-properties 2))) - (goto-char (match-beginning 0)) - (replace-match "") - (setq completions nil default nil) - (when prompt - (setq completions (org-split-string prompt "|") - prompt (pop completions) - default (car completions) - histvar (intern (concat - "org-capture-template-prompt-history::" - (or prompt ""))) - completions (mapcar 'list completions))) - (unless (boundp histvar) (set histvar nil)) - (cond - ((member char '("G" "g")) - (let* ((org-last-tags-completion-table - (org-global-tags-completion-table - (if (equal char "G") - (org-agenda-files) - (and file (list file))))) - (org-add-colon-after-tag-completion t) - (ins (org-icompleting-read - (if prompt (concat prompt ": ") "Tags: ") - 'org-tags-completion-function nil nil nil - 'org-tags-history))) - (setq ins (mapconcat 'identity - (org-split-string - ins (org-re "[^[:alnum:]_@#%]+")) - ":")) - (when (string-match "\\S-" ins) - (or (equal (char-before) ?:) (insert ":")) - (insert ins) - (or (equal (char-after) ?:) (insert ":")) - (and (org-at-heading-p) - (let ((org-ignore-region t)) - (org-set-tags nil 'align)))))) - ((equal char "C") - (cond ((= (length clipboards) 1) (insert (car clipboards))) - ((> (length clipboards) 1) - (insert (read-string "Clipboard/kill value: " - (car clipboards) '(clipboards . 1) - (car clipboards)))))) - ((equal char "L") - (cond ((= (length clipboards) 1) - (org-insert-link 0 (car clipboards))) - ((> (length clipboards) 1) - (org-insert-link 0 (read-string "Clipboard/kill value: " - (car clipboards) - '(clipboards . 1) - (car clipboards)))))) - ((equal char "p") - (org-set-property (org-no-properties prompt) nil)) - (char - ;; These are the date/time related ones - (setq org-time-was-given (equal (upcase char) char)) - (setq time (org-read-date (equal (upcase char) char) t nil - prompt)) - (if (equal (upcase char) char) (setq org-time-was-given t)) - (org-insert-time-stamp time org-time-was-given - (member char '("u" "U")) - nil nil (list org-end-time-was-given))) - (t - (let (org-completion-use-ido) - (push (org-completing-read-no-i - (concat (if prompt prompt "Enter string") - (if default (concat " [" default "]")) - ": ") - completions nil nil nil histvar default) - strings) - (insert (car strings))))))) - ;; Replace %n escapes with nth %^{...} string - (setq strings (nreverse strings)) - (goto-char (point-min)) - (while (re-search-forward "%\\\\\\([1-9][0-9]*\\)" nil t) - (unless (org-capture-escaped-%) - (replace-match - (nth (1- (string-to-number (match-string 1))) strings) - nil t))) + (org-clone-local-variables buffer "\\`org-") + (let (strings) ; Stores interactive answers. + (save-excursion + (let ((regexp "%\\^\\(?:{\\([^}]*\\)}\\)?\\([CgGLptTuU]\\)?")) + (while (re-search-forward regexp nil t) + (let* ((items (and (match-end 1) + (save-match-data + (split-string (match-string-no-properties 1) + "|")))) + (key (match-string 2)) + (beg (copy-marker (match-beginning 0))) + (end (copy-marker (match-end 0))) + (prompt (nth 0 items)) + (default (nth 1 items)) + (completions (nthcdr 2 items))) + (unless (org-capture-escaped-%) + (delete-region beg end) + (set-marker beg nil) + (set-marker end nil) + (pcase key + ((or "G" "g") + (let* ((org-last-tags-completion-table + (org-global-tags-completion-table + (cond ((equal key "G") (org-agenda-files)) + (file (list file)) + (t nil)))) + (org-add-colon-after-tag-completion t) + (ins (mapconcat + #'identity + (org-split-string + (completing-read + (if prompt (concat prompt ": ") "Tags: ") + 'org-tags-completion-function nil nil nil + 'org-tags-history) + "[^[:alnum:]_@#%]+") + ":"))) + (when (org-string-nw-p ins) + (unless (eq (char-before) ?:) (insert ":")) + (insert ins) + (unless (eq (char-after) ?:) (insert ":")) + (and (org-at-heading-p) + (let ((org-ignore-region t)) + (org-set-tags nil 'align)))))) + ((or "C" "L") + (let ((insert-fun (if (equal key "C") #'insert + (lambda (s) (org-insert-link 0 s))))) + (pcase org-capture--clipboards + (`nil nil) + (`(,value) (funcall insert-fun value)) + (`(,first-value . ,_) + (funcall insert-fun + (read-string "Clipboard/kill value: " + first-value + 'org-capture--clipboards + first-value))) + (_ (error "Invalid `org-capture--clipboards' value: %S" + org-capture--clipboards))))) + ("p" (org-set-property prompt nil)) + ((guard key) + ;; These are the date/time related ones. + (let* ((upcase? (equal (upcase key) key)) + (org-time-was-given upcase?) + (org-end-time-was-given) + (time (org-read-date upcase? t nil prompt))) + (org-insert-time-stamp + time org-time-was-given + (member key '("u" "U")) + nil nil (list org-end-time-was-given)))) + (_ + (push (org-completing-read + (concat (or prompt "Enter string") + (and default (format " [%s]" default)) + ": ") + completions nil nil nil nil default) + strings) + (insert (car strings))))))))) + + ;; Replace %n escapes with nth %^{...} string. + (setq strings (nreverse strings)) + (save-excursion + (while (re-search-forward "%\\\\\\([1-9][0-9]*\\)" nil t) + (unless (org-capture-escaped-%) + (replace-match + (nth (1- (string-to-number (match-string 1))) strings) + nil t))))) + ;; Make sure there are no empty lines before the text, and that - ;; it ends with a newline character - (goto-char (point-min)) - (while (looking-at "[ \t]*\n") (replace-match "")) - (if (re-search-forward "[ \t\n]*\\'" nil t) (replace-match "\n")) - ;; Return the expanded template and kill the temporary buffer + ;; it ends with a newline character. + (skip-chars-forward " \t\n") + (delete-region (point-min) (line-beginning-position)) + (goto-char (point-max)) + (skip-chars-backward " \t\n") + (delete-region (point) (point-max)) + (insert "\n") + + ;; Return the expanded template and kill the capture buffer. (untabify (point-min) (point-max)) (set-buffer-modified-p nil) - (prog1 (buffer-string) (kill-buffer (current-buffer)))))) + (prog1 (buffer-substring-no-properties (point-min) (point-max)) + (kill-buffer (current-buffer)))))) (defun org-capture-escaped-% () - "Check if % was escaped - if yes, unescape it now." - (if (equal (char-before (match-beginning 0)) ?\\) - (progn - (delete-region (1- (match-beginning 0)) (match-beginning 0)) - t) - nil)) - -(defun org-capture-expand-embedded-elisp () - "Evaluate embedded elisp %(sexp) and replace with the result." - (goto-char (point-min)) - (while (re-search-forward "%(" nil t) - (unless (org-capture-escaped-%) - (goto-char (match-beginning 0)) - (let ((template-start (point))) - (forward-char 1) - (let* ((sexp (read (current-buffer))) - (result (org-eval - (org-capture--expand-keyword-in-embedded-elisp sexp)))) - (delete-region template-start (point)) - (when result - (if (stringp result) - (insert result) - (error "Capture template sexp `%s' must evaluate to string or nil" - sexp)))))))) + "Non-nil if % was escaped. +If yes, unescape it now. Assume match-data contains the +placeholder to check." + (save-excursion + (goto-char (match-beginning 0)) + (let ((n (abs (skip-chars-backward "\\\\")))) + (delete-char (/ (1+ n) 2)) + (= (% n 2) 1)))) + +(defun org-capture-expand-embedded-elisp (&optional mark) + "Evaluate embedded elisp %(sexp) and replace with the result. +When optional MARK argument is non-nil, mark Sexp with a text +property (`org-embedded-elisp') for later evaluation. Only +marked Sexp are evaluated when this argument is nil." + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "%(" nil t) + (cond + ((get-text-property (match-beginning 0) 'org-embedded-elisp) + (goto-char (match-beginning 0)) + (let ((template-start (point))) + (forward-char 1) + (let* ((sexp (read (current-buffer))) + (result (org-eval + (org-capture--expand-keyword-in-embedded-elisp + sexp)))) + (delete-region template-start (point)) + (cond + ((not result) nil) + ((stringp result) (insert result)) + (t (error + "Capture template sexp `%s' must evaluate to string or nil" + sexp)))))) + ((not mark) nil) + ;; Only mark valid and non-escaped sexp. + ((org-capture-escaped-%) nil) + (t + (let ((end (with-syntax-table emacs-lisp-mode-syntax-table + (ignore-errors (scan-sexps (1- (point)) 1))))) + (when end + (put-text-property (- (point) 2) end 'org-embedded-elisp t)))))))) (defun org-capture--expand-keyword-in-embedded-elisp (attr) "Recursively replace capture link keywords in ATTR sexp. @@ -1771,20 +1845,10 @@ Such keywords are prefixed with \"%:\". See (t attr))) (defun org-capture-inside-embedded-elisp-p () - "Return non-nil if point is inside of embedded elisp %(sexp)." - (let (beg end) - (with-syntax-table emacs-lisp-mode-syntax-table - (save-excursion - ;; `looking-at' and `search-backward' below do not match the "%(" if - ;; point is in its middle - (when (equal (char-before) ?%) - (backward-char)) - (save-match-data - (when (or (looking-at "%(") (search-backward "%(" nil t)) - (setq beg (point)) - (setq end (progn (forward-char) (forward-sexp) (1- (point))))))) - (when (and beg end) - (and (<= (point) end) (>= (point) beg)))))) + "Non-nil if point is inside of embedded elisp %(sexp). +Assume sexps have been marked with +`org-capture-expand-embedded-elisp' beforehand." + (get-text-property (point) 'org-embedded-elisp)) ;;;###autoload (defun org-capture-import-remember-templates () @@ -1828,6 +1892,9 @@ Such keywords are prefixed with \"%:\". See (if jump-to-captured '(:jump-to-captured t))))) org-remember-templates)))) +;;; The function was made obsolete by commit 65399674d5 of +;;; 2013-02-22. This make-obsolete call was added 2016-09-01. +(make-obsolete 'org-capture-import-remember-templates "use the `org-capture-templates' variable instead." "Org 9.0") (provide 'org-capture) diff --git a/lisp/org/org-clock.el b/lisp/org/org-clock.el index 0bba92550f..cb6a6c9ad1 100644 --- a/lisp/org/org-clock.el +++ b/lisp/org/org-clock.el @@ -1,4 +1,4 @@ -;;; org-clock.el --- The time clocking code for Org-mode +;;; org-clock.el --- The time clocking code for Org mode -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -24,40 +24,49 @@ ;; ;;; Commentary: -;; This file contains the time clocking code for Org-mode +;; This file contains the time clocking code for Org mode ;;; Code: -(eval-when-compile - (require 'cl)) +(require 'cl-lib) (require 'org) (declare-function calendar-iso-to-absolute "cal-iso" (date)) (declare-function notifications-notify "notifications" (&rest params)) -(declare-function org-pop-to-buffer-same-window "org-compat" (&optional buffer-or-name norecord label)) -(declare-function org-refresh-properties "org" (dprop tprop)) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-type "org-element" (element)) +(declare-function org-table-goto-line "org-table" (n)) + +(defvar org-frame-title-format-backup frame-title-format) (defvar org-time-stamp-formats) (defvar org-ts-what) -(defvar org-frame-title-format-backup frame-title-format) + (defgroup org-clock nil - "Options concerning clocking working time in Org-mode." + "Options concerning clocking working time in Org mode." :tag "Org Clock" :group 'org-progress) -(defcustom org-clock-into-drawer org-log-into-drawer - "Should clocking info be wrapped into a drawer? -When t, clocking info will always be inserted into a :LOGBOOK: drawer. -If necessary, the drawer will be created. -When nil, the drawer will not be created, but used when present. -When an integer and the number of clocking entries in an item -reaches or exceeds this number, a drawer will be created. -When a string, it names the drawer to be used. - -The default for this variable is the value of `org-log-into-drawer', -which see." +(defcustom org-clock-into-drawer t + "Non-nil when clocking info should be wrapped into a drawer. + +When non-nil, clocking info will be inserted into the same drawer +as log notes (see variable `org-log-into-drawer'), if it exists, +or \"LOGBOOK\" otherwise. If necessary, the drawer will be +created. + +When an integer, the drawer is created only when the number of +clocking entries in an item reaches or exceeds this value. + +When a string, it becomes the name of the drawer, ignoring the +log notes drawer altogether. + +Do not check directly this variable in a Lisp program. Call +function `org-clock-into-drawer' instead." :group 'org-todo :group 'org-clock + :version "26.1" + :package-version '(Org . "8.3") :type '(choice (const :tag "Always" t) (const :tag "Only when drawer exists" nil) @@ -66,26 +75,29 @@ which see." (string :tag "Into Drawer named..."))) (defun org-clock-into-drawer () - "Return the value of `org-clock-into-drawer', but let properties overrule. + "Value of `org-clock-into-drawer'. but let properties overrule. + If the current entry has or inherits a CLOCK_INTO_DRAWER -property, it will be used instead of the default value; otherwise -if the current entry has or inherits a LOG_INTO_DRAWER property, -it will be used instead of the default value. -The default is the value of the customizable variable `org-clock-into-drawer', -which see." - (let ((p (org-entry-get nil "CLOCK_INTO_DRAWER" 'inherit)) - (q (org-entry-get nil "LOG_INTO_DRAWER" 'inherit))) - (cond - ((or (not (or p q)) (equal p "nil") (equal q "nil")) org-clock-into-drawer) - ((or (equal p "t") (equal q "t")) "LOGBOOK") - ((not p) q) - (t p)))) +property, it will be used instead of the default value. + +Return value is either a string, an integer, or nil." + (let ((p (org-entry-get nil "CLOCK_INTO_DRAWER" 'inherit t))) + (cond ((equal p "nil") nil) + ((equal p "t") (or (org-log-into-drawer) "LOGBOOK")) + ((org-string-nw-p p) + (if (string-match-p "\\`[0-9]+\\'" p) (string-to-number p) p)) + ((org-string-nw-p org-clock-into-drawer)) + ((integerp org-clock-into-drawer) org-clock-into-drawer) + ((not org-clock-into-drawer) nil) + ((org-log-into-drawer)) + (t "LOGBOOK")))) (defcustom org-clock-out-when-done t "When non-nil, clock will be stopped when the clocked entry is marked DONE. +\\\ DONE here means any DONE-like state. A nil value means clock will keep running until stopped explicitly with -`C-c C-x C-o', or until the clock is started in a different item. +`\\[org-clock-out]', or until the clock is started in a different item. Instead of t, this can also be a list of TODO states that should trigger clocking out." :group 'org-clock @@ -223,9 +235,6 @@ file name Play this sound file, fall back to beep" (const :tag "Standard beep" t) (file :tag "Play sound file"))) -(define-obsolete-variable-alias 'org-clock-modeline-total - 'org-clock-mode-line-total "24.3") - (defcustom org-clock-mode-line-total 'auto "Default setting for the time included for the mode line clock. This can be overruled locally using the CLOCK_MODELINE_TOTAL property. @@ -244,7 +253,7 @@ auto Automatically, either `all', or `repeat' for repeating tasks" (const :tag "All task time" all) (const :tag "Automatically, `all' or since `repeat'" auto))) -(org-defvaralias 'org-task-overrun-text 'org-clock-task-overrun-text) +(defvaralias 'org-task-overrun-text 'org-clock-task-overrun-text) (defcustom org-clock-task-overrun-text nil "Extra mode line text to indicate that the clock is overrun. The can be nil to indicate that instead of adding text, the clock time @@ -268,14 +277,14 @@ string as argument." (function :tag "Function"))) (defgroup org-clocktable nil - "Options concerning the clock table in Org-mode." + "Options concerning the clock table in Org mode." :tag "Org Clock Table" :group 'org-clock) (defcustom org-clocktable-defaults (list :maxlevel 2 - :lang (or (org-bound-and-true-p org-export-default-language) "en") + :lang (or (bound-and-true-p org-export-default-language) "en") :scope 'file :block nil :wstart 1 @@ -312,7 +321,9 @@ For more information, see `org-clocktable-write-default'." '(("en" "File" "L" "Timestamp" "Headline" "Time" "ALL" "Total time" "File time" "Clock summary at") ("es" "Archivo" "N" "Fecha y hora" "Tarea" "Tiempo" "TODO" "Tiempo total" "Tiempo archivo" "Clock summary at") ("fr" "Fichier" "N" "Horodatage" "En-tête" "Durée" "TOUT" "Durée totale" "Durée fichier" "Horodatage sommaire à") - ("nl" "Bestand" "N" "Tijdstip" "Hoofding" "Duur" "ALLES" "Totale duur" "Bestandstijd" "Clock summary at")) + ("nl" "Bestand" "N" "Tijdstip" "Hoofding" "Duur" "ALLES" "Totale duur" "Bestandstijd" "Clock summary at") + ("de" "Datei" "E" "Zeitstempel" "Kopfzeile" "Dauer" "GESAMT" + "Gesamtdauer" "Dateizeit" "Erstellt am")) "Terms used in clocktable, translated to different languages." :group 'org-clocktable :version "24.1" @@ -371,7 +382,7 @@ play with them." :type 'string) (defcustom org-clock-clocked-in-display 'mode-line - "When clocked in for a task, org-mode can display the current + "When clocked in for a task, Org can display the current task and accumulated time in the mode line and/or frame title. Allowed values are: @@ -413,6 +424,26 @@ if you are using Debian." :package-version '(Org . "8.0") :type 'string) +(defcustom org-clock-goto-before-context 2 + "Number of lines of context to display before currently clocked-in entry. +This applies when using `org-clock-goto'." + :group 'org-clock + :type 'integer) + +(defcustom org-clock-display-default-range 'thisyear + "Default range when displaying clocks with `org-clock-display'." + :group 'org-clock + :type '(choice (const today) + (const yesterday) + (const thisweek) + (const lastweek) + (const thismonth) + (const lastmonth) + (const thisyear) + (const lastyear) + (const untilnow) + (const :tag "Select range interactively" interactive))) + (defvar org-clock-in-prepare-hook nil "Hook run when preparing the clock. This hook is run before anything happens to the task that @@ -430,6 +461,33 @@ to add an effort property.") (defvar org-clock-has-been-used nil "Has the clock been used during the current Emacs session?") +(defvar org-clock-stored-history nil + "Clock history, populated by `org-clock-load'") +(defvar org-clock-stored-resume-clock nil + "Clock to resume, saved by `org-clock-load'") + +(defconst org-clock--oldest-date + (let* ((dichotomy + (lambda (min max pred) + (if (funcall pred min) min + (cl-incf min) + (while (> (- max min) 1) + (let ((mean (+ (ash min -1) (ash max -1) (logand min max 1)))) + (if (funcall pred mean) (setq max mean) (setq min mean))))) + max)) + (high + (funcall dichotomy + most-negative-fixnum + 0 + (lambda (m) (ignore-errors (decode-time (list m 0)))))) + (low + (funcall dichotomy + most-negative-fixnum + 0 + (lambda (m) (ignore-errors (decode-time (list high m))))))) + (list high low)) + "Internal time for oldest date representable on the system.") + ;;; The clock for measuring work time. (defvar org-mode-line-string "") @@ -500,8 +558,17 @@ of a different task.") (org-check-and-save-marker org-clock-hd-marker beg end) (org-check-and-save-marker org-clock-default-task beg end) (org-check-and-save-marker org-clock-interrupted-task beg end) - (mapc (lambda (m) (org-check-and-save-marker m beg end)) - org-clock-history)) + (dolist (m org-clock-history) + (org-check-and-save-marker m beg end))) + +(defun org-clock-drawer-name () + "Return clock drawer's name for current entry, or nil." + (let ((drawer (org-clock-into-drawer))) + (cond ((integerp drawer) + (let ((log-drawer (org-log-into-drawer))) + (if (stringp log-drawer) log-drawer "LOGBOOK"))) + ((stringp drawer) drawer) + (t nil)))) (defun org-clocking-buffer () "Return the clocking buffer if we are currently clocking a task or nil." @@ -519,8 +586,8 @@ of a different task.") (interactive) (let (och chl sel-list rpl (i 0) s) ;; Remove successive dups from the clock history to consider - (mapc (lambda (c) (if (not (equal c (car och))) (push c och))) - org-clock-history) + (dolist (c org-clock-history) + (unless (equal c (car och)) (push c och))) (setq och (reverse och) chl (length och)) (if (zerop chl) (user-error "No recent clock") @@ -541,17 +608,15 @@ of a different task.") (setq s (org-clock-insert-selection-line ?c org-clock-marker)) (push s sel-list)) (insert (org-add-props "Recent Tasks\n" nil 'face 'bold)) - (mapc - (lambda (m) - (when (marker-buffer m) - (setq i (1+ i) - s (org-clock-insert-selection-line - (if (< i 10) - (+ i ?0) - (+ i (- ?A 10))) m)) - (if (fboundp 'int-to-char) (setf (car s) (int-to-char (car s)))) - (push s sel-list))) - och) + (dolist (m och) + (when (marker-buffer m) + (setq i (1+ i) + s (org-clock-insert-selection-line + (if (< i 10) + (+ i ?0) + (+ i (- ?A 10))) m)) + (if (fboundp 'int-to-char) (setf (car s) (int-to-char (car s)))) + (push s sel-list))) (run-hooks 'org-clock-before-select-task-hook) (goto-char (point-min)) ;; Set min-height relatively to circumvent a possible but in @@ -559,6 +624,7 @@ of a different task.") (fit-window-to-buffer nil nil (if (< chl 10) chl (+ 5 chl))) (message (or prompt "Select task for clocking:")) (setq cursor-type nil rpl (read-char-exclusive)) + (kill-buffer) (cond ((eq rpl ?q) nil) ((eq rpl ?x) nil) @@ -570,25 +636,22 @@ of a different task.") And return a cons cell with the selection character integer and the marker pointing to it." (when (marker-buffer marker) - (let (file cat task heading prefix) + (let (cat task heading prefix) (with-current-buffer (org-base-buffer (marker-buffer marker)) - (save-excursion - (save-restriction - (widen) - (ignore-errors - (goto-char marker) - (setq file (buffer-file-name (marker-buffer marker)) - cat (org-get-category) - heading (org-get-heading 'notags) - prefix (save-excursion - (org-back-to-heading t) - (looking-at org-outline-regexp) - (match-string 0)) - task (substring - (org-fontify-like-in-org-mode - (concat prefix heading) - org-odd-levels-only) - (length prefix))))))) + (org-with-wide-buffer + (ignore-errors + (goto-char marker) + (setq cat (org-get-category) + heading (org-get-heading 'notags) + prefix (save-excursion + (org-back-to-heading t) + (looking-at org-outline-regexp) + (match-string 0)) + task (substring + (org-fontify-like-in-org-mode + (concat prefix heading) + org-odd-levels-only) + (length prefix)))))) (when (and cat task) (insert (format "[%c] %-12s %s\n" i cat task)) (cons i marker))))) @@ -608,19 +671,19 @@ If not, show simply the clocked time like 01:50." (let* ((effort-in-minutes (org-duration-string-to-minutes org-clock-effort)) (work-done-str - (org-propertize + (propertize (org-minutes-to-clocksum-string clocked-time) 'face (if (and org-clock-task-overrun (not org-clock-task-overrun-text)) 'org-mode-line-clock-overrun 'org-mode-line-clock))) (effort-str (org-minutes-to-clocksum-string effort-in-minutes)) - (clockstr (org-propertize + (clockstr (propertize (concat " [%s/" effort-str "] (" (replace-regexp-in-string "%" "%%" org-clock-heading) ")") 'face 'org-mode-line-clock))) (format clockstr work-done-str)) - (org-propertize (concat "[" (org-minutes-to-clocksum-string clocked-time) - (format " (%s)" org-clock-heading) "]") - 'face 'org-mode-line-clock)))) + (propertize (concat " [" (org-minutes-to-clocksum-string clocked-time) + "]" (format " (%s)" org-clock-heading)) + 'face 'org-mode-line-clock)))) (defun org-clock-get-last-clock-out-time () "Get the last clock-out time for the current subtree." @@ -635,20 +698,21 @@ If not, show simply the clocked time like 01:50." (org-clock-notify-once-if-expired) (setq org-clock-task-overrun nil)) (setq org-mode-line-string - (org-propertize + (propertize (let ((clock-string (org-clock-get-clock-string)) - (help-text "Org-mode clock is running.\nmouse-1 shows a menu\nmouse-2 will jump to task")) + (help-text "Org mode clock is running.\nmouse-1 shows a \ +menu\nmouse-2 will jump to task")) (if (and (> org-clock-string-limit 0) (> (length clock-string) org-clock-string-limit)) - (org-propertize + (propertize (substring clock-string 0 org-clock-string-limit) 'help-echo (concat help-text ": " org-clock-heading)) - (org-propertize clock-string 'help-echo help-text))) + (propertize clock-string 'help-echo help-text))) 'local-map org-clock-mode-line-map - 'mouse-face (if (featurep 'xemacs) 'highlight 'mode-line-highlight))) + 'mouse-face 'mode-line-highlight)) (if (and org-clock-task-overrun org-clock-task-overrun-text) (setq org-mode-line-string - (concat (org-propertize + (concat (propertize org-clock-task-overrun-text 'face 'org-mode-line-clock-overrun) org-mode-line-string))) (force-mode-line-update)) @@ -739,7 +803,7 @@ use libnotify if available, or fall back on a message." org-show-notification-handler notification)) ((fboundp 'notifications-notify) (notifications-notify - :title "Org-mode message" + :title "Org mode message" :body notification ;; FIXME how to link to the Org icon? ;; :app-icon "~/.emacs.d/icons/mail.png" @@ -776,11 +840,12 @@ If CLOCK-SOUND is non-nil, it overrides `org-clock-sound'." "Search through the given file and find all open clocks." (let ((buf (or (get-file-buffer file) (find-file-noselect file))) + (org-clock-re (concat org-clock-string " \\(\\[.*?\\]\\)$")) clocks) (with-current-buffer buf (save-excursion (goto-char (point-min)) - (while (re-search-forward "CLOCK: \\(\\[.*?\\]\\)$" nil t) + (while (re-search-forward org-clock-re nil t) (push (cons (copy-marker (match-end 1) t) (org-time-string-to-time (match-string 1))) clocks)))) clocks)) @@ -793,12 +858,10 @@ If CLOCK-SOUND is non-nil, it overrides `org-clock-sound'." (defmacro org-with-clock-position (clock &rest forms) "Evaluate FORMS with CLOCK as the current active clock." `(with-current-buffer (marker-buffer (car ,clock)) - (save-excursion - (save-restriction - (widen) - (goto-char (car ,clock)) - (beginning-of-line) - ,@forms)))) + (org-with-wide-buffer + (goto-char (car ,clock)) + (beginning-of-line) + ,@forms))) (def-edebug-spec org-with-clock-position (form body)) (put 'org-with-clock-position 'lisp-indent-function 1) @@ -812,7 +875,7 @@ This macro also protects the current active clock from being altered." (org-clock-effort) (org-clock-marker (car ,clock)) (org-clock-hd-marker (save-excursion - (outline-back-to-heading t) + (org-back-to-heading t) (point-marker)))) ,@forms))) (def-edebug-spec org-with-clock (form body)) @@ -885,7 +948,7 @@ If necessary, clock-out of the currently active clock." (defun org-clock-jump-to-current-clock (&optional effective-clock) (interactive) - (let ((org-clock-into-drawer (org-clock-into-drawer)) + (let ((drawer (org-clock-into-drawer)) (clock (or effective-clock (cons org-clock-marker org-clock-start-time)))) (unless (marker-buffer (car clock)) @@ -893,26 +956,21 @@ If necessary, clock-out of the currently active clock." (org-with-clock clock (org-clock-goto)) (with-current-buffer (marker-buffer (car clock)) (goto-char (car clock)) - (if org-clock-into-drawer - (let ((logbook - (if (stringp org-clock-into-drawer) - (concat ":" org-clock-into-drawer ":") - ":LOGBOOK:"))) - (ignore-errors - (outline-flag-region - (save-excursion - (outline-back-to-heading t) - (search-forward logbook) - (goto-char (match-beginning 0))) - (save-excursion - (outline-back-to-heading t) - (search-forward logbook) - (search-forward ":END:") - (goto-char (match-end 0))) - nil))))))) + (when drawer + (org-with-wide-buffer + (let ((drawer-re (format "^[ \t]*:%s:[ \t]*$" + (regexp-quote (if (stringp drawer) drawer "LOGBOOK")))) + (beg (save-excursion (org-back-to-heading t) (point)))) + (catch 'exit + (while (re-search-backward drawer-re beg t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'drawer) + (when (> (org-element-property :end element) (car clock)) + (org-flag-drawer nil element)) + (throw 'exit nil))))))))))) (defun org-clock-resolve (clock &optional prompt-fn last-valid fail-quietly) - "Resolve an open org-mode clock. + "Resolve an open Org clock. An open clock was found, with `dangling' possibly being non-nil. If this function was invoked with a prefix argument, non-dangling open clocks are ignored. The given clock requires some sort of @@ -930,7 +988,7 @@ The format of clock is (CONS MARKER START-TIME), where MARKER identifies the buffer and position the clock is open at (and thus, the heading it's under), and START-TIME is when the clock was started." - (assert clock) + (cl-assert clock) (let* ((ch (save-window-excursion (save-excursion @@ -947,7 +1005,7 @@ k/K Keep X minutes of the idle time (default is all). If this that many minutes after the time that idling began, and then clocked back in at the present time. -g/G Indicate that you “got back” X minutes ago. This is quite +g/G Indicate that you \"got back\" X minutes ago. This is quite different from `k': it clocks you out from the beginning of the idle period and clock you back in X minutes ago. @@ -963,10 +1021,6 @@ For all these options, using uppercase makes your final state to be CLOCKED OUT.")))) (org-fit-window-to-buffer (get-buffer-window "*Org Clock*")) (let (char-pressed) - (when (featurep 'xemacs) - (message (concat (funcall prompt-fn clock) - " [jkKgGsScCiq]? ")) - (setq char-pressed (read-char-exclusive))) (while (or (null char-pressed) (and (not (memq char-pressed '(?k ?K ?g ?G ?s ?S ?C @@ -1028,7 +1082,7 @@ to be CLOCKED OUT.")))) ;;;###autoload (defun org-resolve-clocks (&optional only-dangling-p prompt-fn last-valid) - "Resolve all currently open org-mode clocks. + "Resolve all currently open Org clocks. If `only-dangling-p' is non-nil, only ask to resolve dangling \(i.e., not currently open and valid) clocks." (interactive "P") @@ -1091,7 +1145,7 @@ This routine returns a floating point number." (defvar org-clock-user-idle-seconds) (defun org-resolve-clocks-if-idle () - "Resolve all currently open org-mode clocks. + "Resolve all currently open Org clocks. This is performed after `org-clock-idle-time' minutes, to check if the user really wants to stay clocked in after being idle for so long." @@ -1106,13 +1160,12 @@ so long." (org-clock-resolve (cons org-clock-marker org-clock-start-time) - (function - (lambda (clock) - (format "Clocked in & idle for %.1f mins" - (/ (float-time - (time-subtract (current-time) - org-clock-user-idle-start)) - 60.0)))) + (lambda (_) + (format "Clocked in & idle for %.1f mins" + (/ (float-time + (time-subtract (current-time) + org-clock-user-idle-start)) + 60.0))) org-clock-user-idle-start))))) (defvar org-clock-current-task nil "Task currently clocked in.") @@ -1122,18 +1175,27 @@ so long." ;;;###autoload (defun org-clock-in (&optional select start-time) "Start the clock on the current item. + If necessary, clock-out of the currently active clock. -With a prefix argument SELECT (\\[universal-argument]), offer a list of recently clocked -tasks to clock into. When SELECT is \\[universal-argument] \\[universal-argument], clock into the current task -and mark it as the default task, a special task that will always be offered -in the clocking selection, associated with the letter `d'. -When SELECT is \\[universal-argument] \\[universal-argument] \\[universal-argument], \ -clock in by using the last clock-out -time as the start time \(see `org-clock-continuously' to -make this the default behavior.)" + +With a `\\[universal-argument]' prefix argument SELECT, offer a list of \ +recently clocked +tasks to clock into. + +When SELECT is `\\[universal-argument] \ \\[universal-argument]', \ +clock into the current task and mark it as +the default task, a special task that will always be offered in the +clocking selection, associated with the letter `d'. + +When SELECT is `\\[universal-argument] \\[universal-argument] \ +\\[universal-argument]', clock in by using the last clock-out +time as the start time. See `org-clock-continuously' to make this +the default behavior." (interactive "P") (setq org-clock-notification-was-shown nil) - (org-refresh-properties org-effort-property 'org-effort) + (org-refresh-properties + org-effort-property '((effort . identity) + (effort-minutes . org-duration-string-to-minutes))) (catch 'abort (let ((interrupting (and (not org-clock-resolving-clocks-due-to-idleness) (org-clocking-p))) @@ -1148,7 +1210,7 @@ make this the default behavior.)" (not org-clock-resolving-clocks)) (setq org-clock-leftover-time nil) (let ((org-clock-clocking-in t)) - (org-resolve-clocks))) ; check if any clocks are dangling + (org-resolve-clocks))) ; check if any clocks are dangling (when (equal select '(64)) ;; Set start-time to `org-clock-out-time' @@ -1201,116 +1263,116 @@ make this the default behavior.)" (set-buffer (org-base-buffer (marker-buffer selected-task))) (setq target-pos (marker-position selected-task)) (move-marker selected-task nil)) - (save-excursion - (save-restriction - (widen) - (goto-char target-pos) - (org-back-to-heading t) - (or interrupting (move-marker org-clock-interrupted-task nil)) - (run-hooks 'org-clock-in-prepare-hook) - (org-clock-history-push) - (setq org-clock-current-task (nth 4 (org-heading-components))) - (cond ((functionp org-clock-in-switch-to-state) - (looking-at org-complex-heading-regexp) - (let ((newstate (funcall org-clock-in-switch-to-state - (match-string 2)))) - (if newstate (org-todo newstate)))) - ((and org-clock-in-switch-to-state - (not (looking-at (concat org-outline-regexp "[ \t]*" - org-clock-in-switch-to-state - "\\>")))) - (org-todo org-clock-in-switch-to-state))) - (setq org-clock-heading - (cond ((and org-clock-heading-function - (functionp org-clock-heading-function)) - (funcall org-clock-heading-function)) - ((nth 4 (org-heading-components)) - (replace-regexp-in-string - "\\[\\[.*?\\]\\[\\(.*?\\)\\]\\]" "\\1" - (match-string-no-properties 4))) - (t "???"))) - (org-clock-find-position org-clock-in-resume) - (cond - ((and org-clock-in-resume - (looking-at - (concat "^[ \t]*" org-clock-string - " \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" - " *\\sw+.? +[012][0-9]:[0-5][0-9]\\)\\][ \t]*$"))) - (message "Matched %s" (match-string 1)) - (setq ts (concat "[" (match-string 1) "]")) - (goto-char (match-end 1)) - (setq org-clock-start-time - (apply 'encode-time - (org-parse-time-string (match-string 1)))) - (setq org-clock-effort (org-entry-get (point) org-effort-property)) - (setq org-clock-total-time (org-clock-sum-current-item - (org-clock-get-sum-start)))) - ((eq org-clock-in-resume 'auto-restart) - ;; called from org-clock-load during startup, - ;; do not interrupt, but warn! - (message "Cannot restart clock because task does not contain unfinished clock") - (ding) - (sit-for 2) - (throw 'abort nil)) - (t - (insert-before-markers "\n") - (backward-char 1) - (org-indent-line) - (when (and (save-excursion - (end-of-line 0) - (org-in-item-p))) - (beginning-of-line 1) - (org-indent-line-to (- (org-get-indentation) 2))) - (insert org-clock-string " ") - (setq org-clock-effort (org-entry-get (point) org-effort-property)) - (setq org-clock-total-time (org-clock-sum-current-item - (org-clock-get-sum-start))) - (setq org-clock-start-time - (or (and org-clock-continuously org-clock-out-time) - (and leftover - (y-or-n-p - (format - "You stopped another clock %d mins ago; start this one from then? " - (/ (- (float-time - (org-current-time org-clock-rounding-minutes t)) - (float-time leftover)) 60))) - leftover) - start-time - (org-current-time org-clock-rounding-minutes t))) - (setq ts (org-insert-time-stamp org-clock-start-time - 'with-hm 'inactive)))) - (move-marker org-clock-marker (point) (buffer-base-buffer)) - (move-marker org-clock-hd-marker - (save-excursion (org-back-to-heading t) (point)) - (buffer-base-buffer)) - (setq org-clock-has-been-used t) - ;; add to mode line - (when (or (eq org-clock-clocked-in-display 'mode-line) - (eq org-clock-clocked-in-display 'both)) - (or global-mode-string (setq global-mode-string '(""))) - (or (memq 'org-mode-line-string global-mode-string) - (setq global-mode-string - (append global-mode-string '(org-mode-line-string))))) - ;; add to frame title - (when (or (eq org-clock-clocked-in-display 'frame-title) - (eq org-clock-clocked-in-display 'both)) - (setq frame-title-format org-clock-frame-title-format)) - (org-clock-update-mode-line) - (when org-clock-mode-line-timer - (cancel-timer org-clock-mode-line-timer) - (setq org-clock-mode-line-timer nil)) - (when org-clock-clocked-in-display - (setq org-clock-mode-line-timer - (run-with-timer org-clock-update-period - org-clock-update-period - 'org-clock-update-mode-line))) - (when org-clock-idle-timer - (cancel-timer org-clock-idle-timer) - (setq org-clock-idle-timer nil)) - (setq org-clock-idle-timer - (run-with-timer 60 60 'org-resolve-clocks-if-idle)) - (message "Clock starts at %s - %s" ts org--msg-extra) - (run-hooks 'org-clock-in-hook))))))) + (org-with-wide-buffer + (goto-char target-pos) + (org-back-to-heading t) + (or interrupting (move-marker org-clock-interrupted-task nil)) + (run-hooks 'org-clock-in-prepare-hook) + (org-clock-history-push) + (setq org-clock-current-task (nth 4 (org-heading-components))) + (cond ((functionp org-clock-in-switch-to-state) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp)) + (let ((newstate (funcall org-clock-in-switch-to-state + (match-string 2)))) + (when newstate (org-todo newstate)))) + ((and org-clock-in-switch-to-state + (not (looking-at (concat org-outline-regexp "[ \t]*" + org-clock-in-switch-to-state + "\\>")))) + (org-todo org-clock-in-switch-to-state))) + (setq org-clock-heading + (cond ((and org-clock-heading-function + (functionp org-clock-heading-function)) + (funcall org-clock-heading-function)) + ((nth 4 (org-heading-components)) + (replace-regexp-in-string + "\\[\\[.*?\\]\\[\\(.*?\\)\\]\\]" "\\1" + (match-string-no-properties 4))) + (t "???"))) + (org-clock-find-position org-clock-in-resume) + (cond + ((and org-clock-in-resume + (looking-at + (concat "^[ \t]*" org-clock-string + " \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" + " *\\sw+.? +[012][0-9]:[0-5][0-9]\\)\\][ \t]*$"))) + (message "Matched %s" (match-string 1)) + (setq ts (concat "[" (match-string 1) "]")) + (goto-char (match-end 1)) + (setq org-clock-start-time + (apply 'encode-time + (org-parse-time-string (match-string 1)))) + (setq org-clock-effort (org-entry-get (point) org-effort-property)) + (setq org-clock-total-time (org-clock-sum-current-item + (org-clock-get-sum-start)))) + ((eq org-clock-in-resume 'auto-restart) + ;; called from org-clock-load during startup, + ;; do not interrupt, but warn! + (message "Cannot restart clock because task does not contain unfinished clock") + (ding) + (sit-for 2) + (throw 'abort nil)) + (t + (insert-before-markers "\n") + (backward-char 1) + (org-indent-line) + (when (and (save-excursion + (end-of-line 0) + (org-in-item-p))) + (beginning-of-line 1) + (indent-line-to (- (org-get-indentation) 2))) + (insert org-clock-string " ") + (setq org-clock-effort (org-entry-get (point) org-effort-property)) + (setq org-clock-total-time (org-clock-sum-current-item + (org-clock-get-sum-start))) + (setq org-clock-start-time + (or (and org-clock-continuously org-clock-out-time) + (and leftover + (y-or-n-p + (format + "You stopped another clock %d mins ago; start this one from then? " + (/ (- (float-time + (org-current-time org-clock-rounding-minutes t)) + (float-time leftover)) + 60))) + leftover) + start-time + (org-current-time org-clock-rounding-minutes t))) + (setq ts (org-insert-time-stamp org-clock-start-time + 'with-hm 'inactive)))) + (move-marker org-clock-marker (point) (buffer-base-buffer)) + (move-marker org-clock-hd-marker + (save-excursion (org-back-to-heading t) (point)) + (buffer-base-buffer)) + (setq org-clock-has-been-used t) + ;; add to mode line + (when (or (eq org-clock-clocked-in-display 'mode-line) + (eq org-clock-clocked-in-display 'both)) + (or global-mode-string (setq global-mode-string '(""))) + (or (memq 'org-mode-line-string global-mode-string) + (setq global-mode-string + (append global-mode-string '(org-mode-line-string))))) + ;; add to frame title + (when (or (eq org-clock-clocked-in-display 'frame-title) + (eq org-clock-clocked-in-display 'both)) + (setq frame-title-format org-clock-frame-title-format)) + (org-clock-update-mode-line) + (when org-clock-mode-line-timer + (cancel-timer org-clock-mode-line-timer) + (setq org-clock-mode-line-timer nil)) + (when org-clock-clocked-in-display + (setq org-clock-mode-line-timer + (run-with-timer org-clock-update-period + org-clock-update-period + 'org-clock-update-mode-line))) + (when org-clock-idle-timer + (cancel-timer org-clock-idle-timer) + (setq org-clock-idle-timer nil)) + (setq org-clock-idle-timer + (run-with-timer 60 60 'org-resolve-clocks-if-idle)) + (message "Clock starts at %s - %s" ts org--msg-extra) + (run-hooks 'org-clock-in-hook)))))) ;;;###autoload (defun org-clock-in-last (&optional arg) @@ -1324,8 +1386,7 @@ With three universal prefix arguments, interactively prompt for a todo state to switch to, overriding the existing value `org-clock-in-switch-to-state'." (interactive "P") - (if (equal arg '(4)) - (org-clock-in (org-clock-select-task)) + (if (equal arg '(4)) (org-clock-in arg) (let ((start-time (if (or org-clock-continuously (equal arg '(16))) (or org-clock-out-time (org-current-time org-clock-rounding-minutes t)) @@ -1371,10 +1432,12 @@ decides which time to use." (current-time)) ((equal cmt "today") (setq org--msg-extra "showing today's task time.") - (let* ((dt (decode-time))) - (setq dt (append (list 0 0 0) (nthcdr 3 dt))) - (if org-extend-today-until - (setf (nth 2 dt) org-extend-today-until)) + (let* ((dt (decode-time)) + (hour (nth 2 dt)) + (day (nth 3 dt))) + (if (< hour org-extend-today-until) (setf (nth 3 dt) (1- day))) + (setf (nth 2 dt) org-extend-today-until) + (setq dt (append (list 0 0) (nthcdr 2 dt))) (apply 'encode-time dt))) ((or (equal cmt "all") (and (or (not cmt) (equal cmt "auto")) @@ -1396,87 +1459,93 @@ When FIND-UNCLOSED is non-nil, first check if there is an unclosed clock line and position cursor in that line." (org-back-to-heading t) (catch 'exit - (let* ((org-clock-into-drawer (org-clock-into-drawer)) - (beg (save-excursion - (beginning-of-line 2) - (or (bolp) (newline)) - (point))) - (end (progn (outline-next-heading) (point))) - (re (concat "^[ \t]*" org-clock-string)) - (cnt 0) - (drawer (if (stringp org-clock-into-drawer) - org-clock-into-drawer "LOGBOOK")) - first last ind-last) - (goto-char beg) - (when (and find-unclosed - (re-search-forward - (concat "^[ \t]*" org-clock-string - " \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" - " *\\sw+ +[012][0-9]:[0-5][0-9]\\)\\][ \t]*$") - end t)) - (beginning-of-line 1) - (throw 'exit t)) - (when (eobp) (newline) (setq end (max (point) end))) - (when (re-search-forward (concat "^[ \t]*:" drawer ":") end t) - ;; we seem to have a CLOCK drawer, so go there. - (beginning-of-line 2) - (or org-log-states-order-reversed - (and (re-search-forward org-property-end-re nil t) - (goto-char (match-beginning 0)))) - (throw 'exit t)) - ;; Lets count the CLOCK lines + (let* ((beg (line-beginning-position)) + (end (save-excursion (outline-next-heading) (point))) + (org-clock-into-drawer (org-clock-into-drawer)) + (drawer (org-clock-drawer-name))) + ;; Look for a running clock if FIND-UNCLOSED in non-nil. + (when find-unclosed + (let ((open-clock-re + (concat "^[ \t]*" + org-clock-string + " \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" + " *\\sw+ +[012][0-9]:[0-5][0-9]\\)\\][ \t]*$"))) + (while (re-search-forward open-clock-re end t) + (let ((element (org-element-at-point))) + (when (and (eq (org-element-type element) 'clock) + (eq (org-element-property :status element) 'running)) + (beginning-of-line) + (throw 'exit t)))))) + ;; Look for an existing clock drawer. + (when drawer + (goto-char beg) + (let ((drawer-re (concat "^[ \t]*:" (regexp-quote drawer) ":[ \t]*$"))) + (while (re-search-forward drawer-re end t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'drawer) + (let ((cend (org-element-property :contents-end element))) + (if (and (not org-log-states-order-reversed) cend) + (goto-char cend) + (forward-line)) + (throw 'exit t))))))) (goto-char beg) - (while (re-search-forward re end t) - (setq first (or first (match-beginning 0)) - last (match-beginning 0) - cnt (1+ cnt))) - (when (and (integerp org-clock-into-drawer) - last - (>= (1+ cnt) org-clock-into-drawer)) - ;; Wrap current entries into a new drawer - (goto-char last) - (setq ind-last (org-get-indentation)) - (beginning-of-line 2) - (if (and (>= (org-get-indentation) ind-last) - (org-at-item-p)) - (when (and (>= (org-get-indentation) ind-last) - (org-at-item-p)) - (let ((struct (org-list-struct))) - (goto-char (org-list-get-bottom-point struct))))) - (insert ":END:\n") - (beginning-of-line 0) - (org-indent-line-to ind-last) - (goto-char first) - (insert ":" drawer ":\n") - (beginning-of-line 0) - (org-indent-line) - (org-flag-drawer t) - (beginning-of-line 2) - (or org-log-states-order-reversed - (and (re-search-forward org-property-end-re nil t) - (goto-char (match-beginning 0)))) - (throw 'exit nil)) - - (goto-char beg) - (while (and (looking-at (concat "[ \t]*" org-keyword-time-regexp)) - (not (equal (match-string 1) org-clock-string))) - ;; Planning info, skip to after it - (beginning-of-line 2) - (or (bolp) (newline))) - (when (or (eq org-clock-into-drawer t) - (stringp org-clock-into-drawer) - (and (integerp org-clock-into-drawer) - (< org-clock-into-drawer 2))) - (insert ":" drawer ":\n:END:\n") - (beginning-of-line -1) - (org-indent-line) - (org-flag-drawer t) - (beginning-of-line 2) - (org-indent-line) - (beginning-of-line) - (or org-log-states-order-reversed - (and (re-search-forward org-property-end-re nil t) - (goto-char (match-beginning 0)))))))) + (let ((clock-re (concat "^[ \t]*" org-clock-string)) + (count 0) + positions) + ;; Count the CLOCK lines and store their positions. + (save-excursion + (while (re-search-forward clock-re end t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'clock) + (setq positions (cons (line-beginning-position) positions) + count (1+ count)))))) + (cond + ((null positions) + ;; Skip planning line and property drawer, if any. + (org-end-of-meta-data) + (unless (bolp) (insert "\n")) + ;; Create a new drawer if necessary. + (when (and org-clock-into-drawer + (or (not (wholenump org-clock-into-drawer)) + (< org-clock-into-drawer 2))) + (let ((beg (point))) + (insert ":" drawer ":\n:END:\n") + (org-indent-region beg (point)) + (goto-char beg) + (org-flag-drawer t) + (forward-line)))) + ;; When a clock drawer needs to be created because of the + ;; number of clock items or simply if it is missing, collect + ;; all clocks in the section and wrap them within the drawer. + ((if (wholenump org-clock-into-drawer) + (>= (1+ count) org-clock-into-drawer) + drawer) + ;; Skip planning line and property drawer, if any. + (org-end-of-meta-data) + (let ((beg (point))) + (insert + (mapconcat + (lambda (p) + (save-excursion + (goto-char p) + (org-trim (delete-and-extract-region + (save-excursion (skip-chars-backward " \r\t\n") + (line-beginning-position 2)) + (line-beginning-position 2))))) + positions "\n") + "\n:END:\n") + (let ((end (point-marker))) + (goto-char beg) + (save-excursion (insert ":" drawer ":\n")) + (org-flag-drawer t) + (org-indent-region (point) end) + (forward-line) + (unless org-log-states-order-reversed + (goto-char end) + (beginning-of-line -1)) + (set-marker end nil)))) + (org-log-states-order-reversed (goto-char (car (last positions)))) + (t (goto-char (car positions)))))))) ;;;###autoload (defun org-clock-out (&optional switch-to-state fail-quietly at-time) @@ -1504,7 +1573,7 @@ to, overriding the existing value of `org-clock-out-switch-to-state'." ts te s h m remove) (setq org-clock-out-time now) (save-excursion ; Do not replace this with `with-current-buffer'. - (org-no-warnings (set-buffer (org-clocking-buffer))) + (with-no-warnings (set-buffer (org-clocking-buffer))) (save-restriction (widen) (goto-char org-clock-marker) @@ -1517,24 +1586,28 @@ to, overriding the existing value of `org-clock-out-switch-to-state'." (delete-region (point) (point-at-eol)) (insert "--") (setq te (org-insert-time-stamp (or at-time now) 'with-hm 'inactive)) - (setq s (- (float-time (apply 'encode-time (org-parse-time-string te))) - (float-time (apply 'encode-time (org-parse-time-string ts)))) + (setq s (- (float-time + (apply #'encode-time (org-parse-time-string te nil t))) + (float-time + (apply #'encode-time (org-parse-time-string ts nil t)))) h (floor (/ s 3600)) s (- s (* 3600 h)) m (floor (/ s 60)) s (- s (* 60 s))) (insert " => " (format "%2d:%02d" h m)) - (when (setq remove (and org-clock-out-remove-zero-time-clocks - (= (+ h m) 0))) - (beginning-of-line 1) - (delete-region (point) (point-at-eol)) - (and (looking-at "\n") (> (point-max) (1+ (point))) - (delete-char 1))) (move-marker org-clock-marker nil) (move-marker org-clock-hd-marker nil) - (when org-log-note-clock-out - (org-add-log-setup 'clock-out nil nil nil nil - (concat "# Task: " (org-get-heading t) "\n\n"))) + ;; Possibly remove zero time clocks. However, do not add + ;; a note associated to the CLOCK line in this case. + (cond ((and org-clock-out-remove-zero-time-clocks + (= (+ h m) 0)) + (setq remove t) + (delete-region (line-beginning-position) + (line-beginning-position 2))) + (org-log-note-clock-out + (org-add-log-setup + 'clock-out nil nil nil + (concat "# Task: " (org-get-heading t) "\n\n")))) (when org-clock-mode-line-timer (cancel-timer org-clock-mode-line-timer) (setq org-clock-mode-line-timer nil)) @@ -1551,10 +1624,11 @@ to, overriding the existing value of `org-clock-out-switch-to-state'." (org-clock-out-when-done nil)) (cond ((functionp org-clock-out-switch-to-state) - (looking-at org-complex-heading-regexp) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp)) (let ((newstate (funcall org-clock-out-switch-to-state (match-string 2)))) - (if newstate (org-todo newstate)))) + (when newstate (org-todo newstate)))) ((and org-clock-out-switch-to-state (not (looking-at (concat org-outline-regexp "[ \t]*" org-clock-out-switch-to-state @@ -1564,34 +1638,25 @@ to, overriding the existing value of `org-clock-out-switch-to-state'." (message (concat "Clock stopped at %s after " (org-minutes-to-clocksum-string (+ (* 60 h) m)) "%s") te (if remove " => LINE REMOVED" "")) - (let ((h org-clock-out-hook)) - ;; If a closing note needs to be stored in the drawer - ;; where clocks are stored, let's temporarily disable - ;; `org-clock-remove-empty-clock-drawer' - (if (and (equal org-clock-into-drawer org-log-into-drawer) - (eq org-log-done 'note) - org-clock-out-when-done) - (setq h (delq 'org-clock-remove-empty-clock-drawer h))) - (mapc (lambda (f) (funcall f)) h)) + (run-hooks 'org-clock-out-hook) (unless (org-clocking-p) (setq org-clock-current-task nil))))))) (add-hook 'org-clock-out-hook 'org-clock-remove-empty-clock-drawer) -(defun org-clock-remove-empty-clock-drawer nil - "Remove empty clock drawer in the current subtree." - (let* ((olid (or (org-entry-get (point) "LOG_INTO_DRAWER") - org-log-into-drawer)) - (clock-drawer (if (eq t olid) "LOGBOOK" olid)) - (end (save-excursion (org-end-of-subtree t t)))) - (when clock-drawer - (save-excursion - (org-back-to-heading t) - (while (and (< (point) end) - (search-forward clock-drawer end t)) - (goto-char (match-beginning 0)) - (org-remove-empty-drawer-at clock-drawer (point)) - (forward-line 1)))))) +(defun org-clock-remove-empty-clock-drawer () + "Remove empty clock drawers in current subtree." + (save-excursion + (org-back-to-heading t) + (org-map-tree + (lambda () + (let ((drawer (org-clock-drawer-name)) + (case-fold-search t)) + (when drawer + (let ((re (format "^[ \t]*:%s:[ \t]*$" (regexp-quote drawer))) + (end (save-excursion (outline-next-heading)))) + (while (re-search-forward re end t) + (org-remove-empty-drawer-at (point)))))))))) (defun org-clock-timestamps-up (&optional n) "Increase CLOCK timestamps at cursor. @@ -1607,7 +1672,7 @@ Optional argument N tells to change by that many units." (defun org-clock-timestamps-change (updown &optional n) "Change CLOCK timestamps synchronously at cursor. -UPDOWN tells whether to change 'up or 'down. +UPDOWN tells whether to change `up' or `down'. Optional argument N tells to change by that many units." (setq org-ts-what nil) (when (org-at-timestamp-p t) @@ -1654,13 +1719,13 @@ Optional argument N tells to change by that many units." (setq frame-title-format org-frame-title-format-backup) (force-mode-line-update) (error "No active clock")) - (save-excursion ; Do not replace this with `with-current-buffer'. - (org-no-warnings (set-buffer (org-clocking-buffer))) + (save-excursion ; Do not replace this with `with-current-buffer'. + (with-no-warnings (set-buffer (org-clocking-buffer))) (goto-char org-clock-marker) - (if (org-looking-back (concat "^[ \t]*" org-clock-string ".*") - (line-beginning-position)) + (if (looking-back (concat "^[ \t]*" org-clock-string ".*") + (line-beginning-position)) (progn (delete-region (1- (point-at-bol)) (point-at-eol)) - (org-remove-empty-drawer-at "LOGBOOK" (point))) + (org-remove-empty-drawer-at (point))) (message "Clock gone, cancel the timer anyway") (sit-for 2))) (move-marker org-clock-marker nil) @@ -1672,12 +1737,6 @@ Optional argument N tells to change by that many units." (message "Clock canceled") (run-hooks 'org-clock-cancel-hook)) -(defcustom org-clock-goto-before-context 2 - "Number of lines of context to display before currently clocked-in entry. -This applies when using `org-clock-goto'." - :group 'org-clock - :type 'integer) - ;;;###autoload (defun org-clock-goto (&optional select) "Go to the currently clocked-in entry, or to the most recently clocked one. @@ -1695,7 +1754,7 @@ With prefix arg SELECT, offer recently clocked tasks for selection." (setq recent t) (car org-clock-history)) (t (error "No active or recent clock task"))))) - (org-pop-to-buffer-same-window (marker-buffer m)) + (pop-to-buffer-same-window (marker-buffer m)) (if (or (< m (point-min)) (> m (point-max))) (widen)) (goto-char m) (org-show-entry) @@ -1707,15 +1766,27 @@ With prefix arg SELECT, offer recently clocked tasks for selection." (message "No running clock, this is the most recently clocked task")) (run-hooks 'org-clock-goto-hook))) -(defvar org-clock-file-total-minutes nil +(defvar-local org-clock-file-total-minutes nil "Holds the file total time in minutes, after a call to `org-clock-sum'.") -(make-variable-buffer-local 'org-clock-file-total-minutes) (defun org-clock-sum-today (&optional headline-filter) "Sum the times for each subtree for today." - (interactive) (let ((range (org-clock-special-range 'today))) - (org-clock-sum (car range) (cadr range) nil :org-clock-minutes-today))) + (org-clock-sum (car range) (cadr range) + headline-filter :org-clock-minutes-today))) + +(defun org-clock-sum-custom (&optional headline-filter range propname) + "Sum the times for each subtree for today." + (let ((r (or (and (symbolp range) (org-clock-special-range range)) + (org-clock-special-range + (intern (completing-read + "Range: " + '("today" "yesterday" "thisweek" "lastweek" + "thismonth" "lastmonth" "thisyear" "lastyear" + "interactive") + nil t)))))) + (org-clock-sum (car r) (cadr r) + headline-filter (or propname :org-clock-minutes-custom)))) ;;;###autoload (defun org-clock-sum (&optional tstart tend headline-filter propname) @@ -1726,7 +1797,6 @@ HEADLINE-FILTER is a zero-arg function that, if specified, is called for each headline in the time range with point at the headline. Headlines for which HEADLINE-FILTER returns nil are excluded from the clock summation. PROPNAME lets you set a custom text property instead of :org-clock-minutes." - (interactive) (org-with-silent-modifications (let* ((re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*" org-clock-string @@ -1753,9 +1823,9 @@ PROPNAME lets you set a custom text property instead of :org-clock-minutes." (setq ts (match-string 2) te (match-string 3) ts (float-time - (apply 'encode-time (org-parse-time-string ts))) + (apply #'encode-time (org-parse-time-string ts nil t))) te (float-time - (apply 'encode-time (org-parse-time-string te))) + (apply #'encode-time (org-parse-time-string te nil t))) ts (if tstart (max ts tstart) ts) te (if tend (min te tend) te) dt (- te ts) @@ -1774,7 +1844,8 @@ PROPNAME lets you set a custom text property instead of :org-clock-minutes." (>= (float-time org-clock-start-time) tstart) (<= (float-time org-clock-start-time) tend)) (let ((time (floor (- (float-time) - (float-time org-clock-start-time)) 60))) + (float-time org-clock-start-time)) + 60))) (setq t1 (+ t1 time)))) (let* ((headline-forced (get-text-property (point) @@ -1784,27 +1855,27 @@ PROPNAME lets you set a custom text property instead of :org-clock-minutes." (save-excursion (save-match-data (funcall headline-filter)))))) (setq level (- (match-end 1) (match-beginning 1))) + (when (>= level lmax) + (setq ltimes (vconcat ltimes (make-vector lmax 0)) lmax (* 2 lmax))) (when (or (> t1 0) (> (aref ltimes level) 0)) (when (or headline-included headline-forced) (if headline-included - (loop for l from 0 to level do - (aset ltimes l (+ (aref ltimes l) t1)))) + (cl-loop for l from 0 to level do + (aset ltimes l (+ (aref ltimes l) t1)))) (setq time (aref ltimes level)) (goto-char (match-beginning 0)) (put-text-property (point) (point-at-eol) (or propname :org-clock-minutes) time) - (if headline-filter - (save-excursion - (save-match-data - (while - (> (funcall outline-level) 1) - (outline-up-heading 1 t) - (put-text-property - (point) (point-at-eol) - :org-clock-force-headline-inclusion t)))))) + (when headline-filter + (save-excursion + (save-match-data + (while (org-up-heading-safe) + (put-text-property + (point) (line-end-position) + :org-clock-force-headline-inclusion t)))))) (setq t1 0) - (loop for l from level to (1- lmax) do - (aset ltimes l 0))))))) + (cl-loop for l from level to (1- lmax) do + (aset ltimes l 0))))))) (setq org-clock-file-total-minutes (aref ltimes 0)))))) (defun org-clock-sum-current-item (&optional tstart) @@ -1816,74 +1887,99 @@ PROPNAME lets you set a custom text property instead of :org-clock-minutes." org-clock-file-total-minutes))) ;;;###autoload -(defun org-clock-display (&optional total-only) +(defun org-clock-display (&optional arg) "Show subtree times in the entire buffer. -If TOTAL-ONLY is non-nil, only show the total time for the entire file -in the echo area. -Use \\[org-clock-remove-overlays] to remove the subtree times." - (interactive) +By default, show the total time for the range defined in +`org-clock-display-default-range'. With `\\[universal-argument]' \ +prefix, show +the total time for today instead. + +With `\\[universal-argument] \\[universal-argument]' prefix, \ +use a custom range, entered at prompt. + +With `\\[universal-argument] \ \\[universal-argument] \ +\\[universal-argument]' prefix, display the total time in the +echo area. + +Use `\\[org-clock-remove-overlays]' to remove the subtree times." + (interactive "P") (org-clock-remove-overlays) - (let (time h m p) - (org-clock-sum) - (unless total-only + (let* ((todayp (equal arg '(4))) + (customp (member arg '((16) today yesterday + thisweek lastweek thismonth + lastmonth thisyear lastyear + untilnow interactive))) + (prop (cond ((not arg) :org-clock-minutes-default) + (todayp :org-clock-minutes-today) + (customp :org-clock-minutes-custom) + (t :org-clock-minutes))) + time h m p) + (cond ((not arg) (org-clock-sum-custom + nil org-clock-display-default-range prop)) + (todayp (org-clock-sum-today)) + (customp (org-clock-sum-custom nil arg)) + (t (org-clock-sum))) + (unless (eq arg '(64)) (save-excursion (goto-char (point-min)) (while (or (and (equal (setq p (point)) (point-min)) - (get-text-property p :org-clock-minutes)) + (get-text-property p prop)) (setq p (next-single-property-change - (point) :org-clock-minutes))) + (point) prop))) (goto-char p) - (when (setq time (get-text-property p :org-clock-minutes)) - (org-clock-put-overlay time (funcall outline-level)))) + (when (setq time (get-text-property p prop)) + (org-clock-put-overlay time))) (setq h (/ org-clock-file-total-minutes 60) m (- org-clock-file-total-minutes (* 60 h))) ;; Arrange to remove the overlays upon next change. (when org-remove-highlights-with-change - (org-add-hook 'before-change-functions 'org-clock-remove-overlays + (add-hook 'before-change-functions 'org-clock-remove-overlays nil 'local)))) - (message (concat "Total file time: " - (org-minutes-to-clocksum-string org-clock-file-total-minutes) - " (%d hours and %d minutes)") h m))) - -(defvar org-clock-overlays nil) -(make-variable-buffer-local 'org-clock-overlays) - -(defun org-clock-put-overlay (time &optional level) + (message (concat (format "Total file time%s: " + (cond (todayp " for today") + (customp " (custom)") + (t ""))) + (org-minutes-to-clocksum-string + org-clock-file-total-minutes) + " (%d hours and %d minutes)") + h m))) + +(defvar-local org-clock-overlays nil) + +(defun org-clock-put-overlay (time) "Put an overlays on the current line, displaying TIME. -If LEVEL is given, prefix time with a corresponding number of stars. This creates a new overlay and stores it in `org-clock-overlays', so that it will be easy to remove." - (let* ((l (if level (org-get-valid-level level 0) 0)) - ov tx) + (let (ov tx) (beginning-of-line) - (when (looking-at org-complex-heading-regexp) - (goto-char (match-beginning 4))) + (let ((case-fold-search nil)) + (when (looking-at org-complex-heading-regexp) + (goto-char (match-beginning 4)))) (setq ov (make-overlay (point) (point-at-eol)) - tx (concat (buffer-substring-no-properties (point) (match-end 4)) - (make-string - (max 0 (- (- 60 (current-column)) - (- (match-end 4) (match-beginning 4)) - (length (org-get-at-bol 'line-prefix)))) ?.) - (org-add-props (concat (make-string l ?*) " " - (org-minutes-to-clocksum-string time) - (make-string (- 16 l) ?\ )) - (list 'face 'org-clock-overlay)) + tx (concat (buffer-substring-no-properties (point) (match-end 4)) + (org-add-props + (make-string + (max 0 (- (- 60 (current-column)) + (- (match-end 4) (match-beginning 4)) + (length (org-get-at-bol 'line-prefix)))) + ?\·) + '(face shadow)) + (org-add-props + (format " %9s " (org-minutes-to-clocksum-string time)) + '(face org-clock-overlay)) "")) - (if (not (featurep 'xemacs)) - (overlay-put ov 'display tx) - (overlay-put ov 'invisible t) - (overlay-put ov 'end-glyph (make-glyph tx))) + (overlay-put ov 'display tx) (push ov org-clock-overlays))) ;;;###autoload -(defun org-clock-remove-overlays (&optional beg end noremove) +(defun org-clock-remove-overlays (&optional _beg _end noremove) "Remove the occur highlights from the buffer. -BEG and END are ignored. If NOREMOVE is nil, remove this function -from the `before-change-functions' in the current buffer." +If NOREMOVE is nil, remove this function from the +`before-change-functions' in the current buffer." (interactive) (unless org-inhibit-highlight-removal - (mapc 'delete-overlay org-clock-overlays) + (mapc #'delete-overlay org-clock-overlays) (setq org-clock-overlays nil) (unless noremove (remove-hook 'before-change-functions @@ -2020,127 +2116,159 @@ buffer and update it." (defun org-clock-special-range (key &optional time as-strings wstart mstart) "Return two times bordering a special time range. -Key is a symbol specifying the range and can be one of `today', `yesterday', -`thisweek', `lastweek', `thismonth', `lastmonth', `thisyear', `lastyear'. -By default, a week starts Monday 0:00 and ends Sunday 24:00. -The range is determined relative to TIME, which defaults to current time. -The return value is a cons cell with two internal times like the ones -returned by `current time' or `encode-time'. -If AS-STRINGS is non-nil, the returned times will be formatted strings. -If WSTART is non-nil, use this number to specify the starting day of a -week (monday is 1). -If MSTART is non-nil, use this number to specify the starting day of a -month (1 is the first day of the month). -If you can combine both, the month starting day will have priority." - (if (integerp key) (setq key (intern (number-to-string key)))) + +KEY is a symbol specifying the range and can be one of `today', +`yesterday', `thisweek', `lastweek', `thismonth', `lastmonth', +`thisyear', `lastyear' or `untilnow'. If set to `interactive', +user is prompted for range boundaries. It can be a string or an +integer. + +By default, a week starts Monday 0:00 and ends Sunday 24:00. The +range is determined relative to TIME, which defaults to current +time. + +The return value is a list containing two internal times, one for +the beginning of the range and one for its end, like the ones +returned by `current time' or `encode-time' and a string used to +display information. If AS-STRINGS is non-nil, the returned +times will be formatted strings. + +If WSTART is non-nil, use this number to specify the starting day +of a week (monday is 1). If MSTART is non-nil, use this number +to specify the starting day of a month (1 is the first day of the +month). If you can combine both, the month starting day will +have priority." (let* ((tm (decode-time time)) - (s 0) (m (nth 1 tm)) (h (nth 2 tm)) - (d (nth 3 tm)) (month (nth 4 tm)) (y (nth 5 tm)) + (m (nth 1 tm)) + (h (nth 2 tm)) + (d (nth 3 tm)) + (month (nth 4 tm)) + (y (nth 5 tm)) (dow (nth 6 tm)) - (ws (or wstart 1)) - (ms (or mstart 1)) - (skey (symbol-name key)) + (skey (format "%s" key)) (shift 0) - (q (cond ((>= (nth 4 tm) 10) 4) - ((>= (nth 4 tm) 7) 3) - ((>= (nth 4 tm) 4) 2) - ((>= (nth 4 tm) 1) 1))) - s1 m1 h1 d1 month1 y1 diff ts te fm txt w date - interval tmp shiftedy shiftedm shiftedq) + (q (cond ((>= month 10) 4) + ((>= month 7) 3) + ((>= month 4) 2) + (t 1))) + m1 h1 d1 month1 y1 shiftedy shiftedm shiftedq) (cond - ((string-match "^[0-9]+$" skey) - (setq y (string-to-number skey) m 1 d 1 key 'year)) - ((string-match "^\\([0-9]+\\)-\\([0-9]\\{1,2\\}\\)$" skey) + ((string-match "\\`[0-9]+\\'" skey) + (setq y (string-to-number skey) month 1 d 1 key 'year)) + ((string-match "\\`\\([0-9]+\\)-\\([0-9]\\{1,2\\}\\)\\'" skey) (setq y (string-to-number (match-string 1 skey)) month (string-to-number (match-string 2 skey)) - d 1 key 'month)) - ((string-match "^\\([0-9]+\\)-[wW]\\([0-9]\\{1,2\\}\\)$" skey) + d 1 + key 'month)) + ((string-match "\\`\\([0-9]+\\)-[wW]\\([0-9]\\{1,2\\}\\)\\'" skey) (require 'cal-iso) - (setq y (string-to-number (match-string 1 skey)) - w (string-to-number (match-string 2 skey))) - (setq date (calendar-gregorian-from-absolute - (calendar-absolute-from-iso (list w 1 y)))) - (setq d (nth 1 date) month (car date) y (nth 2 date) - dow 1 - key 'week)) - ((string-match "^\\([0-9]+\\)-[qQ]\\([1-4]\\)$" skey) + (let ((date (calendar-gregorian-from-absolute + (calendar-iso-to-absolute + (list (string-to-number (match-string 2 skey)) + 1 + (string-to-number (match-string 1 skey))))))) + (setq d (nth 1 date) + month (car date) + y (nth 2 date) + dow 1 + key 'week))) + ((string-match "\\`\\([0-9]+\\)-[qQ]\\([1-4]\\)\\'" skey) (require 'cal-iso) - (setq y (string-to-number (match-string 1 skey))) (setq q (string-to-number (match-string 2 skey))) - (setq date (calendar-gregorian-from-absolute - (calendar-absolute-from-iso (org-quarter-to-date q y)))) - (setq d (nth 1 date) month (car date) y (nth 2 date) - dow 1 - key 'quarter)) - ((string-match "^\\([0-9]+\\)-\\([0-9]\\{1,2\\}\\)-\\([0-9]\\{1,2\\}\\)$" skey) + (let ((date (calendar-gregorian-from-absolute + (calendar-iso-to-absolute + (org-quarter-to-date + q (string-to-number (match-string 1 skey))))))) + (setq d (nth 1 date) + month (car date) + y (nth 2 date) + dow 1 + key 'quarter))) + ((string-match + "\\`\\([0-9]+\\)-\\([0-9]\\{1,2\\}\\)-\\([0-9]\\{1,2\\}\\)\\'" + skey) (setq y (string-to-number (match-string 1 skey)) month (string-to-number (match-string 2 skey)) d (string-to-number (match-string 3 skey)) key 'day)) - ((string-match "\\([-+][0-9]+\\)$" skey) + ((string-match "\\([-+][0-9]+\\)\\'" skey) (setq shift (string-to-number (match-string 1 skey)) - key (intern (substring skey 0 (match-beginning 1)))) - (if (and (memq key '(quarter thisq)) (> shift 0)) - (error "Looking forward with quarters isn't implemented")))) - + key (intern (substring skey 0 (match-beginning 1)))) + (when (and (memq key '(quarter thisq)) (> shift 0)) + (error "Looking forward with quarters isn't implemented")))) (when (= shift 0) - (cond ((eq key 'yesterday) (setq key 'today shift -1)) - ((eq key 'lastweek) (setq key 'week shift -1)) - ((eq key 'lastmonth) (setq key 'month shift -1)) - ((eq key 'lastyear) (setq key 'year shift -1)) - ((eq key 'lastq) (setq key 'quarter shift -1)))) - (cond - ((memq key '(day today)) - (setq d (+ d shift) h 0 m 0 h1 24 m1 0)) - ((memq key '(week thisweek)) - (setq diff (+ (* -7 shift) (if (= dow 0) (- 7 ws) (- dow ws))) - m 0 h 0 d (- d diff) d1 (+ 7 d))) - ((memq key '(month thismonth)) - (setq d (or ms 1) h 0 m 0 d1 (or ms 1) - month (+ month shift) month1 (1+ month) h1 0 m1 0)) - ((memq key '(quarter thisq)) - ;; Compute if this shift remains in this year. If not, compute - ;; how many years and quarters we have to shift (via floor*) and - ;; compute the shifted years, months and quarters. - (cond - ((< (+ (- q 1) shift) 0) ; shift not in this year - (setq interval (* -1 (+ (- q 1) shift))) - ;; Set tmp to ((years to shift) (quarters to shift)). - (setq tmp (org-floor* interval 4)) - ;; Due to the use of floor, 0 quarters actually means 4. - (if (= 0 (nth 1 tmp)) - (setq shiftedy (- y (nth 0 tmp)) - shiftedm 1 - shiftedq 1) - (setq shiftedy (- y (+ 1 (nth 0 tmp))) - shiftedm (- 13 (* 3 (nth 1 tmp))) - shiftedq (- 5 (nth 1 tmp)))) - (setq d 1 h 0 m 0 d1 1 month shiftedm month1 (+ 3 shiftedm) h1 0 m1 0 y shiftedy)) - ((> (+ q shift) 0) ; shift is within this year - (setq shiftedq (+ q shift)) - (setq shiftedy y) - (setq d 1 h 0 m 0 d1 1 month (+ 1 (* 3 (- (+ q shift) 1))) month1 (+ 4 (* 3 (- (+ q shift) 1))) h1 0 m1 0)))) - ((memq key '(year thisyear)) - (setq m 0 h 0 d 1 month 1 y (+ y shift) y1 (1+ y))) - (t (error "No such time block %s" key))) - (setq ts (encode-time s m h d month y) - te (encode-time (or s1 s) (or m1 m) (or h1 h) - (or d1 d) (or month1 month) (or y1 y))) - (setq fm (cdr org-time-stamp-formats)) - (cond - ((memq key '(day today)) - (setq txt (format-time-string "%A, %B %d, %Y" ts))) - ((memq key '(week thisweek)) - (setq txt (format-time-string "week %G-W%V" ts))) - ((memq key '(month thismonth)) - (setq txt (format-time-string "%B %Y" ts))) - ((memq key '(year thisyear)) - (setq txt (format-time-string "the year %Y" ts))) - ((memq key '(quarter thisq)) - (setq txt (concat (org-count-quarter shiftedq) " quarter of " (number-to-string shiftedy))))) - (if as-strings - (list (format-time-string fm ts) (format-time-string fm te) txt) - (list ts te txt)))) + (pcase key + (`yesterday (setq key 'today shift -1)) + (`lastweek (setq key 'week shift -1)) + (`lastmonth (setq key 'month shift -1)) + (`lastyear (setq key 'year shift -1)) + (`lastq (setq key 'quarter shift -1)))) + ;; Prepare start and end times depending on KEY's type. + (pcase key + ((or `day `today) (setq m 0 h 0 h1 24 d (+ d shift))) + ((or `week `thisweek) + (let* ((ws (or wstart 1)) + (diff (+ (* -7 shift) (if (= dow 0) (- 7 ws) (- dow ws))))) + (setq m 0 h 0 d (- d diff) d1 (+ 7 d)))) + ((or `month `thismonth) + (setq h 0 m 0 d (or mstart 1) month (+ month shift) month1 (1+ month))) + ((or `quarter `thisq) + ;; Compute if this shift remains in this year. If not, compute + ;; how many years and quarters we have to shift (via floor*) and + ;; compute the shifted years, months and quarters. + (cond + ((< (+ (- q 1) shift) 0) ; Shift not in this year. + (let* ((interval (* -1 (+ (- q 1) shift))) + ;; Set tmp to ((years to shift) (quarters to shift)). + (tmp (cl-floor interval 4))) + ;; Due to the use of floor, 0 quarters actually means 4. + (if (= 0 (nth 1 tmp)) + (setq shiftedy (- y (nth 0 tmp)) + shiftedm 1 + shiftedq 1) + (setq shiftedy (- y (+ 1 (nth 0 tmp))) + shiftedm (- 13 (* 3 (nth 1 tmp))) + shiftedq (- 5 (nth 1 tmp))))) + (setq m 0 h 0 d 1 month shiftedm month1 (+ 3 shiftedm) y shiftedy)) + ((> (+ q shift) 0) ; Shift is within this year. + (setq shiftedq (+ q shift)) + (setq shiftedy y) + (let ((qshift (* 3 (1- (+ q shift))))) + (setq m 0 h 0 d 1 month (+ 1 qshift) month1 (+ 4 qshift)))))) + ((or `year `thisyear) + (setq m 0 h 0 d 1 month 1 y (+ y shift) y1 (1+ y))) + ((or `interactive `untilnow)) ; Special cases, ignore them. + (_ (user-error "No such time block %s" key))) + ;; Format start and end times according to AS-STRINGS. + (let* ((start (pcase key + (`interactive (org-read-date nil t nil "Range start? ")) + (`untilnow org-clock--oldest-date) + (_ (encode-time 0 m h d month y)))) + (end (pcase key + (`interactive (org-read-date nil t nil "Range end? ")) + (`untilnow (current-time)) + (_ (encode-time 0 + (or m1 m) + (or h1 h) + (or d1 d) + (or month1 month) + (or y1 y))))) + (text + (pcase key + ((or `day `today) (format-time-string "%A, %B %d, %Y" start)) + ((or `week `thisweek) (format-time-string "week %G-W%V" start)) + ((or `month `thismonth) (format-time-string "%B %Y" start)) + ((or `year `thisyear) (format-time-string "the year %Y" start)) + ((or `quarter `thisq) + (concat (org-count-quarter shiftedq) + " quarter of " (number-to-string shiftedy))) + (`interactive "(Range interactively set)") + (`untilnow "now")))) + (if (not as-strings) (list start end text) + (let ((f (cdr org-time-stamp-formats))) + (list (format-time-string f start) + (format-time-string f end) + text)))))) (defun org-count-quarter (n) (cond @@ -2196,7 +2324,7 @@ the currently selected interval size." ((and wp (string-match "w\\|W" wp) mw (> (length wp) 0)) (require 'cal-iso) (setq date (calendar-gregorian-from-absolute - (calendar-absolute-from-iso (list (+ mw n) 1 y)))) + (calendar-iso-to-absolute (list (+ mw n) 1 y)))) (setq ins (format-time-string "%G-W%V" (encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date))))) @@ -2213,7 +2341,7 @@ the currently selected interval size." y (- y 1)) ()) (setq date (calendar-gregorian-from-absolute - (calendar-absolute-from-iso (org-quarter-to-date (+ mw n) y)))) + (calendar-iso-to-absolute (org-quarter-to-date (+ mw n) y)))) (setq ins (format-time-string (concat (number-to-string y) "-Q" (number-to-string (+ mw n))) (encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date))))) @@ -2238,25 +2366,32 @@ the currently selected interval size." (setq params (org-combine-plists org-clocktable-defaults params)) (catch 'exit (let* ((scope (plist-get params :scope)) + (files (pcase scope + (`agenda + (org-agenda-files t)) + (`agenda-with-archives + (org-add-archive-files (org-agenda-files t))) + (`file-with-archives + (and buffer-file-name + (org-add-archive-files (list buffer-file-name)))) + ((pred consp) scope) + (_ (or (buffer-file-name) (current-buffer))))) (block (plist-get params :block)) (ts (plist-get params :tstart)) (te (plist-get params :tend)) - (link (plist-get params :link)) - (maxlevel (or (plist-get params :maxlevel) 3)) (ws (plist-get params :wstart)) (ms (plist-get params :mstart)) (step (plist-get params :step)) - (timestamp (plist-get params :timestamp)) (formatter (or (plist-get params :formatter) org-clock-clocktable-formatter 'org-clocktable-write-default)) - cc range-text ipos pos one-file-with-archives - scope-is-list tbls level) + cc) ;; Check if we need to do steps (when block ;; Get the range text for the header (setq cc (org-clock-special-range block nil t ws ms) - ts (car cc) te (nth 1 cc) range-text (nth 2 cc))) + ts (car cc) + te (nth 1 cc))) (when step ;; Write many tables, in steps (unless (or block (and ts te)) @@ -2264,63 +2399,49 @@ the currently selected interval size." (org-clocktable-steps params) (throw 'exit nil)) - (setq ipos (point)) ; remember the insertion position - - ;; Get the right scope - (setq pos (point)) - (cond - ((and scope (listp scope) (symbolp (car scope))) - (setq scope (eval scope))) - ((eq scope 'agenda) - (setq scope (org-agenda-files t))) - ((eq scope 'agenda-with-archives) - (setq scope (org-agenda-files t)) - (setq scope (org-add-archive-files scope))) - ((eq scope 'file-with-archives) - (setq scope (org-add-archive-files (list (buffer-file-name))) - one-file-with-archives t))) - (setq scope-is-list (and scope (listp scope))) - (if scope-is-list - ;; we collect from several files - (let* ((files scope) - file) - (org-agenda-prepare-buffers files) - (while (setq file (pop files)) - (with-current-buffer (find-buffer-visiting file) - (save-excursion - (save-restriction - (push (org-clock-get-table-data file params) tbls)))))) - ;; Just from the current file - (save-restriction - ;; get the right range into the restriction - (org-agenda-prepare-buffers (list (buffer-file-name))) - (cond - ((not scope)) ; use the restriction as it is now - ((eq scope 'file) (widen)) - ((eq scope 'subtree) (org-narrow-to-subtree)) - ((eq scope 'tree) - (while (org-up-heading-safe)) - (org-narrow-to-subtree)) - ((and (symbolp scope) (string-match "^tree\\([0-9]+\\)$" - (symbol-name scope))) - (setq level (string-to-number (match-string 1 (symbol-name scope)))) - (catch 'exit - (while (org-up-heading-safe) - (looking-at org-outline-regexp) - (if (<= (org-reduced-level (funcall outline-level)) level) - (throw 'exit nil)))) - (org-narrow-to-subtree))) - ;; do the table, with no file name. - (push (org-clock-get-table-data nil params) tbls))) - - ;; OK, at this point we tbls as a list of tables, one per file - (setq tbls (nreverse tbls)) - - (setq params (plist-put params :multifile scope-is-list)) - (setq params (plist-put params :one-file-with-archives - one-file-with-archives)) - - (funcall formatter ipos tbls params)))) + (org-agenda-prepare-buffers (if (consp files) files (list files))) + + (let ((origin (point)) + (tables + (if (consp files) + (mapcar (lambda (file) + (with-current-buffer (find-buffer-visiting file) + (save-excursion + (save-restriction + (org-clock-get-table-data file params))))) + files) + ;; Get the right restriction for the scope. + (save-restriction + (cond + ((not scope)) ;use the restriction as it is now + ((eq scope 'file) (widen)) + ((eq scope 'subtree) (org-narrow-to-subtree)) + ((eq scope 'tree) + (while (org-up-heading-safe)) + (org-narrow-to-subtree)) + ((and (symbolp scope) + (string-match "\\`tree\\([0-9]+\\)\\'" + (symbol-name scope))) + (let ((level (string-to-number + (match-string 1 (symbol-name scope))))) + (catch 'exit + (while (org-up-heading-safe) + (looking-at org-outline-regexp) + (when (<= (org-reduced-level (funcall outline-level)) + level) + (throw 'exit nil)))) + (org-narrow-to-subtree)))) + (list (org-clock-get-table-data nil params))))) + (multifile + ;; Even though `file-with-archives' can consist of + ;; multiple files, we consider this is one extended file + ;; instead. + (and (consp files) (not (eq scope 'file-with-archives))))) + + (funcall formatter + origin + tables + (org-combine-plists params `(:multifile ,multifile))))))) (defun org-clocktable-write-default (ipos tables params) "Write out a clock table at position IPOS in the current buffer. @@ -2335,43 +2456,46 @@ from the dynamic block definition." ;; well-defined number of columns... (let* ((hlchars '((1 . "*") (2 . "/"))) (lwords (assoc (or (plist-get params :lang) - (org-bound-and-true-p org-export-default-language) + (bound-and-true-p org-export-default-language) "en") org-clock-clocktable-language-setup)) (multifile (plist-get params :multifile)) (block (plist-get params :block)) - (ts (plist-get params :tstart)) - (te (plist-get params :tend)) - (header (plist-get params :header)) - (narrow (plist-get params :narrow)) + (sort (plist-get params :sort)) + (header (plist-get params :header)) (ws (or (plist-get params :wstart) 1)) (ms (or (plist-get params :mstart) 1)) (link (plist-get params :link)) - (maxlevel (or (plist-get params :maxlevel) 3)) - (emph (plist-get params :emphasize)) - (level-p (plist-get params :level)) (org-time-clocksum-use-effort-durations (plist-get params :effort-durations)) + (maxlevel (or (plist-get params :maxlevel) 3)) + (emph (plist-get params :emphasize)) + (compact? (plist-get params :compact)) + (narrow (or (plist-get params :narrow) (and compact? '40!))) + (level? (and (not compact?) (plist-get params :level))) (timestamp (plist-get params :timestamp)) (properties (plist-get params :properties)) - (ntcol (max 1 (or (plist-get params :tcolumns) 100))) - (rm-file-column (plist-get params :one-file-with-archives)) - (indent (plist-get params :indent)) + (time-columns + (if (or compact? (< maxlevel 2)) 1 + ;; Deepest headline level is a hard limit for the number + ;; of time columns. + (let ((levels + (cl-mapcan + (lambda (table) + (pcase table + (`(,_ ,(and (pred wholenump) (pred (/= 0))) ,entries) + (mapcar #'car entries)))) + tables))) + (min maxlevel + (or (plist-get params :tcolumns) 100) + (if (null levels) 1 (apply #'max levels)))))) + (indent (or compact? (plist-get params :indent))) + (formula (plist-get params :formula)) (case-fold-search t) - range-text total-time tbl level hlc formula pcol - file-time entries entry headline - recalc content narrow-cut-p tcol) - - ;; Implement abbreviations - (when (plist-get params :compact) - (setq level nil indent t narrow (or narrow '40!) ntcol 1)) - - ;; Some consistency test for parameters - (unless (integerp ntcol) - (setq params (plist-put params :tcolumns (setq ntcol 100)))) + range-text total-time recalc narrow-cut-p) (when (and narrow (integerp narrow) link) - ;; We cannot have both integer narrow and link + ;; We cannot have both integer narrow and link. (message "Using hard narrowing in clocktable to allow for links") (setq narrow (intern (format "%d!" narrow)))) @@ -2389,19 +2513,19 @@ from the dynamic block definition." narrow)))) (when block - ;; Get the range text for the header + ;; Get the range text for the header. (setq range-text (nth 2 (org-clock-special-range block nil t ws ms)))) - ;; Compute the total time - (setq total-time (apply '+ (mapcar 'cadr tables))) + ;; Compute the total time. + (setq total-time (apply #'+ (mapcar #'cadr tables))) - ;; Now we need to output this tsuff + ;; Now we need to output this tsuff. (goto-char ipos) - ;; Insert the text *before* the actual table + ;; Insert the text *before* the actual table. (insert-before-markers (or header - ;; Format the standard header + ;; Format the standard header. (concat "#+CAPTION: " (nth 9 lwords) " [" @@ -2415,155 +2539,144 @@ from the dynamic block definition." ;; Insert the narrowing line (when (and narrow (integerp narrow) (not narrow-cut-p)) (insert-before-markers - "|" ; table line starter - (if multifile "|" "") ; file column, maybe - (if level-p "|" "") ; level column, maybe - (if timestamp "|" "") ; timestamp column, maybe - (if properties (make-string (length properties) ?|) "") ;properties columns, maybe - (format "<%d>| |\n" narrow))) ; headline and time columns + "|" ;table line starter + (if multifile "|" "") ;file column, maybe + (if level? "|" "") ;level column, maybe + (if timestamp "|" "") ;timestamp column, maybe + (if properties (make-string (length properties) ?|) "") ;properties columns, maybe + (format "<%d>| |\n" narrow))) ; headline and time columns ;; Insert the table header line (insert-before-markers - "|" ; table line starter - (if multifile (concat (nth 1 lwords) "|") "") ; file column, maybe - (if level-p (concat (nth 2 lwords) "|") "") ; level column, maybe - (if timestamp (concat (nth 3 lwords) "|") "") ; timestamp column, maybe - (if properties (concat (mapconcat 'identity properties "|") "|") "") ;properties columns, maybe - (concat (nth 4 lwords) "|" - (nth 5 lwords) "|\n")) ; headline and time columns + "|" ;table line starter + (if multifile (concat (nth 1 lwords) "|") "") ;file column, maybe + (if level? (concat (nth 2 lwords) "|") "") ;level column, maybe + (if timestamp (concat (nth 3 lwords) "|") "") ;timestamp column, maybe + (if properties ;properties columns, maybe + (concat (mapconcat #'identity properties "|") "|") + "") + (concat (nth 4 lwords) "|") ;headline + (concat (nth 5 lwords) "|") ;time column + (make-string (max 0 (1- time-columns)) ?|) ;other time columns + (if (eq formula '%) "%|\n" "\n")) ;; Insert the total time in the table (insert-before-markers - "|-\n" ; a hline - "|" ; table line starter + "|-\n" ;a hline + "|" ;table line starter (if multifile (concat "| " (nth 6 lwords) " ") "") - ; file column, maybe - (if level-p "|" "") ; level column, maybe - (if timestamp "|" "") ; timestamp column, maybe - (if properties (make-string (length properties) ?|) "") ; properties columns, maybe - (concat (format org-clock-total-time-cell-format (nth 7 lwords)) "| ") ; instead of a headline + ;file column, maybe + (if level? "|" "") ;level column, maybe + (if timestamp "|" "") ;timestamp column, maybe + (make-string (length properties) ?|) ;properties columns, maybe + (concat (format org-clock-total-time-cell-format (nth 7 lwords)) + "| ") (format org-clock-total-time-cell-format - (org-minutes-to-clocksum-string (or total-time 0))) ; the time - "|\n") ; close line - - ;; Now iterate over the tables and insert the data - ;; but only if any time has been collected + (org-minutes-to-clocksum-string (or total-time 0))) ;time + "|" + (make-string (max 0 (1- time-columns)) ?|) + (cond ((not (eq formula '%)) "") + ((or (not total-time) (= total-time 0)) "0.0|") + (t "100.0|")) + "\n") + + ;; Now iterate over the tables and insert the data but only if any + ;; time has been collected. (when (and total-time (> total-time 0)) - - (while (setq tbl (pop tables)) - ;; now tbl is the table resulting from one file. - (setq file-time (nth 1 tbl)) + (pcase-dolist (`(,file-name ,file-time ,entries) tables) (when (or (and file-time (> file-time 0)) (not (plist-get params :fileskip0))) - (insert-before-markers "|-\n") ; a hline because a new file starts - ;; First the file time, if we have multiple files + (insert-before-markers "|-\n") ;hline at new file + ;; First the file time, if we have multiple files. (when multifile - ;; Summarize the time collected from this file + ;; Summarize the time collected from this file. (insert-before-markers (format (concat "| %s %s | %s%s" - (format org-clock-file-time-cell-format (nth 8 lwords)) + (format org-clock-file-time-cell-format + (nth 8 lwords)) " | *%s*|\n") - (file-name-nondirectory (car tbl)) - (if level-p "| " "") ; level column, maybe - (if timestamp "| " "") ; timestamp column, maybe - (if properties (make-string (length properties) ?|) "") ;properties columns, maybe - (org-minutes-to-clocksum-string (nth 1 tbl))))) ; the time + (file-name-nondirectory file-name) + (if level? "| " "") ;level column, maybe + (if timestamp "| " "") ;timestamp column, maybe + (if properties ;properties columns, maybe + (make-string (length properties) ?|) + "") + (org-minutes-to-clocksum-string file-time)))) ;time ;; Get the list of node entries and iterate over it - (setq entries (nth 2 tbl)) - (while (setq entry (pop entries)) - (setq level (car entry) - headline (nth 1 entry) - hlc (if emph (or (cdr (assoc level hlchars)) "") "")) - (when narrow-cut-p - (if (and (string-match (concat "\\`" org-bracket-link-regexp - "\\'") - headline) - (match-end 3)) - (setq headline - (format "[[%s][%s]]" - (match-string 1 headline) - (org-shorten-string (match-string 3 headline) - narrow))) - (setq headline (org-shorten-string headline narrow)))) - (insert-before-markers - "|" ; start the table line - (if multifile "|" "") ; free space for file name column? - (if level-p (format "%d|" (car entry)) "") ; level, maybe - (if timestamp (concat (nth 2 entry) "|") "") ; timestamp, maybe - (if properties - (concat - (mapconcat - (lambda (p) (or (cdr (assoc p (nth 4 entry))) "")) - properties "|") "|") "") ;properties columns, maybe - (if indent (org-clocktable-indent-string level) "") ; indentation - hlc headline hlc "|" ; headline - (make-string (min (1- ntcol) (or (- level 1))) ?|) - ; empty fields for higher levels - hlc (org-minutes-to-clocksum-string (nth 3 entry)) hlc ; time - "|\n" ; close line - ))))) - ;; When exporting subtrees or regions the region might be - ;; activated, so let's disable ̀delete-active-region' - (let ((delete-active-region nil)) (backward-delete-char 1)) - (if (setq formula (plist-get params :formula)) - (cond - ((eq formula '%) - ;; compute the column where the % numbers need to go - (setq pcol (+ 2 - (if multifile 1 0) - (if level-p 1 0) - (if timestamp 1 0) - (min maxlevel (or ntcol 100)))) - ;; compute the column where the total time is - (setq tcol (+ 2 - (if multifile 1 0) - (if level-p 1 0) - (if timestamp 1 0))) - (insert - (format - "\n#+TBLFM: $%d='(org-clock-time%% @%d$%d $%d..$%d);%%.1f" - pcol ; the column where the % numbers should go - (if (and narrow (not narrow-cut-p)) 3 2) ; row of the total time - tcol ; column of the total time - tcol (1- pcol) ; range of columns where times can be found - )) - (setq recalc t)) - ((stringp formula) - (insert "\n#+TBLFM: " formula) - (setq recalc t)) - (t (error "Invalid formula in clocktable"))) - ;; Should we rescue an old formula? - (when (stringp (setq content (plist-get params :content))) - (when (string-match "^\\([ \t]*#\\+tblfm:.*\\)" content) + (when (> maxlevel 0) + (pcase-dolist (`(,level ,headline ,ts ,time ,props) entries) + (when narrow-cut-p + (setq headline + (if (and (string-match + (format "\\`%s\\'" org-bracket-link-regexp) + headline) + (match-end 3)) + (format "[[%s][%s]]" + (match-string 1 headline) + (org-shorten-string (match-string 3 headline) + narrow)) + (org-shorten-string headline narrow)))) + (let ((hlc (if emph (or (cdr (assoc level hlchars)) "") ""))) + (insert-before-markers + "|" ;start the table line + (if multifile "|" "") ;free space for file name column? + (if level? (format "%d|" level) "") ;level, maybe + (if timestamp (concat ts "|") "") ;timestamp, maybe + (if properties ;properties columns, maybe + (concat (mapconcat (lambda (p) + (or (cdr (assoc p props)) "")) + properties + "|") + "|") + "") + (if indent ;indentation + (org-clocktable-indent-string level) + "") + hlc headline hlc "|" ;headline + ;; Empty fields for higher levels. + (make-string (max 0 (1- (min time-columns level))) ?|) + hlc (org-minutes-to-clocksum-string time) hlc "|" ; time + (make-string (max 0 (- time-columns level)) ?|) + (if (eq formula '%) + (format "%.1f |" (* 100 (/ time (float total-time)))) + "") + "\n"))))))) + (delete-char -1) + (cond + ;; Possibly rescue old formula? + ((or (not formula) (eq formula '%)) + (let ((contents (org-string-nw-p (plist-get params :content)))) + (when (and contents (string-match "^\\([ \t]*#\\+tblfm:.*\\)" contents)) (setq recalc t) - (insert "\n" (match-string 1 (plist-get params :content))) + (insert "\n" (match-string 1 contents)) (beginning-of-line 0)))) - ;; Back to beginning, align the table, recalculate if necessary + ;; Insert specified formula line. + ((stringp formula) + (insert "\n#+TBLFM: " formula) + (setq recalc t)) + (t + (user-error "Invalid :formula parameter in clocktable"))) + ;; Back to beginning, align the table, recalculate if necessary. (goto-char ipos) (skip-chars-forward "^|") (org-table-align) (when org-hide-emphasis-markers - ;; we need to align a second time + ;; We need to align a second time. (org-table-align)) - (when recalc - (if (eq formula '%) - (save-excursion - (if (and narrow (not narrow-cut-p)) (beginning-of-line 2)) - (org-table-goto-column pcol nil 'force) - (insert "%"))) - (org-table-recalculate 'all)) - (when rm-file-column - ;; The file column is actually not wanted - (forward-char 1) - (org-table-delete-column)) + (when sort + (save-excursion + (org-table-goto-line 3) + (org-table-goto-column (car sort)) + (org-table-sort-lines nil (cdr sort)))) + (when recalc (org-table-recalculate 'all)) total-time)) (defun org-clocktable-indent-string (level) + "Return indentation string according to LEVEL. +LEVEL is an integer. Indent by two spaces per level above 1." (if (= level 1) "" - (let ((str " ")) - (dotimes (k (1- level) str) - (setq str (concat "\\emsp" str)))))) + (concat "\\_" (make-string (* 2 (1- level)) ?\s)))) (defun org-clocktable-steps (params) "Step through the range to make a number of clock tables." @@ -2576,26 +2689,28 @@ from the dynamic block definition." (step (cdr (assoc step0 '((day . 86400) (week . 604800))))) (stepskip0 (plist-get p1 :stepskip0)) (block (plist-get p1 :block)) - cc range-text step-time tsb) + cc step-time tsb) (when block (setq cc (org-clock-special-range block nil t ws ms) - ts (car cc) te (nth 1 cc) range-text (nth 2 cc))) + ts (car cc) + te (nth 1 cc))) (cond ((numberp ts) - ;; If ts is a number, it's an absolute day number from org-agenda. - (destructuring-bind (month day year) (calendar-gregorian-from-absolute ts) + ;; If ts is a number, it's an absolute day number from + ;; org-agenda. + (pcase-let ((`(,month ,day ,year) (calendar-gregorian-from-absolute ts))) (setq ts (float-time (encode-time 0 0 0 day month year))))) (ts (setq ts (float-time - (apply 'encode-time (org-parse-time-string ts)))))) + (apply #'encode-time (org-parse-time-string ts nil t)))))) (cond ((numberp te) ;; Likewise for te. - (destructuring-bind (month day year) (calendar-gregorian-from-absolute te) + (pcase-let ((`(,month ,day ,year) (calendar-gregorian-from-absolute te))) (setq te (float-time (encode-time 0 0 0 day month year))))) (te (setq te (float-time - (apply 'encode-time (org-parse-time-string te)))))) + (apply #'encode-time (org-parse-time-string te nil t)))))) (setq tsb (if (eq step0 'week) (- ts (* 86400 (- (nth 6 (decode-time (seconds-to-time ts))) ws))) @@ -2635,19 +2750,22 @@ file time (in minutes) as 1st and 2nd elements. The third element of this list will be a list of headline entries. Each entry has the following structure: - (LEVEL HEADLINE TIMESTAMP TIME) - -LEVEL: The level of the headline, as an integer. This will be - the reduced leve, so 1,2,3,... even if only odd levels - are being used. -HEADLINE: The text of the headline. Depending on PARAMS, this may - already be formatted like a link. -TIMESTAMP: If PARAMS require it, this will be a time stamp found in the - entry, any of SCHEDULED, DEADLINE, NORMAL, or first inactive, - in this sequence. -TIME: The sum of all time spend in this tree, in minutes. This time - will of cause be restricted to the time block and tags match - specified in PARAMS." + (LEVEL HEADLINE TIMESTAMP TIME PROPERTIES) + +LEVEL: The level of the headline, as an integer. This will be + the reduced level, so 1,2,3,... even if only odd levels + are being used. +HEADLINE: The text of the headline. Depending on PARAMS, this may + already be formatted like a link. +TIMESTAMP: If PARAMS require it, this will be a time stamp found in the + entry, any of SCHEDULED, DEADLINE, NORMAL, or first inactive, + in this sequence. +TIME: The sum of all time spend in this tree, in minutes. This time + will of cause be restricted to the time block and tags match + specified in PARAMS. +PROPERTIES: The list properties specified in the `:properties' parameter + along with their value, as an alist following the pattern + (NAME . VALUE)." (let* ((maxlevel (or (plist-get params :maxlevel) 3)) (timestamp (plist-get params :timestamp)) (ts (plist-get params :tstart)) @@ -2659,14 +2777,14 @@ TIME: The sum of all time spend in this tree, in minutes. This time (tags (plist-get params :tags)) (properties (plist-get params :properties)) (inherit-property-p (plist-get params :inherit-props)) - todo-only - (matcher (if tags (cdr (org-make-tags-matcher tags)))) - cc range-text st p time level hdl props tsp tbl) + (matcher (and tags (cdr (org-make-tags-matcher tags)))) + cc st p tbl) (setq org-clock-file-total-minutes nil) (when block (setq cc (org-clock-special-range block nil t ws ms) - ts (car cc) te (nth 1 cc) range-text (nth 2 cc))) + ts (car cc) + te (nth 1 cc))) (when (integerp ts) (setq ts (calendar-gregorian-from-absolute ts))) (when (integerp te) (setq te (calendar-gregorian-from-absolute te))) (when (and ts (listp ts)) @@ -2678,12 +2796,12 @@ TIME: The sum of all time spend in this tree, in minutes. This time (if te (setq te (org-matcher-time te))) (save-excursion (org-clock-sum ts te - (unless (null matcher) - (lambda () - (let* ((tags-list (org-get-tags-at)) - (org-scanner-tags tags-list) - (org-trust-scanner-tags t)) - (eval matcher))))) + (when matcher + `(lambda () + (let* ((tags-list (org-get-tags-at)) + (org-scanner-tags tags-list) + (org-trust-scanner-tags t)) + (funcall ,matcher nil tags-list nil))))) (goto-char (point-min)) (setq st t) (while (or (and (bobp) (prog1 st (setq st nil)) @@ -2692,66 +2810,46 @@ TIME: The sum of all time spend in this tree, in minutes. This time (setq p (next-single-property-change (point) :org-clock-minutes))) (goto-char p) - (when (setq time (get-text-property p :org-clock-minutes)) - (save-excursion - (beginning-of-line 1) - (when (and (looking-at (org-re "\\(\\*+\\)[ \t]+\\(.*?\\)\\([ \t]+:[[:alnum:]_@#%:]+:\\)?[ \t]*$")) - (setq level (org-reduced-level - (- (match-end 1) (match-beginning 1)))) - (<= level maxlevel)) - (setq hdl (if (not link) - (match-string 2) - (org-make-link-string - (format "file:%s::%s" - (buffer-file-name) - (save-match-data - (match-string 2))) - (org-make-org-heading-search-string - (replace-regexp-in-string - org-bracket-link-regexp - (lambda (m) (or (match-string 3 m) - (match-string 1 m))) - (match-string 2))))) - tsp (when timestamp - (setq props (org-entry-properties (point))) - (or (cdr (assoc "SCHEDULED" props)) - (cdr (assoc "DEADLINE" props)) - (cdr (assoc "TIMESTAMP" props)) - (cdr (assoc "TIMESTAMP_IA" props)))) - props (when properties - (remove nil - (mapcar - (lambda (p) - (when (org-entry-get (point) p inherit-property-p) - (cons p (org-entry-get (point) p inherit-property-p)))) - properties)))) - (when (> time 0) (push (list level hdl tsp time props) tbl)))))) - (setq tbl (nreverse tbl)) - (list file org-clock-file-total-minutes tbl)))) - -(defun org-clock-time% (total &rest strings) - "Compute a time fraction in percent. -TOTAL s a time string like 10:21 specifying the total times. -STRINGS is a list of strings that should be checked for a time. -The first string that does have a time will be used. -This function is made for clock tables." - (let ((re "\\([0-9]+\\):\\([0-9]+\\)") - tot s) - (save-match-data - (catch 'exit - (if (not (string-match re total)) - (throw 'exit 0.) - (setq tot (+ (string-to-number (match-string 2 total)) - (* 60 (string-to-number (match-string 1 total))))) - (if (= tot 0.) (throw 'exit 0.))) - (while (setq s (pop strings)) - (if (string-match "\\([0-9]+\\):\\([0-9]+\\)" s) - (throw 'exit - (/ (* 100.0 (+ (string-to-number (match-string 2 s)) - (* 60 (string-to-number - (match-string 1 s))))) - tot)))) - 0)))) + (let ((time (get-text-property p :org-clock-minutes))) + (when (and time (> time 0) (org-at-heading-p)) + (let ((level (org-reduced-level (org-current-level)))) + (when (<= level maxlevel) + (let* ((headline (replace-regexp-in-string + (format "\\`%s[ \t]+" org-comment-string) "" + (nth 4 (org-heading-components)))) + (hdl + (if (not link) headline + (let ((search + (org-make-org-heading-search-string headline))) + (org-make-link-string + (if (not (buffer-file-name)) search + (format "file:%s::%s" (buffer-file-name) search)) + ;; Prune statistics cookies. Replace + ;; links with their description, or + ;; a plain link if there is none. + (org-trim + (org-link-display-format + (replace-regexp-in-string + "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" "" + headline))))))) + (tsp + (and timestamp + (let ((p (org-entry-properties (point) 'special))) + (or (cdr (assoc "SCHEDULED" p)) + (cdr (assoc "DEADLINE" p)) + (cdr (assoc "TIMESTAMP" p)) + (cdr (assoc "TIMESTAMP_IA" p)))))) + (props + (and properties + (delq nil + (mapcar + (lambda (p) + (let ((v (org-entry-get + (point) p inherit-property-p))) + (and v (cons p v)))) + properties))))) + (push (list level hdl tsp time props) tbl))))))) + (list file org-clock-file-total-minutes (nreverse tbl))))) ;; Saving and loading the clock @@ -2789,9 +2887,9 @@ Otherwise, return nil." (setq ts (match-string 1) te (match-string 3)) (setq s (- (float-time - (apply 'encode-time (org-parse-time-string te))) + (apply #'encode-time (org-parse-time-string te nil t))) (float-time - (apply 'encode-time (org-parse-time-string ts)))) + (apply #'encode-time (org-parse-time-string ts nil t)))) neg (< s 0) s (abs s) h (floor (/ s 3600)) @@ -2809,86 +2907,67 @@ The details of what will be saved are regulated by the variable (or org-clock-loaded org-clock-has-been-used (not (file-exists-p org-clock-persist-file)))) - (let (b) - (with-current-buffer (find-file (expand-file-name org-clock-persist-file)) - (progn - (delete-region (point-min) (point-max)) - ;;Store clock - (insert (format ";; org-persist.el - %s at %s\n" - (system-name) (format-time-string - (cdr org-time-stamp-formats)))) - (if (and (memq org-clock-persist '(t clock)) - (setq b (org-clocking-buffer)) - (setq b (or (buffer-base-buffer b) b)) - (buffer-live-p b) - (buffer-file-name b) - (or (not org-clock-persist-query-save) - (y-or-n-p (concat "Save current clock (" - org-clock-heading ") ")))) - (insert "(setq resume-clock '(\"" - (buffer-file-name (org-clocking-buffer)) - "\" . " (int-to-string (marker-position org-clock-marker)) - "))\n")) - ;; Store clocked task history. Tasks are stored reversed to make - ;; reading simpler - (when (and (memq org-clock-persist '(t history)) - org-clock-history) - (insert - "(setq stored-clock-history '(" - (mapconcat - (lambda (m) - (when (and (setq b (marker-buffer m)) - (setq b (or (buffer-base-buffer b) b)) - (buffer-live-p b) - (buffer-file-name b)) - (concat "(\"" (buffer-file-name b) - "\" . " (int-to-string (marker-position m)) - ")"))) - (reverse org-clock-history) " ") "))\n")) - (save-buffer) - (kill-buffer (current-buffer))))))) + (with-temp-file org-clock-persist-file + (insert (format ";; %s - %s at %s\n" + (file-name-nondirectory org-clock-persist-file) + (system-name) + (format-time-string (org-time-stamp-format t)))) + ;; Store clock to be resumed. + (when (and (memq org-clock-persist '(t clock)) + (let ((b (org-base-buffer (org-clocking-buffer)))) + (and (buffer-live-p b) + (buffer-file-name b) + (or (not org-clock-persist-query-save) + (y-or-n-p (format "Save current clock (%s) " + org-clock-heading)))))) + (insert + (format "(setq org-clock-stored-resume-clock '(%S . %d))\n" + (buffer-file-name (org-base-buffer (org-clocking-buffer))) + (marker-position org-clock-marker)))) + ;; Store clocked task history. Tasks are stored reversed to + ;; make reading simpler. + (when (and (memq org-clock-persist '(t history)) + org-clock-history) + (insert + (format "(setq org-clock-stored-history '(%s))\n" + (mapconcat + (lambda (m) + (let ((b (org-base-buffer (marker-buffer m)))) + (when (and (buffer-live-p b) + (buffer-file-name b)) + (format "(%S . %d)" + (buffer-file-name b) + (marker-position m))))) + (reverse org-clock-history) + " "))))))) (defun org-clock-load () "Load clock-related data from disk, maybe resuming a stored clock." (when (and org-clock-persist (not org-clock-loaded)) - (let ((filename (expand-file-name org-clock-persist-file)) - (org-clock-in-resume 'auto-restart) - resume-clock stored-clock-history) - (if (not (file-readable-p filename)) - (message "Not restoring clock data; %s not found" - org-clock-persist-file) - (message "%s" "Restoring clock data") - (setq org-clock-loaded t) - (load-file filename) - ;; load history - (when stored-clock-history - (save-window-excursion - (mapc (lambda (task) - (if (file-exists-p (car task)) - (org-clock-history-push (cdr task) - (find-file (car task))))) - stored-clock-history))) - ;; resume clock - (when (and resume-clock org-clock-persist - (file-exists-p (car resume-clock)) - (or (not org-clock-persist-query-resume) - (y-or-n-p - (concat - "Resume clock (" - (with-current-buffer (find-file (car resume-clock)) - (save-excursion - (goto-char (cdr resume-clock)) - (org-back-to-heading t) - (and (looking-at org-complex-heading-regexp) - (match-string 4)))) - ") ")))) - (when (file-exists-p (car resume-clock)) - (with-current-buffer (find-file (car resume-clock)) - (goto-char (cdr resume-clock)) - (let ((org-clock-auto-clock-resolution nil)) - (org-clock-in) - (if (outline-invisible-p) - (org-show-context)))))))))) + (if (not (file-readable-p org-clock-persist-file)) + (message "Not restoring clock data; %S not found" org-clock-persist-file) + (message "Restoring clock data") + ;; Load history. + (load-file org-clock-persist-file) + (setq org-clock-loaded t) + (pcase-dolist (`(,(and file (pred file-exists-p)) . ,position) + org-clock-stored-history) + (org-clock-history-push position (find-file-noselect file))) + ;; Resume clock. + (pcase org-clock-stored-resume-clock + (`(,(and file (pred file-exists-p)) . ,position) + (with-current-buffer (find-file-noselect file) + (when (or (not org-clock-persist-query-resume) + (y-or-n-p (format "Resume clock (%s) " + (save-excursion + (goto-char position) + (org-get-heading t t))))) + (goto-char position) + (let ((org-clock-in-resume 'auto-restart) + (org-clock-auto-clock-resolution nil)) + (org-clock-in) + (when (org-invisible-p) (org-show-context)))))) + (_ nil))))) ;; Suggested bindings (org-defkey org-mode-map "\C-c\C-x\C-e" 'org-clock-modify-effort-estimate) diff --git a/lisp/org/org-colview.el b/lisp/org/org-colview.el index a2046af29e..ac8f36ad40 100644 --- a/lisp/org/org-colview.el +++ b/lisp/org/org-colview.el @@ -1,4 +1,4 @@ -;;; org-colview.el --- Column View in Org-mode +;;; org-colview.el --- Column View in Org -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -28,42 +28,117 @@ ;;; Code: -(eval-when-compile (require 'cl)) +(require 'cl-lib) (require 'org) (declare-function org-agenda-redo "org-agenda" (&optional all)) (declare-function org-agenda-do-context-action "org-agenda" ()) (declare-function org-clock-sum-today "org-clock" (&optional headline-filter)) - -(when (featurep 'xemacs) - (error "Do not load this file into XEmacs, use `org-colview-xemacs.el' from the contrib/ directory")) - +(declare-function org-element-extract-element "org-element" (element)) +(declare-function org-element-interpret-data "org-element" (data)) +(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated)) +(declare-function org-element-parse-secondary-string "org-element" (string restriction &optional parent)) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-restriction "org-element" (element)) +(declare-function org-element-type "org-element" (element)) + +(defvar org-agenda-columns-add-appointments-to-effort-sum) +(defvar org-agenda-columns-compute-summary-properties) +(defvar org-agenda-columns-show-summaries) +(defvar org-agenda-view-columns-initially) +(defvar org-inlinetask-min-level) + + +;;; Configuration + +(defcustom org-columns-modify-value-for-display-function nil + "Function that modifies values for display in column view. +For example, it can be used to cut out a certain part from a time stamp. +The function must take 2 arguments: + +column-title The title of the column (*not* the property name) +value The value that should be modified. + +The function should return the value that should be displayed, +or nil if the normal value should be used." + :group 'org-properties + :type '(choice (const nil) (function))) + +(defcustom org-columns-summary-types nil + "Alist between operators and summarize functions. + +Each association follows the pattern (LABEL . SUMMARIZE) where + + LABEL is a string used in #+COLUMNS definition describing the + summary type. It can contain any character but \"}\". It is + case-sensitive. + + SUMMARIZE is a function called with two arguments. The first + argument is a non-empty list of values, as non-empty strings. + The second one is a format string or nil. It has to return + a string summarizing the list of values. + +Note that the return value can become one value for an higher +order summary, so the function is expected to handle its own +output. + +Types defined in this variable take precedence over those defined +in `org-columns-summary-types-default', which see." + :group 'org-properties + :version "26.1" + :package-version '(Org . "9.0") + :type '(alist :key-type (string :tag " Label") + :value-type (function :tag "Summarize"))) + + + ;;; Column View (defvar org-columns-overlays nil "Holds the list of current column overlays.") -(defvar org-columns-current-fmt nil +(defvar org-columns--time 0.0 + "Number of seconds since the epoch, as a floating point number.") + +(defvar-local org-columns-current-fmt nil "Local variable, holds the currently active column format.") -(make-variable-buffer-local 'org-columns-current-fmt) -(defvar org-columns-current-fmt-compiled nil + +(defvar-local org-columns-current-fmt-compiled nil "Local variable, holds the currently active column format. This is the compiled version of the format.") -(make-variable-buffer-local 'org-columns-current-fmt-compiled) -(defvar org-columns-current-widths nil - "Loval variable, holds the currently widths of fields.") -(make-variable-buffer-local 'org-columns-current-widths) -(defvar org-columns-current-maxwidths nil - "Loval variable, holds the currently active maximum column widths.") -(make-variable-buffer-local 'org-columns-current-maxwidths) + +(defvar-local org-columns-current-maxwidths nil + "Currently active maximum column widths, as a vector.") + (defvar org-columns-begin-marker (make-marker) "Points to the position where last a column creation command was called.") + (defvar org-columns-top-level-marker (make-marker) "Points to the position where current columns region starts.") (defvar org-columns-map (make-sparse-keymap) "The keymap valid in column display.") +(defconst org-columns-summary-types-default + '(("+" . org-columns--summary-sum) + ("$" . org-columns--summary-currencies) + ("X" . org-columns--summary-checkbox) + ("X/" . org-columns--summary-checkbox-count) + ("X%" . org-columns--summary-checkbox-percent) + ("max" . org-columns--summary-max) + ("mean" . org-columns--summary-mean) + ("min" . org-columns--summary-min) + (":" . org-columns--summary-sum-times) + (":max" . org-columns--summary-max-time) + (":mean" . org-columns--summary-mean-time) + (":min" . org-columns--summary-min-time) + ("@max" . org-columns--summary-max-age) + ("@mean" . org-columns--summary-mean-age) + ("@min" . org-columns--summary-min-age) + ("est+" . org-columns--summary-estimate)) + "Map operators to summarize functions. +See `org-columns-summary-types' for details.") + (defun org-columns-content () "Switch to contents view while in columns view." (interactive) @@ -146,121 +221,181 @@ This is the compiled version of the format.") "--" ["Quit" org-columns-quit t])) -(defun org-columns-new-overlay (beg end &optional string face) +(defun org-columns--displayed-value (spec value) + "Return displayed value for specification SPEC in current entry. +SPEC is a column format specification as stored in +`org-columns-current-fmt-compiled'. VALUE is the real value to +display, as a string." + (or (and (functionp org-columns-modify-value-for-display-function) + (funcall org-columns-modify-value-for-display-function + (nth 1 spec) ;column name + value)) + (pcase spec + (`("ITEM" . ,_) + (concat (make-string (1- (org-current-level)) + (if org-hide-leading-stars ?\s ?*)) + "* " + (org-columns-compact-links value))) + (`(,_ ,_ ,_ ,_ nil) value) + ;; If PRINTF is set, assume we are displaying a number and + ;; obey to the format string. + (`(,_ ,_ ,_ ,_ ,printf) (format printf (string-to-number value))) + (_ (error "Invalid column specification format: %S" spec))))) + +(defun org-columns--collect-values (&optional compiled-fmt) + "Collect values for columns on the current line. + +Return a list of triplets (SPEC VALUE DISPLAYED) suitable for +`org-columns--display-here'. + +This function assumes `org-columns-current-fmt-compiled' is +initialized is set in the current buffer. However, it is +possible to override it with optional argument COMPILED-FMT." + (let ((summaries (get-text-property (point) 'org-summaries))) + (mapcar + (lambda (spec) + (pcase spec + (`(,p . ,_) + (let* ((v (or (cdr (assoc spec summaries)) + (org-entry-get (point) p 'selective t) + (and compiled-fmt ;assume `org-agenda-columns' + ;; Effort property is not defined. Try + ;; to use appointment duration. + org-agenda-columns-add-appointments-to-effort-sum + (string= p (upcase org-effort-property)) + (get-text-property (point) 'duration) + (propertize (org-minutes-to-clocksum-string + (get-text-property (point) 'duration)) + 'face 'org-warning)) + ""))) + (list spec v (org-columns--displayed-value spec v)))))) + (or compiled-fmt org-columns-current-fmt-compiled)))) + +(defun org-columns--set-widths (cache) + "Compute the maximum column widths from the format and CACHE. +This function sets `org-columns-current-maxwidths' as a vector of +integers greater than 0." + (setq org-columns-current-maxwidths + (apply #'vector + (mapcar + (lambda (spec) + (pcase spec + (`(,_ ,_ ,(and width (pred wholenump)) . ,_) width) + (`(,_ ,name . ,_) + ;; No width is specified in the columns format. + ;; Compute it by checking all possible values for + ;; PROPERTY. + (let ((width (length name))) + (dolist (entry cache width) + (let ((value (nth 2 (assoc spec (cdr entry))))) + (setq width (max (length value) width)))))))) + org-columns-current-fmt-compiled)))) + +(defun org-columns--new-overlay (beg end &optional string face) "Create a new column overlay and add it to the list." (let ((ov (make-overlay beg end))) (overlay-put ov 'face (or face 'secondary-selection)) - (remove-text-properties 0 (length string) '(face nil) string) (org-overlay-display ov string face) (push ov org-columns-overlays) ov)) -(defun org-columns-display-here (&optional props dateline) - "Overlay the current line with column display." - (interactive) - (let* ((fmt org-columns-current-fmt-compiled) - (beg (point-at-bol)) - (level-face (save-excursion - (beginning-of-line 1) - (and (looking-at "\\(\\**\\)\\(\\* \\)") - (org-get-level-face 2)))) - (ref-face (or level-face - (and (eq major-mode 'org-agenda-mode) - (get-text-property (point-at-bol) 'face)) - 'default)) - (color (list :foreground (face-attribute ref-face :foreground))) - (font (list :height (face-attribute 'default :height) - :family (face-attribute 'default :family))) - (face (list color font 'org-column ref-face)) - (face1 (list color font 'org-agenda-column-dateline ref-face)) - (cphr (get-text-property (point-at-bol) 'org-complex-heading-regexp)) - pom property ass width f fc string fm ov column val modval s2 title calc) - ;; Check if the entry is in another buffer. - (unless props - (if (eq major-mode 'org-agenda-mode) - (setq pom (or (org-get-at-bol 'org-hd-marker) - (org-get-at-bol 'org-marker)) - props (if pom (org-entry-properties pom) nil)) - (setq props (org-entry-properties nil)))) - ;; Walk the format - (while (setq column (pop fmt)) - (setq property (car column) - title (nth 1 column) - ass (if (equal property "ITEM") - (cons "ITEM" - ;; When in a buffer, get the whole line, - ;; we'll clean it later… - (if (derived-mode-p 'org-mode) - (save-match-data - (org-remove-tabs - (buffer-substring-no-properties - (point-at-bol) (point-at-eol)))) - ;; In agenda, just get the `txt' property - (or (org-get-at-bol 'txt) - (buffer-substring-no-properties - (point) (progn (end-of-line) (point)))))) - (assoc property props)) - width (or (cdr (assoc property org-columns-current-maxwidths)) - (nth 2 column) - (length property)) - f (format "%%-%d.%ds | " width width) - fm (nth 4 column) - fc (nth 5 column) - calc (nth 7 column) - val (or (cdr ass) "") - modval (cond ((and org-columns-modify-value-for-display-function - (functionp - org-columns-modify-value-for-display-function)) - (funcall org-columns-modify-value-for-display-function - title val)) - ((equal property "ITEM") - (org-columns-cleanup-item - val org-columns-current-fmt-compiled - (or org-complex-heading-regexp cphr))) - (fc (org-columns-number-to-string - (org-columns-string-to-number val fm) fm fc)) - ((and calc (functionp calc) - (not (string= val "")) - (not (get-text-property 0 'org-computed val))) - (org-columns-number-to-string - (funcall calc (org-columns-string-to-number - val fm)) fm)))) - (setq s2 (org-columns-add-ellipses (or modval val) width)) - (setq string (format f s2)) - ;; Create the overlay +(defun org-columns--summarize (operator) + "Return summary function associated to string OPERATOR." + (if (not operator) nil + (cdr (or (assoc operator org-columns-summary-types) + (assoc operator org-columns-summary-types-default) + (error "Unknown %S operator" operator))))) + +(defun org-columns--overlay-text (value fmt width property original) + "Return text " + (format fmt + (let ((v (org-columns-add-ellipses value width))) + (pcase property + ("PRIORITY" + (propertize v 'face (org-get-priority-face original))) + ("TAGS" + (if (not org-tags-special-faces-re) + (propertize v 'face 'org-tag) + (replace-regexp-in-string + org-tags-special-faces-re + (lambda (m) (propertize m 'face (org-get-tag-face m))) + v nil nil 1))) + ("TODO" (propertize v 'face (org-get-todo-face original))) + (_ v))))) + +(defun org-columns--display-here (columns &optional dateline) + "Overlay the current line with column display. +COLUMNS is an alist (SPEC VALUE DISPLAYED). Optional argument +DATELINE is non-nil when the face used should be +`org-agenda-column-dateline'." + (save-excursion + (beginning-of-line) + (let* ((level-face (and (looking-at "\\(\\**\\)\\(\\* \\)") + (org-get-level-face 2))) + (ref-face (or level-face + (and (eq major-mode 'org-agenda-mode) + (org-get-at-bol 'face)) + 'default)) + (color (list :foreground (face-attribute ref-face :foreground))) + (font (list :height (face-attribute 'default :height) + :family (face-attribute 'default :family))) + (face (list color font 'org-column ref-face)) + (face1 (list color font 'org-agenda-column-dateline ref-face))) + ;; Each column is an overlay on top of a character. So there has + ;; to be at least as many characters available on the line as + ;; columns to display. + (let ((columns (length org-columns-current-fmt-compiled)) + (chars (- (line-end-position) (line-beginning-position)))) + (when (> columns chars) + (save-excursion + (end-of-line) + (let ((inhibit-read-only t)) + (insert (make-string (- columns chars) ?\s)))))) + ;; Display columns. Create and install the overlay for the + ;; current column on the next character. + (let ((i 0) + (last (1- (length columns)))) + (dolist (column columns) + (pcase column + (`(,spec ,original ,value) + (let* ((property (car spec)) + (width (aref org-columns-current-maxwidths i)) + (fmt (format (if (= i last) "%%-%d.%ds |" + "%%-%d.%ds | ") + width width)) + (ov (org-columns--new-overlay + (point) (1+ (point)) + (org-columns--overlay-text + value fmt width property original) + (if dateline face1 face)))) + (overlay-put ov 'keymap org-columns-map) + (overlay-put ov 'org-columns-key property) + (overlay-put ov 'org-columns-value original) + (overlay-put ov 'org-columns-value-modified value) + (overlay-put ov 'org-columns-format fmt) + (overlay-put ov 'line-prefix "") + (overlay-put ov 'wrap-prefix "") + (forward-char)))) + (cl-incf i))) + ;; Make the rest of the line disappear. + (let ((ov (org-columns--new-overlay (point) (line-end-position)))) + (overlay-put ov 'invisible t) + (overlay-put ov 'keymap org-columns-map) + (overlay-put ov 'line-prefix "") + (overlay-put ov 'wrap-prefix "")) + (let ((ov (make-overlay (1- (line-end-position)) + (line-beginning-position 2)))) + (overlay-put ov 'keymap org-columns-map) + (push ov org-columns-overlays)) (org-with-silent-modifications - (setq ov (org-columns-new-overlay - beg (setq beg (1+ beg)) string (if dateline face1 face))) - (overlay-put ov 'keymap org-columns-map) - (overlay-put ov 'org-columns-key property) - (overlay-put ov 'org-columns-value (cdr ass)) - (overlay-put ov 'org-columns-value-modified modval) - (overlay-put ov 'org-columns-pom pom) - (overlay-put ov 'org-columns-format f) - (overlay-put ov 'line-prefix "") - (overlay-put ov 'wrap-prefix "")) - (if (or (not (char-after beg)) - (equal (char-after beg) ?\n)) - (let ((inhibit-read-only t)) - (save-excursion - (goto-char beg) - (org-unmodified (insert " ")))))) ;; FIXME: add props and remove later? - ;; Make the rest of the line disappear. - (org-unmodified - (setq ov (org-columns-new-overlay beg (point-at-eol))) - (overlay-put ov 'invisible t) - (overlay-put ov 'keymap org-columns-map) - (overlay-put ov 'intangible t) - (overlay-put ov 'line-prefix "") - (overlay-put ov 'wrap-prefix "") - (push ov org-columns-overlays) - (setq ov (make-overlay (1- (point-at-eol)) (1+ (point-at-eol)))) - (overlay-put ov 'keymap org-columns-map) - (push ov org-columns-overlays) - (let ((inhibit-read-only t)) - (put-text-property (max (point-min) (1- (point-at-bol))) - (min (point-max) (1+ (point-at-eol))) - 'read-only "Type `e' to edit property"))))) + (let ((inhibit-read-only t)) + (put-text-property + (line-end-position 0) + (line-beginning-position 2) + 'read-only + (substitute-command-keys + "Type \\`\\[org-columns-edit-value]' \ +to edit property"))))))) (defun org-columns-add-ellipses (string width) "Truncate STRING with WIDTH characters, with ellipses." @@ -285,34 +420,27 @@ for the duration of the command.") (defvar header-line-format) (defvar org-columns-previous-hscroll 0) -(defun org-columns-display-here-title () +(defun org-columns--display-here-title () "Overlay the newline before the current line with the table title." (interactive) - (let ((fmt org-columns-current-fmt-compiled) - string (title "") - property width f column str widths) - (while (setq column (pop fmt)) - (setq property (car column) - str (or (nth 1 column) property) - width (or (cdr (assoc property org-columns-current-maxwidths)) - (nth 2 column) - (length str)) - widths (push width widths) - f (format "%%-%d.%ds | " width width) - string (format f str) - title (concat title string))) - (setq title (concat - (org-add-props " " nil 'display '(space :align-to 0)) - ;;(org-add-props title nil 'face '(:weight bold :underline t :inherit default)))) - (org-add-props title nil 'face 'org-column-title))) - (org-set-local 'org-previous-header-line-format header-line-format) - (org-set-local 'org-columns-current-widths (nreverse widths)) - (setq org-columns-full-header-line-format title) + (let ((title "") + (i 0)) + (dolist (column org-columns-current-fmt-compiled) + (pcase column + (`(,property ,name . ,_) + (let* ((width (aref org-columns-current-maxwidths i)) + (fmt (format "%%-%d.%ds | " width width))) + (setq title (concat title (format fmt (or name property))))))) + (cl-incf i)) + (setq-local org-previous-header-line-format header-line-format) + (setq org-columns-full-header-line-format + (concat + (org-add-props " " nil 'display '(space :align-to 0)) + (org-add-props (substring title 0 -1) nil 'face 'org-column-title))) (setq org-columns-previous-hscroll -1) - ; (org-columns-hscoll-title) - (org-add-hook 'post-command-hook 'org-columns-hscoll-title nil 'local))) + (add-hook 'post-command-hook 'org-columns-hscroll-title nil 'local))) -(defun org-columns-hscoll-title () +(defun org-columns-hscroll-title () "Set the `header-line-format' so that it scrolls along with the table." (sit-for .0001) ; need to force a redisplay to update window-hscroll (when (not (= (window-hscroll) org-columns-previous-hscroll)) @@ -335,7 +463,7 @@ for the duration of the command.") (when (local-variable-p 'org-previous-header-line-format) (setq header-line-format org-previous-header-line-format) (kill-local-variable 'org-previous-header-line-format) - (remove-hook 'post-command-hook 'org-columns-hscoll-title 'local)) + (remove-hook 'post-command-hook 'org-columns-hscroll-title 'local)) (move-marker org-columns-begin-marker nil) (move-marker org-columns-top-level-marker nil) (org-with-silent-modifications @@ -348,29 +476,6 @@ for the duration of the command.") (when (local-variable-p 'org-colview-initial-truncate-line-value) (setq truncate-lines org-colview-initial-truncate-line-value))))) -(defun org-columns-cleanup-item (item fmt cphr) - "Remove from ITEM what is a column in the format FMT. -CPHR is the complex heading regexp to use for parsing ITEM." - (let (fixitem) - (if (not cphr) - item - (unless (string-match "^\\*+ " item) - (setq item (concat "* " item) fixitem t)) - (if (string-match cphr item) - (setq item - (concat - (org-add-props (match-string 1 item) nil - 'org-whitespace (* 2 (1- (org-reduced-level (- (match-end 1) (match-beginning 1)))))) - (and (match-end 2) (not (assoc "TODO" fmt)) (concat " " (match-string 2 item))) - (and (match-end 3) (not (assoc "PRIORITY" fmt)) (concat " " (match-string 3 item))) - " " (save-match-data (org-columns-compact-links (or (match-string 4 item) ""))) - (and (match-end 5) (not (assoc "TAGS" fmt)) (concat " " (match-string 5 item))))) - (add-text-properties - 0 (1+ (match-end 1)) - (list 'org-whitespace (* 2 (1- (org-reduced-level (- (match-end 1) (match-beginning 1)))))) - item)) - (if fixitem (replace-regexp-in-string "^\\*+ " "" item) item)))) - (defun org-columns-compact-links (s) "Replace [[link][desc]] with [desc] or [link]." (while (string-match org-bracket-link-regexp s) @@ -394,25 +499,26 @@ CPHR is the complex heading regexp to use for parsing ITEM." (org-columns-remove-overlays) (let ((inhibit-read-only t)) (remove-text-properties (point-min) (point-max) '(read-only t)))) - (when (eq major-mode 'org-agenda-mode) + (if (not (eq major-mode 'org-agenda-mode)) + (setq org-columns-current-fmt nil) (setq org-agenda-columns-active nil) (message "Modification not yet reflected in Agenda buffer, use `r' to refresh"))) (defun org-columns-check-computed () - "Check if this column value is computed. -If yes, throw an error indicating that changing it does not make sense." - (let ((val (get-char-property (point) 'org-columns-value))) - (when (and (stringp val) - (get-char-property 0 'org-computed val)) - (error "This value is computed from the entry's children")))) - -(defun org-columns-todo (&optional arg) + "Throw an error if current column value is computed." + (let ((spec (nth (current-column) org-columns-current-fmt-compiled))) + (and + (nth 3 spec) + (assoc spec (get-text-property (line-beginning-position) 'org-summaries)) + (error "This value is computed from the entry's children")))) + +(defun org-columns-todo (&optional _arg) "Change the TODO state during column view." (interactive "P") (org-columns-edit-value "TODO")) -(defun org-columns-set-tags-or-toggle (&optional arg) +(defun org-columns-set-tags-or-toggle (&optional _arg) "Toggle checkbox at point, or set tags for current headline." (interactive "P") (if (string-match "\\`\\[[ xX-]\\]\\'" @@ -430,107 +536,76 @@ Where possible, use the standard interface for changing this line." (interactive) (org-columns-check-computed) (let* ((col (current-column)) + (bol (line-beginning-position)) + (eol (line-end-position)) + (pom (or (get-text-property bol 'org-hd-marker) (point))) (key (or key (get-char-property (point) 'org-columns-key))) - (value (get-char-property (point) 'org-columns-value)) - (bol (point-at-bol)) (eol (point-at-eol)) - (pom (or (get-text-property bol 'org-hd-marker) - (point))) ; keep despite of compiler waring - (line-overlays - (delq nil (mapcar (lambda (x) - (and (eq (overlay-buffer x) (current-buffer)) - (>= (overlay-start x) bol) - (<= (overlay-start x) eol) - x)) - org-columns-overlays))) - (org-columns-time (time-to-number-of-days (current-time))) - nval eval allowed) + (org-columns--time (float-time (current-time))) + (action + (pcase key + ("CLOCKSUM" + (error "This special column cannot be edited")) + ("ITEM" + (lambda () (org-with-point-at pom (org-edit-headline)))) + ("TODO" + (lambda () + (org-with-point-at pom (call-interactively #'org-todo)))) + ("PRIORITY" + (lambda () + (org-with-point-at pom + (call-interactively #'org-priority)))) + ("TAGS" + (lambda () + (org-with-point-at pom + (let ((org-fast-tag-selection-single-key + (if (eq org-fast-tag-selection-single-key 'expert) + t + org-fast-tag-selection-single-key))) + (call-interactively #'org-set-tags))))) + ("DEADLINE" + (lambda () + (org-with-point-at pom (call-interactively #'org-deadline)))) + ("SCHEDULED" + (lambda () + (org-with-point-at pom (call-interactively #'org-schedule)))) + ("BEAMER_ENV" + (lambda () + (org-with-point-at pom + (call-interactively #'org-beamer-select-environment)))) + (_ + (let* ((allowed (org-property-get-allowed-values pom key 'table)) + (value (get-char-property (point) 'org-columns-value)) + (nval (org-trim + (if (null allowed) (read-string "Edit: " value) + (completing-read + "Value: " allowed nil + (not (get-text-property + 0 'org-unrestricted (caar allowed)))))))) + (and (not (equal nval value)) + (lambda () (org-entry-put pom key nval)))))))) (cond - ((equal key "CLOCKSUM") - (error "This special column cannot be edited")) - ((equal key "ITEM") - (setq eval '(org-with-point-at pom - (org-edit-headline)))) - ((equal key "TODO") - (setq eval '(org-with-point-at - pom - (call-interactively 'org-todo)))) - ((equal key "PRIORITY") - (setq eval '(org-with-point-at pom - (call-interactively 'org-priority)))) - ((equal key "TAGS") - (setq eval '(org-with-point-at pom - (let ((org-fast-tag-selection-single-key - (if (eq org-fast-tag-selection-single-key 'expert) - t org-fast-tag-selection-single-key))) - (call-interactively 'org-set-tags))))) - ((equal key "DEADLINE") - (setq eval '(org-with-point-at pom - (call-interactively 'org-deadline)))) - ((equal key "SCHEDULED") - (setq eval '(org-with-point-at pom - (call-interactively 'org-schedule)))) - ((equal key "BEAMER_env") - (setq eval '(org-with-point-at pom - (call-interactively 'org-beamer-select-environment)))) + ((null action)) + ((eq major-mode 'org-agenda-mode) + (org-columns--call action) + ;; The following let preserves the current format, and makes + ;; sure that in only a single file things need to be updated. + (let* ((org-agenda-overriding-columns-format org-columns-current-fmt) + (buffer (marker-buffer pom)) + (org-agenda-contributing-files + (list (with-current-buffer buffer + (buffer-file-name (buffer-base-buffer)))))) + (org-agenda-columns))) (t - (setq allowed (org-property-get-allowed-values pom key 'table)) - (if allowed - (setq nval (org-icompleting-read - "Value: " allowed nil - (not (get-text-property 0 'org-unrestricted - (caar allowed))))) - (setq nval (read-string "Edit: " value))) - (setq nval (org-trim nval)) - (when (not (equal nval value)) - (setq eval '(org-entry-put pom key nval))))) - (when eval - - (cond - ((equal major-mode 'org-agenda-mode) - (org-columns-eval eval) - ;; The following let preserves the current format, and makes sure - ;; that in only a single file things need to be updated. - (let* ((org-agenda-overriding-columns-format org-columns-current-fmt) - (buffer (marker-buffer pom)) - (org-agenda-contributing-files - (list (with-current-buffer buffer - (buffer-file-name (buffer-base-buffer)))))) - (org-agenda-columns))) - (t - (let ((inhibit-read-only t)) - (org-with-silent-modifications - (remove-text-properties - (max (point-min) (1- bol)) eol '(read-only t))) - (unwind-protect - (progn - (setq org-columns-overlays - (org-delete-all line-overlays org-columns-overlays)) - (mapc 'delete-overlay line-overlays) - (org-columns-eval eval)) - (org-columns-display-here))) - (org-move-to-column col) - (if (and (derived-mode-p 'org-mode) - (nth 3 (assoc key org-columns-current-fmt-compiled))) - (org-columns-update key))))))) - -(defun org-edit-headline () ; FIXME: this is not columns specific. Make interactive????? Use from agenda???? - "Edit the current headline, the part without TODO keyword, TAGS." - (org-back-to-heading) - (when (looking-at org-todo-line-regexp) - (let ((pos (point)) - (pre (buffer-substring (match-beginning 0) (match-beginning 3))) - (txt (match-string 3)) - (post "") - txt2) - (if (string-match (org-re "[ \t]+:[[:alnum:]:_@#%]+:[ \t]*$") txt) - (setq post (match-string 0 txt) - txt (substring txt 0 (match-beginning 0)))) - (setq txt2 (read-string "Edit: " txt)) - (when (not (equal txt txt2)) - (goto-char pos) - (insert pre txt2 post) - (delete-region (point) (point-at-eol)) - (org-set-tags nil t))))) + (let ((inhibit-read-only t)) + (org-with-silent-modifications + (remove-text-properties (max (point-min) (1- bol)) eol '(read-only t))) + (org-columns--call action)) + ;; Some properties can modify headline (e.g., "TODO"), and + ;; possible shuffle overlays. Make sure they are still all at + ;; the right place on the current line. + (let ((org-columns-inhibit-recalculation)) (org-columns-redo)) + (org-columns-update key) + (org-move-to-column col))))) (defun org-columns-edit-allowed () "Edit the list of allowed values for the current property." @@ -553,15 +628,15 @@ Where possible, use the standard interface for changing this line." (t pom)) key1 nval))) -(defun org-columns-eval (form) - (let (hidep) - (save-excursion - (beginning-of-line 1) - ;; `next-line' is needed here, because it skips invisible line. - (condition-case nil (org-no-warnings (next-line 1)) (error nil)) - (setq hidep (org-at-heading-p 1))) - (eval form) - (and hidep (hide-entry)))) +(defun org-columns--call (fun) + "Call function FUN while preserving heading visibility. +FUN is a function called with no argument." + (let ((hide-body (and (/= (line-end-position) (point-max)) + (save-excursion + (move-beginning-of-line 2) + (org-at-heading-p t))))) + (unwind-protect (funcall fun) + (when hide-body (outline-hide-entry))))) (defun org-columns-previous-allowed-value () "Switch to the previous allowed value for this column." @@ -574,72 +649,57 @@ When PREVIOUS is set, go to the previous value. When NTH is an integer, select that value." (interactive) (org-columns-check-computed) - (let* ((col (current-column)) + (let* ((column (current-column)) (key (get-char-property (point) 'org-columns-key)) (value (get-char-property (point) 'org-columns-value)) - (bol (point-at-bol)) (eol (point-at-eol)) - (pom (or (get-text-property bol 'org-hd-marker) - (point))) ; keep despite of compiler waring - (line-overlays - (delq nil (mapcar (lambda (x) - (and (eq (overlay-buffer x) (current-buffer)) - (>= (overlay-start x) bol) - (<= (overlay-start x) eol) - x)) - org-columns-overlays))) - (allowed (or (org-property-get-allowed-values pom key) - (and (memq - (nth 4 (assoc key org-columns-current-fmt-compiled)) - '(checkbox checkbox-n-of-m checkbox-percent)) - '("[ ]" "[X]")) - (org-colview-construct-allowed-dates value))) - nval) - (when (integerp nth) - (setq nth (1- nth)) - (if (= nth -1) (setq nth 9))) - (when (equal key "ITEM") - (error "Cannot edit item headline from here")) + (pom (or (get-text-property (line-beginning-position) 'org-hd-marker) + (point))) + (allowed + (let ((all + (or (org-property-get-allowed-values pom key) + (pcase (nth column org-columns-current-fmt-compiled) + (`(,_ ,_ ,_ ,(or "X" "X/" "X%") ,_) '("[ ]" "[X]"))) + (org-colview-construct-allowed-dates value)))) + (if previous (reverse all) all)))) + (when (equal key "ITEM") (error "Cannot edit item headline from here")) (unless (or allowed (member key '("SCHEDULED" "DEADLINE" "CLOCKSUM"))) (error "Allowed values for this property have not been defined")) - (if (member key '("SCHEDULED" "DEADLINE" "CLOCKSUM")) - (setq nval (if previous 'earlier 'later)) - (if previous (setq allowed (reverse allowed))) + (let* ((l (length allowed)) + (new + (cond + ((member key '("SCHEDULED" "DEADLINE" "CLOCKSUM")) + (if previous 'earlier 'later)) + ((integerp nth) + (when (> (abs nth) l) + (user-error "Only %d allowed values for property `%s'" l key)) + (nth (mod (1- nth) l) allowed)) + ((member value allowed) + (when (= l 1) (error "Only one allowed value for this property")) + (or (nth 1 (member value allowed)) (car allowed))) + (t (car allowed)))) + (action (lambda () (org-entry-put pom key new)))) (cond - (nth - (setq nval (nth nth allowed)) - (if (not nval) - (error "There are only %d allowed values for property `%s'" - (length allowed) key))) - ((member value allowed) - (setq nval (or (car (cdr (member value allowed))) - (car allowed))) - (if (equal nval value) - (error "Only one allowed value for this property"))) - (t (setq nval (car allowed))))) - (cond - ((equal major-mode 'org-agenda-mode) - (org-columns-eval '(org-entry-put pom key nval)) - ;; The following let preserves the current format, and makes sure - ;; that in only a single file things need to be updated. - (let* ((org-agenda-overriding-columns-format org-columns-current-fmt) - (buffer (marker-buffer pom)) - (org-agenda-contributing-files - (list (with-current-buffer buffer - (buffer-file-name (buffer-base-buffer)))))) - (org-agenda-columns))) - (t - (let ((inhibit-read-only t)) - (remove-text-properties (1- bol) eol '(read-only t)) - (unwind-protect - (progn - (setq org-columns-overlays - (org-delete-all line-overlays org-columns-overlays)) - (mapc 'delete-overlay line-overlays) - (org-columns-eval '(org-entry-put pom key nval))) - (org-columns-display-here))) - (org-move-to-column col) - (and (nth 3 (assoc key org-columns-current-fmt-compiled)) - (org-columns-update key)))))) + ((eq major-mode 'org-agenda-mode) + (org-columns--call action) + ;; The following let preserves the current format, and makes + ;; sure that in only a single file things need to be updated. + (let* ((org-agenda-overriding-columns-format org-columns-current-fmt) + (buffer (marker-buffer pom)) + (org-agenda-contributing-files + (list (with-current-buffer buffer + (buffer-file-name (buffer-base-buffer)))))) + (org-agenda-columns))) + (t + (let ((inhibit-read-only t)) + (remove-text-properties (line-end-position 0) (line-end-position) + '(read-only t)) + (org-columns--call action)) + ;; Some properties can modify headline (e.g., "TODO"), and + ;; possible shuffle overlays. Make sure they are still all at + ;; the right place on the current line. + (let ((org-columns-inhibit-recalculation)) (org-columns-redo)) + (org-columns-update key) + (org-move-to-column column)))))) (defun org-colview-construct-allowed-dates (s) "Construct a list of three dates around the date in S. @@ -662,13 +722,6 @@ around it." (mapcar (lambda (x) (format-time-string fmt (apply 'encode-time x))) (list time-before time time-after))))) -(defun org-verify-version (task) - (cond - ((eq task 'columns) - (if (or (featurep 'xemacs) - (< emacs-major-version 22)) - (error "Emacs 22 is required for the columns feature"))))) - (defun org-columns-open-link (&optional arg) (interactive "P") (let ((value (get-char-property (point) 'org-columns-value))) @@ -681,179 +734,165 @@ around it." fmt)) (defun org-columns-get-format (&optional fmt-string) + "Return columns format specifications. +When optional argument FMT-STRING is non-nil, use it as the +current specifications. This function also sets +`org-columns-current-fmt-compiled' and +`org-columns-current-fmt'." (interactive) - (let (fmt-as-property fmt) - (when (condition-case nil (org-back-to-heading) (error nil)) - (setq fmt-as-property (org-entry-get nil "COLUMNS" t))) - (setq fmt (or fmt-string fmt-as-property org-columns-default-format)) - (org-set-local 'org-columns-current-fmt fmt) - (org-columns-compile-format fmt) - fmt)) + (let ((format + (or fmt-string + (org-entry-get nil "COLUMNS" t) + (org-with-wide-buffer + (goto-char (point-min)) + (catch :found + (let ((case-fold-search t)) + (while (re-search-forward "^[ \t]*#\\+COLUMNS: .+$" nil t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'keyword) + (throw :found (org-element-property :value element))))) + nil))) + org-columns-default-format))) + (setq org-columns-current-fmt format) + (org-columns-compile-format format) + format)) (defun org-columns-goto-top-level () - (when (condition-case nil (org-back-to-heading) (error nil)) - (org-entry-get nil "COLUMNS" t)) - (if (marker-position org-entry-property-inherited-from) - (move-marker org-columns-top-level-marker org-entry-property-inherited-from) - (move-marker org-columns-top-level-marker (point)))) + "Move to the beginning of the column view area. +Also sets `org-columns-top-level-marker' to the new position." + (goto-char + (move-marker + org-columns-top-level-marker + (cond ((org-before-first-heading-p) (point-min)) + ((org-entry-get nil "COLUMNS" t) org-entry-property-inherited-from) + (t (org-back-to-heading) (point)))))) ;;;###autoload -(defun org-columns (&optional columns-fmt-string) - "Turn on column view on an org-mode file. +(defun org-columns (&optional global columns-fmt-string) + "Turn on column view on an Org mode file. + +Column view applies to the whole buffer if point is before the +first headline. Otherwise, it applies to the first ancestor +setting \"COLUMNS\" property. If there is none, it defaults to +the current headline. With a `\\[universal-argument]' prefix \ +argument, turn on column +view for the whole buffer unconditionally. + When COLUMNS-FMT-STRING is non-nil, use it as the column format." - (interactive) - (org-verify-version 'columns) + (interactive "P") (org-columns-remove-overlays) + (when global (goto-char (point-min))) (move-marker org-columns-begin-marker (point)) - (let ((org-columns-time (time-to-number-of-days (current-time))) - beg end fmt cache maxwidths) - (org-columns-goto-top-level) - (setq fmt (org-columns-get-format columns-fmt-string)) + (org-columns-goto-top-level) + ;; Initialize `org-columns-current-fmt' and + ;; `org-columns-current-fmt-compiled'. + (let ((org-columns--time (float-time (current-time)))) + (org-columns-get-format columns-fmt-string) + (unless org-columns-inhibit-recalculation (org-columns-compute-all)) (save-excursion - (goto-char org-columns-top-level-marker) - (setq beg (point)) - (unless org-columns-inhibit-recalculation - (org-columns-compute-all)) - (setq end (or (condition-case nil (org-end-of-subtree t t) (error nil)) - (point-max))) - ;; Get and cache the properties - (goto-char beg) - (when (assoc "CLOCKSUM" org-columns-current-fmt-compiled) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (org-clock-sum)))) - (when (assoc "CLOCKSUM_T" org-columns-current-fmt-compiled) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (org-clock-sum-today)))) - (while (re-search-forward org-outline-regexp-bol end t) - (if (and org-columns-skip-archived-trees - (looking-at (concat ".*:" org-archive-tag ":"))) - (org-end-of-subtree t) - (push (cons (org-current-line) (org-entry-properties)) cache))) - (when cache - (setq maxwidths (org-columns-get-autowidth-alist fmt cache)) - (org-set-local 'org-columns-current-maxwidths maxwidths) - (org-columns-display-here-title) - (when (org-set-local 'org-columns-flyspell-was-active - (org-bound-and-true-p flyspell-mode)) - (flyspell-mode 0)) - (unless (local-variable-p 'org-colview-initial-truncate-line-value) - (org-set-local 'org-colview-initial-truncate-line-value - truncate-lines)) - (setq truncate-lines t) - (mapc (lambda (x) - (org-goto-line (car x)) - (org-columns-display-here (cdr x))) - cache))))) - -(eval-when-compile (defvar org-columns-time)) - -(defvar org-columns-compile-map - '(("none" none +) - (":" add_times +) - ("+" add_numbers +) - ("$" currency +) - ("X" checkbox +) - ("X/" checkbox-n-of-m +) - ("X%" checkbox-percent +) - ("max" max_numbers max) - ("min" min_numbers min) - ("mean" mean_numbers - (lambda (&rest x) (/ (apply '+ x) (float (length x))))) - (":max" max_times max) - (":min" min_times min) - (":mean" mean_times - (lambda (&rest x) (/ (apply '+ x) (float (length x))))) - ("@min" min_age min (lambda (x) (- org-columns-time x))) - ("@max" max_age max (lambda (x) (- org-columns-time x))) - ("@mean" mean_age - (lambda (&rest x) (/ (apply '+ x) (float (length x)))) - (lambda (x) (- org-columns-time x))) - ("est+" estimate org-estimate-combine)) - "Operator <-> format,function,calc map. -Used to compile/uncompile columns format and completing read in -interactive function `org-columns-new'. - -operator string used in #+COLUMNS definition describing the - summary type -format symbol describing summary type selected interactively in - `org-columns-new' and internally in - `org-columns-number-to-string' and - `org-columns-string-to-number' -function called with a list of values as argument to calculate - the summary value -calc function called on every element before summarizing. This is - optional and should only be specified if needed") - -(defun org-columns-new (&optional prop title width op fmt fun &rest rest) - "Insert a new column, to the left of the current column." + (save-restriction + (when (and (not global) (org-at-heading-p)) + (narrow-to-region (point) (org-end-of-subtree t t))) + (when (assoc "CLOCKSUM" org-columns-current-fmt-compiled) + (org-clock-sum)) + (when (assoc "CLOCKSUM_T" org-columns-current-fmt-compiled) + (org-clock-sum-today)) + (let ((cache + ;; Collect contents of columns ahead of time so as to + ;; compute their maximum width. + (org-map-entries + (lambda () (cons (point) (org-columns--collect-values))) + nil nil (and org-columns-skip-archived-trees 'archive)))) + (when cache + (org-columns--set-widths cache) + (org-columns--display-here-title) + (when (setq-local org-columns-flyspell-was-active + (bound-and-true-p flyspell-mode)) + (flyspell-mode 0)) + (unless (local-variable-p 'org-colview-initial-truncate-line-value) + (setq-local org-colview-initial-truncate-line-value + truncate-lines)) + (setq truncate-lines t) + (dolist (entry cache) + (goto-char (car entry)) + (org-columns--display-here (cdr entry))))))))) + +(defun org-columns-new (&optional spec &rest attributes) + "Insert a new column, to the left of the current column. +Interactively fill attributes for new column. When column format +specification SPEC is provided, edit it instead. + +When optional argument attributes can be a list of columns +specifications attributes to create the new column +non-interactively. See `org-columns-compile-format' for +details." (interactive) - (let ((editp (and prop (assoc prop org-columns-current-fmt-compiled))) - cell) - (setq prop (org-icompleting-read - "Property: " (mapcar 'list (org-buffer-property-keys t nil t)) - nil nil prop)) - (setq title (read-string (concat "Column title [" prop "]: ") (or title prop))) - (setq width (read-string "Column width: " (if width (number-to-string width)))) - (if (string-match "\\S-" width) - (setq width (string-to-number width)) - (setq width nil)) - (setq fmt (org-icompleting-read - "Summary [none]: " - (mapcar (lambda (x) (list (symbol-name (cadr x)))) - org-columns-compile-map) - nil t)) - (setq fmt (intern fmt) - fun (cdr (assoc fmt (mapcar 'cdr org-columns-compile-map)))) - (if (eq fmt 'none) (setq fmt nil)) - (if editp - (progn - (setcar editp prop) - (setcdr editp (list title width nil fmt nil fun))) - (setq cell (nthcdr (1- (current-column)) - org-columns-current-fmt-compiled)) - (setcdr cell (cons (list prop title width nil fmt nil - (car fun) (cadr fun)) - (cdr cell)))) + (let ((new (or attributes + (let ((prop + (completing-read + "Property: " + (mapcar #'list (org-buffer-property-keys t nil t)) + nil nil (nth 0 spec)))) + (list prop + (read-string (format "Column title [%s]: " prop) + (nth 1 spec)) + ;; Use `read-string' instead of `read-number' + ;; to allow empty width. + (let ((w (read-string + "Column width: " + (and (nth 2 spec) + (number-to-string (nth 2 spec)))))) + (and (org-string-nw-p w) (string-to-number w))) + (org-string-nw-p + (completing-read + "Summary: " + (delete-dups + (cons '("") ;Allow empty operator. + (mapcar (lambda (x) (list (car x))) + (append + org-columns-summary-types + org-columns-summary-types-default)))) + nil t (nth 3 spec))) + (org-string-nw-p + (read-string "Format: " (nth 4 spec)))))))) + (if spec + (progn (setcar spec (car new)) + (setcdr spec (cdr new))) + (push new (nthcdr (current-column) org-columns-current-fmt-compiled))) (org-columns-store-format) (org-columns-redo))) (defun org-columns-delete () "Delete the column at point from columns view." (interactive) - (let* ((n (current-column)) - (title (nth 1 (nth n org-columns-current-fmt-compiled)))) - (when (y-or-n-p - (format "Are you sure you want to remove column \"%s\"? " title)) + (let ((spec (nth (current-column) org-columns-current-fmt-compiled))) + (when (y-or-n-p (format "Are you sure you want to remove column %S? " + (nth 1 spec))) (setq org-columns-current-fmt-compiled - (delq (nth n org-columns-current-fmt-compiled) - org-columns-current-fmt-compiled)) + (delq spec org-columns-current-fmt-compiled)) (org-columns-store-format) - (org-columns-redo) - (if (>= (current-column) (length org-columns-current-fmt-compiled)) - (backward-char 1))))) + ;; This may leave a now wrong value in a node property. However + ;; updating it may prove counter-intuitive. See comments in + ;; `org-columns-move-right' for details. + (let ((org-columns-inhibit-recalculation t)) (org-columns-redo)) + (when (>= (current-column) (length org-columns-current-fmt-compiled)) + (backward-char))))) (defun org-columns-edit-attributes () "Edit the attributes of the current column." (interactive) - (let* ((n (current-column)) - (info (nth n org-columns-current-fmt-compiled))) - (apply 'org-columns-new info))) + (org-columns-new (nth (current-column) org-columns-current-fmt-compiled))) (defun org-columns-widen (arg) "Make the column wider by ARG characters." (interactive "p") (let* ((n (current-column)) (entry (nth n org-columns-current-fmt-compiled)) - (width (or (nth 2 entry) - (cdr (assoc (car entry) org-columns-current-maxwidths))))) + (width (aref org-columns-current-maxwidths n))) (setq width (max 1 (+ width arg))) (setcar (nthcdr 2 entry) width) (org-columns-store-format) - (org-columns-redo))) + (let ((org-columns-inhibit-recalculation t)) (org-columns-redo)))) (defun org-columns-narrow (arg) "Make the column narrower by ARG characters." @@ -872,7 +911,16 @@ calc function called on every element before summarizing. This is (setcar cell (car (cdr cell))) (setcdr cell (cons e (cdr (cdr cell)))) (org-columns-store-format) - (org-columns-redo) + ;; Do not compute again properties, since we're just moving + ;; columns around. It can put a property value a bit off when + ;; switching between an non-computed and a computed value for the + ;; same property, e.g. from "%A %A{+}" to "%A{+} %A". + ;; + ;; In this case, the value needs to be updated since the first + ;; column related to a property determines how its value is + ;; computed. However, (correctly) updating the value could be + ;; surprising, so we leave it as-is nonetheless. + (let ((org-columns-inhibit-recalculation t)) (org-columns-redo)) (forward-char 1))) (defun org-columns-move-left () @@ -886,358 +934,455 @@ calc function called on every element before summarizing. This is (backward-char 1))) (defun org-columns-store-format () - "Store the text version of the current columns format in appropriate place. -This is either in the COLUMNS property of the node starting the current column -display, or in the #+COLUMNS line of the current buffer." - (let (fmt (cnt 0)) - (setq fmt (org-columns-uncompile-format org-columns-current-fmt-compiled)) - (org-set-local 'org-columns-current-fmt fmt) - (if (marker-position org-columns-top-level-marker) - (save-excursion - (goto-char org-columns-top-level-marker) - (if (and (org-at-heading-p) - (org-entry-get nil "COLUMNS")) - (org-entry-put nil "COLUMNS" fmt) - (goto-char (point-min)) - ;; Overwrite all #+COLUMNS lines.... - (while (re-search-forward "^#\\+COLUMNS:.*" nil t) - (setq cnt (1+ cnt)) - (replace-match (concat "#+COLUMNS: " fmt) t t)) - (unless (> cnt 0) - (goto-char (point-min)) - (or (org-at-heading-p t) (outline-next-heading)) - (let ((inhibit-read-only t)) - (insert-before-markers "#+COLUMNS: " fmt "\n"))) - (org-set-local 'org-columns-default-format fmt)))))) - -(defun org-columns-get-autowidth-alist (s cache) - "Derive the maximum column widths from the format and the cache." - (let ((start 0) rtn) - (while (string-match (org-re "%\\([[:alpha:]][[:alnum:]_-]*\\)") s start) - (push (cons (match-string 1 s) 1) rtn) - (setq start (match-end 0))) - (mapc (lambda (x) - (setcdr x (apply 'max - (mapcar - (lambda (y) - (length (or (cdr (assoc (car x) (cdr y))) " "))) - cache)))) - rtn) - rtn)) - -(defun org-columns-compute-all () - "Compute all columns that have operators defined." - (org-with-silent-modifications - (remove-text-properties (point-min) (point-max) '(org-summaries t))) - (let ((columns org-columns-current-fmt-compiled) - (org-columns-time (time-to-number-of-days (current-time))) - col) - (while (setq col (pop columns)) - (when (nth 3 col) - (save-excursion - (org-columns-compute (car col))))))) + "Store the text version of the current columns format. +The format is stored either in the COLUMNS property of the node +starting the current column display, or in a #+COLUMNS line of +the current buffer." + (let ((fmt (org-columns-uncompile-format org-columns-current-fmt-compiled))) + (setq-local org-columns-current-fmt fmt) + (when (marker-position org-columns-top-level-marker) + (org-with-wide-buffer + (goto-char org-columns-top-level-marker) + (if (and (org-at-heading-p) (org-entry-get nil "COLUMNS")) + (org-entry-put nil "COLUMNS" fmt) + (goto-char (point-min)) + (let ((case-fold-search t)) + ;; Try to replace the first COLUMNS keyword available. + (catch :found + (while (re-search-forward "^[ \t]*#\\+COLUMNS:\\(.*\\)" nil t) + (let ((element (save-match-data (org-element-at-point)))) + (when (and (eq (org-element-type element) 'keyword) + (equal (org-element-property :key element) + "COLUMNS")) + (replace-match (concat " " fmt) t t nil 1) + (throw :found nil)))) + ;; No COLUMNS keyword in the buffer. Insert one at the + ;; beginning, right before the first heading, if any. + (goto-char (point-min)) + (unless (org-at-heading-p t) (outline-next-heading)) + (let ((inhibit-read-only t)) + (insert-before-markers "#+COLUMNS: " fmt "\n")))) + (setq-local org-columns-default-format fmt)))))) (defun org-columns-update (property) "Recompute PROPERTY, and update the columns display for it." (org-columns-compute property) - (let (fmt val pos) - (save-excursion - (mapc (lambda (ov) - (when (equal (overlay-get ov 'org-columns-key) property) - (setq pos (overlay-start ov)) - (goto-char pos) - (when (setq val (cdr (assoc property - (get-text-property - (point-at-bol) 'org-summaries)))) - (setq fmt (overlay-get ov 'org-columns-format)) - (overlay-put ov 'org-columns-value val) - (overlay-put ov 'display (format fmt val))))) - org-columns-overlays)))) - -(defvar org-inlinetask-min-level - (if (featurep 'org-inlinetask) org-inlinetask-min-level 15)) - -;;;###autoload -(defun org-columns-compute (property) - "Sum the values of property PROPERTY hierarchically, for the entire buffer." - (interactive) - (let* ((re org-outline-regexp-bol) - (lmax 30) ; Does anyone use deeper levels??? - (lvals (make-vector lmax nil)) - (lflag (make-vector lmax nil)) - (level 0) - (ass (assoc property org-columns-current-fmt-compiled)) - (format (nth 4 ass)) - (printf (nth 5 ass)) - (fun (nth 6 ass)) - (calc (or (nth 7 ass) 'identity)) - (beg org-columns-top-level-marker) - (inminlevel org-inlinetask-min-level) - (last-level org-inlinetask-min-level) - val valflag flag end sumpos sum-alist sum str str1 useval) - (save-excursion - ;; Find the region to compute - (goto-char beg) - (setq end (condition-case nil (org-end-of-subtree t) (error (point-max)))) - (goto-char end) - ;; Walk the tree from the back and do the computations - (while (re-search-backward re beg t) - (setq sumpos (match-beginning 0) - last-level (if (not (or (zerop level) (eq level inminlevel))) - level last-level) - level (org-outline-level) - val (org-entry-get nil property) - valflag (and val (string-match "\\S-" val))) - (cond - ((< level last-level) - ;; put the sum of lower levels here as a property - (setq sum (+ (if (and (/= last-level inminlevel) - (aref lvals last-level)) - (apply fun (aref lvals last-level)) 0) - (if (aref lvals inminlevel) - (apply fun (aref lvals inminlevel)) 0)) - flag (or (aref lflag last-level) ; any valid entries from children? - (aref lflag inminlevel)) ; or inline tasks? - str (org-columns-number-to-string sum format printf) - str1 (org-add-props (copy-sequence str) nil 'org-computed t 'face 'bold) - useval (if flag str1 (if valflag val "")) - sum-alist (get-text-property sumpos 'org-summaries)) - (if (assoc property sum-alist) - (setcdr (assoc property sum-alist) useval) - (push (cons property useval) sum-alist) - (org-with-silent-modifications - (add-text-properties sumpos (1+ sumpos) - (list 'org-summaries sum-alist)))) - (when (and val (not (equal val (if flag str val)))) - (org-entry-put nil property (if flag str val))) - ;; add current to current level accumulator - (when (or flag valflag) - (push (if flag - sum - (funcall calc (org-columns-string-to-number - (if flag str val) format))) - (aref lvals level)) - (aset lflag level t)) - ;; clear accumulators for deeper levels - (loop for l from (1+ level) to (1- lmax) do - (aset lvals l nil) - (aset lflag l nil))) - ((>= level last-level) - ;; add what we have here to the accumulator for this level - (when valflag - (push (funcall calc (org-columns-string-to-number val format)) - (aref lvals level)) - (aset lflag level t))) - (t (error "This should not happen"))))))) + (org-with-wide-buffer + (let ((p (upcase property))) + (dolist (ov org-columns-overlays) + (let ((key (overlay-get ov 'org-columns-key))) + (when (and key (equal key p) (overlay-start ov)) + (goto-char (overlay-start ov)) + (let* ((spec (nth (current-column) org-columns-current-fmt-compiled)) + (value + (or (cdr (assoc spec + (get-text-property (line-beginning-position) + 'org-summaries))) + (org-entry-get (point) key)))) + (when value + (let ((displayed (org-columns--displayed-value spec value)) + (format (overlay-get ov 'org-columns-format)) + (width + (aref org-columns-current-maxwidths (current-column)))) + (overlay-put ov 'org-columns-value value) + (overlay-put ov 'org-columns-value-modified displayed) + (overlay-put ov + 'display + (org-columns--overlay-text + displayed format width property value))))))))))) (defun org-columns-redo () "Construct the column display again." (interactive) (message "Recomputing columns...") - (let ((line (org-current-line)) - (col (current-column))) - (save-excursion - (if (marker-position org-columns-begin-marker) - (goto-char org-columns-begin-marker)) - (org-columns-remove-overlays) - (if (derived-mode-p 'org-mode) - (call-interactively 'org-columns) - (org-agenda-redo) - (call-interactively 'org-agenda-columns))) - (org-goto-line line) - (move-to-column col)) + (org-with-wide-buffer + (when (marker-position org-columns-begin-marker) + (goto-char org-columns-begin-marker)) + (org-columns-remove-overlays) + (if (derived-mode-p 'org-mode) + ;; Since we already know the columns format, provide it instead + ;; of computing again. + (call-interactively #'org-columns org-columns-current-fmt) + (org-agenda-redo) + (call-interactively #'org-agenda-columns))) (message "Recomputing columns...done")) -(defun org-columns-not-in-agenda () - (if (eq major-mode 'org-agenda-mode) - (error "This command is only allowed in Org-mode buffers"))) - -(defun org-string-to-number (s) - "Convert string to number, and interpret hh:mm:ss." - (if (not (string-match ":" s)) - (string-to-number s) - (let ((l (nreverse (org-split-string s ":"))) (sum 0.0)) - (while l - (setq sum (+ (string-to-number (pop l)) (/ sum 60)))) - sum))) - -;;;###autoload -(defun org-columns-number-to-string (n fmt &optional printf) - "Convert a computed column number to a string value, according to FMT." - (cond - ((memq fmt '(estimate)) (org-estimate-print n printf)) - ((not (numberp n)) "") - ((memq fmt '(add_times max_times min_times mean_times)) - (org-hours-to-clocksum-string n)) - ((eq fmt 'checkbox) - (cond ((= n (floor n)) "[X]") - ((> n 1.) "[-]") - (t "[ ]"))) - ((memq fmt '(checkbox-n-of-m checkbox-percent)) - (let* ((n1 (floor n)) (n2 (floor (+ .5 (* 1000000 (- n n1)))))) - (org-nofm-to-completion n1 (+ n2 n1) (eq fmt 'checkbox-percent)))) - (printf (format printf n)) - ((eq fmt 'currency) - (format "%.2f" n)) - ((memq fmt '(min_age max_age mean_age)) - (org-format-time-period n)) - (t (number-to-string n)))) - -(defun org-nofm-to-completion (n m &optional percent) - (if (not percent) - (format "[%d/%d]" n m) - (format "[%d%%]" (round (* 100.0 n) m)))) - - -(defun org-columns-string-to-number (s fmt) - "Convert a column value to a number that can be used for column computing." - (if s - (cond - ((memq fmt '(min_age max_age mean_age)) - (cond ((string= s "") org-columns-time) - ((string-match - "\\([0-9]+\\)d \\([0-9]+\\)h \\([0-9]+\\)m \\([0-9]+\\)s" - s) - (+ (* 60 (+ (* 60 (+ (* 24 (string-to-number (match-string 1 s))) - (string-to-number (match-string 2 s)))) - (string-to-number (match-string 3 s)))) - (string-to-number (match-string 4 s)))) - (t (time-to-number-of-days (apply 'encode-time - (org-parse-time-string s t)))))) - ((string-match ":" s) - (let ((l (nreverse (org-split-string s ":"))) (sum 0.0)) - (while l - (setq sum (+ (string-to-number (pop l)) (/ sum 60)))) - sum)) - ((string-match (concat "\\([0-9.]+\\) *\\(" - (regexp-opt (mapcar 'car org-effort-durations)) - "\\)") s) - (setq s (concat "0:" (org-duration-string-to-minutes s t))) - (let ((l (nreverse (org-split-string s ":"))) (sum 0.0)) - (while l - (setq sum (+ (string-to-number (pop l)) (/ sum 60)))) - sum)) - ((memq fmt '(checkbox checkbox-n-of-m checkbox-percent)) - (if (equal s "[X]") 1. 0.000001)) - ((memq fmt '(estimate)) (org-string-to-estimate s)) - (t (string-to-number s))))) - -(defun org-columns-uncompile-format (cfmt) - "Turn the compiled columns format back into a string representation." - (let ((rtn "") e s prop title op op-match width fmt printf fun calc) - (while (setq e (pop cfmt)) - (setq prop (car e) - title (nth 1 e) - width (nth 2 e) - op (nth 3 e) - fmt (nth 4 e) - printf (nth 5 e) - fun (nth 6 e) - calc (nth 7 e)) - (when (setq op-match (rassoc (list fmt fun calc) org-columns-compile-map)) - (setq op (car op-match))) - (if (and op printf) (setq op (concat op ";" printf))) - (if (equal title prop) (setq title nil)) - (setq s (concat "%" (if width (number-to-string width)) - prop - (if title (concat "(" title ")")) - (if op (concat "{" op "}")))) - (setq rtn (concat rtn " " s))) - (org-trim rtn))) +(defun org-columns-uncompile-format (compiled) + "Turn the compiled columns format back into a string representation. +COMPILED is an alist, as returned by +`org-columns-compile-format', which see." + (mapconcat + (lambda (spec) + (pcase spec + (`(,prop ,title ,width ,op ,printf) + (concat "%" + (and width (number-to-string width)) + prop + (and title (not (equal prop title)) (format "(%s)" title)) + (cond ((not op) nil) + (printf (format "{%s;%s}" op printf)) + (t (format "{%s}" op))))))) + compiled " ")) (defun org-columns-compile-format (fmt) - "Turn a column format string into an alist of specifications. + "Turn a column format string FMT into an alist of specifications. + The alist has one entry for each column in the format. The elements of that list are: -property the property -title the title field for the columns -width the column width in characters, can be nil for automatic -operator the operator if any -format the output format for computed results, derived from operator -printf a printf format for computed values -fun the lisp function to compute summary values, derived from operator -calc function to get values from base elements" - (let ((start 0) width prop title op op-match f printf fun calc) - (setq org-columns-current-fmt-compiled nil) +property the property name, as an upper-case string +title the title field for the columns, as a string +width the column width in characters, can be nil for automatic width +operator the summary operator, as a string, or nil +printf a printf format for computed values, as a string, or nil + +This function updates `org-columns-current-fmt-compiled'." + (setq org-columns-current-fmt-compiled nil) + (let ((start 0)) (while (string-match - (org-re "%\\([0-9]+\\)?\\([[:alnum:]_-]+\\)\\(?:(\\([^)]+\\))\\)?\\(?:{\\([^}]+\\)}\\)?\\s-*") + "%\\([0-9]+\\)?\\([[:alnum:]_-]+\\)\\(?:(\\([^)]+\\))\\)?\ +\\(?:{\\([^}]+\\)}\\)?\\s-*" fmt start) - (setq start (match-end 0) - width (match-string 1 fmt) - prop (match-string 2 fmt) - title (or (match-string 3 fmt) prop) - op (match-string 4 fmt) - f nil - printf nil - fun '+ - calc nil) - (if width (setq width (string-to-number width))) - (when (and op (string-match ";" op)) - (setq printf (substring op (match-end 0)) - op (substring op 0 (match-beginning 0)))) - (when (setq op-match (assoc op org-columns-compile-map)) - (setq f (cadr op-match) - fun (caddr op-match) - calc (cadddr op-match))) - (push (list prop title width op f printf fun calc) - org-columns-current-fmt-compiled)) + (setq start (match-end 0)) + (let* ((width (and (match-end 1) (string-to-number (match-string 1 fmt)))) + (prop (match-string-no-properties 2 fmt)) + (title (or (match-string-no-properties 3 fmt) prop)) + (operator (match-string-no-properties 4 fmt))) + (push (if (not operator) (list (upcase prop) title width nil nil) + (let (printf) + (when (string-match ";" operator) + (setq printf (substring operator (match-end 0))) + (setq operator (substring operator 0 (match-beginning 0)))) + (list (upcase prop) title width operator printf))) + org-columns-current-fmt-compiled))) (setq org-columns-current-fmt-compiled (nreverse org-columns-current-fmt-compiled)))) + +;;;; Column View Summary + +(defconst org-columns--duration-re + (concat "[0-9.]+ *" (regexp-opt (mapcar #'car org-effort-durations))) + "Regexp matching a duration.") + +(defun org-columns--time-to-seconds (s) + "Turn time string S into a number of seconds. +A time is expressed as HH:MM, HH:MM:SS, or with units defined in +`org-effort-durations'. Plain numbers are considered as hours." + (cond + ((string-match-p org-columns--duration-re s) + (* 60 (org-duration-string-to-minutes s))) + ((string-match "\\`\\([0-9]+\\):\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?\\'" s) + (+ (* 3600 (string-to-number (match-string 1 s))) + (* 60 (string-to-number (match-string 2 s))) + (if (match-end 3) (string-to-number (match-string 3 s)) 0))) + (t (* 3600 (string-to-number s))))) + +(defun org-columns--age-to-seconds (s) + "Turn age string S into a number of seconds. +An age is either computed from a given time-stamp, or indicated +as days/hours/minutes/seconds." + (cond + ((string-match-p org-ts-regexp s) + (floor + (- org-columns--time + (float-time (apply #'encode-time (org-parse-time-string s nil t)))))) + ;; Match own output for computations in upper levels. + ((string-match "\\([0-9]+\\)d \\([0-9]+\\)h \\([0-9]+\\)m \\([0-9]+\\)s" s) + (+ (* 86400 (string-to-number (match-string 1 s))) + (* 3600 (string-to-number (match-string 2 s))) + (* 60 (string-to-number (match-string 3 s))) + (string-to-number (match-string 4 s)))) + (t (user-error "Invalid age: %S" s)))) + +(defun org-columns--summary-apply-times (fun times) + "Apply FUN to time values TIMES. +If TIMES contains any time value expressed as a duration, return +the result as a duration. If it contains any H:M:S, use that +format instead. Otherwise, use H:M format." + (let* ((hms-flag nil) + (duration-flag nil) + (seconds + (apply fun + (mapcar + (lambda (time) + (cond + (duration-flag) + ((string-match-p org-columns--duration-re time) + (setq duration-flag t)) + (hms-flag) + ((string-match-p "\\`[0-9]+:[0-9]+:[0-9]+\\'" time) + (setq hms-flag t))) + (org-columns--time-to-seconds time)) + times)))) + (cond (duration-flag (org-minutes-to-clocksum-string (/ seconds 60.0))) + (hms-flag (format-seconds "%h:%.2m:%.2s" seconds)) + (t (format-seconds "%h:%.2m" seconds))))) + +(defun org-columns--compute-spec (spec &optional update) + "Update tree according to SPEC. +SPEC is a column format specification. When optional argument +UPDATE is non-nil, summarized values can replace existing ones in +properties drawers." + (let* ((lmax (if (bound-and-true-p org-inlinetask-min-level) + org-inlinetask-min-level + 29)) ;Hard-code deepest level. + (lvals (make-vector (1+ lmax) nil)) + (level 0) + (inminlevel lmax) + (last-level lmax) + (property (car spec)) + (printf (nth 4 spec)) + (summarize (org-columns--summarize (nth 3 spec)))) + (org-with-wide-buffer + ;; Find the region to compute. + (goto-char org-columns-top-level-marker) + (goto-char (condition-case nil (org-end-of-subtree t) (error (point-max)))) + ;; Walk the tree from the back and do the computations. + (while (re-search-backward + org-outline-regexp-bol org-columns-top-level-marker t) + (unless (or (= level 0) (eq level inminlevel)) + (setq last-level level)) + (setq level (org-reduced-level (org-outline-level))) + (let* ((pos (match-beginning 0)) + (value (org-entry-get nil property)) + (value-set (org-string-nw-p value))) + (cond + ((< level last-level) + ;; Collect values from lower levels and inline tasks here + ;; and summarize them using SUMMARIZE. Store them in text + ;; property `org-summaries', in alist whose key is SPEC. + (let* ((summary + (and summarize + (let ((values (append (and (/= last-level inminlevel) + (aref lvals last-level)) + (aref lvals inminlevel)))) + (and values (funcall summarize values printf)))))) + ;; Leaf values are not summaries: do not mark them. + (when summary + (let* ((summaries-alist (get-text-property pos 'org-summaries)) + (old (assoc spec summaries-alist))) + (if old (setcdr old summary) + (push (cons spec summary) summaries-alist) + (org-with-silent-modifications + (add-text-properties + pos (1+ pos) (list 'org-summaries summaries-alist))))) + ;; When PROPERTY exists in current node, even if empty, + ;; but its value doesn't match the one computed, use + ;; the latter instead. + ;; + ;; Ignore leading or trailing white spaces that might + ;; have been introduced in summary, since those are not + ;; significant in properties value. + (let ((new-value (org-trim summary))) + (when (and update value (not (equal value new-value))) + (org-entry-put (point) property new-value)))) + ;; Add current to current level accumulator. + (when (or summary value-set) + (push (or summary value) (aref lvals level))) + ;; Clear accumulators for deeper levels. + (cl-loop for l from (1+ level) to lmax do (aset lvals l nil)))) + (value-set (push value (aref lvals level))) + (t nil))))))) + +;;;###autoload +(defun org-columns-compute (property) + "Summarize the values of PROPERTY hierarchically. +Also update existing values for PROPERTY according to the first +column specification." + (interactive) + (let ((main-flag t) + (upcase-prop (upcase property))) + (dolist (spec org-columns-current-fmt-compiled) + (pcase spec + (`(,(pred (equal upcase-prop)) . ,_) + (org-columns--compute-spec spec main-flag) + ;; Only the first summary can update the property value. + (when main-flag (setq main-flag nil))))))) +(defun org-columns-compute-all () + "Compute all columns that have operators defined." + (org-with-silent-modifications + (remove-text-properties (point-min) (point-max) '(org-summaries t))) + (let ((org-columns--time (float-time (current-time))) + seen) + (dolist (spec org-columns-current-fmt-compiled) + (let ((property (car spec))) + ;; Property value is updated only the first time a given + ;; property is encountered. + (org-columns--compute-spec spec (not (member property seen))) + (push property seen))))) + +(defun org-columns--summary-sum (values printf) + "Compute the sum of VALUES. +When PRINTF is non-nil, use it to format the result." + (format (or printf "%s") (apply #'+ (mapcar #'string-to-number values)))) + +(defun org-columns--summary-currencies (values _) + "Compute the sum of VALUES, with two decimals." + (format "%.2f" (apply #'+ (mapcar #'string-to-number values)))) + +(defun org-columns--summary-checkbox (check-boxes _) + "Summarize CHECK-BOXES with a check-box." + (let ((done (cl-count "[X]" check-boxes :test #'equal)) + (all (length check-boxes))) + (cond ((= done all) "[X]") + ((> done 0) "[-]") + (t "[ ]")))) + +(defun org-columns--summary-checkbox-count (check-boxes _) + "Summarize CHECK-BOXES with a check-box cookie." + (format "[%d/%d]" + (cl-count-if (lambda (b) (or (equal b "[X]") + (string-match-p "\\[\\([1-9]\\)/\\1\\]" b))) + check-boxes) + (length check-boxes))) + +(defun org-columns--summary-checkbox-percent (check-boxes _) + "Summarize CHECK-BOXES with a check-box percent." + (format "[%d%%]" + (round (* 100.0 (cl-count-if (lambda (b) (member b '("[X]" "[100%]"))) + check-boxes)) + (length check-boxes)))) + +(defun org-columns--summary-min (values printf) + "Compute the minimum of VALUES. +When PRINTF is non-nil, use it to format the result." + (format (or printf "%s") + (apply #'min (mapcar #'string-to-number values)))) + +(defun org-columns--summary-max (values printf) + "Compute the maximum of VALUES. +When PRINTF is non-nil, use it to format the result." + (format (or printf "%s") + (apply #'max (mapcar #'string-to-number values)))) + +(defun org-columns--summary-mean (values printf) + "Compute the mean of VALUES. +When PRINTF is non-nil, use it to format the result." + (format (or printf "%s") + (/ (apply #'+ (mapcar #'string-to-number values)) + (float (length values))))) + +(defun org-columns--summary-sum-times (times _) + "Sum TIMES." + (org-columns--summary-apply-times #'+ times)) + +(defun org-columns--summary-min-time (times _) + "Compute the minimum time among TIMES." + (org-columns--summary-apply-times #'min times)) + +(defun org-columns--summary-max-time (times _) + "Compute the maximum time among TIMES." + (org-columns--summary-apply-times #'max times)) + +(defun org-columns--summary-mean-time (times _) + "Compute the mean time among TIMES." + (org-columns--summary-apply-times + (lambda (&rest values) (/ (apply #'+ values) (float (length values)))) + times)) + +(defun org-columns--summary-min-age (ages _) + "Compute the minimum time among AGES." + (format-seconds + "%dd %.2hh %mm %ss" + (apply #'min (mapcar #'org-columns--age-to-seconds ages)))) + +(defun org-columns--summary-max-age (ages _) + "Compute the maximum time among AGES." + (format-seconds + "%dd %.2hh %mm %ss" + (apply #'max (mapcar #'org-columns--age-to-seconds ages)))) + +(defun org-columns--summary-mean-age (ages _) + "Compute the minimum time among AGES." + (format-seconds + "%dd %.2hh %mm %ss" + (/ (apply #'+ (mapcar #'org-columns--age-to-seconds ages)) + (float (length ages))))) + +(defun org-columns--summary-estimate (estimates _) + "Combine a list of estimates, using mean and variance. +The mean and variance of the result will be the sum of the means +and variances (respectively) of the individual estimates." + (let ((mean 0) + (var 0)) + (dolist (e estimates) + (pcase (mapcar #'string-to-number (split-string e "-")) + (`(,low ,high) + (let ((m (/ (+ low high) 2.0))) + (cl-incf mean m) + (cl-incf var (- (/ (+ (* low low) (* high high)) 2.0) (* m m))))) + (`(,value) (cl-incf mean value)))) + (let ((sd (sqrt var))) + (format "%s-%s" + (format "%.0f" (- mean sd)) + (format "%.0f" (+ mean sd)))))) + + + ;;; Dynamic block for Column view -(defvar org-heading-regexp) ; defined in org.el -(defvar org-heading-keyword-regexp-format) ; defined in org.el -(defun org-columns-capture-view (&optional maxlevel skip-empty-rows) - "Get the column view of the current buffer or subtree. -The first optional argument MAXLEVEL sets the level limit. A -second optional argument SKIP-EMPTY-ROWS tells whether to skip +(defun org-columns--capture-view (maxlevel skip-empty format local) + "Get the column view of the current buffer. + +MAXLEVEL sets the level limit. SKIP-EMPTY tells whether to skip empty rows, an empty row being one where all the column view -specifiers except ITEM are empty. This function returns a list -containing the title row and all other rows. Each row is a list -of fields." - (save-excursion - (let* ((title (mapcar 'cadr org-columns-current-fmt-compiled)) - (re-comment (format org-heading-keyword-regexp-format - org-comment-string)) - (re-archive (concat ".*:" org-archive-tag ":")) - (n (length title)) row tbl) - (goto-char (point-min)) - (while (re-search-forward org-heading-regexp nil t) - (catch 'next - (when (and (or (null maxlevel) - (>= maxlevel - (if org-odd-levels-only - (/ (1+ (length (match-string 1))) 2) - (length (match-string 1))))) - (get-char-property (match-beginning 0) 'org-columns-key)) - (when (save-excursion - (goto-char (point-at-bol)) - (or (looking-at re-comment) - (looking-at re-archive))) - (org-end-of-subtree t) - (throw 'next t)) - (setq row nil) - (loop for i from 0 to (1- n) do - (push - (org-quote-vert - (or (get-char-property (+ (match-beginning 0) i) 'org-columns-value-modified) - (get-char-property (+ (match-beginning 0) i) 'org-columns-value) - "")) - row)) - (setq row (nreverse row)) - (unless (and skip-empty-rows - (eq 1 (length (delete "" (delete-dups (copy-sequence row)))))) - (push row tbl))))) - (append (list title 'hline) (nreverse tbl))))) +specifiers but ITEM are empty. FORMAT is a format string for +columns, or nil. When LOCAL is non-nil, only capture headings in +current subtree. + +This function returns a list containing the title row and all +other rows. Each row is a list of fields, as strings, or +`hline'." + (org-columns (not local) format) + (goto-char org-columns-top-level-marker) + (let ((columns (length org-columns-current-fmt-compiled)) + (has-item (assoc "ITEM" org-columns-current-fmt-compiled)) + table) + (org-map-entries + (lambda () + (when (get-char-property (point) 'org-columns-key) + (let (row) + (dotimes (i columns) + (let* ((col (+ (line-beginning-position) i)) + (p (get-char-property col 'org-columns-key))) + (push (org-quote-vert + (get-char-property col + (if (string= p "ITEM") + 'org-columns-value + 'org-columns-value-modified))) + row))) + (unless (and skip-empty + (let ((r (delete-dups (remove "" row)))) + (or (null r) (and has-item (= (length r) 1))))) + (push (cons (org-reduced-level (org-current-level)) (nreverse row)) + table))))) + (and maxlevel (format "LEVEL<=%d" maxlevel)) + (and local 'tree) + 'archive 'comment) + (org-columns-quit) + ;; Add column titles and a horizontal rule in front of the table. + (cons (mapcar #'cadr org-columns-current-fmt-compiled) + (cons 'hline (nreverse table))))) + +(defun org-columns--clean-item (item) + "Remove sensitive contents from string ITEM. +This includes objects that may not be duplicated within +a document, e.g., a target, or those forbidden in tables, e.g., +an inline src-block." + (let ((data (org-element-parse-secondary-string + item (org-element-restriction 'headline)))) + (org-element-map data + '(footnote-reference inline-babel-call inline-src-block target + radio-target statistics-cookie) + #'org-element-extract-element) + (org-no-properties (org-element-interpret-data data)))) ;;;###autoload (defun org-dblock-write:columnview (params) "Write the column view table. PARAMS is a property list of parameters: -:width enforce same column widths with specifiers. :id the :ID: property of the entry where the columns view should be built. When the symbol `local', call locally. When `global' call column view with the cursor at the beginning @@ -1247,339 +1392,269 @@ PARAMS is a property list of parameters: using `org-id-find'. :hlines When t, insert a hline before each item. When a number, insert a hline before each level <= that number. +:indent When non-nil, indent each ITEM field according to its level. :vlines When t, make each column a colgroup to enforce vertical lines. :maxlevel When set to a number, don't capture headlines below this level. :skip-empty-rows When t, skip rows where all specifiers other than ITEM are empty. +:width apply widths specified in columns format using specifiers. :format When non-nil, specify the column view format to use." - (let ((pos (point-marker)) - (hlines (plist-get params :hlines)) - (vlines (plist-get params :vlines)) - (maxlevel (plist-get params :maxlevel)) - (content-lines (org-split-string (plist-get params :content) "\n")) - (skip-empty-rows (plist-get params :skip-empty-rows)) - (columns-fmt (plist-get params :format)) - (case-fold-search t) - tbl id idpos nfields tmp recalc line - id-as-string view-file view-pos) - (when (setq id (plist-get params :id)) - (setq id-as-string (cond ((numberp id) (number-to-string id)) - ((symbolp id) (symbol-name id)) - ((stringp id) id) - (t ""))) - (cond ((not id) nil) - ((eq id 'global) (setq view-pos (point-min))) - ((eq id 'local)) - ((string-match "^file:\\(.*\\)" id-as-string) - (setq view-file (match-string 1 id-as-string) - view-pos 1) - (unless (file-exists-p view-file) - (error "No such file: \"%s\"" id-as-string))) - ((setq idpos (org-find-entry-with-id id)) - (setq view-pos idpos)) - ((setq idpos (org-id-find id)) - (setq view-file (car idpos)) - (setq view-pos (cdr idpos))) - (t (error "Cannot find entry with :ID: %s" id)))) - (with-current-buffer (if view-file - (get-file-buffer view-file) - (current-buffer)) - (save-excursion - (save-restriction - (widen) - (goto-char (or view-pos (point))) - (org-columns columns-fmt) - (setq tbl (org-columns-capture-view maxlevel skip-empty-rows)) - (setq nfields (length (car tbl))) - (org-columns-quit)))) - (goto-char pos) - (move-marker pos nil) - (when tbl - (when (plist-get params :hlines) - (setq tmp nil) - (while tbl - (if (eq (car tbl) 'hline) - (push (pop tbl) tmp) - (if (string-match "\\` *\\(\\*+\\)" (caar tbl)) - (if (and (not (eq (car tmp) 'hline)) - (or (eq hlines t) - (and (numberp hlines) - (<= (- (match-end 1) (match-beginning 1)) - hlines)))) - (push 'hline tmp))) - (push (pop tbl) tmp))) - (setq tbl (nreverse tmp))) - (when vlines - (setq tbl (mapcar (lambda (x) - (if (eq 'hline x) x (cons "" x))) - tbl)) - (setq tbl (append tbl (list (cons "/" (make-list nfields "<>")))))) - (when content-lines - (while (string-match "^#" (car content-lines)) - (insert (pop content-lines) "\n"))) - (setq pos (point)) - (insert (org-listtable-to-string tbl)) + (let ((table + (let ((id (plist-get params :id)) + view-file view-pos) + (pcase id + (`global nil) + ((or `local `nil) (setq view-pos (point))) + ((and (let id-string (format "%s" id)) + (guard (string-match "^file:\\(.*\\)" id-string))) + (setq view-file (match-string-no-properties 1 id-string)) + (unless (file-exists-p view-file) + (user-error "No such file: %S" id-string))) + ((and (let idpos (org-find-entry-with-id id)) (guard idpos)) + (setq view-pos idpos)) + ((let `(,filename . ,position) (org-id-find id)) + (setq view-file filename) + (setq view-pos position)) + (_ (user-error "Cannot find entry with :ID: %s" id))) + (with-current-buffer (if view-file (get-file-buffer view-file) + (current-buffer)) + (org-with-wide-buffer + (when view-pos (goto-char view-pos)) + (org-columns--capture-view (plist-get params :maxlevel) + (plist-get params :skip-empty-rows) + (plist-get params :format) + view-pos)))))) + (when table + ;; Prune level information from the table. Also normalize + ;; headings: remove stars, add indentation entities, if + ;; required, and possibly precede some of them with a horizontal + ;; rule. + (let ((item-index + (let ((p (assoc "ITEM" org-columns-current-fmt-compiled))) + (and p (cl-position p + org-columns-current-fmt-compiled + :test #'equal)))) + (hlines (plist-get params :hlines)) + (indent (plist-get params :indent)) + new-table) + ;; Copy header and first rule. + (push (pop table) new-table) + (push (pop table) new-table) + (dolist (row table (setq table (nreverse new-table))) + (let ((level (car row))) + (when (and (not (eq (car new-table) 'hline)) + (or (eq hlines t) + (and (numberp hlines) (<= level hlines)))) + (push 'hline new-table)) + (when item-index + (let ((item (org-columns--clean-item (nth item-index (cdr row))))) + (setf (nth item-index (cdr row)) + (if (and indent (> level 1)) + (concat "\\_" (make-string (* 2 (1- level)) ?\s) item) + item)))) + (push (cdr row) new-table)))) (when (plist-get params :width) - (insert "\n|" (mapconcat (lambda (x) (format "<%d>" (max 3 x))) - org-columns-current-widths "|"))) - (while (setq line (pop content-lines)) - (when (string-match "^#" line) - (insert "\n" line) - (when (string-match "^[ \t]*#\\+tblfm" line) - (setq recalc t)))) - (if recalc - (progn (goto-char pos) (org-table-recalculate 'all)) - (goto-char pos) + (setq table + (append table + (list + (mapcar (lambda (spec) + (let ((w (nth 2 spec))) + (if w (format "<%d>" (max 3 w)) ""))) + org-columns-current-fmt-compiled))))) + (when (plist-get params :vlines) + (setq table + (let ((size (length org-columns-current-fmt-compiled))) + (append (mapcar (lambda (x) (if (eq 'hline x) x (cons "" x))) + table) + (list (cons "/" (make-list size "<>"))))))) + (let ((content-lines (org-split-string (plist-get params :content) "\n")) + recalc) + ;; Insert affiliated keywords before the table. + (when content-lines + (while (string-match-p "\\`[ \t]*#\\+" (car content-lines)) + (insert (pop content-lines) "\n"))) + (save-excursion + ;; Insert table at point. + (insert + (mapconcat (lambda (row) + (if (eq row 'hline) "|-|" + (format "|%s|" (mapconcat #'identity row "|")))) + table + "\n")) + ;; Insert TBLFM lines following table. + (let ((case-fold-search t)) + (dolist (line content-lines) + (when (string-match-p "\\`[ \t]*#\\+TBLFM:" line) + (insert "\n" line) + (unless recalc (setq recalc t)))))) + (when recalc (org-table-recalculate 'all t)) (org-table-align))))) -(defun org-listtable-to-string (tbl) - "Convert a listtable TBL to a string that contains the Org-mode table. -The table still need to be aligned. The resulting string has no leading -and tailing newline characters." - (mapconcat - (lambda (x) - (cond - ((listp x) - (concat "|" (mapconcat 'identity x "|") "|")) - ((eq x 'hline) "|-|") - (t (error "Garbage in listtable: %s" x)))) - tbl "\n")) - ;;;###autoload -(defun org-insert-columns-dblock () +(defun org-columns-insert-dblock () "Create a dynamic block capturing a column view table." (interactive) - (let ((defaults '(:name "columnview" :hlines 1)) - (id (org-icompleting-read + (let ((id (completing-read "Capture columns (local, global, entry with :ID: property) [local]: " (append '(("global") ("local")) - (mapcar 'list (org-property-values "ID")))))) - (if (equal id "") (setq id 'local)) - (if (equal id "global") (setq id 'global)) - (setq defaults (append defaults (list :id id))) - (org-create-dblock defaults) - (org-update-dblock))) + (mapcar #'list (org-property-values "ID")))))) + (org-create-dblock + (list :name "columnview" + :hlines 1 + :id (cond ((string= id "global") 'global) + ((member id '("" "local")) 'local) + (id))))) + (org-update-dblock)) -;;; Column view in the agenda - -(defvar org-agenda-view-columns-initially nil - "When set, switch to columns view immediately after creating the agenda.") -(defvar org-agenda-columns-show-summaries) ; defined in org-agenda.el -(defvar org-agenda-columns-compute-summary-properties); defined in org-agenda.el -(defvar org-agenda-columns-add-appointments-to-effort-sum); as well + +;;; Column view in the agenda ;;;###autoload (defun org-agenda-columns () "Turn on or update column view in the agenda." (interactive) - (org-verify-version 'columns) (org-columns-remove-overlays) (move-marker org-columns-begin-marker (point)) - (let ((org-columns-time (time-to-number-of-days (current-time))) - cache maxwidths m p a d fmt) - (cond - ((and (boundp 'org-agenda-overriding-columns-format) - org-agenda-overriding-columns-format) - (setq fmt org-agenda-overriding-columns-format)) - ((setq m (org-get-at-bol 'org-hd-marker)) - (setq fmt (or (org-entry-get m "COLUMNS" t) - (with-current-buffer (marker-buffer m) - org-columns-default-format)))) - ((and (boundp 'org-columns-current-fmt) - (local-variable-p 'org-columns-current-fmt) - org-columns-current-fmt) - (setq fmt org-columns-current-fmt)) - ((setq m (next-single-property-change (point-min) 'org-hd-marker)) - (setq m (get-text-property m 'org-hd-marker)) - (setq fmt (or (org-entry-get m "COLUMNS" t) - (with-current-buffer (marker-buffer m) - org-columns-default-format))))) - (setq fmt (or fmt org-columns-default-format)) - (org-set-local 'org-columns-current-fmt fmt) - (org-columns-compile-format fmt) + (let* ((org-columns--time (float-time (current-time))) + (fmt + (cond + ((bound-and-true-p org-agenda-overriding-columns-format)) + ((let ((m (org-get-at-bol 'org-hd-marker))) + (and m + (or (org-entry-get m "COLUMNS" t) + (with-current-buffer (marker-buffer m) + org-columns-default-format))))) + ((and (local-variable-p 'org-columns-current-fmt) + org-columns-current-fmt)) + ((let ((m (next-single-property-change (point-min) 'org-hd-marker))) + (and m + (let ((m (get-text-property m 'org-hd-marker))) + (or (org-entry-get m "COLUMNS" t) + (with-current-buffer (marker-buffer m) + org-columns-default-format)))))) + (t org-columns-default-format))) + (compiled-fmt (org-columns-compile-format fmt))) + (setq org-columns-current-fmt fmt) (when org-agenda-columns-compute-summary-properties (org-agenda-colview-compute org-columns-current-fmt-compiled)) (save-excursion - ;; Get and cache the properties + ;; Collect properties for each headline in current view. (goto-char (point-min)) - (while (not (eobp)) - (when (setq m (or (org-get-at-bol 'org-hd-marker) - (org-get-at-bol 'org-marker))) - (setq p (org-entry-properties m)) - - (when (or (not (setq a (assoc org-effort-property p))) - (not (string-match "\\S-" (or (cdr a) "")))) - ;; OK, the property is not defined. Use appointment duration? - (when (and org-agenda-columns-add-appointments-to-effort-sum - (setq d (get-text-property (point) 'duration))) - (setq d (org-minutes-to-clocksum-string d)) - (put-text-property 0 (length d) 'face 'org-warning d) - (push (cons org-effort-property d) p))) - (push (cons (org-current-line) p) cache)) - (beginning-of-line 2)) - (when cache - (setq maxwidths (org-columns-get-autowidth-alist fmt cache)) - (org-set-local 'org-columns-current-maxwidths maxwidths) - (org-columns-display-here-title) - (when (org-set-local 'org-columns-flyspell-was-active - (org-bound-and-true-p flyspell-mode)) - (flyspell-mode 0)) - (mapc (lambda (x) - (org-goto-line (car x)) - (org-columns-display-here (cdr x))) - cache) - (when org-agenda-columns-show-summaries - (org-agenda-colview-summarize cache)))))) + (let (cache) + (while (not (eobp)) + (let ((m (org-get-at-bol 'org-hd-marker))) + (when m + (push (cons (line-beginning-position) + ;; `org-columns-current-fmt-compiled' is + ;; initialized but only set locally to the + ;; agenda buffer. Since current buffer is + ;; changing, we need to force the original + ;; compiled-fmt there. + (org-with-point-at m + (org-columns--collect-values compiled-fmt))) + cache))) + (forward-line)) + (when cache + (org-columns--set-widths cache) + (org-columns--display-here-title) + (when (setq-local org-columns-flyspell-was-active + (bound-and-true-p flyspell-mode)) + (flyspell-mode 0)) + (dolist (entry cache) + (goto-char (car entry)) + (org-columns--display-here (cdr entry))) + (when org-agenda-columns-show-summaries + (org-agenda-colview-summarize cache))))))) (defun org-agenda-colview-summarize (cache) "Summarize the summarizable columns in column view in the agenda. This will add overlays to the date lines, to show the summary for each day." - (let* ((fmt (mapcar (lambda (x) - (if (string-match "CLOCKSUM.*" (car x)) - (list (match-string 0 (car x)) - (nth 1 x) (nth 2 x) ":" 'add_times - nil '+ nil) - x)) - org-columns-current-fmt-compiled)) - line c c1 stype calc sumfunc props lsum entries prop v title) - (catch 'exit - (when (delq nil (mapcar 'cadr fmt)) - ;; OK, at least one summation column, it makes sense to try this - (goto-char (point-max)) + (let ((fmt (mapcar + (lambda (spec) + (pcase spec + (`(,property ,title ,width . ,_) + (if (member property '("CLOCKSUM" "CLOCKSUM_T")) + (list property title width ":" nil) + spec)))) + org-columns-current-fmt-compiled))) + ;; Ensure there's at least one summation column. + (when (cl-some (lambda (spec) (nth 3 spec)) fmt) + (goto-char (point-max)) + (catch :complete (while t (when (or (get-text-property (point) 'org-date-line) (eq (get-text-property (point) 'face) 'org-agenda-structure)) - ;; OK, this is a date line that should be used - (setq line (org-current-line)) - (setq entries nil c cache cache nil) - (while (setq c1 (pop c)) - (if (> (car c1) line) - (push c1 entries) - (push c1 cache))) - ;; now ENTRIES are the ones we want to use, CACHE is the rest - ;; Compute the summaries for the properties we want, - ;; set nil properties for the rest. - (when (setq entries (mapcar 'cdr entries)) - (setq props - (mapcar - (lambda (f) - (setq prop (car f) - title (nth 1 f) - stype (nth 4 f) - sumfunc (nth 6 f) - calc (or (nth 7 f) 'identity)) - (cond - ((equal prop "ITEM") - (cons prop (buffer-substring (point-at-bol) - (point-at-eol)))) - ((not stype) (cons prop "")) - (t ;; do the summary - (setq lsum nil) - (dolist (x entries) - (setq v (cdr (assoc prop x))) - (if v - (push - (funcall - (if (not (get-text-property 0 'org-computed v)) - calc - 'identity) - (org-columns-string-to-number - v stype)) - lsum))) - (setq lsum (remove nil lsum)) - (setq lsum - (cond ((> (length lsum) 1) - (org-columns-number-to-string - (apply sumfunc lsum) stype)) - ((eq (length lsum) 1) - (org-columns-number-to-string - (car lsum) stype)) - (t ""))) - (put-text-property 0 (length lsum) 'face 'bold lsum) - (unless (eq calc 'identity) - (put-text-property 0 (length lsum) 'org-computed t lsum)) - (cons prop lsum)))) - fmt)) - (org-columns-display-here props 'dateline) - (org-set-local 'org-agenda-columns-active t))) - (if (bobp) (throw 'exit t)) - (beginning-of-line 0)))))) + ;; OK, this is a date line that should be used. + (let (entries) + (let (rest) + (dolist (c cache) + (if (> (car c) (point)) + (push c entries) + (push c rest))) + (setq cache rest)) + ;; ENTRIES contains entries below the current one. + ;; CACHE is the rest. Compute the summaries for the + ;; properties we want, set nil properties for the rest. + (when (setq entries (mapcar #'cdr entries)) + (org-columns--display-here + (mapcar + (lambda (spec) + (pcase spec + (`("ITEM" . ,_) + ;; Replace ITEM with current date. Preserve + ;; properties for fontification. + (let ((date (buffer-substring + (line-beginning-position) + (line-end-position)))) + (list spec date date))) + (`(,_ ,_ ,_ nil ,_) (list spec "" "")) + (`(,_ ,_ ,_ ,operator ,printf) + (let* ((summarize (org-columns--summarize operator)) + (values + ;; Use real values for summary, not + ;; those prepared for display. + (delq nil + (mapcar + (lambda (e) (org-string-nw-p + (nth 1 (assoc spec e)))) + entries))) + (final (if values + (funcall summarize values printf) + ""))) + (unless (equal final "") + (put-text-property 0 (length final) + 'face 'bold final)) + (list spec final final))))) + fmt) + 'dateline) + (setq-local org-agenda-columns-active t)))) + (if (bobp) (throw :complete t) (forward-line -1))))))) (defun org-agenda-colview-compute (fmt) "Compute the relevant columns in the contributing source buffers." (let ((files org-agenda-contributing-files) (org-columns-begin-marker (make-marker)) - (org-columns-top-level-marker (make-marker)) - f fm a b) - (while (setq f (pop files)) - (setq b (find-buffer-visiting f)) - (with-current-buffer (or (buffer-base-buffer b) b) - (save-excursion - (save-restriction - (widen) - (org-with-silent-modifications - (remove-text-properties (point-min) (point-max) '(org-summaries t))) - (goto-char (point-min)) - (org-columns-get-format-and-top-level) - (while (setq fm (pop fmt)) - (cond ((equal (car fm) "CLOCKSUM") - (org-clock-sum)) - ((equal (car fm) "CLOCKSUM_T") - (org-clock-sum-today)) - ((and (nth 4 fm) - (setq a (assoc (car fm) - org-columns-current-fmt-compiled)) - (equal (nth 4 a) (nth 4 fm))) - (org-columns-compute (car fm))))))))))) - -(defun org-format-time-period (interval) - "Convert time in fractional days to days/hours/minutes/seconds." - (if (numberp interval) - (let* ((days (floor interval)) - (frac-hours (* 24 (- interval days))) - (hours (floor frac-hours)) - (minutes (floor (* 60 (- frac-hours hours)))) - (seconds (floor (* 60 (- (* 60 (- frac-hours hours)) minutes))))) - (format "%dd %02dh %02dm %02ds" days hours minutes seconds)) - "")) - -(defun org-estimate-mean-and-var (v) - "Return the mean and variance of an estimate." - (let* ((low (float (car v))) - (high (float (cadr v))) - (mean (/ (+ low high) 2.0)) - (var (/ (+ (expt (- mean low) 2.0) (expt (- high mean) 2.0)) 2.0))) - (list mean var))) - -(defun org-estimate-combine (&rest el) - "Combine a list of estimates, using mean and variance. -The mean and variance of the result will be the sum of the means -and variances (respectively) of the individual estimates." - (let ((mean 0) - (var 0)) - (mapc (lambda (e) - (let ((stats (org-estimate-mean-and-var e))) - (setq mean (+ mean (car stats))) - (setq var (+ var (cadr stats))))) - el) - (let ((stdev (sqrt var))) - (list (- mean stdev) (+ mean stdev))))) - -(defun org-estimate-print (e &optional fmt) - "Prepare a string representation of an estimate. -This formats these numbers as two numbers with a \"-\" between them." - (if (null fmt) (set 'fmt "%.0f")) - (format "%s" (mapconcat (lambda (n) (format fmt n)) e "-"))) - -(defun org-string-to-estimate (s) - "Convert a string to an estimate. -The string should be two numbers joined with a \"-\"." - (if (string-match "\\(.*\\)-\\(.*\\)" s) - (list (string-to-number (match-string 1 s)) - (string-to-number(match-string 2 s))) - (list (string-to-number s) (string-to-number s)))) + (org-columns-top-level-marker (make-marker))) + (dolist (f files) + (let ((b (find-buffer-visiting f))) + (with-current-buffer (or (buffer-base-buffer b) b) + (org-with-wide-buffer + (org-with-silent-modifications + (remove-text-properties (point-min) (point-max) '(org-summaries t))) + (goto-char (point-min)) + (org-columns-get-format-and-top-level) + (dolist (spec fmt) + (let ((prop (car spec))) + (cond + ((equal prop "CLOCKSUM") (org-clock-sum)) + ((equal prop "CLOCKSUM_T") (org-clock-sum-today)) + ((and (nth 3 spec) + (let ((a (assoc prop org-columns-current-fmt-compiled))) + (equal (nth 3 a) (nth 3 spec)))) + (org-columns-compute prop))))))))))) + (provide 'org-colview) diff --git a/lisp/org/org-compat.el b/lisp/org/org-compat.el index 42e2271c07..e1d40369f1 100644 --- a/lisp/org/org-compat.el +++ b/lisp/org/org-compat.el @@ -1,4 +1,4 @@ -;;; org-compat.el --- Compatibility code for Org-mode +;;; org-compat.el --- Compatibility Code for Older Emacsen -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -24,65 +24,287 @@ ;; ;;; Commentary: -;; This file contains code needed for compatibility with XEmacs and older +;; This file contains code needed for compatibility with older ;; versions of GNU Emacs. ;;; Code: -(eval-when-compile - (require 'cl)) - +(require 'cl-lib) (require 'org-macs) -;; The following constant is for backward compatibility. We do not use -;; it in org-mode, because the Byte compiler evaluates (featurep 'xemacs) -;; at compilation time and can therefore optimize code better. -(defconst org-xemacs-p (featurep 'xemacs)) -(defconst org-format-transports-properties-p - (let ((x "a")) - (add-text-properties 0 1 '(test t) x) - (get-text-property 0 'test (format "%s" x))) - "Does format transport text properties?") +(declare-function org-at-table.el-p "org" (&optional table-type)) +(declare-function org-element-at-point "org-element" ()) +(declare-function org-element-type "org-element" (element)) +(declare-function org-link-set-parameters "org" (type &rest rest)) +(declare-function org-table-end (&optional table-type)) +(declare-function table--at-cell-p "table" (position &optional object at-column)) + +(defvar org-table-any-border-regexp) +(defvar org-table-dataline-regexp) +(defvar org-table-tab-recognizes-table.el) +(defvar org-table1-hline-regexp) + +;; As of Emacs 25.1, `outline-mode' functions are under the 'outline-' +;; prefix, `find-tag' is replaced with `xref-find-definition' and +;; `x-get-selection' with `gui-get-selection'. +(when (< emacs-major-version 25) + (defalias 'outline-hide-entry 'hide-entry) + (defalias 'outline-hide-sublevels 'hide-sublevels) + (defalias 'outline-hide-subtree 'hide-subtree) + (defalias 'outline-show-all 'show-all) + (defalias 'outline-show-branches 'show-branches) + (defalias 'outline-show-children 'show-children) + (defalias 'outline-show-entry 'show-entry) + (defalias 'outline-show-subtree 'show-subtree) + (defalias 'xref-find-definitions 'find-tag) + (defalias 'format-message 'format) + (defalias 'gui-get-selection 'x-get-selection)) + + +;;; Obsolete aliases (remove them after the next major release). + +;;;; XEmacs compatibility, now removed. +(define-obsolete-function-alias 'org-activate-mark 'activate-mark) +(define-obsolete-function-alias 'org-add-hook 'add-hook "Org 9.0") +(define-obsolete-function-alias 'org-bound-and-true-p 'bound-and-true-p "Org 9.0") +(define-obsolete-function-alias 'org-decompose-region 'decompose-region "Org 9.0") +(define-obsolete-function-alias 'org-defvaralias 'defvaralias "Org 9.0") +(define-obsolete-function-alias 'org-detach-overlay 'delete-overlay "Org 9.0") +(define-obsolete-function-alias 'org-file-equal-p 'file-equal-p "Org 9.0") +(define-obsolete-function-alias 'org-float-time 'float-time "Org 9.0") +(define-obsolete-function-alias 'org-indent-line-to 'indent-line-to "Org 9.0") +(define-obsolete-function-alias 'org-indent-to-column 'indent-to-column "Org 9.0") +(define-obsolete-function-alias 'org-looking-at-p 'looking-at-p "Org 9.0") +(define-obsolete-function-alias 'org-looking-back 'looking-back "Org 9.0") +(define-obsolete-function-alias 'org-match-string-no-properties 'match-string-no-properties "Org 9.0") +(define-obsolete-function-alias 'org-propertize 'propertize "Org 9.0") +(define-obsolete-function-alias 'org-select-frame-set-input-focus 'select-frame-set-input-focus "Org 9.0") + +(defmacro org-re (s) + "Replace posix classes in regular expression S." + (declare (debug (form)) + (obsolete "you can safely remove it." "Org 9.0")) + s) + +;;;; Functions from cl-lib that Org used to have its own implementation of. +(define-obsolete-function-alias 'org-count 'cl-count "Org 9.0") +(define-obsolete-function-alias 'org-every 'cl-every "Org 9.0") +(define-obsolete-function-alias 'org-find-if 'cl-find-if "Org 9.0") +(define-obsolete-function-alias 'org-reduce 'cl-reduce "Org 9.0") +(define-obsolete-function-alias 'org-remove-if 'cl-remove-if "Org 9.0") +(define-obsolete-function-alias 'org-remove-if-not 'cl-remove-if-not "Org 9.0") +(define-obsolete-function-alias 'org-some 'cl-some "Org 9.0") +(define-obsolete-function-alias 'org-floor* 'cl-floor "Org 9.0") + +(defun org-sublist (list start end) + "Return a section of LIST, from START to END. +Counting starts at 1." + (cl-subseq list (1- start) end)) +(make-obsolete 'org-sublist + "use cl-subseq (note the 0-based counting)." + "Org 9.0") + + +;;;; Functions available since Emacs 24.3 +(define-obsolete-function-alias 'org-buffer-narrowed-p 'buffer-narrowed-p "Org 9.0") +(define-obsolete-function-alias 'org-called-interactively-p 'called-interactively-p "Org 9.0") +(define-obsolete-function-alias 'org-char-to-string 'char-to-string "Org 9.0") +(define-obsolete-function-alias 'org-delete-directory 'delete-directory "Org 9.0") +(define-obsolete-function-alias 'org-format-seconds 'format-seconds "Org 9.0") +(define-obsolete-function-alias 'org-link-escape-browser 'url-encode-url "Org 9.0") +(define-obsolete-function-alias 'org-no-warnings 'with-no-warnings "Org 9.0") +(define-obsolete-function-alias 'org-number-sequence 'number-sequence "Org 9.0") +(define-obsolete-function-alias 'org-pop-to-buffer-same-window 'pop-to-buffer-same-window "Org 9.0") +(define-obsolete-function-alias 'org-string-match-p 'string-match-p "Org 9.0") + +;;;; Functions and variables from previous releases now obsolete. +(define-obsolete-function-alias 'org-element-remove-indentation + 'org-remove-indentation "Org 9.0") +(define-obsolete-variable-alias 'org-hierarchical-checkbox-statistics + 'org-checkbox-hierarchical-statistics "Org 8.0") +(define-obsolete-variable-alias 'org-description-max-indent + 'org-list-description-max-indent "Org 8.0") +(define-obsolete-variable-alias 'org-latex-create-formula-image-program + 'org-preview-latex-default-process "Org 9.0") +(define-obsolete-variable-alias 'org-latex-preview-ltxpng-directory + 'org-preview-latex-image-directory "Org 9.0") +(define-obsolete-function-alias 'org-table-p 'org-at-table-p "Org 9.0") +(define-obsolete-function-alias 'org-on-heading-p 'org-at-heading-p "Org 9.0") +(define-obsolete-function-alias 'org-at-regexp-p 'org-in-regexp "Org 8.3") +(define-obsolete-function-alias 'org-speed-command-default-hook + 'org-speed-command-activate "Org 8.0") +(define-obsolete-function-alias 'org-babel-speed-command-hook + 'org-babel-speed-command-activate "Org 8.0") +(define-obsolete-function-alias 'org-image-file-name-regexp + 'image-file-name-regexp "Org 9.0") +(define-obsolete-function-alias 'org-get-legal-level + 'org-get-valid-level "Org 7.8") +(define-obsolete-function-alias 'org-completing-read-no-i + 'completing-read "Org 9.0") +(define-obsolete-function-alias 'org-icompleting-read + 'completing-read "Org 9.0") +(define-obsolete-function-alias 'org-iread-file-name 'read-file-name "Org 9.0") +(define-obsolete-function-alias 'org-days-to-time + 'org-time-stamp-to-now "Org 8.2") +(define-obsolete-variable-alias 'org-agenda-ignore-drawer-properties + 'org-agenda-ignore-properties "Org 9.0") +(define-obsolete-function-alias 'org-preview-latex-fragment + 'org-toggle-latex-fragment "Org 8.3") +(define-obsolete-function-alias 'org-display-inline-modification-hook + 'org-display-inline-remove-overlay "Org 8.0") +(define-obsolete-function-alias 'org-export-get-genealogy + 'org-element-lineage "Org 9.0") +(define-obsolete-variable-alias 'org-latex-with-hyperref + 'org-latex-hyperref-template "Org 9.0") +(define-obsolete-variable-alias 'org-link-to-org-use-id + 'org-id-link-to-org-use-id "Org 8.0") +(define-obsolete-variable-alias 'hfy-optimisations 'hfy-optimizations "Org 9.0") +(define-obsolete-variable-alias 'org-clock-modeline-total + 'org-clock-mode-line-total "Org 8.0") +(define-obsolete-function-alias 'org-protocol-unhex-compound + 'org-link-unescape-compound "Org 7.8") +(define-obsolete-function-alias 'org-protocol-unhex-string + 'org-link-unescape "Org 7.8") +(define-obsolete-function-alias 'org-protocol-unhex-single-byte-sequence + 'org-link-unescape-single-byte-sequence "Org 7.8") +(define-obsolete-variable-alias 'org-export-htmlized-org-css-url + 'org-org-htmlized-css-url "Org 8.2") +(define-obsolete-variable-alias 'org-alphabetical-lists + 'org-list-allow-alphabetical "Org 8.0") +(define-obsolete-function-alias 'org-list-parse-list 'org-list-to-lisp "Org 9.0") +(define-obsolete-variable-alias 'org-agenda-menu-two-column + 'org-agenda-menu-two-columns "Org 8.0") +(define-obsolete-variable-alias 'org-finalize-agenda-hook + 'org-agenda-finalize-hook "Org 8.0") +(make-obsolete-variable 'org-agenda-ndays 'org-agenda-span "Org 7.8") +(define-obsolete-function-alias 'org-agenda-post-command-hook + 'org-agenda-update-agenda-type "Org 8.0") +(define-obsolete-function-alias 'org-agenda-todayp + 'org-agenda-today-p "Org 9.0") +(define-obsolete-function-alias 'org-babel-examplize-region + 'org-babel-examplify-region "Org 9.0") +(define-obsolete-function-alias 'org-babel-trim 'org-trim "Org 9.0") +(define-obsolete-variable-alias 'org-html-style-include-scripts + 'org-html-head-include-scripts "Org 8.0") +(define-obsolete-variable-alias 'org-html-style-include-default + 'org-html-head-include-default-style "Org 8.0") +(define-obsolete-variable-alias 'org-html-style 'org-html-head "24.4") +(define-obsolete-function-alias 'org-insert-columns-dblock + 'org-columns-insert-dblock "Org 9.0") +(define-obsolete-function-alias 'org-activate-bracket-links + 'org-activate-links "Org 9.0") +(define-obsolete-function-alias 'org-activate-plain-links 'ignore "Org 9.0") +(define-obsolete-function-alias 'org-activate-angle-links 'ignore "Org 9.0") + +(defun org-in-fixed-width-region-p () + "Non-nil if point in a fixed-width region." + (save-match-data + (eq 'fixed-width (org-element-type (org-element-at-point))))) +(make-obsolete 'org-in-fixed-width-region-p + "use `org-element' library" + "Org 9.0") + +(defcustom org-read-date-minibuffer-setup-hook nil + "Hook to be used to set up keys for the date/time interface. +Add key definitions to `minibuffer-local-map', which will be a +temporary copy." + :group 'org-time + :type 'hook) +(make-obsolete-variable + 'org-read-date-minibuffer-setup-hook + "set `org-read-date-minibuffer-local-map' instead." "Org 8.0") (defun org-compatible-face (inherits specs) "Make a compatible face specification. -If INHERITS is an existing face and if the Emacs version supports it, -just inherit the face. If INHERITS is set and the Emacs version does -not support it, copy the face specification from the inheritance face. -If INHERITS is not given and SPECS is, use SPECS to define the face. -XEmacs and Emacs 21 do not know about the `min-colors' attribute. -For them we convert a (min-colors 8) entry to a `tty' entry and move it -to the top of the list. The `min-colors' attribute will be removed from -any other entries, and any resulting duplicates will be removed entirely." - (when (and inherits (facep inherits) (not specs)) - (setq specs (or specs - (get inherits 'saved-face) - (get inherits 'face-defface-spec)))) - (cond - ((and inherits (facep inherits) - (not (featurep 'xemacs)) - (>= emacs-major-version 22) - ;; do not inherit outline faces before Emacs 23 - (or (>= emacs-major-version 23) - (not (string-match "\\`outline-[0-9]+" - (symbol-name inherits))))) - (list (list t :inherit inherits))) - ((or (featurep 'xemacs) (< emacs-major-version 22)) - ;; These do not understand the `min-colors' attribute. - (let (r e a) - (while (setq e (pop specs)) - (cond - ((memq (car e) '(t default)) (push e r)) - ((setq a (member '(min-colors 8) (car e))) - (nconc r (list (cons (cons '(type tty) (delq (car a) (car e))) - (cdr e))))) - ((setq a (assq 'min-colors (car e))) - (setq e (cons (delq a (car e)) (cdr e))) - (or (assoc (car e) r) (push e r))) - (t (or (assoc (car e) r) (push e r))))) - (nreverse r))) - (t specs))) -(put 'org-compatible-face 'lisp-indent-function 1) +If INHERITS is an existing face and if the Emacs version supports +it, just inherit the face. If INHERITS is not given and SPECS +is, use SPECS to define the face." + (declare (indent 1)) + (if (facep inherits) + (list (list t :inherit inherits)) + specs)) +(make-obsolete 'org-compatible-face "you can remove it." "Org 9.0") + +(defun org-add-link-type (type &optional follow export) + "Add a new TYPE link. +FOLLOW and EXPORT are two functions. + +FOLLOW should take the link path as the single argument and do whatever +is necessary to follow the link, for example find a file or display +a mail message. + +EXPORT should format the link path for export to one of the export formats. +It should be a function accepting three arguments: + + path the path of the link, the text after the prefix (like \"http:\") + desc the description of the link, if any + format the export format, a symbol like `html' or `latex' or `ascii'. + +The function may use the FORMAT information to return different values +depending on the format. The return value will be put literally into +the exported file. If the return value is nil, this means Org should +do what it normally does with links which do not have EXPORT defined. + +Org mode has a built-in default for exporting links. If you are happy with +this default, there is no need to define an export function for the link +type. For a simple example of an export function, see `org-bbdb.el'. + +If TYPE already exists, update it with the arguments. +See `org-link-parameters' for documentation on the other parameters." + (org-link-set-parameters type :follow follow :export export) + (message "Created %s link." type)) + +(make-obsolete 'org-add-link-type "use `org-link-set-parameters' instead." "Org 9.0") + +(defun org-table-recognize-table.el () + "If there is a table.el table nearby, recognize it and move into it." + (when (and org-table-tab-recognizes-table.el (org-at-table.el-p)) + (beginning-of-line) + (unless (or (looking-at org-table-dataline-regexp) + (not (looking-at org-table1-hline-regexp))) + (forward-line) + (when (looking-at org-table-any-border-regexp) + (forward-line -2))) + (if (re-search-forward "|" (org-table-end t) t) + (progn + (require 'table) + (if (table--at-cell-p (point)) t + (message "recognizing table.el table...") + (table-recognize-table) + (message "recognizing table.el table...done"))) + (error "This should not happen")))) + +;; Not used by Org core since commit 6d1e3082, Feb 2010. +(make-obsolete 'org-table-recognize-table.el + "please notify the org mailing list if you use this function." + "Org 9.0") + +(define-obsolete-function-alias + 'org-minutes-to-hh:mm-string 'org-minutes-to-clocksum-string "Org 8.0") + +(defun org-remove-angle-brackets (s) + (org-unbracket-string "<" ">" s)) +(make-obsolete 'org-remove-angle-brackets 'org-unbracket-string "Org 9.0") + +(defun org-remove-double-quotes (s) + (org-unbracket-string "\"" "\"" s)) +(make-obsolete 'org-remove-double-quotes 'org-unbracket-string "Org 9.0") + +(define-obsolete-function-alias 'org-babel-number-p + 'org-babel--string-to-number "Org 9.0") + + + +;;;; Obsolete link types + +(eval-after-load 'org + '(progn + (org-link-set-parameters "file+emacs") ;since Org 9.0 + (org-link-set-parameters "file+sys"))) ;since Org 9.0 + + + +;;; Miscellaneous functions (defun org-version-check (version feature level) (let* ((v1 (mapcar 'string-to-number (split-string version "[.]"))) @@ -108,110 +330,19 @@ any other entries, and any resulting duplicates will be removed entirely." t)) t))) - -;;;; Emacs/XEmacs compatibility - -(eval-and-compile - (defun org-defvaralias (new-alias base-variable &optional docstring) - "Compatibility function for defvaralias. -Don't do the aliasing when `defvaralias' is not bound." - (declare (indent 1)) - (when (fboundp 'defvaralias) - (defvaralias new-alias base-variable docstring))) - - (when (and (not (boundp 'user-emacs-directory)) - (boundp 'user-init-directory)) - (org-defvaralias 'user-emacs-directory 'user-init-directory))) - -(when (featurep 'xemacs) - (defadvice custom-handle-keyword - (around org-custom-handle-keyword - activate preactivate) - "Remove custom keywords not recognized to avoid producing an error." - (cond - ((eq (ad-get-arg 1) :package-version)) - (t ad-do-it))) - (defadvice define-obsolete-variable-alias - (around org-define-obsolete-variable-alias - (obsolete-name current-name &optional when docstring) - activate preactivate) - "Declare arguments defined in later versions of Emacs." - ad-do-it) - (defadvice define-obsolete-function-alias - (around org-define-obsolete-function-alias - (obsolete-name current-name &optional when docstring) - activate preactivate) - "Declare arguments defined in later versions of Emacs." - ad-do-it) - (defvar customize-package-emacs-version-alist nil) - (defvar temporary-file-directory (temp-directory))) - -;; Keys -(defconst org-xemacs-key-equivalents - '(([mouse-1] . [button1]) - ([mouse-2] . [button2]) - ([mouse-3] . [button3]) - ([C-mouse-4] . [(control mouse-4)]) - ([C-mouse-5] . [(control mouse-5)])) - "Translation alist for a couple of keys.") - -;; Overlay compatibility functions -(defun org-detach-overlay (ovl) - (if (featurep 'xemacs) (detach-extent ovl) (delete-overlay ovl))) -(defun org-overlay-display (ovl text &optional face evap) - "Make overlay OVL display TEXT with face FACE." - (if (featurep 'xemacs) - (let ((gl (make-glyph text))) - (and face (set-glyph-face gl face)) - (set-extent-property ovl 'invisible t) - (set-extent-property ovl 'end-glyph gl)) - (overlay-put ovl 'display text) - (if face (overlay-put ovl 'face face)) - (if evap (overlay-put ovl 'evaporate t)))) -(defun org-overlay-before-string (ovl text &optional face evap) - "Make overlay OVL display TEXT with face FACE." - (if (featurep 'xemacs) - (let ((gl (make-glyph text))) - (and face (set-glyph-face gl face)) - (set-extent-property ovl 'begin-glyph gl)) - (if face (org-add-props text nil 'face face)) - (overlay-put ovl 'before-string text) - (if evap (overlay-put ovl 'evaporate t)))) -(defun org-find-overlays (prop &optional pos delete) - "Find all overlays specifying PROP at POS or point. -If DELETE is non-nil, delete all those overlays." - (let ((overlays (overlays-at (or pos (point)))) - ov found) - (while (setq ov (pop overlays)) - (if (overlay-get ov prop) - (if delete (delete-overlay ov) (push ov found)))) - found)) - (defun org-get-x-clipboard (value) - "Get the value of the x or Windows clipboard, compatible with XEmacs, and GNU Emacs 21." - (cond ((eq window-system 'x) - (let ((x (org-get-x-clipboard-compat value))) - (if x (org-no-properties x)))) + "Get the value of the X or Windows clipboard." + (cond ((and (eq window-system 'x) + (fboundp 'gui-get-selection)) ;Silence byte-compiler. + (org-no-properties + (ignore-errors + (or (gui-get-selection value 'UTF8_STRING) + (gui-get-selection value 'COMPOUND_TEXT) + (gui-get-selection value 'STRING) + (gui-get-selection value 'TEXT))))) ((and (eq window-system 'w32) (fboundp 'w32-get-clipboard-data)) (w32-get-clipboard-data)))) -(defsubst org-decompose-region (beg end) - "Decompose from BEG to END." - (if (featurep 'xemacs) - (let ((modified-p (buffer-modified-p)) - (buffer-read-only nil)) - (remove-text-properties beg end '(composition nil)) - (set-buffer-modified-p modified-p)) - (decompose-region beg end))) - -;; Miscellaneous functions - -(defun org-add-hook (hook function &optional append local) - "Add-hook, compatible with both Emacsen." - (if (and local (featurep 'xemacs)) - (add-local-hook hook function append) - (add-hook hook function append local))) - (defun org-add-props (string plist &rest props) "Add text properties to entire string, from beginning to end. PLIST may be a list of properties, PROPS are individual properties and values @@ -238,66 +369,29 @@ ignored in this case." (shrink-window-if-larger-than-buffer window))) (or window (selected-window))) -(defun org-number-sequence (from &optional to inc) - "Call `number-sequence' or emulate it." - (if (fboundp 'number-sequence) - (number-sequence from to inc) - (if (or (not to) (= from to)) - (list from) - (or inc (setq inc 1)) - (when (zerop inc) (error "The increment can not be zero")) - (let (seq (n 0) (next from)) - (if (> inc 0) - (while (<= next to) - (setq seq (cons next seq) - n (1+ n) - next (+ from (* n inc)))) - (while (>= next to) - (setq seq (cons next seq) - n (1+ n) - next (+ from (* n inc))))) - (nreverse seq))))) - ;; `set-transient-map' is only in Emacs >= 24.4 (defalias 'org-set-transient-map (if (fboundp 'set-transient-map) 'set-transient-map 'set-temporary-overlay-map)) -;; Region compatibility +;;; Region compatibility (defvar org-ignore-region nil "Non-nil means temporarily disable the active region.") (defun org-region-active-p () - "Is `transient-mark-mode' on and the region active? -Works on both Emacs and XEmacs." - (if org-ignore-region - nil - (if (featurep 'xemacs) - (and zmacs-regions (region-active-p)) - (if (fboundp 'use-region-p) - (use-region-p) - (and transient-mark-mode mark-active))))) ; Emacs 22 and before + "Non-nil when the region active. +Unlike to `use-region-p', this function also checks +`org-ignore-region'." + (and (not org-ignore-region) (use-region-p))) (defun org-cursor-to-region-beginning () (when (and (org-region-active-p) (> (point) (region-beginning))) (exchange-point-and-mark))) -;; Emacs 22 misses `activate-mark' -(if (fboundp 'activate-mark) - (defalias 'org-activate-mark 'activate-mark) - (defun org-activate-mark () - (when (mark t) - (setq mark-active t) - (when (and (boundp 'transient-mark-mode) - (not transient-mark-mode)) - (set (make-local-variable 'transient-mark-mode) 'lambda)) - (when (boundp 'zmacs-regions) - (setq zmacs-regions t))))) - -;; Invisibility compatibility +;;; Invisibility compatibility (defun org-remove-from-invisibility-spec (arg) "Remove elements from `buffer-invisibility-spec'." @@ -312,63 +406,14 @@ Works on both Emacs and XEmacs." (if (consp buffer-invisibility-spec) (member arg buffer-invisibility-spec))) -(defmacro org-xemacs-without-invisibility (&rest body) - "Turn off extents with invisibility while executing BODY." - `(let ((ext-inv (extent-list nil (point-at-bol) (point-at-eol) - 'all-extents-closed-open 'invisible)) - ext-inv-specs) - (dolist (ext ext-inv) - (when (extent-property ext 'invisible) - (add-to-list 'ext-inv-specs (list ext (extent-property - ext 'invisible))) - (set-extent-property ext 'invisible nil))) - ,@body - (dolist (ext-inv-spec ext-inv-specs) - (set-extent-property (car ext-inv-spec) 'invisible - (cadr ext-inv-spec))))) -(def-edebug-spec org-xemacs-without-invisibility (body)) - -(defun org-indent-to-column (column &optional minimum buffer) - "Work around a bug with extents with invisibility in XEmacs." - (if (featurep 'xemacs) - (org-xemacs-without-invisibility (indent-to-column column minimum buffer)) - (indent-to-column column minimum))) - -(defun org-indent-line-to (column) - "Work around a bug with extents with invisibility in XEmacs." - (if (featurep 'xemacs) - (org-xemacs-without-invisibility (indent-line-to column)) - (indent-line-to column))) - -(defun org-move-to-column (column &optional force buffer) +(defun org-move-to-column (column &optional force _buffer) "Move to column COLUMN. -Pass COLUMN and FORCE to `move-to-column'. -Pass BUFFER to the XEmacs version of `move-to-column'." +Pass COLUMN and FORCE to `move-to-column'." (let ((buffer-invisibility-spec - (remove '(org-filtered) buffer-invisibility-spec))) - (if (featurep 'xemacs) - (org-xemacs-without-invisibility - (move-to-column column force buffer)) - (move-to-column column force)))) - -(defun org-get-x-clipboard-compat (value) - "Get the clipboard value on XEmacs or Emacs 21." - (cond ((featurep 'xemacs) - (org-no-warnings (get-selection-no-error value))) - ((fboundp 'x-get-selection) - (condition-case nil - (or (x-get-selection value 'UTF8_STRING) - (x-get-selection value 'COMPOUND_TEXT) - (x-get-selection value 'STRING) - (x-get-selection value 'TEXT)) - (error nil))))) - -(defun org-propertize (string &rest properties) - (if (featurep 'xemacs) - (progn - (add-text-properties 0 (length string) properties string) - string) - (apply 'propertize string properties))) + (if (listp buffer-invisibility-spec) + (remove '(org-filtered) buffer-invisibility-spec) + buffer-invisibility-spec))) + (move-to-column column force))) (defmacro org-find-library-dir (library) `(file-name-directory (or (locate-library ,library) ""))) @@ -387,37 +432,20 @@ Pass BUFFER to the XEmacs version of `move-to-column'." string) (apply 'kill-new string args)) -(defun org-select-frame-set-input-focus (frame) - "Select FRAME, raise it, and set input focus, if possible." - (cond ((featurep 'xemacs) - (if (fboundp 'select-frame-set-input-focus) - (select-frame-set-input-focus frame) - (raise-frame frame) - (select-frame frame) - (focus-frame frame))) - ;; `select-frame-set-input-focus' defined in Emacs 21 will not - ;; set the input focus. - ((>= emacs-major-version 22) - (select-frame-set-input-focus frame)) - (t - (raise-frame frame) - (select-frame frame) - (cond ((memq window-system '(x ns mac)) - (x-focus-frame frame)) - ((eq window-system 'w32) - (w32-focus-frame frame))) - (when focus-follows-mouse - (set-mouse-position frame (1- (frame-width frame)) 0))))) - -(define-obsolete-function-alias 'org-float-time 'float-time "26.1") - -;; `user-error' is only available from 24.3 on -(unless (fboundp 'user-error) - (defalias 'user-error 'error)) - -;; ‘format-message’ is available only from 25 on -(unless (fboundp 'format-message) - (defalias 'format-message 'format)) +;; `font-lock-ensure' is only available from 24.4.50 on +(defalias 'org-font-lock-ensure + (if (fboundp 'font-lock-ensure) + #'font-lock-ensure + (lambda (&optional _beg _end) + (with-no-warnings (font-lock-fontify-buffer))))) + +;; `file-local-name' was added in Emacs 26.1. +(defalias 'org-babel-local-file-name + (if (fboundp 'file-local-name) + 'file-local-name + (lambda (file) + "Return the local name component of FILE." + (or (file-remote-p file 'localname) file)))) (defmacro org-no-popups (&rest body) "Suppress popup windows. @@ -429,93 +457,6 @@ effect, which variables to use depends on the Emacs version." `(let (pop-up-frames special-display-buffer-names special-display-regexps special-display-function) ,@body))) -(if (fboundp 'string-match-p) - (defalias 'org-string-match-p 'string-match-p) - (defun org-string-match-p (regexp string &optional start) - (save-match-data - (funcall 'string-match regexp string start)))) - -(if (fboundp 'looking-at-p) - (defalias 'org-looking-at-p 'looking-at-p) - (defun org-looking-at-p (&rest args) - (save-match-data - (apply 'looking-at args)))) - -;; XEmacs does not have `looking-back'. -(if (fboundp 'looking-back) - (defalias 'org-looking-back 'looking-back) - (defun org-looking-back (regexp &optional limit greedy) - "Return non-nil if text before point matches regular expression REGEXP. -Like `looking-at' except matches before point, and is slower. -LIMIT if non-nil speeds up the search by specifying a minimum -starting position, to avoid checking matches that would start -before LIMIT. - -If GREEDY is non-nil, extend the match backwards as far as -possible, stopping when a single additional previous character -cannot be part of a match for REGEXP. When the match is -extended, its starting position is allowed to occur before -LIMIT." - (let ((start (point)) - (pos - (save-excursion - (and (re-search-backward (concat "\\(?:" regexp "\\)\\=") limit t) - (point))))) - (if (and greedy pos) - (save-restriction - (narrow-to-region (point-min) start) - (while (and (> pos (point-min)) - (save-excursion - (goto-char pos) - (backward-char 1) - (looking-at (concat "\\(?:" regexp "\\)\\'")))) - (setq pos (1- pos))) - (save-excursion - (goto-char pos) - (looking-at (concat "\\(?:" regexp "\\)\\'"))))) - (not (null pos))))) - -(defalias 'org-font-lock-ensure - (if (fboundp 'font-lock-ensure) - #'font-lock-ensure - (lambda (&optional _beg _end) (font-lock-fontify-buffer)))) - -(defun org-floor* (x &optional y) - "Return a list of the floor of X and the fractional part of X. -With two arguments, return floor and remainder of their quotient." - (let ((q (floor x y))) - (list q (- x (if y (* y q) q))))) - -;; `pop-to-buffer-same-window' has been introduced in Emacs 24.1. -(defun org-pop-to-buffer-same-window - (&optional buffer-or-name norecord label) - "Pop to buffer specified by BUFFER-OR-NAME in the selected window." - (if (fboundp 'pop-to-buffer-same-window) - (funcall - 'pop-to-buffer-same-window buffer-or-name norecord) - (funcall 'switch-to-buffer buffer-or-name norecord))) - -;; RECURSIVE has been introduced with Emacs 23.2. -;; This is copying and adapted from `tramp-compat-delete-directory' -(defun org-delete-directory (directory &optional recursive) - "Compatibility function for `delete-directory'." - (if (null recursive) - (delete-directory directory) - (condition-case nil - (funcall 'delete-directory directory recursive) - ;; This Emacs version does not support the RECURSIVE flag. We - ;; use the implementation from Emacs 23.2. - (wrong-number-of-arguments - (setq directory (directory-file-name (expand-file-name directory))) - (if (not (file-symlink-p directory)) - (mapc (lambda (file) - (if (eq t (car (file-attributes file))) - (org-delete-directory file recursive) - (delete-file file))) - (directory-files - directory 'full "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*"))) - (delete-directory directory))))) - ;;;###autoload (defmacro org-check-version () "Try very hard to provide sensible version strings." @@ -534,29 +475,33 @@ With two arguments, return floor and remainder of their quotient." (defun org-release () "N/A") (defun org-git-version () "N/A !!check installation!!")))))) -(defun org-file-equal-p (f1 f2) - "Return t if files F1 and F2 are the same. -Implements `file-equal-p' for older emacsen and XEmacs." - (if (fboundp 'file-equal-p) - (file-equal-p f1 f2) - (let (f1-attr f2-attr) - (and (setq f1-attr (file-attributes (file-truename f1))) - (setq f2-attr (file-attributes (file-truename f2))) - (equal f1-attr f2-attr))))) - -;; `buffer-narrowed-p' is available for Emacs >=24.3 -(defun org-buffer-narrowed-p () - "Compatibility function for `buffer-narrowed-p'." - (if (fboundp 'buffer-narrowed-p) - (buffer-narrowed-p) - (/= (- (point-max) (point-min)) (buffer-size)))) - (defmacro org-with-silent-modifications (&rest body) (if (fboundp 'with-silent-modifications) `(with-silent-modifications ,@body) `(org-unmodified ,@body))) (def-edebug-spec org-with-silent-modifications (body)) +;; Functions for Emacs < 24.4 compatibility +(defun org-define-error (name message) + "Define NAME as a new error signal. +MESSAGE is a string that will be output to the echo area if such +an error is signaled without being caught by a `condition-case'. +Implements `define-error' for older emacsen." + (if (fboundp 'define-error) (define-error name message) + (put name 'error-conditions + (copy-sequence (cons name (get 'error 'error-conditions)))))) + +(unless (fboundp 'string-suffix-p) + ;; From Emacs subr.el. + (defun string-suffix-p (suffix string &optional ignore-case) + "Return non-nil if SUFFIX is a suffix of STRING. +If IGNORE-CASE is non-nil, the comparison is done without paying +attention to case differences." + (let ((start-pos (- (length string) (length suffix)))) + (and (>= start-pos 0) + (eq t (compare-strings suffix nil nil + string start-pos nil ignore-case)))))) + (provide 'org-compat) ;;; org-compat.el ends here diff --git a/lisp/org/org-crypt.el b/lisp/org/org-crypt.el index 36144e2530..3c431e4fdd 100644 --- a/lisp/org/org-crypt.el +++ b/lisp/org/org-crypt.el @@ -1,5 +1,4 @@ -;;; org-crypt.el --- Public key encryption for org-mode entries - +;;; org-crypt.el --- Public Key Encryption for Org Entries -*- lexical-binding: t; -*- ;; Copyright (C) 2007-2017 Free Software Foundation, Inc. ;; Emacs Lisp Archive Entry @@ -7,7 +6,7 @@ ;; Keywords: org-mode ;; Author: John Wiegley ;; Maintainer: Peter Jones -;; Description: Adds public key encryption to org-mode buffers +;; Description: Adds public key encryption to Org buffers ;; URL: http://www.newartisans.com/software/emacs.html ;; Compatibility: Emacs22 @@ -104,10 +103,10 @@ t : Disable auto-save-mode for the current buffer nil : Leave auto-save-mode enabled. This may cause data to be written to disk unencrypted! -'ask : Ask user whether or not to disable auto-save-mode +`ask' : Ask user whether or not to disable auto-save-mode for the current buffer. -'encrypt : Leave auto-save-mode enabled for the current buffer, +`encrypt': Leave auto-save-mode enabled for the current buffer, but automatically re-encrypt all decrypted entries *before* auto-saving. NOTE: This only works for entries which have a tag @@ -142,7 +141,7 @@ See `org-crypt-disable-auto-save'." (message "org-decrypt: Decrypting entry with auto-save-mode enabled. This may cause leakage.")) ((eq org-crypt-disable-auto-save 'encrypt) (message "org-decrypt: Enabling re-encryption on auto-save.") - (org-add-hook 'auto-save-hook + (add-hook 'auto-save-hook (lambda () (message "org-crypt: Re-encrypting all decrypted entries due to auto-save.") (org-encrypt-entries)) @@ -164,96 +163,96 @@ See `org-crypt-disable-auto-save'." (if (and (string= crypt-key (get-text-property 0 'org-crypt-key str)) (string= (sha1 str) (get-text-property 0 'org-crypt-checksum str))) (get-text-property 0 'org-crypt-text str) - (set (make-local-variable 'epg-context) (epg-make-context nil t t)) + (setq-local epg-context (epg-make-context nil t t)) (epg-encrypt-string epg-context str (epg-list-keys epg-context crypt-key)))) (defun org-encrypt-entry () "Encrypt the content of the current headline." (interactive) (require 'epg) - (save-excursion - (org-back-to-heading t) - (set (make-local-variable 'epg-context) (epg-make-context nil t t)) - (let ((start-heading (point))) - (forward-line) - (when (not (looking-at "-----BEGIN PGP MESSAGE-----")) - (let ((folded (outline-invisible-p)) - (crypt-key (org-crypt-key-for-heading)) - (beg (point)) - end encrypted-text) - (goto-char start-heading) - (org-end-of-subtree t t) - (org-back-over-empty-lines) - (setq end (point) - encrypted-text - (org-encrypt-string (buffer-substring beg end) crypt-key)) - (delete-region beg end) - (insert encrypted-text) - (when folded - (goto-char start-heading) - (hide-subtree)) - nil))))) + (org-with-wide-buffer + (org-back-to-heading t) + (setq-local epg-context (epg-make-context nil t t)) + (let ((start-heading (point))) + (org-end-of-meta-data) + (unless (looking-at-p "-----BEGIN PGP MESSAGE-----") + (let ((folded (org-invisible-p)) + (crypt-key (org-crypt-key-for-heading)) + (beg (point))) + (goto-char start-heading) + (org-end-of-subtree t t) + (org-back-over-empty-lines) + (let ((contents (delete-and-extract-region beg (point)))) + (condition-case err + (insert (org-encrypt-string contents crypt-key)) + ;; If encryption failed, make sure to insert back entry + ;; contents in the buffer. + (error (insert contents) (error (nth 1 err))))) + (when folded + (goto-char start-heading) + (outline-hide-subtree)) + nil))))) (defun org-decrypt-entry () "Decrypt the content of the current headline." (interactive) (require 'epg) (unless (org-before-first-heading-p) - (save-excursion - (org-back-to-heading t) - (let ((heading-point (point)) - (heading-was-invisible-p - (save-excursion - (outline-end-of-heading) - (outline-invisible-p)))) - (forward-line) - (when (looking-at "-----BEGIN PGP MESSAGE-----") - (org-crypt-check-auto-save) - (set (make-local-variable 'epg-context) (epg-make-context nil t t)) - (let* ((end (save-excursion - (search-forward "-----END PGP MESSAGE-----") - (forward-line) - (point))) - (encrypted-text (buffer-substring-no-properties (point) end)) - (decrypted-text - (decode-coding-string - (epg-decrypt-string - epg-context - encrypted-text) - 'utf-8))) - ;; Delete region starting just before point, because the - ;; outline property starts at the \n of the heading. - (delete-region (1- (point)) end) - ;; Store a checksum of the decrypted and the encrypted - ;; text value. This allow reusing the same encrypted text - ;; if the text does not change, and therefore avoid a - ;; re-encryption process. - (insert "\n" (propertize decrypted-text - 'org-crypt-checksum (sha1 decrypted-text) - 'org-crypt-key (org-crypt-key-for-heading) - 'org-crypt-text encrypted-text)) - (when heading-was-invisible-p - (goto-char heading-point) - (org-flag-subtree t)) - nil)))))) + (org-with-wide-buffer + (org-back-to-heading t) + (let ((heading-point (point)) + (heading-was-invisible-p + (save-excursion + (outline-end-of-heading) + (org-invisible-p)))) + (org-end-of-meta-data) + (when (looking-at "-----BEGIN PGP MESSAGE-----") + (org-crypt-check-auto-save) + (setq-local epg-context (epg-make-context nil t t)) + (let* ((end (save-excursion + (search-forward "-----END PGP MESSAGE-----") + (forward-line) + (point))) + (encrypted-text (buffer-substring-no-properties (point) end)) + (decrypted-text + (decode-coding-string + (epg-decrypt-string + epg-context + encrypted-text) + 'utf-8))) + ;; Delete region starting just before point, because the + ;; outline property starts at the \n of the heading. + (delete-region (1- (point)) end) + ;; Store a checksum of the decrypted and the encrypted + ;; text value. This allows reusing the same encrypted text + ;; if the text does not change, and therefore avoid a + ;; re-encryption process. + (insert "\n" (propertize decrypted-text + 'org-crypt-checksum (sha1 decrypted-text) + 'org-crypt-key (org-crypt-key-for-heading) + 'org-crypt-text encrypted-text)) + (when heading-was-invisible-p + (goto-char heading-point) + (org-flag-subtree t)) + nil)))))) (defun org-encrypt-entries () "Encrypt all top-level entries in the current buffer." (interactive) - (let (todo-only) + (let ((org--matcher-tags-todo-only nil)) (org-scan-tags 'org-encrypt-entry (cdr (org-make-tags-matcher org-crypt-tag-matcher)) - todo-only))) + org--matcher-tags-todo-only))) (defun org-decrypt-entries () "Decrypt all entries in the current buffer." (interactive) - (let (todo-only) + (let ((org--matcher-tags-todo-only nil)) (org-scan-tags 'org-decrypt-entry (cdr (org-make-tags-matcher org-crypt-tag-matcher)) - todo-only))) + org--matcher-tags-todo-only))) (defun org-at-encrypted-entry-p () "Is the current entry encrypted?" @@ -267,7 +266,7 @@ See `org-crypt-disable-auto-save'." "Add a hook to automatically encrypt entries before a file is saved to disk." (add-hook 'org-mode-hook - (lambda () (org-add-hook 'before-save-hook 'org-encrypt-entries nil t)))) + (lambda () (add-hook 'before-save-hook 'org-encrypt-entries nil t)))) (add-hook 'org-reveal-start-hook 'org-decrypt-entry) diff --git a/lisp/org/org-ctags.el b/lisp/org/org-ctags.el index 1ecf674482..98eb8068a8 100644 --- a/lisp/org/org-ctags.el +++ b/lisp/org/org-ctags.el @@ -1,4 +1,4 @@ -;;; org-ctags.el - Integrate Emacs "tags" facility with org mode. +;;; org-ctags.el - Integrate Emacs "tags" Facility with Org -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2007-2017 Free Software Foundation, Inc. @@ -26,20 +26,21 @@ ;; Synopsis ;; ======== ;; -;; Allows org-mode to make use of the Emacs `etags' system. Defines tag -;; destinations in org-mode files as any text between <>. This allows the tags-generation program `exuberant ctags' to -;; parse these files and create tag tables that record where these -;; destinations are found. Plain [[links]] in org mode files which do not have -;; <> within the same file will then be interpreted as -;; links to these 'tagged' destinations, allowing seamless navigation between -;; multiple org-mode files. Topics can be created in any org mode file and -;; will always be found by plain links from other files. Other file types -;; recognized by ctags (source code files, latex files, etc) will also be -;; available as destinations for plain links, and similarly, org-mode links -;; will be available as tags from source files. Finally, the function -;; `org-ctags-find-tag-interactive' lets you choose any known tag, using -;; autocompletion, and quickly jump to it. +;; Allows Org mode to make use of the Emacs `etags' system. Defines +;; tag destinations in Org files as any text between <>. This allows the tags-generation program `exuberant +;; ctags' to parse these files and create tag tables that record where +;; these destinations are found. Plain [[links]] in org mode files +;; which do not have <> within the same file +;; will then be interpreted as links to these 'tagged' destinations, +;; allowing seamless navigation between multiple Org files. Topics +;; can be created in any org mode file and will always be found by +;; plain links from other files. Other file types recognized by ctags +;; (source code files, latex files, etc) will also be available as +;; destinations for plain links, and similarly, Org links will be +;; available as tags from source files. Finally, the function +;; `org-ctags-find-tag-interactive' lets you choose any known tag, +;; using autocompletion, and quickly jump to it. ;; ;; Installation ;; ============ @@ -110,8 +111,9 @@ ;; Keeping the TAGS file up to date ;; ================================ ;; -;; Tags mode has no way of knowing that you have created new tags by typing in -;; your org-mode buffer. New tags make it into the TAGS file in 3 ways: +;; Tags mode has no way of knowing that you have created new tags by +;; typing in your Org buffer. New tags make it into the TAGS file in +;; 3 ways: ;; ;; 1. You re-run (org-ctags-create-tags "directory") to rebuild the file. ;; 2. You put the function `org-ctags-ask-rebuild-tags-file-then-find-tag' in @@ -135,12 +137,8 @@ ;;; Code: -(eval-when-compile (require 'cl)) - (require 'org) -(declare-function org-pop-to-buffer-same-window "org-compat" (&optional buffer-or-name norecord label)) - (defgroup org-ctags nil "Options concerning use of ctags within org mode." :tag "Org-Ctags" @@ -151,7 +149,7 @@ (defvar org-ctags-tag-regexp "/<<([^>]+)>>/\\1/d,definition/" "Regexp expression used by ctags external program. -The regexp matches tag destinations in org-mode files. +The regexp matches tag destinations in Org files. Format is: /REGEXP/TAGNAME/FLAGS,TAGTYPE/ See the ctags documentation for more information.") @@ -210,8 +208,8 @@ The following patterns are replaced in the string: (defadvice visit-tags-table (after org-ctags-load-tag-list activate compile) (when (and org-ctags-enabled-p tags-file-name) - (set (make-local-variable 'org-ctags-tag-list) - (org-ctags-all-tags-in-current-tags-table)))) + (setq-local org-ctags-tag-list + (org-ctags-all-tags-in-current-tags-table)))) (defun org-ctags-enable () @@ -273,11 +271,6 @@ Return the list." (replace-regexp-in-string (regexp-quote search) replace string t t)) -(defun y-or-n-minibuffer (prompt) - (let ((use-dialog-box nil)) - (y-or-n-p prompt))) - - ;;; Internal functions ======================================================= @@ -285,29 +278,28 @@ Return the list." "Visit or create a file called `NAME.org', and insert a new topic. The new topic will be titled NAME (or TITLE if supplied)." (interactive "sFile name: ") - (let ((filename (substitute-in-file-name (expand-file-name name)))) - (condition-case v - (progn - (org-open-file name t) - (message "Opened file OK") - (goto-char (point-max)) - (insert (org-ctags-string-search-and-replace - "%t" (capitalize (or title name)) - org-ctags-new-topic-template)) - (message "Inserted new file text OK") - (org-mode-restart)) - (error (error "Error %S in org-ctags-open-file" v))))) + (condition-case v + (progn + (org-open-file name t) + (message "Opened file OK") + (goto-char (point-max)) + (insert (org-ctags-string-search-and-replace + "%t" (capitalize (or title name)) + org-ctags-new-topic-template)) + (message "Inserted new file text OK") + (org-mode-restart)) + (error (error "Error %S in org-ctags-open-file" v)))) ;;;; Misc interoperability with etags system ================================= -(defadvice find-tag (before org-ctags-set-org-mark-before-finding-tag - activate compile) +(defadvice xref-find-definitions + (before org-ctags-set-org-mark-before-finding-tag activate compile) "Before trying to find a tag, save our current position on org mark ring." (save-excursion - (if (and (derived-mode-p 'org-mode) org-ctags-enabled-p) - (org-mark-ring-push)))) + (when (and (derived-mode-p 'org-mode) org-ctags-enabled-p) + (org-mark-ring-push)))) @@ -359,7 +351,7 @@ visit the file and location where the tag is found." (old-pnt (point-marker)) (old-mark (copy-marker (mark-marker)))) (condition-case nil - (progn (find-tag name) + (progn (xref-find-definitions name) t) (error ;; only restore old location if find-tag raises error @@ -386,7 +378,7 @@ the new file." (cond ((get-buffer (concat name ".org")) ;; Buffer is already open - (org-pop-to-buffer-same-window (get-buffer (concat name ".org")))) + (pop-to-buffer-same-window (get-buffer (concat name ".org")))) ((file-exists-p filename) ;; File exists but is not open --> open it (message "Opening existing org file `%S'..." @@ -421,7 +413,6 @@ the heading a destination for the tag `NAME'." (insert (org-ctags-string-search-and-replace "%t" (capitalize name) org-ctags-new-topic-template)) (backward-char 4) - (org-update-radio-target-regexp) (end-of-line) (forward-line 2) (when narrowp @@ -464,10 +455,10 @@ Wrapper for org-ctags-rebuild-tags-file-then-find-tag." nil)) -(defun org-ctags-fail-silently (name) +(defun org-ctags-fail-silently (_name) "This function is intended to be used in ORG-OPEN-LINK-FUNCTIONS. -Put as the last function in the list if you want to prevent org's default -behavior of free text search." +Put as the last function in the list if you want to prevent Org's +default behavior of free text search." t) @@ -484,7 +475,7 @@ end up in one file, called TAGS, located in the directory. This function may take several seconds to finish if the directory or its subdirectories contain large numbers of taggable files." (interactive) - (assert (buffer-file-name)) + (cl-assert (buffer-file-name)) (let ((dir-name (or directory-name (file-name-directory (buffer-file-name)))) (exitcode nil)) @@ -499,8 +490,8 @@ its subdirectories contain large numbers of taggable files." (expand-file-name (concat dir-name "/*"))))) (cond ((eql 0 exitcode) - (set (make-local-variable 'org-ctags-tag-list) - (org-ctags-all-tags-in-current-tags-table))) + (setq-local org-ctags-tag-list + (org-ctags-all-tags-in-current-tags-table))) (t ;; This seems to behave differently on Linux, so just ignore ;; error codes for now @@ -528,7 +519,7 @@ a new topic." ((member tag org-ctags-tag-list) ;; Existing tag (push tag org-ctags-find-tag-history) - (find-tag tag)) + (xref-find-definitions tag)) (t ;; New tag (run-hook-with-args-until-success diff --git a/lisp/org/org-datetree.el b/lisp/org/org-datetree.el index 891e64f909..540753d67c 100644 --- a/lisp/org/org-datetree.el +++ b/lisp/org/org-datetree.el @@ -1,4 +1,4 @@ -;;; org-datetree.el --- Create date entries in a tree +;;; org-datetree.el --- Create date entries in a tree -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -34,12 +34,14 @@ (defvar org-datetree-base-level 1 "The level at which years should be placed in the date tree. -This is normally one, but if the buffer has an entry with a DATE_TREE -property (any value), the date tree will become a subtree under that entry, -so the base level will be properly adjusted.") +This is normally one, but if the buffer has an entry with a +DATE_TREE (or WEEK_TREE for ISO week entries) property (any +value), the date tree will become a subtree under that entry, so +the base level will be properly adjusted.") (defcustom org-datetree-add-timestamp nil - "When non-nil, add a time stamp when create a datetree entry." + "When non-nil, add a time stamp matching date of entry. +Added time stamp is active unless value is `inactive'." :group 'org-capture :version "24.3" :type '(choice @@ -48,115 +50,129 @@ so the base level will be properly adjusted.") (const :tag "Add an active time stamp" active))) ;;;###autoload -(defun org-datetree-find-date-create (date &optional keep-restriction) - "Find or create an entry for DATE. +(defun org-datetree-find-date-create (d &optional keep-restriction) + "Find or create an entry for date D. If KEEP-RESTRICTION is non-nil, do not widen the buffer. When it is nil, the buffer will be widened to make sure an existing date tree can be found." - (let ((year (nth 2 date)) - (month (car date)) - (day (nth 1 date))) - (org-set-local 'org-datetree-base-level 1) - (or keep-restriction (widen)) + (setq-local org-datetree-base-level 1) + (or keep-restriction (widen)) + (save-restriction + (let ((prop (org-find-property "DATE_TREE"))) + (when prop + (goto-char prop) + (setq-local org-datetree-base-level + (org-get-valid-level (org-current-level) 1)) + (org-narrow-to-subtree))) (goto-char (point-min)) - (save-restriction - (when (re-search-forward "^[ \t]*:DATE_TREE:[ \t]+\\S-" nil t) - (org-back-to-heading t) - (org-set-local 'org-datetree-base-level - (org-get-valid-level (funcall outline-level) 1)) - (org-narrow-to-subtree)) - (goto-char (point-min)) - (org-datetree-find-year-create year) - (org-datetree-find-month-create year month) - (org-datetree-find-day-create year month day) - (goto-char (prog1 (point) (widen)))))) - -(defun org-datetree-find-year-create (year) - "Find the YEAR datetree or create it." - (let ((re "^\\*+[ \t]+\\([12][0-9]\\{3\\}\\)\\(\\s-*?\\([ \t]:[[:alnum:]:_@#%]+:\\)?\\s-*$\\)") - match) - (goto-char (point-min)) - (while (and (setq match (re-search-forward re nil t)) - (goto-char (match-beginning 1)) - (< (string-to-number (match-string 1)) year))) - (cond - ((not match) - (goto-char (point-max)) - (or (bolp) (newline)) - (org-datetree-insert-line year)) - ((= (string-to-number (match-string 1)) year) - (goto-char (point-at-bol))) - (t - (beginning-of-line 1) - (org-datetree-insert-line year))))) + (let ((year (calendar-extract-year d)) + (month (calendar-extract-month d)) + (day (calendar-extract-day d))) + (org-datetree--find-create + "^\\*+[ \t]+\\([12][0-9]\\{3\\}\\)\\(\\s-*?\ +\\([ \t]:[[:alnum:]:_@#%%]+:\\)?\\s-*$\\)" + year) + (org-datetree--find-create + "^\\*+[ \t]+%d-\\([01][0-9]\\) \\w+$" + year month) + (org-datetree--find-create + "^\\*+[ \t]+%d-%02d-\\([0123][0-9]\\) \\w+$" + year month day)))) -(defun org-datetree-find-month-create (year month) - "Find the datetree for YEAR and MONTH or create it." - (org-narrow-to-subtree) - (let ((re (format "^\\*+[ \t]+%d-\\([01][0-9]\\) \\w+$" year)) - match) +;;;###autoload +(defun org-datetree-find-iso-week-create (d &optional keep-restriction) + "Find or create an ISO week entry for date D. +Compared to `org-datetree-find-date-create' this function creates +entries ordered by week instead of months. +If KEEP-RESTRICTION is non-nil, do not widen the buffer. When it +is nil, the buffer will be widened to make sure an existing date +tree can be found." + (setq-local org-datetree-base-level 1) + (or keep-restriction (widen)) + (save-restriction + (let ((prop (org-find-property "WEEK_TREE"))) + (when prop + (goto-char prop) + (setq-local org-datetree-base-level + (org-get-valid-level (org-current-level) 1)) + (org-narrow-to-subtree))) (goto-char (point-min)) - (while (and (setq match (re-search-forward re nil t)) - (goto-char (match-beginning 1)) - (< (string-to-number (match-string 1)) month))) - (cond - ((not match) - (goto-char (point-max)) - (or (bolp) (newline)) - (org-datetree-insert-line year month)) - ((= (string-to-number (match-string 1)) month) - (goto-char (point-at-bol))) - (t - (beginning-of-line 1) - (org-datetree-insert-line year month))))) - -(defun org-datetree-find-day-create (year month day) - "Find the datetree for YEAR, MONTH and DAY or create it." - (org-narrow-to-subtree) - (let ((re (format "^\\*+[ \t]+%d-%02d-\\([0123][0-9]\\) \\w+$" year month)) + (require 'cal-iso) + (let* ((year (calendar-extract-year d)) + (month (calendar-extract-month d)) + (day (calendar-extract-day d)) + (time (encode-time 0 0 0 day month year)) + (iso-date (calendar-iso-from-absolute + (calendar-absolute-from-gregorian d))) + (weekyear (nth 2 iso-date)) + (week (nth 0 iso-date))) + ;; ISO 8601 week format is %G-W%V(-%u) + (org-datetree--find-create + "^\\*+[ \t]+\\([12][0-9]\\{3\\}\\)\\(\\s-*?\ +\\([ \t]:[[:alnum:]:_@#%%]+:\\)?\\s-*$\\)" + weekyear nil nil + (format-time-string "%G" time)) + (org-datetree--find-create + "^\\*+[ \t]+%d-W\\([0-5][0-9]\\)$" + weekyear week nil + (format-time-string "%G-W%V" time)) + ;; For the actual day we use the regular date instead of ISO week. + (org-datetree--find-create + "^\\*+[ \t]+%d-%02d-\\([0123][0-9]\\) \\w+$" + year month day)))) + +(defun org-datetree--find-create (regex year &optional month day insert) + "Find the datetree matched by REGEX for YEAR, MONTH, or DAY. +REGEX is passed to `format' with YEAR, MONTH, and DAY as +arguments. Match group 1 is compared against the specified date +component. If INSERT is non-nil and there is no match then it is +inserted into the buffer." + (when (or month day) + (org-narrow-to-subtree)) + (let ((re (format regex year month day)) match) (goto-char (point-min)) (while (and (setq match (re-search-forward re nil t)) (goto-char (match-beginning 1)) - (< (string-to-number (match-string 1)) day))) + (< (string-to-number (match-string 1)) (or day month year)))) (cond ((not match) (goto-char (point-max)) - (or (bolp) (newline)) - (org-datetree-insert-line year month day)) - ((= (string-to-number (match-string 1)) day) - (goto-char (point-at-bol))) + (unless (bolp) (insert "\n")) + (org-datetree-insert-line year month day insert)) + ((= (string-to-number (match-string 1)) (or day month year)) + (beginning-of-line)) (t - (beginning-of-line 1) - (org-datetree-insert-line year month day))))) - -(defun org-datetree-insert-line (year &optional month day) - (let ((pos (point)) ts-type) - (skip-chars-backward " \t\n") - (delete-region (point) pos) - (insert "\n" (make-string org-datetree-base-level ?*) " \n") - (backward-char 1) - (if month (org-do-demote)) - (if day (org-do-demote)) + (beginning-of-line) + (org-datetree-insert-line year month day insert))))) + +(defun org-datetree-insert-line (year &optional month day text) + (delete-region (save-excursion (skip-chars-backward " \t\n") (point)) (point)) + (insert "\n" (make-string org-datetree-base-level ?*) " \n") + (backward-char) + (when month (org-do-demote)) + (when day (org-do-demote)) + (if text + (insert text) (insert (format "%d" year)) (when month - (insert (format "-%02d" month)) - (if day - (insert (format "-%02d %s" - day (format-time-string - "%A" (encode-time 0 0 0 day month year)))) - (insert (format " %s" - (format-time-string - "%B" (encode-time 0 0 0 1 month year)))))) - (when (and day (setq ts-type org-datetree-add-timestamp)) + (insert + (if day + (format-time-string "-%m-%d %A" (encode-time 0 0 0 day month year)) + (format-time-string "-%m %B" (encode-time 0 0 0 1 month year)))))) + (when (and day org-datetree-add-timestamp) + (save-excursion (insert "\n") (org-indent-line) - (org-insert-time-stamp (encode-time 0 0 0 day month year) nil ts-type)) - (beginning-of-line 1))) - -(defun org-datetree-file-entry-under (txt date) - "Insert a node TXT into the date tree under DATE." - (org-datetree-find-date-create date) + (org-insert-time-stamp + (encode-time 0 0 0 day month year) + nil + (eq org-datetree-add-timestamp 'inactive)))) + (beginning-of-line)) + +(defun org-datetree-file-entry-under (txt d) + "Insert a node TXT into the date tree under date D." + (org-datetree-find-date-create d) (let ((level (org-get-valid-level (funcall outline-level) 1))) (org-end-of-subtree t t) (org-back-over-empty-lines) @@ -169,44 +185,42 @@ before running this command, even though the command tries to be smart." (interactive) (goto-char (point-min)) (let ((dre (concat "\\<" org-deadline-string "\\>[ \t]*\\'")) - (sre (concat "\\<" org-scheduled-string "\\>[ \t]*\\'")) - dct ts tmp date year month day pos hdl-pos) + (sre (concat "\\<" org-scheduled-string "\\>[ \t]*\\'"))) (while (re-search-forward org-ts-regexp nil t) (catch 'next - (setq ts (match-string 0)) - (setq tmp (buffer-substring - (max (point-at-bol) (- (match-beginning 0) - org-ds-keyword-length)) - (match-beginning 0))) - (if (or (string-match "-\\'" tmp) - (string-match dre tmp) - (string-match sre tmp)) + (let ((tmp (buffer-substring + (max (line-beginning-position) + (- (match-beginning 0) org-ds-keyword-length)) + (match-beginning 0)))) + (when (or (string-suffix-p "-" tmp) + (string-match dre tmp) + (string-match sre tmp)) (throw 'next nil)) - (setq dct (decode-time (org-time-string-to-time (match-string 0))) - date (list (nth 4 dct) (nth 3 dct) (nth 5 dct)) - year (nth 2 date) - month (car date) - day (nth 1 date) - pos (point)) - (org-back-to-heading t) - (setq hdl-pos (point)) - (unless (org-up-heading-safe) - ;; No parent, we are not in a date tree - (goto-char pos) - (throw 'next nil)) - (unless (looking-at "\\*+[ \t]+[0-9]+-[0-1][0-9]-[0-3][0-9]") - ;; Parent looks wrong, we are not in a date tree - (goto-char pos) - (throw 'next nil)) - (when (looking-at (format "\\*+[ \t]+%d-%02d-%02d" year month day)) - ;; At correct date already, do nothing - (progn (goto-char pos) (throw 'next nil))) - ;; OK, we need to refile this entry - (goto-char hdl-pos) - (org-cut-subtree) - (save-excursion - (save-restriction - (org-datetree-file-entry-under (current-kill 0) date))))))) + (let* ((dct (decode-time (org-time-string-to-time (match-string 0)))) + (date (list (nth 4 dct) (nth 3 dct) (nth 5 dct))) + (year (nth 2 date)) + (month (car date)) + (day (nth 1 date)) + (pos (point)) + (hdl-pos (progn (org-back-to-heading t) (point)))) + (unless (org-up-heading-safe) + ;; No parent, we are not in a date tree. + (goto-char pos) + (throw 'next nil)) + (unless (looking-at "\\*+[ \t]+[0-9]+-[0-1][0-9]-[0-3][0-9]") + ;; Parent looks wrong, we are not in a date tree. + (goto-char pos) + (throw 'next nil)) + (when (looking-at (format "\\*+[ \t]+%d-%02d-%02d" year month day)) + ;; At correct date already, do nothing. + (goto-char pos) + (throw 'next nil)) + ;; OK, we need to refile this entry. + (goto-char hdl-pos) + (org-cut-subtree) + (save-excursion + (save-restriction + (org-datetree-file-entry-under (current-kill 0) date))))))))) (provide 'org-datetree) diff --git a/lisp/org/org-docview.el b/lisp/org/org-docview.el index c5d01158c9..dfad89332a 100644 --- a/lisp/org/org-docview.el +++ b/lisp/org/org-docview.el @@ -1,4 +1,4 @@ -;;; org-docview.el --- support for links to doc-view-mode buffers +;;; org-docview.el --- Support for links to doc-view-mode buffers -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -25,7 +25,7 @@ ;;; Commentary: ;; This file implements links to open files in doc-view-mode. -;; Org-mode loads this module by default - if this is not what you want, +;; Org mode loads this module by default - if this is not what you want, ;; configure the variable `org-modules'. ;; The links take the form @@ -49,13 +49,15 @@ (declare-function doc-view-goto-page "doc-view" (page)) (declare-function image-mode-window-get "image-mode" (prop &optional winprops)) -(org-add-link-type "docview" 'org-docview-open 'org-docview-export) -(add-hook 'org-store-link-functions 'org-docview-store-link) +(org-link-set-parameters "docview" + :follow #'org-docview-open + :export #'org-docview-export + :store #'org-docview-store-link) (defun org-docview-export (link description format) "Export a docview link from Org files." - (let* ((path (when (string-match "\\(.+\\)::.+" link) - (match-string 1 link))) + (let* ((path (if (string-match "\\(.+\\)::.+" link) (match-string 1 link) + link)) (desc (or description link))) (when (stringp path) (setq path (org-link-escape (expand-file-name path))) @@ -66,13 +68,14 @@ (t path))))) (defun org-docview-open (link) - (when (string-match "\\(.*\\)::\\([0-9]+\\)$" link) - (let* ((path (match-string 1 link)) - (page (string-to-number (match-string 2 link)))) - (org-open-file path 1) ;; let org-mode open the file (in-emacs = 1) - ;; to ensure org-link-frame-setup is respected - (doc-view-goto-page page) - ))) + (string-match "\\(.*?\\)\\(?:::\\([0-9]+\\)\\)?$" link) + (let ((path (match-string 1 link)) + (page (and (match-beginning 2) + (string-to-number (match-string 2 link))))) + ;; Let Org mode open the file (in-emacs = 1) to ensure + ;; org-link-frame-setup is respected. + (org-open-file path 1) + (when page (doc-view-goto-page page)))) (defun org-docview-store-link () "Store a link to a docview buffer." @@ -80,8 +83,7 @@ ;; This buffer is in doc-view-mode (let* ((path buffer-file-name) (page (image-mode-window-get 'page)) - (link (concat "docview:" path "::" (number-to-string page))) - (description "")) + (link (concat "docview:" path "::" (number-to-string page)))) (org-store-link-props :type "docview" :link link diff --git a/lisp/org/org-element.el b/lisp/org/org-element.el index e9731c1783..41b4a3ac78 100644 --- a/lisp/org/org-element.el +++ b/lisp/org/org-element.el @@ -1,4 +1,4 @@ -;;; org-element.el --- Parser And Applications for Org syntax +;;; org-element.el --- Parser for Org Syntax -*- lexical-binding: t; -*- ;; Copyright (C) 2012-2017 Free Software Foundation, Inc. @@ -22,80 +22,21 @@ ;;; Commentary: ;; -;; Org syntax can be divided into three categories: "Greater -;; elements", "Elements" and "Objects". +;; See for details about +;; Org syntax. ;; -;; Elements are related to the structure of the document. Indeed, all -;; elements are a cover for the document: each position within belongs -;; to at least one element. -;; -;; An element always starts and ends at the beginning of a line. With -;; a few exceptions (`clock', `headline', `inlinetask', `item', -;; `planning', `node-property', `quote-section' `section' and -;; `table-row' types), it can also accept a fixed set of keywords as -;; attributes. Those are called "affiliated keywords" to distinguish -;; them from other keywords, which are full-fledged elements. Almost -;; all affiliated keywords are referenced in -;; `org-element-affiliated-keywords'; the others are export attributes -;; and start with "ATTR_" prefix. -;; -;; Element containing other elements (and only elements) are called -;; greater elements. Concerned types are: `center-block', `drawer', -;; `dynamic-block', `footnote-definition', `headline', `inlinetask', -;; `item', `plain-list', `property-drawer', `quote-block', `section' -;; and `special-block'. -;; -;; Other element types are: `babel-call', `clock', `comment', -;; `comment-block', `diary-sexp', `example-block', `export-block', -;; `fixed-width', `horizontal-rule', `keyword', `latex-environment', -;; `node-property', `paragraph', `planning', `quote-section', -;; `src-block', `table', `table-row' and `verse-block'. Among them, -;; `paragraph' and `verse-block' types can contain Org objects and -;; plain text. -;; -;; Objects are related to document's contents. Some of them are -;; recursive. Associated types are of the following: `bold', `code', -;; `entity', `export-snippet', `footnote-reference', -;; `inline-babel-call', `inline-src-block', `italic', -;; `latex-fragment', `line-break', `link', `macro', `radio-target', -;; `statistics-cookie', `strike-through', `subscript', `superscript', -;; `table-cell', `target', `timestamp', `underline' and `verbatim'. -;; -;; Some elements also have special properties whose value can hold -;; objects themselves (e.g. an item tag or a headline name). Such -;; values are called "secondary strings". Any object belongs to -;; either an element or a secondary string. -;; -;; Notwithstanding affiliated keywords, each greater element, element -;; and object has a fixed set of properties attached to it. Among -;; them, four are shared by all types: `:begin' and `:end', which -;; refer to the beginning and ending buffer positions of the -;; considered element or object, `:post-blank', which holds the number -;; of blank lines, or white spaces, at its end and `:parent' which -;; refers to the element or object containing it. Greater elements, -;; elements and objects containing objects will also have -;; `:contents-begin' and `:contents-end' properties to delimit -;; contents. Eventually, greater elements and elements accepting -;; affiliated keywords will have a `:post-affiliated' property, -;; referring to the buffer position after all such keywords. -;; -;; At the lowest level, a `:parent' property is also attached to any -;; string, as a text property. -;; -;; Lisp-wise, an element or an object can be represented as a list. +;; Lisp-wise, a syntax object can be represented as a list. ;; It follows the pattern (TYPE PROPERTIES CONTENTS), where: -;; TYPE is a symbol describing the Org element or object. +;; TYPE is a symbol describing the object. ;; PROPERTIES is the property list attached to it. See docstring of -;; appropriate parsing function to get an exhaustive -;; list. -;; CONTENTS is a list of elements, objects or raw strings contained -;; in the current element or object, when applicable. +;; appropriate parsing function to get an exhaustive list. +;; CONTENTS is a list of syntax objects or raw strings contained +;; in the current object, when applicable. ;; -;; An Org buffer is a nested list of such elements and objects, whose -;; type is `org-data' and properties is nil. +;; For the whole document, TYPE is `org-data' and PROPERTIES is nil. ;; -;; The first part of this file defines Org syntax, while the second -;; one provide accessors and setters functions. +;; The first part of this file defines constants for the Org syntax, +;; while the second one provide accessors and setters functions. ;; ;; The next part implements a parser and an interpreter for each ;; element and object type in Org syntax. @@ -111,13 +52,15 @@ ;; ;; The library ends by furnishing `org-element-at-point' function, and ;; a way to give information about document structure around point -;; with `org-element-context'. +;; with `org-element-context'. A cache mechanism is also provided for +;; these functions. ;;; Code: -(eval-when-compile (require 'cl)) (require 'org) +(require 'avl-tree) +(require 'cl-lib) @@ -127,56 +70,116 @@ ;; along with the affiliated keywords recognized. Also set up ;; restrictions on recursive objects combinations. ;; -;; These variables really act as a control center for the parsing -;; process. - -(defconst org-element-paragraph-separate - (concat "^\\(?:" - ;; Headlines, inlinetasks. - org-outline-regexp "\\|" - ;; Footnote definitions. - "\\[\\(?:[0-9]+\\|fn:[-_[:word:]]+\\)\\]" "\\|" - ;; Diary sexps. - "%%(" "\\|" - "[ \t]*\\(?:" - ;; Empty lines. - "$" "\\|" - ;; Tables (any type). - "\\(?:|\\|\\+-[-+]\\)" "\\|" - ;; Blocks (any type), Babel calls and keywords. Note: this - ;; is only an indication and need some thorough check. - "#\\(?:[+ ]\\|$\\)" "\\|" - ;; Drawers (any type) and fixed-width areas. This is also - ;; only an indication. - ":" "\\|" - ;; Horizontal rules. - "-\\{5,\\}[ \t]*$" "\\|" - ;; LaTeX environments. - "\\\\begin{\\([A-Za-z0-9]+\\*?\\)}" "\\|" - ;; Planning and Clock lines. - (regexp-opt (list org-scheduled-string - org-deadline-string - org-closed-string - org-clock-string)) - "\\|" - ;; Lists. - (let ((term (case org-plain-list-ordered-item-terminator - (?\) ")") (?. "\\.") (otherwise "[.)]"))) - (alpha (and org-list-allow-alphabetical "\\|[A-Za-z]"))) - (concat "\\(?:[-+*]\\|\\(?:[0-9]+" alpha "\\)" term "\\)" - "\\(?:[ \t]\\|$\\)")) - "\\)\\)") +;; `org-element-update-syntax' builds proper syntax regexps according +;; to current setup. + +(defvar org-element-paragraph-separate nil "Regexp to separate paragraphs in an Org buffer. In the case of lines starting with \"#\" and \":\", this regexp is not sufficient to know if point is at a paragraph ending. See `org-element-paragraph-parser' for more information.") +(defvar org-element--object-regexp nil + "Regexp possibly matching the beginning of an object. +This regexp allows false positives. Dedicated parser (e.g., +`org-export-bold-parser') will take care of further filtering. +Radio links are not matched by this regexp, as they are treated +specially in `org-element--object-lex'.") + +(defun org-element--set-regexps () + "Build variable syntax regexps." + (setq org-element-paragraph-separate + (concat "^\\(?:" + ;; Headlines, inlinetasks. + org-outline-regexp "\\|" + ;; Footnote definitions. + "\\[fn:[-_[:word:]]+\\]" "\\|" + ;; Diary sexps. + "%%(" "\\|" + "[ \t]*\\(?:" + ;; Empty lines. + "$" "\\|" + ;; Tables (any type). + "|" "\\|" + "\\+\\(?:-+\\+\\)+[ \t]*$" "\\|" + ;; Comments, keyword-like or block-like constructs. + ;; Blocks and keywords with dual values need to be + ;; double-checked. + "#\\(?: \\|$\\|\\+\\(?:" + "BEGIN_\\S-+" "\\|" + "\\S-+\\(?:\\[.*\\]\\)?:[ \t]*\\)\\)" + "\\|" + ;; Drawers (any type) and fixed-width areas. Drawers + ;; need to be double-checked. + ":\\(?: \\|$\\|[-_[:word:]]+:[ \t]*$\\)" "\\|" + ;; Horizontal rules. + "-\\{5,\\}[ \t]*$" "\\|" + ;; LaTeX environments. + "\\\\begin{\\([A-Za-z0-9*]+\\)}" "\\|" + ;; Clock lines. + (regexp-quote org-clock-string) "\\|" + ;; Lists. + (let ((term (pcase org-plain-list-ordered-item-terminator + (?\) ")") (?. "\\.") (_ "[.)]"))) + (alpha (and org-list-allow-alphabetical "\\|[A-Za-z]"))) + (concat "\\(?:[-+*]\\|\\(?:[0-9]+" alpha "\\)" term "\\)" + "\\(?:[ \t]\\|$\\)")) + "\\)\\)") + org-element--object-regexp + (mapconcat #'identity + (let ((link-types (regexp-opt (org-link-types)))) + (list + ;; Sub/superscript. + "\\(?:[_^][-{(*+.,[:alnum:]]\\)" + ;; Bold, code, italic, strike-through, underline + ;; and verbatim. + (concat "[*~=+_/]" + (format "[^%s]" + (nth 2 org-emphasis-regexp-components))) + ;; Plain links. + (concat "\\<" link-types ":") + ;; Objects starting with "[": regular link, + ;; footnote reference, statistics cookie, + ;; timestamp (inactive). + (concat "\\[\\(?:" + "fn:" "\\|" + "\\[" "\\|" + "[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" "\\|" + "[0-9]*\\(?:%\\|/[0-9]*\\)\\]" + "\\)") + ;; Objects starting with "@": export snippets. + "@@" + ;; Objects starting with "{": macro. + "{{{" + ;; Objects starting with "<" : timestamp + ;; (active, diary), target, radio target and + ;; angular links. + (concat "<\\(?:%%\\|<\\|[0-9]\\|" link-types "\\)") + ;; Objects starting with "$": latex fragment. + "\\$" + ;; Objects starting with "\": line break, + ;; entity, latex fragment. + "\\\\\\(?:[a-zA-Z[(]\\|\\\\[ \t]*$\\|_ +\\)" + ;; Objects starting with raw text: inline Babel + ;; source block, inline Babel call. + "\\(?:call\\|src\\)_")) + "\\|"))) + +(org-element--set-regexps) + +;;;###autoload +(defun org-element-update-syntax () + "Update parser internals." + (interactive) + (org-element--set-regexps) + (org-element-cache-reset 'all)) + (defconst org-element-all-elements '(babel-call center-block clock comment comment-block diary-sexp drawer dynamic-block example-block export-block fixed-width footnote-definition headline horizontal-rule inlinetask item keyword latex-environment node-property paragraph plain-list - planning property-drawer quote-block quote-section section + planning property-drawer quote-block section special-block src-block table table-row verse-block) "Complete list of element types.") @@ -186,23 +189,6 @@ is not sufficient to know if point is at a paragraph ending. See special-block table) "List of recursive element types aka Greater Elements.") -(defconst org-element-all-successors - '(link export-snippet footnote-reference inline-babel-call - inline-src-block latex-or-entity line-break macro plain-link - radio-target statistics-cookie sub/superscript table-cell target - text-markup timestamp) - "Complete list of successors.") - -(defconst org-element-object-successor-alist - '((subscript . sub/superscript) (superscript . sub/superscript) - (bold . text-markup) (code . text-markup) (italic . text-markup) - (strike-through . text-markup) (underline . text-markup) - (verbatim . text-markup) (entity . latex-or-entity) - (latex-fragment . latex-or-entity)) - "Alist of translations between object type and successor name. -Sharing the same successor comes handy when, for example, the -regexp matching one object can also match the other object.") - (defconst org-element-all-objects '(bold code entity export-snippet footnote-reference inline-babel-call inline-src-block italic line-break latex-fragment link macro @@ -211,26 +197,13 @@ regexp matching one object can also match the other object.") "Complete list of object types.") (defconst org-element-recursive-objects - '(bold italic link subscript radio-target strike-through superscript - table-cell underline) + '(bold footnote-reference italic link subscript radio-target strike-through + superscript table-cell underline) "List of recursive object types.") -(defvar org-element-block-name-alist - '(("CENTER" . org-element-center-block-parser) - ("COMMENT" . org-element-comment-block-parser) - ("EXAMPLE" . org-element-example-block-parser) - ("QUOTE" . org-element-quote-block-parser) - ("SRC" . org-element-src-block-parser) - ("VERSE" . org-element-verse-block-parser)) - "Alist between block names and the associated parsing function. -Names must be uppercase. Any block whose name has no association -is parsed with `org-element-special-block-parser'.") - -(defconst org-element-link-type-is-file - '("file" "file+emacs" "file+sys" "docview") - "List of link types equivalent to \"file\". -Only these types can accept search options and an explicit -application to open them.") +(defconst org-element-object-containers + (append org-element-recursive-objects '(paragraph table-row verse-block)) + "List of object or element types that can directly contain objects.") (defconst org-element-affiliated-keywords '("CAPTION" "DATA" "HEADER" "HEADERS" "LABEL" "NAME" "PLOT" "RESNAME" "RESULT" @@ -268,6 +241,13 @@ strings and objects. This list is checked after translations have been applied. See `org-element-keyword-translation-alist'.") +(defconst org-element--parsed-properties-alist + (mapcar (lambda (k) (cons k (intern (concat ":" (downcase k))))) + org-element-parsed-keywords) + "Alist of parsed keywords and associated properties. +This is generated from `org-element-parsed-keywords', which +see.") + (defconst org-element-dual-keywords '("CAPTION" "RESULTS") "List of affiliated keywords which can have a secondary value. @@ -280,13 +260,8 @@ associated to a hash value with the following: This list is checked after translations have been applied. See `org-element-keyword-translation-alist'.") -(defconst org-element-document-properties '("AUTHOR" "DATE" "TITLE") - "List of properties associated to the whole document. -Any keyword in this list will have its value parsed and stored as -a secondary string.") - (defconst org-element--affiliated-re - (format "[ \t]*#\\+\\(?:%s\\):\\(?: \\|$\\)" + (format "[ \t]*#\\+\\(?:%s\\):[ \t]*" (concat ;; Dual affiliated keywords. (format "\\(?1:%s\\)\\(?:\\[\\(.*\\)\\]\\)?" @@ -295,9 +270,8 @@ a secondary string.") ;; Regular affiliated keywords. (format "\\(?1:%s\\)" (regexp-opt - (org-remove-if - #'(lambda (keyword) - (member keyword org-element-dual-keywords)) + (cl-remove-if + (lambda (k) (member k org-element-dual-keywords)) org-element-affiliated-keywords))) "\\|" ;; Export attributes. @@ -311,8 +285,7 @@ match group 2. Don't modify it, set `org-element-affiliated-keywords' instead.") (defconst org-element-object-restrictions - (let* ((standard-set - (remq 'plain-link (remq 'table-cell org-element-all-successors))) + (let* ((standard-set (remq 'table-cell org-element-all-objects)) (standard-set-no-line-break (remq 'line-break standard-set))) `((bold ,@standard-set) (footnote-reference ,@standard-set) @@ -320,30 +293,34 @@ Don't modify it, set `org-element-affiliated-keywords' instead.") (inlinetask ,@standard-set-no-line-break) (italic ,@standard-set) (item ,@standard-set-no-line-break) - (keyword ,@standard-set) - ;; Ignore all links excepted plain links in a link description. - ;; Also ignore radio-targets and line breaks. - (link export-snippet inline-babel-call inline-src-block latex-or-entity - macro plain-link statistics-cookie sub/superscript text-markup) + (keyword ,@(remq 'footnote-reference standard-set)) + ;; Ignore all links excepted plain links and angular links in + ;; a link description. Also ignore radio-targets and line + ;; breaks. + (link bold code entity export-snippet inline-babel-call inline-src-block + italic latex-fragment macro simple-link statistics-cookie + strike-through subscript superscript underline verbatim) (paragraph ,@standard-set) ;; Remove any variable object from radio target as it would ;; prevent it from being properly recognized. - (radio-target latex-or-entity sub/superscript text-markup) + (radio-target bold code entity italic latex-fragment strike-through + subscript superscript underline superscript) (strike-through ,@standard-set) (subscript ,@standard-set) (superscript ,@standard-set) ;; Ignore inline babel call and inline src block as formulas are ;; possible. Also ignore line breaks and statistics cookies. - (table-cell link export-snippet footnote-reference latex-or-entity macro - radio-target sub/superscript target text-markup timestamp) + (table-cell bold code entity export-snippet footnote-reference italic + latex-fragment link macro radio-target strike-through + subscript superscript target timestamp underline verbatim) (table-row table-cell) (underline ,@standard-set) (verse-block ,@standard-set))) "Alist of objects restrictions. -CAR is an element or object type containing objects and CDR is -a list of successors that will be called within an element or -object of such type. +key is an element or object type containing objects and value is +a list of types that can be contained within an element or object +of such type. For example, in a `radio-target' object, one can only find entities, latex-fragments, subscript, superscript and text @@ -354,12 +331,56 @@ This alist also applies to secondary string. For example, an still has an entry since one of its properties (`:title') does.") (defconst org-element-secondary-value-alist - '((headline . :title) - (inlinetask . :title) - (item . :tag) - (footnote-reference . :inline-definition)) - "Alist between element types and location of secondary value.") - + '((headline :title) + (inlinetask :title) + (item :tag)) + "Alist between element types and locations of secondary values.") + +(defconst org-element--pair-round-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?\( "()" table) + (modify-syntax-entry ?\) ")(" table) + (dolist (char '(?\{ ?\} ?\[ ?\] ?\< ?\>) table) + (modify-syntax-entry char " " table))) + "Table used internally to pair only round brackets. +Other brackets are treated as spaces.") + +(defconst org-element--pair-square-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?\[ "(]" table) + (modify-syntax-entry ?\] ")[" table) + (dolist (char '(?\{ ?\} ?\( ?\) ?\< ?\>) table) + (modify-syntax-entry char " " table))) + "Table used internally to pair only square brackets. +Other brackets are treated as spaces.") + +(defconst org-element--pair-curly-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?\{ "(}" table) + (modify-syntax-entry ?\} "){" table) + (dolist (char '(?\[ ?\] ?\( ?\) ?\< ?\>) table) + (modify-syntax-entry char " " table))) + "Table used internally to pair only curly brackets. +Other brackets are treated as spaces.") + +(defun org-element--parse-paired-brackets (char) + "Parse paired brackets at point. +CHAR is the opening bracket to consider, as a character. Return +contents between brackets, as a string, or nil. Also move point +past the brackets." + (when (eq char (char-after)) + (let ((syntax-table (pcase char + (?\{ org-element--pair-curly-table) + (?\[ org-element--pair-square-table) + (?\( org-element--pair-round-table) + (_ nil))) + (pos (point))) + (when syntax-table + (with-syntax-table syntax-table + (let ((end (ignore-errors (scan-lists pos 1 0)))) + (when end + (goto-char end) + (buffer-substring-no-properties (1+ pos) (1- end))))))))) ;;; Accessors and Setters @@ -368,10 +389,18 @@ still has an entry since one of its properties (`:title') does.") ;; `org-element-contents' and `org-element-restriction'. ;; ;; Setter functions allow modification of elements by side effect. -;; There is `org-element-put-property', `org-element-set-contents', -;; `org-element-set-element' and `org-element-adopt-element'. Note -;; that `org-element-set-element' and `org-element-adopt-elements' are -;; higher level functions since also update `:parent' property. +;; There is `org-element-put-property', `org-element-set-contents'. +;; These low-level functions are useful to build a parse tree. +;; +;; `org-element-adopt-elements', `org-element-set-element', +;; `org-element-extract-element' and `org-element-insert-before' are +;; high-level functions useful to modify a parse tree. +;; +;; `org-element-secondary-p' is a predicate used to know if a given +;; object belongs to a secondary string. `org-element-class' tells if +;; some parsed data is an element or an object, handling pseudo +;; elements and objects. `org-element-copy' returns an element or +;; object, stripping its parent property in the process. (defsubst org-element-type (element) "Return type of ELEMENT. @@ -411,29 +440,49 @@ Return modified element." element)) (defsubst org-element-set-contents (element &rest contents) - "Set ELEMENT contents to CONTENTS. -Return modified element." - (cond ((not element) (list contents)) + "Set ELEMENT's contents to CONTENTS. +Return ELEMENT." + (cond ((null element) contents) ((not (symbolp (car element))) contents) - ((cdr element) (setcdr (cdr element) contents)) + ((cdr element) (setcdr (cdr element) contents) element) (t (nconc element contents)))) -(defsubst org-element-set-element (old new) - "Replace element or object OLD with element or object NEW. -The function takes care of setting `:parent' property for NEW." - ;; Since OLD is going to be changed into NEW by side-effect, first - ;; make sure that every element or object within NEW has OLD as - ;; parent. - (mapc (lambda (blob) (org-element-put-property blob :parent old)) - (org-element-contents new)) - ;; Transfer contents. - (apply 'org-element-set-contents old (org-element-contents new)) - ;; Ensure NEW has same parent as OLD, then overwrite OLD properties - ;; with NEW's. - (org-element-put-property new :parent (org-element-property :parent old)) - (setcar (cdr old) (nth 1 new)) - ;; Transfer type. - (setcar old (car new))) +(defun org-element-secondary-p (object) + "Non-nil when OBJECT directly belongs to a secondary string. +Return value is the property name, as a keyword, or nil." + (let* ((parent (org-element-property :parent object)) + (properties (cdr (assq (org-element-type parent) + org-element-secondary-value-alist)))) + (catch 'exit + (dolist (p properties) + (and (memq object (org-element-property p parent)) + (throw 'exit p)))))) + +(defun org-element-class (datum &optional parent) + "Return class for ELEMENT, as a symbol. +Class is either `element' or `object'. Optional argument PARENT +is the element or object containing DATUM. It defaults to the +value of DATUM `:parent' property." + (let ((type (org-element-type datum)) + (parent (or parent (org-element-property :parent datum)))) + (cond + ;; Trivial cases. + ((memq type org-element-all-objects) 'object) + ((memq type org-element-all-elements) 'element) + ;; Special cases. + ((eq type 'org-data) 'element) + ((eq type 'plain-text) 'object) + ((not type) 'object) + ;; Pseudo object or elements. Make a guess about its class. + ;; Basically a pseudo object is contained within another object, + ;; a secondary string or a container element. + ((not parent) 'element) + (t + (let ((parent-type (org-element-type parent))) + (cond ((not parent-type) 'object) + ((memq parent-type org-element-object-containers) 'object) + ((org-element-secondary-p datum) 'object) + (t 'element))))))) (defsubst org-element-adopt-elements (parent &rest children) "Append elements to the contents of another element. @@ -443,18 +492,108 @@ objects, or a strings. The function takes care of setting `:parent' property for CHILD. Return parent element." - ;; Link every child to PARENT. If PARENT is nil, it is a secondary - ;; string: parent is the list itself. - (mapc (lambda (child) - (org-element-put-property child :parent (or parent children))) - children) - ;; Add CHILDREN at the end of PARENT contents. - (when parent - (apply 'org-element-set-contents - parent - (nconc (org-element-contents parent) children))) - ;; Return modified PARENT element. - (or parent children)) + (if (not children) parent + ;; Link every child to PARENT. If PARENT is nil, it is a secondary + ;; string: parent is the list itself. + (dolist (child children) + (org-element-put-property child :parent (or parent children))) + ;; Add CHILDREN at the end of PARENT contents. + (when parent + (apply #'org-element-set-contents + parent + (nconc (org-element-contents parent) children))) + ;; Return modified PARENT element. + (or parent children))) + +(defun org-element-extract-element (element) + "Extract ELEMENT from parse tree. +Remove element from the parse tree by side-effect, and return it +with its `:parent' property stripped out." + (let ((parent (org-element-property :parent element)) + (secondary (org-element-secondary-p element))) + (if secondary + (org-element-put-property + parent secondary + (delq element (org-element-property secondary parent))) + (apply #'org-element-set-contents + parent + (delq element (org-element-contents parent)))) + ;; Return ELEMENT with its :parent removed. + (org-element-put-property element :parent nil))) + +(defun org-element-insert-before (element location) + "Insert ELEMENT before LOCATION in parse tree. +LOCATION is an element, object or string within the parse tree. +Parse tree is modified by side effect." + (let* ((parent (org-element-property :parent location)) + (property (org-element-secondary-p location)) + (siblings (if property (org-element-property property parent) + (org-element-contents parent))) + ;; Special case: LOCATION is the first element of an + ;; independent secondary string (e.g. :title property). Add + ;; ELEMENT in-place. + (specialp (and (not property) + (eq siblings parent) + (eq (car parent) location)))) + ;; Install ELEMENT at the appropriate LOCATION within SIBLINGS. + (cond (specialp) + ((or (null siblings) (eq (car siblings) location)) + (push element siblings)) + ((null location) (nconc siblings (list element))) + (t + (let ((index (cl-position location siblings))) + (unless index (error "No location found to insert element")) + (push element (cdr (nthcdr (1- index) siblings)))))) + ;; Store SIBLINGS at appropriate place in parse tree. + (cond + (specialp (setcdr parent (copy-sequence parent)) (setcar parent element)) + (property (org-element-put-property parent property siblings)) + (t (apply #'org-element-set-contents parent siblings))) + ;; Set appropriate :parent property. + (org-element-put-property element :parent parent))) + +(defun org-element-set-element (old new) + "Replace element or object OLD with element or object NEW. +The function takes care of setting `:parent' property for NEW." + ;; Ensure OLD and NEW have the same parent. + (org-element-put-property new :parent (org-element-property :parent old)) + (if (or (memq (org-element-type old) '(plain-text nil)) + (memq (org-element-type new) '(plain-text nil))) + ;; We cannot replace OLD with NEW since one of them is not an + ;; object or element. We take the long path. + (progn (org-element-insert-before new old) + (org-element-extract-element old)) + ;; Since OLD is going to be changed into NEW by side-effect, first + ;; make sure that every element or object within NEW has OLD as + ;; parent. + (dolist (blob (org-element-contents new)) + (org-element-put-property blob :parent old)) + ;; Transfer contents. + (apply #'org-element-set-contents old (org-element-contents new)) + ;; Overwrite OLD's properties with NEW's. + (setcar (cdr old) (nth 1 new)) + ;; Transfer type. + (setcar old (car new)))) + +(defun org-element-create (type &optional props &rest children) + "Create a new element of type TYPE. +Optional argument PROPS, when non-nil, is a plist defining the +properties of the element. CHILDREN can be elements, objects or +strings." + (apply #'org-element-adopt-elements (list type props) children)) + +(defun org-element-copy (datum) + "Return a copy of DATUM. +DATUM is an element, object, string or nil. `:parent' property +is cleared and contents are removed in the process." + (when datum + (let ((type (org-element-type datum))) + (pcase type + (`org-data (list 'org-data nil)) + (`plain-text (substring-no-properties datum)) + (`nil (copy-sequence datum)) + (_ + (list type (plist-put (copy-sequence (nth 1 datum)) :parent nil))))))) @@ -467,7 +606,7 @@ Return parent element." ;; Most of them accepts no argument. Though, exceptions exist. Hence ;; every element containing a secondary string (see ;; `org-element-secondary-value-alist') will accept an optional -;; argument to toggle parsing of that secondary string. Moreover, +;; argument to toggle parsing of these secondary strings. Moreover, ;; `item' parser requires current list's structure as its first ;; element. ;; @@ -503,8 +642,8 @@ keyword and CDR is a plist of affiliated keywords along with their value. Return a list whose CAR is `center-block' and CDR is a plist -containing `:begin', `:end', `:hiddenp', `:contents-begin', -`:contents-end', `:post-blank' and `:post-affiliated' keywords. +containing `:begin', `:end', `:contents-begin', `:contents-end', +`:post-blank' and `:post-affiliated' keywords. Assume point is at the beginning of the block." (let ((case-fold-search t)) @@ -520,7 +659,6 @@ Assume point is at the beginning of the block." (and (< (point) block-end-line) (point)))) (contents-end (and contents-begin block-end-line)) - (hidden (org-invisible-p2)) (pos-before-blank (progn (goto-char block-end-line) (forward-line) (point))) @@ -531,15 +669,14 @@ Assume point is at the beginning of the block." (nconc (list :begin begin :end end - :hiddenp hidden :contents-begin contents-begin :contents-end contents-end :post-blank (count-lines pos-before-blank end) :post-affiliated post-affiliated) (cdr affiliated)))))))) -(defun org-element-center-block-interpreter (center-block contents) - "Interpret CENTER-BLOCK element as Org syntax. +(defun org-element-center-block-interpreter (_ contents) + "Interpret a center-block element as Org syntax. CONTENTS is the contents of the element." (format "#+BEGIN_CENTER\n%s#+END_CENTER" contents)) @@ -555,7 +692,7 @@ keyword and CDR is a plist of affiliated keywords along with their value. Return a list whose CAR is `drawer' and CDR is a plist containing -`:drawer-name', `:begin', `:end', `:hiddenp', `:contents-begin', +`:drawer-name', `:begin', `:end', `:contents-begin', `:contents-end', `:post-blank' and `:post-affiliated' keywords. Assume point is at beginning of drawer." @@ -566,7 +703,7 @@ Assume point is at beginning of drawer." (save-excursion (let* ((drawer-end-line (match-beginning 0)) (name (progn (looking-at org-drawer-regexp) - (org-match-string-no-properties 1))) + (match-string-no-properties 1))) (begin (car affiliated)) (post-affiliated (point)) ;; Empty drawers have no contents. @@ -574,7 +711,6 @@ Assume point is at beginning of drawer." (and (< (point) drawer-end-line) (point)))) (contents-end (and contents-begin drawer-end-line)) - (hidden (org-invisible-p2)) (pos-before-blank (progn (goto-char drawer-end-line) (forward-line) (point))) @@ -585,7 +721,6 @@ Assume point is at beginning of drawer." (list :begin begin :end end :drawer-name name - :hiddenp hidden :contents-begin contents-begin :contents-end contents-end :post-blank (count-lines pos-before-blank end) @@ -611,9 +746,9 @@ keyword and CDR is a plist of affiliated keywords along with their value. Return a list whose CAR is `dynamic-block' and CDR is a plist -containing `:block-name', `:begin', `:end', `:hiddenp', -`:contents-begin', `:contents-end', `:arguments', `:post-blank' -and `:post-affiliated' keywords. +containing `:block-name', `:begin', `:end', `:contents-begin', +`:contents-end', `:arguments', `:post-blank' and +`:post-affiliated' keywords. Assume point is at beginning of dynamic block." (let ((case-fold-search t)) @@ -624,8 +759,8 @@ Assume point is at beginning of dynamic block." (let ((block-end-line (match-beginning 0))) (save-excursion (let* ((name (progn (looking-at org-dblock-start-re) - (org-match-string-no-properties 1))) - (arguments (org-match-string-no-properties 3)) + (match-string-no-properties 1))) + (arguments (match-string-no-properties 3)) (begin (car affiliated)) (post-affiliated (point)) ;; Empty blocks have no contents. @@ -633,7 +768,6 @@ Assume point is at beginning of dynamic block." (and (< (point) block-end-line) (point)))) (contents-end (and contents-begin block-end-line)) - (hidden (org-invisible-p2)) (pos-before-blank (progn (goto-char block-end-line) (forward-line) (point))) @@ -645,7 +779,6 @@ Assume point is at beginning of dynamic block." :end end :block-name name :arguments arguments - :hiddenp hidden :contents-begin contents-begin :contents-end contents-end :post-blank (count-lines pos-before-blank end) @@ -658,12 +791,18 @@ CONTENTS is the contents of the element." (format "#+BEGIN: %s%s\n%s#+END:" (org-element-property :block-name dynamic-block) (let ((args (org-element-property :arguments dynamic-block))) - (and args (concat " " args))) + (if args (concat " " args) "")) contents)) ;;;; Footnote Definition +(defconst org-element--footnote-separator + (concat org-outline-regexp-bol "\\|" + org-footnote-definition-re "\\|" + "^\\([ \t]*\n\\)\\{2,\\}") + "Regexp used as a footnote definition separator.") + (defun org-element-footnote-definition-parser (limit affiliated) "Parse a footnote definition. @@ -679,59 +818,104 @@ a plist containing `:label', `:begin' `:end', `:contents-begin', Assume point is at the beginning of the footnote definition." (save-excursion (let* ((label (progn (looking-at org-footnote-definition-re) - (org-match-string-no-properties 1))) + (match-string-no-properties 1))) (begin (car affiliated)) (post-affiliated (point)) - (ending (save-excursion - (if (progn - (end-of-line) - (re-search-forward - (concat org-outline-regexp-bol "\\|" - org-footnote-definition-re "\\|" - "^\\([ \t]*\n\\)\\{2,\\}") limit 'move)) - (match-beginning 0) - (point)))) - (contents-begin (progn - (search-forward "]") - (skip-chars-forward " \r\t\n" ending) - (cond ((= (point) ending) nil) - ((= (line-beginning-position) begin) (point)) - (t (line-beginning-position))))) - (contents-end (and contents-begin ending)) - (end (progn (goto-char ending) - (skip-chars-forward " \r\t\n" limit) - (if (eobp) (point) (line-beginning-position))))) + (end + (save-excursion + (end-of-line) + (cond + ((not + (re-search-forward org-element--footnote-separator limit t)) + limit) + ((eq ?\[ (char-after (match-beginning 0))) + ;; At a new footnote definition, make sure we end + ;; before any affiliated keyword above. + (forward-line -1) + (while (and (> (point) post-affiliated) + (looking-at-p org-element--affiliated-re)) + (forward-line -1)) + (line-beginning-position 2)) + ((eq ?* (char-after (match-beginning 0))) (match-beginning 0)) + (t (skip-chars-forward " \r\t\n" limit) + (if (= limit (point)) limit (line-beginning-position)))))) + (contents-begin + (progn (search-forward "]") + (skip-chars-forward " \r\t\n" end) + (cond ((= (point) end) nil) + ((= (line-beginning-position) post-affiliated) (point)) + (t (line-beginning-position))))) + (contents-end + (progn (goto-char end) + (skip-chars-backward " \r\t\n") + (line-beginning-position 2)))) (list 'footnote-definition (nconc (list :label label :begin begin :end end :contents-begin contents-begin - :contents-end contents-end - :post-blank (count-lines ending end) + :contents-end (and contents-begin contents-end) + :post-blank (count-lines contents-end end) :post-affiliated post-affiliated) (cdr affiliated)))))) (defun org-element-footnote-definition-interpreter (footnote-definition contents) "Interpret FOOTNOTE-DEFINITION element as Org syntax. CONTENTS is the contents of the footnote-definition." - (concat (format "[%s]" (org-element-property :label footnote-definition)) + (concat (format "[fn:%s]" (org-element-property :label footnote-definition)) " " contents)) ;;;; Headline +(defun org-element--get-node-properties () + "Return node properties associated to headline at point. +Upcase property names. It avoids confusion between properties +obtained through property drawer and default properties from the +parser (e.g. `:end' and :END:). Return value is a plist." + (save-excursion + (forward-line) + (when (looking-at-p org-planning-line-re) (forward-line)) + (when (looking-at org-property-drawer-re) + (forward-line) + (let ((end (match-end 0)) properties) + (while (< (line-end-position) end) + (looking-at org-property-re) + (push (match-string-no-properties 3) properties) + (push (intern (concat ":" (upcase (match-string 2)))) properties) + (forward-line)) + properties)))) + +(defun org-element--get-time-properties () + "Return time properties associated to headline at point. +Return value is a plist." + (save-excursion + (when (progn (forward-line) (looking-at org-planning-line-re)) + (let ((end (line-end-position)) plist) + (while (re-search-forward org-keyword-time-not-clock-regexp end t) + (goto-char (match-end 1)) + (skip-chars-forward " \t") + (let ((keyword (match-string 1)) + (time (org-element-timestamp-parser))) + (cond ((equal keyword org-scheduled-string) + (setq plist (plist-put plist :scheduled time))) + ((equal keyword org-deadline-string) + (setq plist (plist-put plist :deadline time))) + (t (setq plist (plist-put plist :closed time)))))) + plist)))) + (defun org-element-headline-parser (limit &optional raw-secondary-p) "Parse a headline. Return a list whose CAR is `headline' and CDR is a plist -containing `:raw-value', `:title', `:alt-title', `:begin', -`:end', `:pre-blank', `:hiddenp', `:contents-begin', -`:contents-end', `:level', `:priority', `:tags', -`:todo-keyword',`:todo-type', `:scheduled', `:deadline', -`:closed', `:quotedp', `:archivedp', `:commentedp', -`:footnote-section-p' and `:post-blank' keywords. +containing `:raw-value', `:title', `:begin', `:end', +`:pre-blank', `:contents-begin' and `:contents-end', `:level', +`:priority', `:tags', `:todo-keyword',`:todo-type', `:scheduled', +`:deadline', `:closed', `:archivedp', `:commentedp' +`:footnote-section-p', `:post-blank' and `:post-affiliated' +keywords. The plist also contains any property set in the property drawer, with its name in upper cases and colons added at the @@ -744,80 +928,46 @@ parsed as a secondary string, but as a plain string instead. Assume point is at beginning of the headline." (save-excursion - (let* ((components (org-heading-components)) - (level (nth 1 components)) - (todo (nth 2 components)) + (let* ((begin (point)) + (level (prog1 (org-reduced-level (skip-chars-forward "*")) + (skip-chars-forward " \t"))) + (todo (and org-todo-regexp + (let (case-fold-search) (looking-at org-todo-regexp)) + (progn (goto-char (match-end 0)) + (skip-chars-forward " \t") + (match-string 0)))) (todo-type (and todo (if (member todo org-done-keywords) 'done 'todo))) - (tags (let ((raw-tags (nth 5 components))) - (and raw-tags (org-split-string raw-tags ":")))) - (raw-value (or (nth 4 components) "")) - (quotedp - (let ((case-fold-search nil)) - (string-match (format "^%s\\( \\|$\\)" org-quote-string) - raw-value))) + (priority (and (looking-at "\\[#.\\][ \t]*") + (progn (goto-char (match-end 0)) + (aref (match-string 0) 2)))) (commentedp - (let ((case-fold-search nil)) - (string-match (format "^%s\\( \\|$\\)" org-comment-string) - raw-value))) + (and (let (case-fold-search) (looking-at org-comment-string)) + (goto-char (match-end 0)))) + (title-start (point)) + (tags (when (re-search-forward + "[ \t]+\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" + (line-end-position) + 'move) + (goto-char (match-beginning 0)) + (org-split-string (match-string 1) ":"))) + (title-end (point)) + (raw-value (org-trim + (buffer-substring-no-properties title-start title-end))) (archivedp (member org-archive-tag tags)) (footnote-section-p (and org-footnote-section (string= org-footnote-section raw-value))) - ;; Upcase property names. It avoids confusion between - ;; properties obtained through property drawer and default - ;; properties from the parser (e.g. `:end' and :END:) - (standard-props - (let (plist) - (mapc - (lambda (p) - (setq plist - (plist-put plist - (intern (concat ":" (upcase (car p)))) - (cdr p)))) - (org-entry-properties nil 'standard)) - plist)) - (time-props - ;; Read time properties on the line below the headline. - (save-excursion - (when (progn (forward-line) - (looking-at org-planning-or-clock-line-re)) - (let ((end (line-end-position)) plist) - (while (re-search-forward - org-keyword-time-not-clock-regexp end t) - (goto-char (match-end 1)) - (skip-chars-forward " \t") - (let ((keyword (match-string 1)) - (time (org-element-timestamp-parser))) - (cond ((equal keyword org-scheduled-string) - (setq plist (plist-put plist :scheduled time))) - ((equal keyword org-deadline-string) - (setq plist (plist-put plist :deadline time))) - (t (setq plist (plist-put plist :closed time)))))) - plist)))) - (begin (point)) + (standard-props (org-element--get-node-properties)) + (time-props (org-element--get-time-properties)) (end (min (save-excursion (org-end-of-subtree t t)) limit)) - (pos-after-head (progn (forward-line) (point))) (contents-begin (save-excursion + (forward-line) (skip-chars-forward " \r\t\n" end) (and (/= (point) end) (line-beginning-position)))) - (hidden (org-invisible-p2)) (contents-end (and contents-begin (progn (goto-char end) (skip-chars-backward " \r\t\n") - (forward-line) - (point))))) - ;; Clean RAW-VALUE from any quote or comment string. - (when (or quotedp commentedp) - (let ((case-fold-search nil)) - (setq raw-value - (replace-regexp-in-string - (concat - (regexp-opt (list org-quote-string org-comment-string)) - "\\(?: \\|$\\)") - "" - raw-value)))) - ;; Clean TAGS from archive tag, if any. - (when archivedp (setq tags (delete org-archive-tag tags))) + (line-beginning-position 2))))) (let ((headline (list 'headline (nconc @@ -826,36 +976,37 @@ Assume point is at beginning of the headline." :end end :pre-blank (if (not contents-begin) 0 - (count-lines pos-after-head contents-begin)) - :hiddenp hidden + (1- (count-lines begin contents-begin))) :contents-begin contents-begin :contents-end contents-end :level level - :priority (nth 3 components) + :priority priority :tags tags :todo-keyword todo :todo-type todo-type - :post-blank (count-lines - (or contents-end pos-after-head) - end) + :post-blank + (if contents-end + (count-lines contents-end end) + (1- (count-lines begin end))) :footnote-section-p footnote-section-p :archivedp archivedp :commentedp commentedp - :quotedp quotedp) + :post-affiliated begin) time-props standard-props)))) - (let ((alt-title (org-element-property :ALT_TITLE headline))) - (when alt-title - (org-element-put-property - headline :alt-title - (if raw-secondary-p alt-title - (org-element-parse-secondary-string - alt-title (org-element-restriction 'headline) headline))))) (org-element-put-property headline :title (if raw-secondary-p raw-value - (org-element-parse-secondary-string - raw-value (org-element-restriction 'headline) headline))))))) + (org-element--parse-objects + (progn (goto-char title-start) + (skip-chars-forward " \t") + (point)) + (progn (goto-char title-end) + (skip-chars-backward " \t") + (point)) + nil + (org-element-restriction 'headline) + headline))))))) (defun org-element-headline-interpreter (headline contents) "Interpret HEADLINE element as Org syntax. @@ -865,22 +1016,17 @@ CONTENTS is the contents of the element." (priority (org-element-property :priority headline)) (title (org-element-interpret-data (org-element-property :title headline))) - (tags (let ((tag-list (if (org-element-property :archivedp headline) - (cons org-archive-tag - (org-element-property :tags headline)) - (org-element-property :tags headline)))) + (tags (let ((tag-list (org-element-property :tags headline))) (and tag-list (format ":%s:" (mapconcat #'identity tag-list ":"))))) (commentedp (org-element-property :commentedp headline)) - (quotedp (org-element-property :quotedp headline)) (pre-blank (or (org-element-property :pre-blank headline) 0)) (heading (concat (make-string (if org-odd-levels-only (1- (* level 2)) level) ?*) (and todo (concat " " todo)) - (and quotedp (concat " " org-quote-string)) (and commentedp (concat " " org-comment-string)) - (and priority (format " [#%s]" (char-to-string priority))) + (and priority (format " [#%c]" priority)) " " (if (and org-footnote-section (org-element-property :footnote-section-p headline)) @@ -912,10 +1058,11 @@ CONTENTS is the contents of the element." "Parse an inline task. Return a list whose CAR is `inlinetask' and CDR is a plist -containing `:title', `:begin', `:end', `:hiddenp', +containing `:title', `:begin', `:end', `:pre-blank', `:contents-begin' and `:contents-end', `:level', `:priority', `:raw-value', `:tags', `:todo-keyword', `:todo-type', -`:scheduled', `:deadline', `:closed' and `:post-blank' keywords. +`:scheduled', `:deadline', `:closed', `:post-blank' and +`:post-affiliated' keywords. The plist also contains any property set in the property drawer, with its name in upper cases and colons added at the @@ -928,59 +1075,45 @@ string instead. Assume point is at beginning of the inline task." (save-excursion (let* ((begin (point)) - (components (org-heading-components)) - (todo (nth 2 components)) + (level (prog1 (org-reduced-level (skip-chars-forward "*")) + (skip-chars-forward " \t"))) + (todo (and org-todo-regexp + (let (case-fold-search) (looking-at org-todo-regexp)) + (progn (goto-char (match-end 0)) + (skip-chars-forward " \t") + (match-string 0)))) (todo-type (and todo (if (member todo org-done-keywords) 'done 'todo))) - (tags (let ((raw-tags (nth 5 components))) - (and raw-tags (org-split-string raw-tags ":")))) - (raw-value (or (nth 4 components) "")) - ;; Upcase property names. It avoids confusion between - ;; properties obtained through property drawer and default - ;; properties from the parser (e.g. `:end' and :END:) - (standard-props - (let (plist) - (mapc - (lambda (p) - (setq plist - (plist-put plist - (intern (concat ":" (upcase (car p)))) - (cdr p)))) - (org-entry-properties nil 'standard)) - plist)) - (time-props - ;; Read time properties on the line below the inlinetask - ;; opening string. - (save-excursion - (when (progn (forward-line) - (looking-at org-planning-or-clock-line-re)) - (let ((end (line-end-position)) plist) - (while (re-search-forward - org-keyword-time-not-clock-regexp end t) - (goto-char (match-end 1)) - (skip-chars-forward " \t") - (let ((keyword (match-string 1)) - (time (org-element-timestamp-parser))) - (cond ((equal keyword org-scheduled-string) - (setq plist (plist-put plist :scheduled time))) - ((equal keyword org-deadline-string) - (setq plist (plist-put plist :deadline time))) - (t (setq plist (plist-put plist :closed time)))))) - plist)))) + (priority (and (looking-at "\\[#.\\][ \t]*") + (progn (goto-char (match-end 0)) + (aref (match-string 0) 2)))) + (title-start (point)) + (tags (when (re-search-forward + "[ \t]+\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" + (line-end-position) + 'move) + (goto-char (match-beginning 0)) + (org-split-string (match-string 1) ":"))) + (title-end (point)) + (raw-value (org-trim + (buffer-substring-no-properties title-start title-end))) (task-end (save-excursion (end-of-line) (and (re-search-forward org-outline-regexp-bol limit t) - (org-looking-at-p "END[ \t]*$") + (looking-at-p "[ \t]*END[ \t]*$") (line-beginning-position)))) - (contents-begin (progn (forward-line) - (and task-end (< (point) task-end) (point)))) - (hidden (and contents-begin (org-invisible-p2))) + (standard-props (and task-end (org-element--get-node-properties))) + (time-props (and task-end (org-element--get-time-properties))) + (contents-begin (and task-end + (< (point) task-end) + (progn + (forward-line) + (skip-chars-forward " \t\n") + (line-beginning-position)))) (contents-end (and contents-begin task-end)) - (before-blank (if (not task-end) (point) - (goto-char task-end) - (forward-line) - (point))) - (end (progn (skip-chars-forward " \r\t\n" limit) + (end (progn (when task-end (goto-char task-end)) + (forward-line) + (skip-chars-forward " \r\t\n" limit) (if (eobp) (point) (line-beginning-position)))) (inlinetask (list 'inlinetask @@ -988,22 +1121,31 @@ Assume point is at beginning of the inline task." (list :raw-value raw-value :begin begin :end end - :hiddenp hidden + :pre-blank + (if (not contents-begin) 0 + (1- (count-lines begin contents-begin))) :contents-begin contents-begin :contents-end contents-end - :level (nth 1 components) - :priority (nth 3 components) + :level level + :priority priority :tags tags :todo-keyword todo :todo-type todo-type - :post-blank (count-lines before-blank end)) + :post-blank (1- (count-lines (or task-end begin) end)) + :post-affiliated begin) time-props standard-props)))) (org-element-put-property inlinetask :title (if raw-secondary-p raw-value - (org-element-parse-secondary-string - raw-value + (org-element--parse-objects + (progn (goto-char title-start) + (skip-chars-forward " \t") + (point)) + (progn (goto-char title-end) + (skip-chars-backward " \t") + (point)) + nil (org-element-restriction 'inlinetask) inlinetask)))))) @@ -1020,8 +1162,7 @@ CONTENTS is the contents of inlinetask." (format ":%s:" (mapconcat 'identity tag-list ":"))))) (task (concat (make-string level ?*) (and todo (concat " " todo)) - (and priority - (format " [#%s]" (char-to-string priority))) + (and priority (format " [#%c]" priority)) (and title (concat " " title))))) (concat task ;; Align tags. @@ -1048,15 +1189,15 @@ CONTENTS is the contents of inlinetask." ;;;; Item -(defun org-element-item-parser (limit struct &optional raw-secondary-p) +(defun org-element-item-parser (_ struct &optional raw-secondary-p) "Parse an item. STRUCT is the structure of the plain list. Return a list whose CAR is `item' and CDR is a plist containing `:bullet', `:begin', `:end', `:contents-begin', `:contents-end', -`:checkbox', `:counter', `:tag', `:structure', `:hiddenp' and -`:post-blank' keywords. +`:checkbox', `:counter', `:tag', `:structure', `:post-blank' and +`:post-affiliated' keywords. When optional argument RAW-SECONDARY-P is non-nil, item's tag, if any, will not be parsed as a secondary string, but as a plain @@ -1067,12 +1208,12 @@ Assume point is at the beginning of the item." (beginning-of-line) (looking-at org-list-full-item-re) (let* ((begin (point)) - (bullet (org-match-string-no-properties 1)) - (checkbox (let ((box (org-match-string-no-properties 3))) + (bullet (match-string-no-properties 1)) + (checkbox (let ((box (match-string 3))) (cond ((equal "[ ]" box) 'off) ((equal "[X]" box) 'on) ((equal "[-]" box) 'trans)))) - (counter (let ((c (org-match-string-no-properties 2))) + (counter (let ((c (match-string 2))) (save-match-data (cond ((not c) nil) @@ -1081,9 +1222,8 @@ Assume point is at the beginning of the item." 64)) ((string-match "[0-9]+" c) (string-to-number (match-string 0 c))))))) - (end (save-excursion (goto-char (org-list-get-item-end begin struct)) - (unless (bolp) (forward-line)) - (point))) + (end (progn (goto-char (nth 6 (assq (point) struct))) + (if (bolp) (point) (line-beginning-position 2)))) (contents-begin (progn (goto-char ;; Ignore tags in un-ordered lists: they are just @@ -1092,40 +1232,37 @@ Assume point is at the beginning of the item." (save-match-data (string-match "[.)]" bullet))) (match-beginning 4) (match-end 0))) - (skip-chars-forward " \r\t\n" limit) - ;; If first line isn't empty, contents really start - ;; at the text after item's meta-data. - (if (= (point-at-bol) begin) (point) (point-at-bol)))) - (hidden (progn (forward-line) - (and (not (= (point) end)) (org-invisible-p2)))) - (contents-end (progn (goto-char end) - (skip-chars-backward " \r\t\n") - (forward-line) - (point))) + (skip-chars-forward " \r\t\n" end) + (cond ((= (point) end) nil) + ;; If first line isn't empty, contents really + ;; start at the text after item's meta-data. + ((= (line-beginning-position) begin) (point)) + (t (line-beginning-position))))) + (contents-end (and contents-begin + (progn (goto-char end) + (skip-chars-backward " \r\t\n") + (line-beginning-position 2)))) (item (list 'item (list :bullet bullet :begin begin :end end - ;; CONTENTS-BEGIN and CONTENTS-END may be - ;; mixed up in the case of an empty item - ;; separated from the next by a blank line. - ;; Thus ensure the former is always the - ;; smallest. - :contents-begin (min contents-begin contents-end) - :contents-end (max contents-begin contents-end) + :contents-begin contents-begin + :contents-end contents-end :checkbox checkbox :counter counter - :hiddenp hidden :structure struct - :post-blank (count-lines contents-end end))))) + :post-blank (count-lines (or contents-end begin) end) + :post-affiliated begin)))) (org-element-put-property item :tag - (let ((raw-tag (org-list-get-tag begin struct))) - (and raw-tag - (if raw-secondary-p raw-tag - (org-element-parse-secondary-string - raw-tag (org-element-restriction 'item) item)))))))) + (let ((raw (org-list-get-tag begin struct))) + (when raw + (if raw-secondary-p raw + (org-element--parse-objects + (match-beginning 4) (match-end 4) nil + (org-element-restriction 'item) + item)))))))) (defun org-element-item-interpreter (item contents) "Interpret ITEM element as Org syntax. @@ -1148,10 +1285,11 @@ CONTENTS is the contents of the element." (concat bullet (and counter (format "[@%d] " counter)) - (case checkbox - (on "[X] ") - (off "[ ] ") - (trans "[-] ")) + (pcase checkbox + (`on "[X] ") + (`off "[ ] ") + (`trans "[-] ") + (_ nil)) (and tag (format "%s :: " tag)) (when contents (let ((contents (replace-regexp-in-string @@ -1168,9 +1306,6 @@ CONTENTS is the contents of the element." (let ((case-fold-search t) (top-ind limit) (item-re (org-item-re)) - (drawers-re (concat ":\\(" - (mapconcat 'regexp-quote org-drawers "\\|") - "\\):[ \t]*$")) (inlinetask-re (and (featurep 'org-inlinetask) "^\\*+ ")) items struct) (save-excursion @@ -1222,11 +1357,12 @@ CONTENTS is the contents of the element." (forward-line) (let ((origin (point))) (when (re-search-forward inlinetask-re limit t) - (if (org-looking-at-p "END[ \t]*$") (forward-line) + (if (looking-at-p "END[ \t]*$") (forward-line) (goto-char origin))))) ;; At some text line. Check if it ends any previous item. (t - (let ((ind (progn (skip-chars-forward " \t") (current-column)))) + (let ((ind (save-excursion (skip-chars-forward " \t") + (current-column)))) (when (<= ind top-ind) (skip-chars-backward " \r\t\n") (forward-line)) @@ -1235,15 +1371,14 @@ CONTENTS is the contents of the element." (setcar (nthcdr 6 item) (line-beginning-position)) (push item struct) (unless items - (throw 'exit (sort struct 'car-less-than-car)))))) + (throw 'exit (sort struct #'car-less-than-car)))))) ;; Skip blocks (any type) and drawers contents. (cond - ((and (looking-at "#\\+BEGIN\\(:\\|_\\S-+\\)") + ((and (looking-at "[ \t]*#\\+BEGIN\\(:\\|_\\S-+\\)") (re-search-forward - (format "^[ \t]*#\\+END%s[ \t]*$" - (org-match-string-no-properties 1)) + (format "^[ \t]*#\\+END%s[ \t]*$" (match-string 1)) limit t))) - ((and (looking-at drawers-re) + ((and (looking-at org-drawer-regexp) (re-search-forward "^[ \t]*:END:[ \t]*$" limit t)))) (forward-line)))))))) @@ -1264,15 +1399,20 @@ containing `:type', `:begin', `:end', `:contents-begin' and Assume point is at the beginning of the list." (save-excursion (let* ((struct (or structure (org-element--list-struct limit))) - (prevs (org-list-prevs-alist struct)) - (type (org-list-get-list-type (point) struct prevs)) + (type (cond ((looking-at-p "[ \t]*[A-Za-z0-9]") 'ordered) + ((nth 5 (assq (point) struct)) 'descriptive) + (t 'unordered))) (contents-begin (point)) (begin (car affiliated)) - (contents-end - (progn (goto-char (org-list-get-list-end (point) struct prevs)) - (unless (bolp) (forward-line)) - (point))) - (end (progn (skip-chars-forward " \r\t\n" limit) + (contents-end (let* ((item (assq contents-begin struct)) + (ind (nth 1 item)) + (pos (nth 6 item))) + (while (and (setq item (assq pos struct)) + (= (nth 1 item) ind)) + (setq pos (nth 6 item))) + pos)) + (end (progn (goto-char contents-end) + (skip-chars-forward " \r\t\n" limit) (if (= (point) limit) limit (line-beginning-position))))) ;; Return value. (list 'plain-list @@ -1287,8 +1427,8 @@ Assume point is at the beginning of the list." :post-affiliated contents-begin) (cdr affiliated)))))) -(defun org-element-plain-list-interpreter (plain-list contents) - "Interpret PLAIN-LIST element as Org syntax. +(defun org-element-plain-list-interpreter (_ contents) + "Interpret plain-list element as Org syntax. CONTENTS is the contents of the element." (with-temp-buffer (insert contents) @@ -1299,52 +1439,36 @@ CONTENTS is the contents of the element." ;;;; Property Drawer -(defun org-element-property-drawer-parser (limit affiliated) +(defun org-element-property-drawer-parser (limit) "Parse a property drawer. -LIMIT bounds the search. AFFILIATED is a list of which CAR is -the buffer position at the beginning of the first affiliated -keyword and CDR is a plist of affiliated keywords along with -their value. +LIMIT bounds the search. -Return a list whose CAR is `property-drawer' and CDR is a plist -containing `:begin', `:end', `:hiddenp', `:contents-begin', -`:contents-end', `:post-blank' and `:post-affiliated' keywords. +Return a list whose car is `property-drawer' and cdr is a plist +containing `:begin', `:end', `:contents-begin', `:contents-end', +`:post-blank' and `:post-affiliated' keywords. Assume point is at the beginning of the property drawer." - (let ((case-fold-search t)) - (if (not (save-excursion (re-search-forward "^[ \t]*:END:[ \t]*$" limit t))) - ;; Incomplete drawer: parse it as a paragraph. - (org-element-paragraph-parser limit affiliated) - (save-excursion - (let* ((drawer-end-line (match-beginning 0)) - (begin (car affiliated)) - (post-affiliated (point)) - (contents-begin - (progn - (forward-line) - (and (re-search-forward org-property-re drawer-end-line t) - (line-beginning-position)))) - (contents-end (and contents-begin drawer-end-line)) - (hidden (org-invisible-p2)) - (pos-before-blank (progn (goto-char drawer-end-line) - (forward-line) - (point))) - (end (progn (skip-chars-forward " \r\t\n" limit) - (if (eobp) (point) (line-beginning-position))))) - (list 'property-drawer - (nconc - (list :begin begin - :end end - :hiddenp hidden - :contents-begin contents-begin - :contents-end contents-end - :post-blank (count-lines pos-before-blank end) - :post-affiliated post-affiliated) - (cdr affiliated)))))))) - -(defun org-element-property-drawer-interpreter (property-drawer contents) - "Interpret PROPERTY-DRAWER element as Org syntax. + (save-excursion + (let ((case-fold-search t) + (begin (point)) + (contents-begin (line-beginning-position 2))) + (re-search-forward "^[ \t]*:END:[ \t]*$" limit t) + (let ((contents-end (and (> (match-beginning 0) contents-begin) + (match-beginning 0))) + (before-blank (progn (forward-line) (point))) + (end (progn (skip-chars-forward " \r\t\n" limit) + (if (eobp) (point) (line-beginning-position))))) + (list 'property-drawer + (list :begin begin + :end end + :contents-begin (and contents-end contents-begin) + :contents-end contents-end + :post-blank (count-lines before-blank end) + :post-affiliated begin)))))) + +(defun org-element-property-drawer-interpreter (_ contents) + "Interpret property-drawer element as Org syntax. CONTENTS is the properties within the drawer." (format ":PROPERTIES:\n%s:END:" contents)) @@ -1360,8 +1484,8 @@ keyword and CDR is a plist of affiliated keywords along with their value. Return a list whose CAR is `quote-block' and CDR is a plist -containing `:begin', `:end', `:hiddenp', `:contents-begin', -`:contents-end', `:post-blank' and `:post-affiliated' keywords. +containing `:begin', `:end', `:contents-begin', `:contents-end', +`:post-blank' and `:post-affiliated' keywords. Assume point is at the beginning of the block." (let ((case-fold-search t)) @@ -1378,7 +1502,6 @@ Assume point is at the beginning of the block." (and (< (point) block-end-line) (point)))) (contents-end (and contents-begin block-end-line)) - (hidden (org-invisible-p2)) (pos-before-blank (progn (goto-char block-end-line) (forward-line) (point))) @@ -1388,29 +1511,26 @@ Assume point is at the beginning of the block." (nconc (list :begin begin :end end - :hiddenp hidden :contents-begin contents-begin :contents-end contents-end :post-blank (count-lines pos-before-blank end) :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-quote-block-interpreter (quote-block contents) - "Interpret QUOTE-BLOCK element as Org syntax. +(defun org-element-quote-block-interpreter (_ contents) + "Interpret quote-block element as Org syntax. CONTENTS is the contents of the element." (format "#+BEGIN_QUOTE\n%s#+END_QUOTE" contents)) ;;;; Section -(defun org-element-section-parser (limit) +(defun org-element-section-parser (_) "Parse a section. -LIMIT bounds the search. - Return a list whose CAR is `section' and CDR is a plist -containing `:begin', `:end', `:contents-begin', `contents-end' -and `:post-blank' keywords." +containing `:begin', `:end', `:contents-begin', `contents-end', +`:post-blank' and `:post-affiliated' keywords." (save-excursion ;; Beginning of section is the beginning of the first non-blank ;; line after previous headline. @@ -1418,17 +1538,17 @@ and `:post-blank' keywords." (end (progn (org-with-limited-levels (outline-next-heading)) (point))) (pos-before-blank (progn (skip-chars-backward " \r\t\n") - (forward-line) - (point)))) + (line-beginning-position 2)))) (list 'section (list :begin begin :end end :contents-begin begin :contents-end pos-before-blank - :post-blank (count-lines pos-before-blank end)))))) + :post-blank (count-lines pos-before-blank end) + :post-affiliated begin))))) -(defun org-element-section-interpreter (section contents) - "Interpret SECTION element as Org syntax. +(defun org-element-section-interpreter (_ contents) + "Interpret section element as Org syntax. CONTENTS is the contents of the element." contents) @@ -1444,14 +1564,13 @@ keyword and CDR is a plist of affiliated keywords along with their value. Return a list whose CAR is `special-block' and CDR is a plist -containing `:type', `:begin', `:end', `:hiddenp', -`:contents-begin', `:contents-end', `:post-blank' and -`:post-affiliated' keywords. +containing `:type', `:begin', `:end', `:contents-begin', +`:contents-end', `:post-blank' and `:post-affiliated' keywords. Assume point is at the beginning of the block." (let* ((case-fold-search t) (type (progn (looking-at "[ \t]*#\\+BEGIN_\\(\\S-+\\)") - (upcase (match-string-no-properties 1))))) + (match-string-no-properties 1)))) (if (not (save-excursion (re-search-forward (format "^[ \t]*#\\+END_%s[ \t]*$" (regexp-quote type)) @@ -1467,7 +1586,6 @@ Assume point is at the beginning of the block." (and (< (point) block-end-line) (point)))) (contents-end (and contents-begin block-end-line)) - (hidden (org-invisible-p2)) (pos-before-blank (progn (goto-char block-end-line) (forward-line) (point))) @@ -1478,7 +1596,6 @@ Assume point is at the beginning of the block." (list :type type :begin begin :end end - :hiddenp hidden :contents-begin contents-begin :contents-end contents-end :post-blank (count-lines pos-before-blank end) @@ -1502,9 +1619,6 @@ CONTENTS is the contents of the element." ;; through the following steps: implement a parser and an interpreter, ;; tweak `org-element--current-element' so that it recognizes the new ;; type and add that new type to `org-element-all-elements'. -;; -;; As a special case, when the newly defined type is a block type, -;; `org-element-block-name-alist' has to be modified accordingly. ;;;; Babel Call @@ -1512,43 +1626,61 @@ CONTENTS is the contents of the element." (defun org-element-babel-call-parser (limit affiliated) "Parse a babel call. -LIMIT bounds the search. AFFILIATED is a list of which CAR is +LIMIT bounds the search. AFFILIATED is a list of which car is the buffer position at the beginning of the first affiliated -keyword and CDR is a plist of affiliated keywords along with +keyword and cdr is a plist of affiliated keywords along with their value. -Return a list whose CAR is `babel-call' and CDR is a plist -containing `:begin', `:end', `:info', `:post-blank' and +Return a list whose car is `babel-call' and cdr is a plist +containing `:call', `:inside-header', `:arguments', +`:end-header', `:begin', `:end', `:value', `:post-blank' and `:post-affiliated' as keywords." (save-excursion - (let ((case-fold-search t) - (info (progn (looking-at org-babel-block-lob-one-liner-regexp) - (org-babel-lob-get-info))) - (begin (car affiliated)) - (post-affiliated (point)) - (pos-before-blank (progn (forward-line) (point))) - (end (progn (skip-chars-forward " \r\t\n" limit) - (if (eobp) (point) (line-beginning-position))))) + (let* ((begin (car affiliated)) + (post-affiliated (point)) + (before-blank (line-beginning-position 2)) + (value (progn (search-forward ":" before-blank t) + (skip-chars-forward " \t") + (org-trim + (buffer-substring-no-properties + (point) (line-end-position))))) + (call + (or (org-string-nw-p + (buffer-substring-no-properties + (point) (progn (skip-chars-forward "^[]()" before-blank) + (point)))))) + (inside-header (org-element--parse-paired-brackets ?\[)) + (arguments (org-string-nw-p + (org-element--parse-paired-brackets ?\())) + (end-header + (org-string-nw-p + (org-trim + (buffer-substring-no-properties (point) (line-end-position))))) + (end (progn (forward-line) + (skip-chars-forward " \r\t\n" limit) + (if (eobp) (point) (line-beginning-position))))) (list 'babel-call (nconc - (list :begin begin + (list :call call + :inside-header inside-header + :arguments arguments + :end-header end-header + :begin begin :end end - :info info - :post-blank (count-lines pos-before-blank end) + :value value + :post-blank (count-lines before-blank end) :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-babel-call-interpreter (babel-call contents) - "Interpret BABEL-CALL element as Org syntax. -CONTENTS is nil." - (let* ((babel-info (org-element-property :info babel-call)) - (main (car babel-info)) - (post-options (nth 1 babel-info))) - (concat "#+CALL: " - (if (not (string-match "\\[\\(\\[.*?\\]\\)\\]" main)) main - ;; Remove redundant square brackets. - (replace-match (match-string 1 main) nil nil main)) - (and post-options (format "[%s]" post-options))))) +(defun org-element-babel-call-interpreter (babel-call _) + "Interpret BABEL-CALL element as Org syntax." + (concat "#+CALL: " + (org-element-property :call babel-call) + (let ((h (org-element-property :inside-header babel-call))) + (and h (format "[%s]" h))) + (concat "(" (org-element-property :arguments babel-call) ")") + (let ((h (org-element-property :end-header babel-call))) + (and h (concat " " h))))) ;;;; Clock @@ -1559,8 +1691,8 @@ CONTENTS is nil." LIMIT bounds the search. Return a list whose CAR is `clock' and CDR is a plist containing -`:status', `:value', `:time', `:begin', `:end' and `:post-blank' -as keywords." +`:status', `:value', `:time', `:begin', `:end', `:post-blank' and +`:post-affiliated' as keywords." (save-excursion (let* ((case-fold-search nil) (begin (point)) @@ -1570,7 +1702,7 @@ as keywords." (duration (and (search-forward " => " (line-end-position) t) (progn (skip-chars-forward " \t") (looking-at "\\(\\S-+\\)[ \t]*$")) - (org-match-string-no-properties 1))) + (match-string-no-properties 1))) (status (if duration 'closed 'running)) (post-blank (let ((before-blank (progn (forward-line) (point)))) (skip-chars-forward " \r\t\n" limit) @@ -1584,11 +1716,11 @@ as keywords." :duration duration :begin begin :end end - :post-blank post-blank))))) + :post-blank post-blank + :post-affiliated begin))))) -(defun org-element-clock-interpreter (clock contents) - "Interpret CLOCK element as Org syntax. -CONTENTS is nil." +(defun org-element-clock-interpreter (clock _) + "Interpret CLOCK element as Org syntax." (concat org-clock-string " " (org-element-timestamp-interpreter (org-element-property :value clock) nil) @@ -1647,7 +1779,7 @@ Assume point is at comment beginning." :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-comment-interpreter (comment contents) +(defun org-element-comment-interpreter (comment _) "Interpret COMMENT element as Org syntax. CONTENTS is nil." (replace-regexp-in-string "^" "# " (org-element-property :value comment))) @@ -1664,8 +1796,8 @@ keyword and CDR is a plist of affiliated keywords along with their value. Return a list whose CAR is `comment-block' and CDR is a plist -containing `:begin', `:end', `:hiddenp', `:value', `:post-blank' -and `:post-affiliated' keywords. +containing `:begin', `:end', `:value', `:post-blank' and +`:post-affiliated' keywords. Assume point is at comment block beginning." (let ((case-fold-search t)) @@ -1678,7 +1810,6 @@ Assume point is at comment block beginning." (let* ((begin (car affiliated)) (post-affiliated (point)) (contents-begin (progn (forward-line) (point))) - (hidden (org-invisible-p2)) (pos-before-blank (progn (goto-char contents-end) (forward-line) (point))) @@ -1691,16 +1822,16 @@ Assume point is at comment block beginning." (list :begin begin :end end :value value - :hiddenp hidden :post-blank (count-lines pos-before-blank end) :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-comment-block-interpreter (comment-block contents) - "Interpret COMMENT-BLOCK element as Org syntax. -CONTENTS is nil." +(defun org-element-comment-block-interpreter (comment-block _) + "Interpret COMMENT-BLOCK element as Org syntax." (format "#+BEGIN_COMMENT\n%s#+END_COMMENT" - (org-remove-indentation (org-element-property :value comment-block)))) + (org-element-normalize-string + (org-remove-indentation + (org-element-property :value comment-block))))) ;;;; Diary Sexp @@ -1720,7 +1851,7 @@ containing `:begin', `:end', `:value', `:post-blank' and (let ((begin (car affiliated)) (post-affiliated (point)) (value (progn (looking-at "\\(%%(.*\\)[ \t]*$") - (org-match-string-no-properties 1))) + (match-string-no-properties 1))) (pos-before-blank (progn (forward-line) (point))) (end (progn (skip-chars-forward " \r\t\n" limit) (if (eobp) (point) (line-beginning-position))))) @@ -1733,43 +1864,13 @@ containing `:begin', `:end', `:value', `:post-blank' and :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-diary-sexp-interpreter (diary-sexp contents) - "Interpret DIARY-SEXP as Org syntax. -CONTENTS is nil." +(defun org-element-diary-sexp-interpreter (diary-sexp _) + "Interpret DIARY-SEXP as Org syntax." (org-element-property :value diary-sexp)) ;;;; Example Block -(defun org-element--remove-indentation (s &optional n) - "Remove maximum common indentation in string S and return it. -When optional argument N is a positive integer, remove exactly -that much characters from indentation, if possible, or return -S as-is otherwise. Unlike to `org-remove-indentation', this -function doesn't call `untabify' on S." - (catch 'exit - (with-temp-buffer - (insert s) - (goto-char (point-min)) - ;; Find maximum common indentation, if not specified. - (setq n (or n - (let ((min-ind (point-max))) - (save-excursion - (while (re-search-forward "^[ \t]*\\S-" nil t) - (let ((ind (1- (current-column)))) - (if (zerop ind) (throw 'exit s) - (setq min-ind (min min-ind ind)))))) - min-ind))) - (if (zerop n) s - ;; Remove exactly N indentation, but give up if not possible. - (while (not (eobp)) - (let ((ind (progn (skip-chars-forward " \t") (current-column)))) - (cond ((eolp) (delete-region (line-beginning-position) (point))) - ((< ind n) (throw 'exit s)) - (t (org-indent-line-to (- ind n)))) - (forward-line))) - (buffer-string))))) - (defun org-element-example-block-parser (limit affiliated) "Parse an example block. @@ -1780,9 +1881,8 @@ their value. Return a list whose CAR is `example-block' and CDR is a plist containing `:begin', `:end', `:number-lines', `:preserve-indent', -`:retain-labels', `:use-labels', `:label-fmt', `:hiddenp', -`:switches', `:value', `:post-blank' and `:post-affiliated' -keywords." +`:retain-labels', `:use-labels', `:label-fmt', `:switches', +`:value', `:post-blank' and `:post-affiliated' keywords." (let ((case-fold-search t)) (if (not (save-excursion (re-search-forward "^[ \t]*#\\+END_EXAMPLE[ \t]*$" limit t))) @@ -1793,15 +1893,22 @@ keywords." (let* ((switches (progn (looking-at "^[ \t]*#\\+BEGIN_EXAMPLE\\(?: +\\(.*\\)\\)?") - (org-match-string-no-properties 1))) - ;; Switches analysis + (match-string-no-properties 1))) + ;; Switches analysis. (number-lines - (cond ((not switches) nil) - ((string-match "-n\\>" switches) 'new) - ((string-match "+n\\>" switches) 'continued))) + (and switches + (string-match "\\([-+]\\)n\\(?: *\\([0-9]+\\)\\)?\\>" + switches) + (cons + (if (equal (match-string 1 switches) "-") + 'new + 'continued) + (if (not (match-end 2)) 0 + ;; Subtract 1 to give number of lines before + ;; first line. + (1- (string-to-number (match-string 2 switches))))))) (preserve-indent - (or org-src-preserve-indentation - (and switches (string-match "-i\\>" switches)))) + (and switches (string-match "-i\\>" switches))) ;; Should labels be retained in (or stripped from) example ;; blocks? (retain-labels @@ -1821,14 +1928,10 @@ keywords." ;; Standard block parsing. (begin (car affiliated)) (post-affiliated (point)) - (block-ind (progn (skip-chars-forward " \t") (current-column))) - (contents-begin (progn (forward-line) (point))) - (hidden (org-invisible-p2)) - (value (org-element--remove-indentation - (org-unescape-code-in-string - (buffer-substring-no-properties - contents-begin contents-end)) - (and preserve-indent block-ind))) + (contents-begin (line-beginning-position 2)) + (value (org-unescape-code-in-string + (buffer-substring-no-properties + contents-begin contents-end))) (pos-before-blank (progn (goto-char contents-end) (forward-line) (point))) @@ -1845,18 +1948,21 @@ keywords." :retain-labels retain-labels :use-labels use-labels :label-fmt label-fmt - :hiddenp hidden :post-blank (count-lines pos-before-blank end) :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-example-block-interpreter (example-block contents) - "Interpret EXAMPLE-BLOCK element as Org syntax. -CONTENTS is nil." - (let ((switches (org-element-property :switches example-block))) +(defun org-element-example-block-interpreter (example-block _) + "Interpret EXAMPLE-BLOCK element as Org syntax." + (let ((switches (org-element-property :switches example-block)) + (value (org-element-property :value example-block))) (concat "#+BEGIN_EXAMPLE" (and switches (concat " " switches)) "\n" - (org-escape-code-in-string - (org-element-property :value example-block)) + (org-element-normalize-string + (org-escape-code-in-string + (if (or org-src-preserve-indentation + (org-element-property :preserve-indent example-block)) + value + (org-remove-indentation value)))) "#+END_EXAMPLE"))) @@ -1871,49 +1977,48 @@ keyword and CDR is a plist of affiliated keywords along with their value. Return a list whose CAR is `export-block' and CDR is a plist -containing `:begin', `:end', `:type', `:hiddenp', `:value', -`:post-blank' and `:post-affiliated' keywords. +containing `:begin', `:end', `:type', `:value', `:post-blank' and +`:post-affiliated' keywords. Assume point is at export-block beginning." - (let* ((case-fold-search t) - (type (progn (looking-at "[ \t]*#\\+BEGIN_\\(\\S-+\\)") - (upcase (org-match-string-no-properties 1))))) + (let* ((case-fold-search t)) (if (not (save-excursion - (re-search-forward - (format "^[ \t]*#\\+END_%s[ \t]*$" type) limit t))) + (re-search-forward "^[ \t]*#\\+END_EXPORT[ \t]*$" limit t))) ;; Incomplete block: parse it as a paragraph. (org-element-paragraph-parser limit affiliated) - (let ((contents-end (match-beginning 0))) - (save-excursion - (let* ((begin (car affiliated)) - (post-affiliated (point)) - (contents-begin (progn (forward-line) (point))) - (hidden (org-invisible-p2)) - (pos-before-blank (progn (goto-char contents-end) - (forward-line) - (point))) - (end (progn (skip-chars-forward " \r\t\n" limit) - (if (eobp) (point) (line-beginning-position)))) - (value (buffer-substring-no-properties contents-begin - contents-end))) - (list 'export-block - (nconc - (list :begin begin - :end end - :type type - :value value - :hiddenp hidden - :post-blank (count-lines pos-before-blank end) - :post-affiliated post-affiliated) - (cdr affiliated))))))))) + (save-excursion + (let* ((contents-end (match-beginning 0)) + (backend + (progn + (looking-at + "[ \t]*#\\+BEGIN_EXPORT\\(?:[ \t]+\\(\\S-+\\)\\)?[ \t]*$") + (match-string-no-properties 1))) + (begin (car affiliated)) + (post-affiliated (point)) + (contents-begin (progn (forward-line) (point))) + (pos-before-blank (progn (goto-char contents-end) + (forward-line) + (point))) + (end (progn (skip-chars-forward " \r\t\n" limit) + (if (eobp) (point) (line-beginning-position)))) + (value (org-unescape-code-in-string + (buffer-substring-no-properties contents-begin + contents-end)))) + (list 'export-block + (nconc + (list :type (and backend (upcase backend)) + :begin begin + :end end + :value value + :post-blank (count-lines pos-before-blank end) + :post-affiliated post-affiliated) + (cdr affiliated)))))))) -(defun org-element-export-block-interpreter (export-block contents) - "Interpret EXPORT-BLOCK element as Org syntax. -CONTENTS is nil." - (let ((type (org-element-property :type export-block))) - (concat (format "#+BEGIN_%s\n" type) - (org-element-property :value export-block) - (format "#+END_%s" type)))) +(defun org-element-export-block-interpreter (export-block _) + "Interpret EXPORT-BLOCK element as Org syntax." + (format "#+BEGIN_EXPORT %s\n%s#+END_EXPORT" + (org-element-property :type export-block) + (org-element-property :value export-block))) ;;;; Fixed-width @@ -1958,9 +2063,8 @@ Assume point is at the beginning of the fixed-width area." :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-fixed-width-interpreter (fixed-width contents) - "Interpret FIXED-WIDTH element as Org syntax. -CONTENTS is nil." +(defun org-element-fixed-width-interpreter (fixed-width _) + "Interpret FIXED-WIDTH element as Org syntax." (let ((value (org-element-property :value fixed-width))) (and value (replace-regexp-in-string @@ -1995,9 +2099,8 @@ keywords." :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-horizontal-rule-interpreter (horizontal-rule contents) - "Interpret HORIZONTAL-RULE element as Org syntax. -CONTENTS is nil." +(defun org-element-horizontal-rule-interpreter (&rest _) + "Interpret HORIZONTAL-RULE element as Org syntax." "-----") @@ -2015,10 +2118,13 @@ Return a list whose CAR is `keyword' and CDR is a plist containing `:key', `:value', `:begin', `:end', `:post-blank' and `:post-affiliated' keywords." (save-excursion - (let ((begin (car affiliated)) + ;; An orphaned affiliated keyword is considered as a regular + ;; keyword. In this case AFFILIATED is nil, so we take care of + ;; this corner case. + (let ((begin (or (car affiliated) (point))) (post-affiliated (point)) (key (progn (looking-at "[ \t]*#\\+\\(\\S-+*\\):") - (upcase (org-match-string-no-properties 1)))) + (upcase (match-string-no-properties 1)))) (value (org-trim (buffer-substring-no-properties (match-end 0) (point-at-eol)))) (pos-before-blank (progn (forward-line) (point))) @@ -2034,9 +2140,8 @@ containing `:key', `:value', `:begin', `:end', `:post-blank' and :post-affiliated post-affiliated) (cdr affiliated)))))) -(defun org-element-keyword-interpreter (keyword contents) - "Interpret KEYWORD element as Org syntax. -CONTENTS is nil." +(defun org-element-keyword-interpreter (keyword _) + "Interpret KEYWORD element as Org syntax." (format "#+%s: %s" (org-element-property :key keyword) (org-element-property :value keyword))) @@ -2044,6 +2149,18 @@ CONTENTS is nil." ;;;; Latex Environment +(defconst org-element--latex-begin-environment + "^[ \t]*\\\\begin{\\([A-Za-z0-9*]+\\)}" + "Regexp matching the beginning of a LaTeX environment. +The environment is captured by the first group. + +See also `org-element--latex-end-environment'.") + +(defconst org-element--latex-end-environment + "\\\\end{%s}[ \t]*$" + "Format string matching the ending of a LaTeX environment. +See also `org-element--latex-begin-environment'.") + (defun org-element-latex-environment-parser (limit affiliated) "Parse a LaTeX environment. @@ -2060,8 +2177,8 @@ Assume point is at the beginning of the latex environment." (save-excursion (let ((case-fold-search t) (code-begin (point))) - (looking-at "[ \t]*\\\\begin{\\([A-Za-z0-9]+\\*?\\)}") - (if (not (re-search-forward (format "^[ \t]*\\\\end{%s}[ \t]*$" + (looking-at org-element--latex-begin-environment) + (if (not (re-search-forward (format org-element--latex-end-environment (regexp-quote (match-string 1))) limit t)) ;; Incomplete latex environment: parse it as a paragraph. @@ -2080,9 +2197,8 @@ Assume point is at the beginning of the latex environment." :post-affiliated code-begin) (cdr affiliated)))))))) -(defun org-element-latex-environment-interpreter (latex-environment contents) - "Interpret LATEX-ENVIRONMENT element as Org syntax. -CONTENTS is nil." +(defun org-element-latex-environment-interpreter (latex-environment _) + "Interpret LATEX-ENVIRONMENT element as Org syntax." (org-element-property :value latex-environment)) @@ -2094,12 +2210,13 @@ CONTENTS is nil." LIMIT bounds the search. Return a list whose CAR is `node-property' and CDR is a plist -containing `:key', `:value', `:begin', `:end' and `:post-blank' -keywords." +containing `:key', `:value', `:begin', `:end', `:post-blank' and +`:post-affiliated' keywords." (looking-at org-property-re) - (let ((begin (point)) - (key (org-match-string-no-properties 2)) - (value (org-match-string-no-properties 3)) + (let ((case-fold-search t) + (begin (point)) + (key (match-string-no-properties 2)) + (value (match-string-no-properties 3)) (end (save-excursion (end-of-line) (if (re-search-forward org-property-re limit t) @@ -2110,11 +2227,11 @@ keywords." :value value :begin begin :end end - :post-blank 0)))) + :post-blank 0 + :post-affiliated begin)))) -(defun org-element-node-property-interpreter (node-property contents) - "Interpret NODE-PROPERTY element as Org syntax. -CONTENTS is nil." +(defun org-element-node-property-interpreter (node-property _) + "Interpret NODE-PROPERTY element as Org syntax." (format org-property-format (format ":%s:" (org-element-property :key node-property)) (or (org-element-property :value node-property) ""))) @@ -2141,66 +2258,42 @@ Assume point is at the beginning of the paragraph." (before-blank (let ((case-fold-search t)) (end-of-line) - (if (not (re-search-forward - org-element-paragraph-separate limit 'm)) - limit - ;; A matching `org-element-paragraph-separate' is not - ;; necessarily the end of the paragraph. In - ;; particular, lines starting with # or : as a first - ;; non-space character are ambiguous. We have to - ;; check if they are valid Org syntax (e.g., not an - ;; incomplete keyword). - (beginning-of-line) - (while (not - (or - ;; There's no ambiguity for other symbols or - ;; empty lines: stop here. - (looking-at "[ \t]*\\(?:[^:#]\\|$\\)") - ;; Stop at valid fixed-width areas. - (looking-at "[ \t]*:\\(?: \\|$\\)") - ;; Stop at drawers. - (and (looking-at org-drawer-regexp) - (save-excursion - (re-search-forward - "^[ \t]*:END:[ \t]*$" limit t))) - ;; Stop at valid comments. - (looking-at "[ \t]*#\\(?: \\|$\\)") - ;; Stop at valid dynamic blocks. - (and (looking-at org-dblock-start-re) - (save-excursion - (re-search-forward - "^[ \t]*#\\+END:?[ \t]*$" limit t))) - ;; Stop at valid blocks. - (and (looking-at "[ \t]*#\\+BEGIN_\\(\\S-+\\)") - (save-excursion - (re-search-forward - (format "^[ \t]*#\\+END_%s[ \t]*$" - (regexp-quote - (org-match-string-no-properties 1))) - limit t))) - ;; Stop at valid latex environments. - (and (looking-at - "[ \t]*\\\\begin{\\([A-Za-z0-9]+\\*?\\)}") - (save-excursion - (re-search-forward - (format "^[ \t]*\\\\end{%s}[ \t]*$" - (regexp-quote - (org-match-string-no-properties 1))) - limit t))) - ;; Stop at valid keywords. - (looking-at "[ \t]*#\\+\\S-+:") - ;; Skip everything else. - (not - (progn - (end-of-line) - (re-search-forward org-element-paragraph-separate - limit 'm))))) - (beginning-of-line))) + ;; A matching `org-element-paragraph-separate' is not + ;; necessarily the end of the paragraph. In particular, + ;; drawers, blocks or LaTeX environments opening lines + ;; must be closed. Moreover keywords with a secondary + ;; value must belong to "dual keywords". + (while (not + (cond + ((not (and (re-search-forward + org-element-paragraph-separate limit 'move) + (progn (beginning-of-line) t)))) + ((looking-at org-drawer-regexp) + (save-excursion + (re-search-forward "^[ \t]*:END:[ \t]*$" limit t))) + ((looking-at "[ \t]*#\\+BEGIN_\\(\\S-+\\)") + (save-excursion + (re-search-forward + (format "^[ \t]*#\\+END_%s[ \t]*$" + (regexp-quote (match-string 1))) + limit t))) + ((looking-at org-element--latex-begin-environment) + (save-excursion + (re-search-forward + (format org-element--latex-end-environment + (regexp-quote (match-string 1))) + limit t))) + ((looking-at "[ \t]*#\\+\\(\\S-+\\)\\[.*\\]:") + (member-ignore-case (match-string 1) + org-element-dual-keywords)) + ;; Everything else is unambiguous. + (t))) + (end-of-line)) (if (= (point) limit) limit (goto-char (line-beginning-position))))) - (contents-end (progn (skip-chars-backward " \r\t\n" contents-begin) - (forward-line) - (point))) + (contents-end (save-excursion + (skip-chars-backward " \r\t\n" contents-begin) + (line-beginning-position 2))) (end (progn (skip-chars-forward " \r\t\n" limit) (if (eobp) (point) (line-beginning-position))))) (list 'paragraph @@ -2213,8 +2306,8 @@ Assume point is at the beginning of the paragraph." :post-affiliated contents-begin) (cdr affiliated)))))) -(defun org-element-paragraph-interpreter (paragraph contents) - "Interpret PARAGRAPH element as Org syntax. +(defun org-element-paragraph-interpreter (_ contents) + "Interpret paragraph element as Org syntax. CONTENTS is the contents of the element." contents) @@ -2227,8 +2320,8 @@ CONTENTS is the contents of the element." LIMIT bounds the search. Return a list whose CAR is `planning' and CDR is a plist -containing `:closed', `:deadline', `:scheduled', `:begin', `:end' -and `:post-blank' keywords." +containing `:closed', `:deadline', `:scheduled', `:begin', +`:end', `:post-blank' and `:post-affiliated' keywords." (save-excursion (let* ((case-fold-search nil) (begin (point)) @@ -2254,13 +2347,13 @@ and `:post-blank' keywords." :scheduled scheduled :begin begin :end end - :post-blank post-blank))))) + :post-blank post-blank + :post-affiliated begin))))) -(defun org-element-planning-interpreter (planning contents) - "Interpret PLANNING element as Org syntax. -CONTENTS is nil." +(defun org-element-planning-interpreter (planning _) + "Interpret PLANNING element as Org syntax." (mapconcat - 'identity + #'identity (delq nil (list (let ((deadline (org-element-property :deadline planning))) (when deadline @@ -2277,37 +2370,6 @@ CONTENTS is nil." " ")) -;;;; Quote Section - -(defun org-element-quote-section-parser (limit) - "Parse a quote section. - -LIMIT bounds the search. - -Return a list whose CAR is `quote-section' and CDR is a plist -containing `:begin', `:end', `:value' and `:post-blank' keywords. - -Assume point is at beginning of the section." - (save-excursion - (let* ((begin (point)) - (end (progn (org-with-limited-levels (outline-next-heading)) - (point))) - (pos-before-blank (progn (skip-chars-backward " \r\t\n") - (forward-line) - (point))) - (value (buffer-substring-no-properties begin pos-before-blank))) - (list 'quote-section - (list :begin begin - :end end - :value value - :post-blank (count-lines pos-before-blank end)))))) - -(defun org-element-quote-section-interpreter (quote-section contents) - "Interpret QUOTE-SECTION element as Org syntax. -CONTENTS is nil." - (org-element-property :value quote-section)) - - ;;;; Src Block (defun org-element-src-block-parser (limit affiliated) @@ -2320,9 +2382,9 @@ their value. Return a list whose CAR is `src-block' and CDR is a plist containing `:language', `:switches', `:parameters', `:begin', -`:end', `:hiddenp', `:number-lines', `:retain-labels', -`:use-labels', `:label-fmt', `:preserve-indent', `:value', -`:post-blank' and `:post-affiliated' keywords. +`:end', `:number-lines', `:retain-labels', `:use-labels', +`:label-fmt', `:preserve-indent', `:value', `:post-blank' and +`:post-affiliated' keywords. Assume point is at the beginning of the block." (let ((case-fold-search t)) @@ -2338,23 +2400,30 @@ Assume point is at the beginning of the block." (language (progn (looking-at - (concat "^[ \t]*#\\+BEGIN_SRC" - "\\(?: +\\(\\S-+\\)\\)?" - "\\(\\(?: +\\(?:-l \".*?\"\\|[-+][A-Za-z]\\)\\)+\\)?" - "\\(.*\\)[ \t]*$")) - (org-match-string-no-properties 1))) + "^[ \t]*#\\+BEGIN_SRC\ +\\(?: +\\(\\S-+\\)\\)?\ +\\(\\(?: +\\(?:-\\(?:l \".+\"\\|[ikr]\\)\\|[-+]n\\(?: *[0-9]+\\)?\\)\\)+\\)?\ +\\(.*\\)[ \t]*$") + (match-string-no-properties 1))) ;; Get switches. - (switches (org-match-string-no-properties 2)) + (switches (match-string-no-properties 2)) ;; Get parameters. - (parameters (org-match-string-no-properties 3)) - ;; Switches analysis + (parameters (match-string-no-properties 3)) + ;; Switches analysis. (number-lines - (cond ((not switches) nil) - ((string-match "-n\\>" switches) 'new) - ((string-match "+n\\>" switches) 'continued))) - (preserve-indent (or org-src-preserve-indentation - (and switches - (string-match "-i\\>" switches)))) + (and switches + (string-match "\\([-+]\\)n\\(?: *\\([0-9]+\\)\\)?\\>" + switches) + (cons + (if (equal (match-string 1 switches) "-") + 'new + 'continued) + (if (not (match-end 2)) 0 + ;; Subtract 1 to give number of lines before + ;; first line. + (1- (string-to-number (match-string 2 switches))))))) + (preserve-indent (and switches + (string-match "-i\\>" switches))) (label-fmt (and switches (string-match "-l +\"\\([^\"\n]+\\)\"" switches) @@ -2371,16 +2440,10 @@ Assume point is at the beginning of the block." (or (not switches) (and retain-labels (not (string-match "-k\\>" switches))))) - ;; Indentation. - (block-ind (progn (skip-chars-forward " \t") (current-column))) - ;; Get visibility status. - (hidden (progn (forward-line) (org-invisible-p2))) ;; Retrieve code. - (value (org-element--remove-indentation - (org-unescape-code-in-string - (buffer-substring-no-properties - (point) contents-end)) - (and preserve-indent block-ind))) + (value (org-unescape-code-in-string + (buffer-substring-no-properties + (line-beginning-position 2) contents-end))) (pos-before-blank (progn (goto-char contents-end) (forward-line) (point))) @@ -2401,32 +2464,33 @@ Assume point is at the beginning of the block." :retain-labels retain-labels :use-labels use-labels :label-fmt label-fmt - :hiddenp hidden :value value :post-blank (count-lines pos-before-blank end) :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-src-block-interpreter (src-block contents) - "Interpret SRC-BLOCK element as Org syntax. -CONTENTS is nil." +(defun org-element-src-block-interpreter (src-block _) + "Interpret SRC-BLOCK element as Org syntax." (let ((lang (org-element-property :language src-block)) (switches (org-element-property :switches src-block)) (params (org-element-property :parameters src-block)) - (value (let ((val (org-element-property :value src-block))) - (cond - ((org-element-property :preserve-indent src-block) val) - ((zerop org-edit-src-content-indentation) val) - (t - (let ((ind (make-string - org-edit-src-content-indentation 32))) - (replace-regexp-in-string - "\\(^\\)[ \t]*\\S-" ind val nil nil 1))))))) + (value + (let ((val (org-element-property :value src-block))) + (cond + ((or org-src-preserve-indentation + (org-element-property :preserve-indent src-block)) + val) + ((zerop org-edit-src-content-indentation) + (org-remove-indentation val)) + (t + (let ((ind (make-string org-edit-src-content-indentation ?\s))) + (replace-regexp-in-string + "^" ind (org-remove-indentation val)))))))) (concat (format "#+BEGIN_SRC%s\n" (concat (and lang (concat " " lang)) (and switches (concat " " switches)) (and params (concat " " params)))) - (org-escape-code-in-string value) + (org-element-normalize-string (org-escape-code-in-string value)) "#+END_SRC"))) @@ -2449,15 +2513,17 @@ Assume point is at the beginning of the table." (save-excursion (let* ((case-fold-search t) (table-begin (point)) - (type (if (org-at-table.el-p) 'table.el 'org)) + (type (if (looking-at "[ \t]*|") 'org 'table.el)) + (end-re (format "^[ \t]*\\($\\|[^| \t%s]\\)" + (if (eq type 'org) "" "+"))) (begin (car affiliated)) (table-end - (if (re-search-forward org-table-any-border-regexp limit 'm) + (if (re-search-forward end-re limit 'move) (goto-char (match-beginning 0)) (point))) (tblfm (let (acc) (while (looking-at "[ \t]*#\\+TBLFM: +\\(.*\\)[ \t]*$") - (push (org-match-string-no-properties 1) acc) + (push (match-string-no-properties 1) acc) (forward-line)) acc)) (pos-before-blank (point)) @@ -2496,41 +2562,38 @@ CONTENTS is a string, if table's type is `org', or nil." ;;;; Table Row -(defun org-element-table-row-parser (limit) +(defun org-element-table-row-parser (_) "Parse table row at point. -LIMIT bounds the search. - Return a list whose CAR is `table-row' and CDR is a plist containing `:begin', `:end', `:contents-begin', `:contents-end', -`:type' and `:post-blank' keywords." +`:type', `:post-blank' and `:post-affiliated' keywords." (save-excursion (let* ((type (if (looking-at "^[ \t]*|-") 'rule 'standard)) (begin (point)) ;; A table rule has no contents. In that case, ensure ;; CONTENTS-BEGIN matches CONTENTS-END. - (contents-begin (and (eq type 'standard) - (search-forward "|") - (point))) + (contents-begin (and (eq type 'standard) (search-forward "|"))) (contents-end (and (eq type 'standard) (progn (end-of-line) (skip-chars-backward " \t") (point)))) - (end (progn (forward-line) (point)))) + (end (line-beginning-position 2))) (list 'table-row (list :type type :begin begin :end end :contents-begin contents-begin :contents-end contents-end - :post-blank 0))))) + :post-blank 0 + :post-affiliated begin))))) (defun org-element-table-row-interpreter (table-row contents) "Interpret TABLE-ROW element as Org syntax. CONTENTS is the contents of the table row." (if (eq (org-element-property :type table-row) 'rule) "|-" - (concat "| " contents))) + (concat "|" contents))) ;;;; Verse Block @@ -2545,7 +2608,7 @@ their value. Return a list whose CAR is `verse-block' and CDR is a plist containing `:begin', `:end', `:contents-begin', `:contents-end', -`:hiddenp', `:post-blank' and `:post-affiliated' keywords. +`:post-blank' and `:post-affiliated' keywords. Assume point is at beginning of the block." (let ((case-fold-search t)) @@ -2557,8 +2620,7 @@ Assume point is at beginning of the block." (save-excursion (let* ((begin (car affiliated)) (post-affiliated (point)) - (hidden (progn (forward-line) (org-invisible-p2))) - (contents-begin (point)) + (contents-begin (progn (forward-line) (point))) (pos-before-blank (progn (goto-char contents-end) (forward-line) (point))) @@ -2570,13 +2632,12 @@ Assume point is at beginning of the block." :end end :contents-begin contents-begin :contents-end contents-end - :hiddenp hidden :post-blank (count-lines pos-before-blank end) :post-affiliated post-affiliated) (cdr affiliated))))))))) -(defun org-element-verse-block-interpreter (verse-block contents) - "Interpret VERSE-BLOCK element as Org syntax. +(defun org-element-verse-block-interpreter (_ contents) + "Interpret verse-block element as Org syntax. CONTENTS is verse block contents." (format "#+BEGIN_VERSE\n%s#+END_VERSE" contents)) @@ -2584,373 +2645,289 @@ CONTENTS is verse block contents." ;;; Objects ;; -;; Unlike to elements, interstices can be found between objects. -;; That's why, along with the parser, successor functions are provided -;; for each object. Some objects share the same successor (e.g., -;; `code' and `verbatim' objects). -;; -;; A successor must accept a single argument bounding the search. It -;; will return either a cons cell whose CAR is the object's type, as -;; a symbol, and CDR the position of its next occurrence, or nil. -;; -;; Successors follow the naming convention: -;; org-element-NAME-successor, where NAME is the name of the -;; successor, as defined in `org-element-all-successors'. +;; Unlike to elements, raw text can be found between objects. Hence, +;; `org-element--object-lex' is provided to find the next object in +;; buffer. ;; ;; Some object types (e.g., `italic') are recursive. Restrictions on ;; object types they can contain will be specified in ;; `org-element-object-restrictions'. ;; -;; Adding a new type of object is simple. Implement a successor, -;; a parser, and an interpreter for it, all following the naming -;; convention. Register type in `org-element-all-objects' and -;; successor in `org-element-all-successors'. Maybe tweak -;; restrictions about it, and that's it. - +;; Creating a new type of object requires to alter +;; `org-element--object-regexp' and `org-element--object-lex', add the +;; new type in `org-element-all-objects', and possibly add +;; restrictions in `org-element-object-restrictions'. ;;;; Bold (defun org-element-bold-parser () - "Parse bold object at point. + "Parse bold object at point, if any. -Return a list whose CAR is `bold' and CDR is a plist with -`:begin', `:end', `:contents-begin' and `:contents-end' and -`:post-blank' keywords. +When at a bold object, return a list whose car is `bold' and cdr +is a plist with `:begin', `:end', `:contents-begin' and +`:contents-end' and `:post-blank' keywords. Otherwise, return +nil. Assume point is at the first star marker." (save-excursion (unless (bolp) (backward-char 1)) - (looking-at org-emph-re) - (let ((begin (match-beginning 2)) - (contents-begin (match-beginning 4)) - (contents-end (match-end 4)) - (post-blank (progn (goto-char (match-end 2)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'bold - (list :begin begin - :end end - :contents-begin contents-begin - :contents-end contents-end - :post-blank post-blank))))) - -(defun org-element-bold-interpreter (bold contents) - "Interpret BOLD object as Org syntax. + (when (looking-at org-emph-re) + (let ((begin (match-beginning 2)) + (contents-begin (match-beginning 4)) + (contents-end (match-end 4)) + (post-blank (progn (goto-char (match-end 2)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'bold + (list :begin begin + :end end + :contents-begin contents-begin + :contents-end contents-end + :post-blank post-blank)))))) + +(defun org-element-bold-interpreter (_ contents) + "Interpret bold object as Org syntax. CONTENTS is the contents of the object." (format "*%s*" contents)) -(defun org-element-text-markup-successor () - "Search for the next text-markup object. - -Return value is a cons cell whose CAR is a symbol among `bold', -`italic', `underline', `strike-through', `code' and `verbatim' -and CDR is beginning position." - (save-excursion - (unless (bolp) (backward-char)) - (when (re-search-forward org-emph-re nil t) - (let ((marker (match-string 3))) - (cons (cond - ((equal marker "*") 'bold) - ((equal marker "/") 'italic) - ((equal marker "_") 'underline) - ((equal marker "+") 'strike-through) - ((equal marker "~") 'code) - ((equal marker "=") 'verbatim) - (t (error "Unknown marker at %d" (match-beginning 3)))) - (match-beginning 2)))))) - ;;;; Code (defun org-element-code-parser () - "Parse code object at point. + "Parse code object at point, if any. -Return a list whose CAR is `code' and CDR is a plist with -`:value', `:begin', `:end' and `:post-blank' keywords. +When at a code object, return a list whose car is `code' and cdr +is a plist with `:value', `:begin', `:end' and `:post-blank' +keywords. Otherwise, return nil. Assume point is at the first tilde marker." (save-excursion (unless (bolp) (backward-char 1)) - (looking-at org-emph-re) - (let ((begin (match-beginning 2)) - (value (org-match-string-no-properties 4)) - (post-blank (progn (goto-char (match-end 2)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'code - (list :value value - :begin begin - :end end - :post-blank post-blank))))) - -(defun org-element-code-interpreter (code contents) - "Interpret CODE object as Org syntax. -CONTENTS is nil." + (when (looking-at org-emph-re) + (let ((begin (match-beginning 2)) + (value (match-string-no-properties 4)) + (post-blank (progn (goto-char (match-end 2)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'code + (list :value value + :begin begin + :end end + :post-blank post-blank)))))) + +(defun org-element-code-interpreter (code _) + "Interpret CODE object as Org syntax." (format "~%s~" (org-element-property :value code))) ;;;; Entity (defun org-element-entity-parser () - "Parse entity at point. + "Parse entity at point, if any. -Return a list whose CAR is `entity' and CDR a plist with -`:begin', `:end', `:latex', `:latex-math-p', `:html', `:latin1', -`:utf-8', `:ascii', `:use-brackets-p' and `:post-blank' as -keywords. +When at an entity, return a list whose car is `entity' and cdr +a plist with `:begin', `:end', `:latex', `:latex-math-p', +`:html', `:latin1', `:utf-8', `:ascii', `:use-brackets-p' and +`:post-blank' as keywords. Otherwise, return nil. Assume point is at the beginning of the entity." - (save-excursion - (looking-at "\\\\\\(there4\\|sup[123]\\|frac[13][24]\\|[a-zA-Z]+\\)\\($\\|{}\\|[^[:alpha:]]\\)") - (let* ((value (org-entity-get (match-string 1))) - (begin (match-beginning 0)) - (bracketsp (string= (match-string 2) "{}")) - (post-blank (progn (goto-char (match-end 1)) - (when bracketsp (forward-char 2)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'entity - (list :name (car value) - :latex (nth 1 value) - :latex-math-p (nth 2 value) - :html (nth 3 value) - :ascii (nth 4 value) - :latin1 (nth 5 value) - :utf-8 (nth 6 value) - :begin begin - :end end - :use-brackets-p bracketsp - :post-blank post-blank))))) - -(defun org-element-entity-interpreter (entity contents) - "Interpret ENTITY object as Org syntax. -CONTENTS is nil." + (catch 'no-object + (when (looking-at "\\\\\\(?:\\(?1:_ +\\)\\|\\(?1:there4\\|sup[123]\\|frac[13][24]\\|[a-zA-Z]+\\)\\(?2:$\\|{}\\|[^[:alpha:]]\\)\\)") + (save-excursion + (let* ((value (or (org-entity-get (match-string 1)) + (throw 'no-object nil))) + (begin (match-beginning 0)) + (bracketsp (string= (match-string 2) "{}")) + (post-blank (progn (goto-char (match-end 1)) + (when bracketsp (forward-char 2)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'entity + (list :name (car value) + :latex (nth 1 value) + :latex-math-p (nth 2 value) + :html (nth 3 value) + :ascii (nth 4 value) + :latin1 (nth 5 value) + :utf-8 (nth 6 value) + :begin begin + :end end + :use-brackets-p bracketsp + :post-blank post-blank))))))) + +(defun org-element-entity-interpreter (entity _) + "Interpret ENTITY object as Org syntax." (concat "\\" (org-element-property :name entity) (when (org-element-property :use-brackets-p entity) "{}"))) -(defun org-element-latex-or-entity-successor () - "Search for the next latex-fragment or entity object. - -Return value is a cons cell whose CAR is `entity' or -`latex-fragment' and CDR is beginning position." - (save-excursion - (unless (bolp) (backward-char)) - (let ((matchers (cdr org-latex-regexps)) - ;; ENTITY-RE matches both LaTeX commands and Org entities. - (entity-re - "\\\\\\(there4\\|sup[123]\\|frac[13][24]\\|[a-zA-Z]+\\)\\($\\|{}\\|[^[:alpha:]]\\)")) - (when (re-search-forward - (concat (mapconcat #'cadr matchers "\\|") "\\|" entity-re) nil t) - (goto-char (match-beginning 0)) - (if (looking-at entity-re) - ;; Determine if it's a real entity or a LaTeX command. - (cons (if (org-entity-get (match-string 1)) 'entity 'latex-fragment) - (match-beginning 0)) - ;; No entity nor command: point is at a LaTeX fragment. - ;; Determine its type to get the correct beginning position. - (cons 'latex-fragment - (catch 'return - (dolist (e matchers) - (when (looking-at (nth 1 e)) - (throw 'return (match-beginning (nth 2 e))))) - (point)))))))) - ;;;; Export Snippet (defun org-element-export-snippet-parser () "Parse export snippet at point. -Return a list whose CAR is `export-snippet' and CDR a plist with -`:begin', `:end', `:back-end', `:value' and `:post-blank' as -keywords. +When at an export snippet, return a list whose car is +`export-snippet' and cdr a plist with `:begin', `:end', +`:back-end', `:value' and `:post-blank' as keywords. Otherwise, +return nil. Assume point is at the beginning of the snippet." (save-excursion - (re-search-forward "@@\\([-A-Za-z0-9]+\\):" nil t) - (let* ((begin (match-beginning 0)) - (back-end (org-match-string-no-properties 1)) - (value (buffer-substring-no-properties - (point) - (progn (re-search-forward "@@" nil t) (match-beginning 0)))) - (post-blank (skip-chars-forward " \t")) - (end (point))) - (list 'export-snippet - (list :back-end back-end - :value value - :begin begin - :end end - :post-blank post-blank))))) - -(defun org-element-export-snippet-interpreter (export-snippet contents) - "Interpret EXPORT-SNIPPET object as Org syntax. -CONTENTS is nil." + (let (contents-end) + (when (and (looking-at "@@\\([-A-Za-z0-9]+\\):") + (setq contents-end + (save-match-data (goto-char (match-end 0)) + (re-search-forward "@@" nil t) + (match-beginning 0)))) + (let* ((begin (match-beginning 0)) + (back-end (match-string-no-properties 1)) + (value (buffer-substring-no-properties + (match-end 0) contents-end)) + (post-blank (skip-chars-forward " \t")) + (end (point))) + (list 'export-snippet + (list :back-end back-end + :value value + :begin begin + :end end + :post-blank post-blank))))))) + +(defun org-element-export-snippet-interpreter (export-snippet _) + "Interpret EXPORT-SNIPPET object as Org syntax." (format "@@%s:%s@@" (org-element-property :back-end export-snippet) (org-element-property :value export-snippet))) -(defun org-element-export-snippet-successor () - "Search for the next export-snippet object. - -Return value is a cons cell whose CAR is `export-snippet' and CDR -its beginning position." - (save-excursion - (let (beg) - (when (and (re-search-forward "@@[-A-Za-z0-9]+:" nil t) - (setq beg (match-beginning 0)) - (search-forward "@@" nil t)) - (cons 'export-snippet beg))))) - ;;;; Footnote Reference (defun org-element-footnote-reference-parser () - "Parse footnote reference at point. - -Return a list whose CAR is `footnote-reference' and CDR a plist -with `:label', `:type', `:inline-definition', `:begin', `:end' -and `:post-blank' as keywords." - (save-excursion - (looking-at org-footnote-re) - (let* ((begin (point)) - (label (or (org-match-string-no-properties 2) - (org-match-string-no-properties 3) - (and (match-string 1) - (concat "fn:" (org-match-string-no-properties 1))))) - (type (if (or (not label) (match-string 1)) 'inline 'standard)) - (inner-begin (match-end 0)) - (inner-end - (let ((count 1)) - (forward-char) - (while (and (> count 0) (re-search-forward "[][]" nil t)) - (if (equal (match-string 0) "[") (incf count) (decf count))) - (1- (point)))) - (post-blank (progn (goto-char (1+ inner-end)) - (skip-chars-forward " \t"))) - (end (point)) - (footnote-reference + "Parse footnote reference at point, if any. + +When at a footnote reference, return a list whose car is +`footnote-reference' and cdr a plist with `:label', `:type', +`:begin', `:end', `:content-begin', `:contents-end' and +`:post-blank' as keywords. Otherwise, return nil." + (when (looking-at org-footnote-re) + (let ((closing (with-syntax-table org-element--pair-square-table + (ignore-errors (scan-lists (point) 1 0))))) + (when closing + (save-excursion + (let* ((begin (point)) + (label (match-string-no-properties 1)) + (inner-begin (match-end 0)) + (inner-end (1- closing)) + (type (if (match-end 2) 'inline 'standard)) + (post-blank (progn (goto-char closing) + (skip-chars-forward " \t"))) + (end (point))) (list 'footnote-reference (list :label label :type type :begin begin :end end - :post-blank post-blank)))) - (org-element-put-property - footnote-reference :inline-definition - (and (eq type 'inline) - (org-element-parse-secondary-string - (buffer-substring inner-begin inner-end) - (org-element-restriction 'footnote-reference) - footnote-reference)))))) + :contents-begin (and (eq type 'inline) inner-begin) + :contents-end (and (eq type 'inline) inner-end) + :post-blank post-blank)))))))) (defun org-element-footnote-reference-interpreter (footnote-reference contents) "Interpret FOOTNOTE-REFERENCE object as Org syntax. -CONTENTS is nil." - (let ((label (or (org-element-property :label footnote-reference) "fn:")) - (def - (let ((inline-def - (org-element-property :inline-definition footnote-reference))) - (if (not inline-def) "" - (concat ":" (org-element-interpret-data inline-def)))))) - (format "[%s]" (concat label def)))) - -(defun org-element-footnote-reference-successor () - "Search for the next footnote-reference object. - -Return value is a cons cell whose CAR is `footnote-reference' and -CDR is beginning position." - (save-excursion - (catch 'exit - (while (re-search-forward org-footnote-re nil t) - (save-excursion - (let ((beg (match-beginning 0)) - (count 1)) - (backward-char) - (while (re-search-forward "[][]" nil t) - (if (equal (match-string 0) "[") (incf count) (decf count)) - (when (zerop count) - (throw 'exit (cons 'footnote-reference beg)))))))))) +CONTENTS is its definition, when inline, or nil." + (format "[fn:%s%s]" + (or (org-element-property :label footnote-reference) "") + (if contents (concat ":" contents) ""))) ;;;; Inline Babel Call (defun org-element-inline-babel-call-parser () - "Parse inline babel call at point. + "Parse inline babel call at point, if any. -Return a list whose CAR is `inline-babel-call' and CDR a plist -with `:begin', `:end', `:info' and `:post-blank' as keywords. +When at an inline babel call, return a list whose car is +`inline-babel-call' and cdr a plist with `:call', +`:inside-header', `:arguments', `:end-header', `:begin', `:end', +`:value' and `:post-blank' as keywords. Otherwise, return nil. Assume point is at the beginning of the babel call." (save-excursion - (unless (bolp) (backward-char)) - (looking-at org-babel-inline-lob-one-liner-regexp) - (let ((info (save-match-data (org-babel-lob-get-info))) - (begin (match-end 1)) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'inline-babel-call - (list :begin begin - :end end - :info info - :post-blank post-blank))))) - -(defun org-element-inline-babel-call-interpreter (inline-babel-call contents) - "Interpret INLINE-BABEL-CALL object as Org syntax. -CONTENTS is nil." - (let* ((babel-info (org-element-property :info inline-babel-call)) - (main-source (car babel-info)) - (post-options (nth 1 babel-info))) - (concat "call_" - (if (string-match "\\[\\(\\[.*?\\]\\)\\]" main-source) - ;; Remove redundant square brackets. - (replace-match - (match-string 1 main-source) nil nil main-source) - main-source) - (and post-options (format "[%s]" post-options))))) - -(defun org-element-inline-babel-call-successor () - "Search for the next inline-babel-call object. - -Return value is a cons cell whose CAR is `inline-babel-call' and -CDR is beginning position." - (save-excursion - (when (re-search-forward org-babel-inline-lob-one-liner-regexp nil t) - (cons 'inline-babel-call (match-end 1))))) + (catch :no-object + (when (let ((case-fold-search nil)) + (looking-at "\\ + (setq format 'plain) + (setq raw-link (match-string-no-properties 0)) + (setq type (match-string-no-properties 1)) + (setq link-end (match-end 0)) + (setq path (match-string-no-properties 2))) + ;; Type 4: Angular link, e.g., . Unlike to + ;; bracket links, follow RFC 3986 and remove any extra + ;; whitespace in URI. ((looking-at org-angle-link-re) - (setq raw-link (buffer-substring-no-properties - (match-beginning 1) (match-end 2)) - type (org-match-string-no-properties 1) - link-end (match-end 0) - path (org-match-string-no-properties 2)))) + (setq format 'angle) + (setq type (match-string-no-properties 1)) + (setq link-end (match-end 0)) + (setq raw-link + (buffer-substring-no-properties + (match-beginning 1) (match-end 2))) + (setq path (replace-regexp-in-string + "[ \t]*\n[ \t]*" "" (match-string-no-properties 2)))) + (t (throw 'no-object nil))) ;; In any case, deduce end point after trailing white space from ;; LINK-END variable. - (setq post-blank (progn (goto-char link-end) (skip-chars-forward " \t")) - end (point)) - ;; Special "file" type link processing. - (when (member type org-element-link-type-is-file) - ;; Extract opening application and search option. - (cond ((string-match "^file\\+\\(.*\\)$" type) - (setq application (match-string 1 type))) - ((not (string-match "^file" type)) - (setq application type))) + (save-excursion + (setq post-blank + (progn (goto-char link-end) (skip-chars-forward " \t"))) + (setq end (point))) + ;; Special "file" type link processing. Extract opening + ;; application and search option, if any. Also normalize URI. + (when (string-match "\\`file\\(?:\\+\\(.+\\)\\)?\\'" type) + (setq application (match-string 1 type) type "file") (when (string-match "::\\(.*\\)\\'" path) - (setq search-option (match-string 1 path) - path (replace-match "" nil nil path))) - ;; Normalize URI. - (when (and (not (org-string-match-p "\\`//" path)) - (file-name-absolute-p path)) - (setq path (concat "//" (expand-file-name path)))) - ;; Make sure TYPE always reports "file". - (setq type "file")) + (setq search-option (match-string 1 path)) + (setq path (replace-match "" nil nil path))) + (setq path (replace-regexp-in-string "\\`///*\\(.:\\)?/" "\\1/" path))) + ;; Translate link, if `org-link-translation-function' is set. + (let ((trans (and (functionp org-link-translation-function) + (funcall org-link-translation-function type path)))) + (when trans + (setq type (car trans)) + (setq path (cdr trans)))) (list 'link (list :type type :path path + :format format :raw-link (or raw-link path) :application application :search-option search-option @@ -3180,197 +3170,167 @@ Assume point is at the beginning of the link." "Interpret LINK object as Org syntax. CONTENTS is the contents of the object, or nil." (let ((type (org-element-property :type link)) - (raw-link (org-element-property :raw-link link))) - (if (string= type "radio") raw-link - (format "[[%s]%s]" - raw-link - (if contents (format "[%s]" contents) ""))))) - -(defun org-element-link-successor () - "Search for the next link object. - -Return value is a cons cell whose CAR is `link' and CDR is -beginning position." - (save-excursion - (let ((link-regexp - (if (not org-target-link-regexp) org-any-link-re - (concat org-any-link-re "\\|" org-target-link-regexp)))) - (when (re-search-forward link-regexp nil t) - (cons 'link (match-beginning 0)))))) - -(defun org-element-plain-link-successor () - "Search for the next plain link object. - -Return value is a cons cell whose CAR is `link' and CDR is -beginning position." - (and (save-excursion (re-search-forward org-plain-link-re nil t)) - (cons 'link (match-beginning 0)))) + (path (org-element-property :path link))) + (if (string= type "radio") path + (let ((fmt (pcase (org-element-property :format link) + ;; Links with contents and internal links have to + ;; use bracket syntax. Ignore `:format' in these + ;; cases. This is also the default syntax when the + ;; property is not defined, e.g., when the object + ;; was crafted by the user. + ((guard contents) + (format "[[%%s][%s]]" + ;; Since this is going to be used as + ;; a format string, escape percent signs + ;; in description. + (replace-regexp-in-string "%" "%%" contents))) + ((or `bracket + `nil + (guard (member type '("coderef" "custom-id" "fuzzy")))) + "[[%s]]") + ;; Otherwise, just obey to `:format'. + (`angle "<%s>") + (`plain "%s") + (f (error "Wrong `:format' value: %s" f))))) + (format fmt + (pcase type + ("coderef" (format "(%s)" path)) + ("custom-id" (concat "#" path)) + ("file" + (let ((app (org-element-property :application link)) + (opt (org-element-property :search-option link))) + (concat type (and app (concat "+" app)) ":" + path + (and opt (concat "::" opt))))) + ("fuzzy" path) + (_ (concat type ":" path)))))))) ;;;; Macro (defun org-element-macro-parser () - "Parse macro at point. + "Parse macro at point, if any. -Return a list whose CAR is `macro' and CDR a plist with `:key', -`:args', `:begin', `:end', `:value' and `:post-blank' as -keywords. +When at a macro, return a list whose car is `macro' and cdr +a plist with `:key', `:args', `:begin', `:end', `:value' and +`:post-blank' as keywords. Otherwise, return nil. Assume point is at the macro." (save-excursion - (looking-at "{{{\\([a-zA-Z][-a-zA-Z0-9_]*\\)\\(([ \t\n]*\\([^\000]*?\\))\\)?}}}") - (let ((begin (point)) - (key (downcase (org-match-string-no-properties 1))) - (value (org-match-string-no-properties 0)) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point)) - (args (let ((args (org-match-string-no-properties 3))) - (when args - ;; Do not use `org-split-string' since empty - ;; strings are meaningful here. - (split-string - (replace-regexp-in-string - "\\(\\\\*\\)\\(,\\)" - (lambda (str) - (let ((len (length (match-string 1 str)))) - (concat (make-string (/ len 2) ?\\) - (if (zerop (mod len 2)) "\000" ",")))) - args nil t) - "\000"))))) - (list 'macro - (list :key key - :value value - :args args - :begin begin - :end end - :post-blank post-blank))))) - -(defun org-element-macro-interpreter (macro contents) - "Interpret MACRO object as Org syntax. -CONTENTS is nil." + (when (looking-at "{{{\\([a-zA-Z][-a-zA-Z0-9_]*\\)\\(([ \t\n]*\\([^\000]*?\\))\\)?}}}") + (let ((begin (point)) + (key (downcase (match-string-no-properties 1))) + (value (match-string-no-properties 0)) + (post-blank (progn (goto-char (match-end 0)) + (skip-chars-forward " \t"))) + (end (point)) + (args (let ((args (match-string-no-properties 3))) + (and args (org-macro-extract-arguments args))))) + (list 'macro + (list :key key + :value value + :args args + :begin begin + :end end + :post-blank post-blank)))))) + +(defun org-element-macro-interpreter (macro _) + "Interpret MACRO object as Org syntax." (org-element-property :value macro)) -(defun org-element-macro-successor () - "Search for the next macro object. - -Return value is cons cell whose CAR is `macro' and CDR is -beginning position." - (save-excursion - (when (re-search-forward - "{{{\\([a-zA-Z][-a-zA-Z0-9_]*\\)\\(([ \t\n]*\\([^\000]*?\\))\\)?}}}" - nil t) - (cons 'macro (match-beginning 0))))) - ;;;; Radio-target (defun org-element-radio-target-parser () - "Parse radio target at point. + "Parse radio target at point, if any. -Return a list whose CAR is `radio-target' and CDR a plist with -`:begin', `:end', `:contents-begin', `:contents-end', `:value' -and `:post-blank' as keywords. +When at a radio target, return a list whose car is `radio-target' +and cdr a plist with `:begin', `:end', `:contents-begin', +`:contents-end', `:value' and `:post-blank' as keywords. +Otherwise, return nil. Assume point is at the radio target." (save-excursion - (looking-at org-radio-target-regexp) - (let ((begin (point)) - (contents-begin (match-beginning 1)) - (contents-end (match-end 1)) - (value (org-match-string-no-properties 1)) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'radio-target - (list :begin begin - :end end - :contents-begin contents-begin - :contents-end contents-end - :post-blank post-blank - :value value))))) - -(defun org-element-radio-target-interpreter (target contents) - "Interpret TARGET object as Org syntax. + (when (looking-at org-radio-target-regexp) + (let ((begin (point)) + (contents-begin (match-beginning 1)) + (contents-end (match-end 1)) + (value (match-string-no-properties 1)) + (post-blank (progn (goto-char (match-end 0)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'radio-target + (list :begin begin + :end end + :contents-begin contents-begin + :contents-end contents-end + :post-blank post-blank + :value value)))))) + +(defun org-element-radio-target-interpreter (_ contents) + "Interpret target object as Org syntax. CONTENTS is the contents of the object." (concat "<<<" contents ">>>")) -(defun org-element-radio-target-successor () - "Search for the next radio-target object. - -Return value is a cons cell whose CAR is `radio-target' and CDR -is beginning position." - (save-excursion - (when (re-search-forward org-radio-target-regexp nil t) - (cons 'radio-target (match-beginning 0))))) - ;;;; Statistics Cookie (defun org-element-statistics-cookie-parser () - "Parse statistics cookie at point. + "Parse statistics cookie at point, if any. -Return a list whose CAR is `statistics-cookie', and CDR a plist -with `:begin', `:end', `:value' and `:post-blank' keywords. +When at a statistics cookie, return a list whose car is +`statistics-cookie', and cdr a plist with `:begin', `:end', +`:value' and `:post-blank' keywords. Otherwise, return nil. Assume point is at the beginning of the statistics-cookie." (save-excursion - (looking-at "\\[[0-9]*\\(%\\|/[0-9]*\\)\\]") - (let* ((begin (point)) - (value (buffer-substring-no-properties - (match-beginning 0) (match-end 0))) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'statistics-cookie - (list :begin begin - :end end - :value value - :post-blank post-blank))))) - -(defun org-element-statistics-cookie-interpreter (statistics-cookie contents) - "Interpret STATISTICS-COOKIE object as Org syntax. -CONTENTS is nil." + (when (looking-at "\\[[0-9]*\\(%\\|/[0-9]*\\)\\]") + (let* ((begin (point)) + (value (buffer-substring-no-properties + (match-beginning 0) (match-end 0))) + (post-blank (progn (goto-char (match-end 0)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'statistics-cookie + (list :begin begin + :end end + :value value + :post-blank post-blank)))))) + +(defun org-element-statistics-cookie-interpreter (statistics-cookie _) + "Interpret STATISTICS-COOKIE object as Org syntax." (org-element-property :value statistics-cookie)) -(defun org-element-statistics-cookie-successor () - "Search for the next statistics cookie object. - -Return value is a cons cell whose CAR is `statistics-cookie' and -CDR is beginning position." - (save-excursion - (when (re-search-forward "\\[[0-9]*\\(%\\|/[0-9]*\\)\\]" nil t) - (cons 'statistics-cookie (match-beginning 0))))) - ;;;; Strike-Through (defun org-element-strike-through-parser () - "Parse strike-through object at point. + "Parse strike-through object at point, if any. -Return a list whose CAR is `strike-through' and CDR is a plist -with `:begin', `:end', `:contents-begin' and `:contents-end' and -`:post-blank' keywords. +When at a strike-through object, return a list whose car is +`strike-through' and cdr is a plist with `:begin', `:end', +`:contents-begin' and `:contents-end' and `:post-blank' keywords. +Otherwise, return nil. Assume point is at the first plus sign marker." (save-excursion (unless (bolp) (backward-char 1)) - (looking-at org-emph-re) - (let ((begin (match-beginning 2)) - (contents-begin (match-beginning 4)) - (contents-end (match-end 4)) - (post-blank (progn (goto-char (match-end 2)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'strike-through - (list :begin begin - :end end - :contents-begin contents-begin - :contents-end contents-end - :post-blank post-blank))))) - -(defun org-element-strike-through-interpreter (strike-through contents) - "Interpret STRIKE-THROUGH object as Org syntax. + (when (looking-at org-emph-re) + (let ((begin (match-beginning 2)) + (contents-begin (match-beginning 4)) + (contents-end (match-end 4)) + (post-blank (progn (goto-char (match-end 2)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'strike-through + (list :begin begin + :end end + :contents-begin contents-begin + :contents-end contents-end + :post-blank post-blank)))))) + +(defun org-element-strike-through-interpreter (_ contents) + "Interpret strike-through object as Org syntax. CONTENTS is the contents of the object." (format "+%s+" contents)) @@ -3378,32 +3338,32 @@ CONTENTS is the contents of the object." ;;;; Subscript (defun org-element-subscript-parser () - "Parse subscript at point. + "Parse subscript at point, if any. -Return a list whose CAR is `subscript' and CDR a plist with -`:begin', `:end', `:contents-begin', `:contents-end', -`:use-brackets-p' and `:post-blank' as keywords. +When at a subscript object, return a list whose car is +`subscript' and cdr a plist with `:begin', `:end', +`:contents-begin', `:contents-end', `:use-brackets-p' and +`:post-blank' as keywords. Otherwise, return nil. Assume point is at the underscore." (save-excursion (unless (bolp) (backward-char)) - (let ((bracketsp (if (looking-at org-match-substring-with-braces-regexp) - t - (not (looking-at org-match-substring-regexp)))) - (begin (match-beginning 2)) - (contents-begin (or (match-beginning 5) - (match-beginning 3))) - (contents-end (or (match-end 5) (match-end 3))) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'subscript - (list :begin begin - :end end - :use-brackets-p bracketsp - :contents-begin contents-begin - :contents-end contents-end - :post-blank post-blank))))) + (when (looking-at org-match-substring-regexp) + (let ((bracketsp (match-beginning 4)) + (begin (match-beginning 2)) + (contents-begin (or (match-beginning 4) + (match-beginning 3))) + (contents-end (or (match-end 4) (match-end 3))) + (post-blank (progn (goto-char (match-end 0)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'subscript + (list :begin begin + :end end + :use-brackets-p bracketsp + :contents-begin contents-begin + :contents-end contents-end + :post-blank post-blank)))))) (defun org-element-subscript-interpreter (subscript contents) "Interpret SUBSCRIPT object as Org syntax. @@ -3412,46 +3372,36 @@ CONTENTS is the contents of the object." (if (org-element-property :use-brackets-p subscript) "_{%s}" "_%s") contents)) -(defun org-element-sub/superscript-successor () - "Search for the next sub/superscript object. - -Return value is a cons cell whose CAR is either `subscript' or -`superscript' and CDR is beginning position." - (save-excursion - (unless (bolp) (backward-char)) - (when (re-search-forward org-match-substring-regexp nil t) - (cons (if (string= (match-string 2) "_") 'subscript 'superscript) - (match-beginning 2))))) - ;;;; Superscript (defun org-element-superscript-parser () - "Parse superscript at point. + "Parse superscript at point, if any. -Return a list whose CAR is `superscript' and CDR a plist with -`:begin', `:end', `:contents-begin', `:contents-end', -`:use-brackets-p' and `:post-blank' as keywords. +When at a superscript object, return a list whose car is +`superscript' and cdr a plist with `:begin', `:end', +`:contents-begin', `:contents-end', `:use-brackets-p' and +`:post-blank' as keywords. Otherwise, return nil. Assume point is at the caret." (save-excursion (unless (bolp) (backward-char)) - (let ((bracketsp (if (looking-at org-match-substring-with-braces-regexp) t - (not (looking-at org-match-substring-regexp)))) - (begin (match-beginning 2)) - (contents-begin (or (match-beginning 5) - (match-beginning 3))) - (contents-end (or (match-end 5) (match-end 3))) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'superscript - (list :begin begin - :end end - :use-brackets-p bracketsp - :contents-begin contents-begin - :contents-end contents-end - :post-blank post-blank))))) + (when (looking-at org-match-substring-regexp) + (let ((bracketsp (match-beginning 4)) + (begin (match-beginning 2)) + (contents-begin (or (match-beginning 4) + (match-beginning 3))) + (contents-end (or (match-end 4) (match-end 3))) + (post-blank (progn (goto-char (match-end 0)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'superscript + (list :begin begin + :end end + :use-brackets-p bracketsp + :contents-begin contents-begin + :contents-end contents-end + :post-blank post-blank)))))) (defun org-element-superscript-interpreter (superscript contents) "Interpret SUPERSCRIPT object as Org syntax. @@ -3465,8 +3415,7 @@ CONTENTS is the contents of the object." (defun org-element-table-cell-parser () "Parse table cell at point. - -Return a list whose CAR is `table-cell' and CDR is a plist +Return a list whose car is `table-cell' and cdr is a plist containing `:begin', `:end', `:contents-begin', `:contents-end' and `:post-blank' keywords." (looking-at "[ \t]*\\(.*?\\)[ \t]*\\(?:|\\|$\\)") @@ -3481,299 +3430,275 @@ and `:post-blank' keywords." :contents-end contents-end :post-blank 0)))) -(defun org-element-table-cell-interpreter (table-cell contents) - "Interpret TABLE-CELL element as Org syntax. +(defun org-element-table-cell-interpreter (_ contents) + "Interpret table-cell element as Org syntax. CONTENTS is the contents of the cell, or nil." (concat " " contents " |")) -(defun org-element-table-cell-successor () - "Search for the next table-cell object. - -Return value is a cons cell whose CAR is `table-cell' and CDR is -beginning position." - (when (looking-at "[ \t]*.*?[ \t]*\\(|\\|$\\)") (cons 'table-cell (point)))) - ;;;; Target (defun org-element-target-parser () - "Parse target at point. + "Parse target at point, if any. -Return a list whose CAR is `target' and CDR a plist with -`:begin', `:end', `:value' and `:post-blank' as keywords. +When at a target, return a list whose car is `target' and cdr +a plist with `:begin', `:end', `:value' and `:post-blank' as +keywords. Otherwise, return nil. Assume point is at the target." (save-excursion - (looking-at org-target-regexp) - (let ((begin (point)) - (value (org-match-string-no-properties 1)) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'target - (list :begin begin - :end end - :value value - :post-blank post-blank))))) - -(defun org-element-target-interpreter (target contents) - "Interpret TARGET object as Org syntax. -CONTENTS is nil." + (when (looking-at org-target-regexp) + (let ((begin (point)) + (value (match-string-no-properties 1)) + (post-blank (progn (goto-char (match-end 0)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'target + (list :begin begin + :end end + :value value + :post-blank post-blank)))))) + +(defun org-element-target-interpreter (target _) + "Interpret TARGET object as Org syntax." (format "<<%s>>" (org-element-property :value target))) -(defun org-element-target-successor () - "Search for the next target object. - -Return value is a cons cell whose CAR is `target' and CDR is -beginning position." - (save-excursion - (when (re-search-forward org-target-regexp nil t) - (cons 'target (match-beginning 0))))) - ;;;; Timestamp +(defconst org-element--timestamp-regexp + (concat org-ts-regexp-both + "\\|" + "\\(?:<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[dwmy]>\\)" + "\\|" + "\\(?:<%%\\(?:([^>\n]+)\\)>\\)") + "Regexp matching any timestamp type object.") + (defun org-element-timestamp-parser () - "Parse time stamp at point. + "Parse time stamp at point, if any. -Return a list whose CAR is `timestamp', and CDR a plist with -`:type', `:raw-value', `:year-start', `:month-start', -`:day-start', `:hour-start', `:minute-start', `:year-end', -`:month-end', `:day-end', `:hour-end', `:minute-end', -`:repeater-type', `:repeater-value', `:repeater-unit', -`:warning-type', `:warning-value', `:warning-unit', `:begin', -`:end' and `:post-blank' keywords. +When at a time stamp, return a list whose car is `timestamp', and +cdr a plist with `:type', `:raw-value', `:year-start', +`:month-start', `:day-start', `:hour-start', `:minute-start', +`:year-end', `:month-end', `:day-end', `:hour-end', +`:minute-end', `:repeater-type', `:repeater-value', +`:repeater-unit', `:warning-type', `:warning-value', +`:warning-unit', `:begin', `:end' and `:post-blank' keywords. +Otherwise, return nil. Assume point is at the beginning of the timestamp." - (save-excursion - (let* ((begin (point)) - (activep (eq (char-after) ?<)) - (raw-value - (progn - (looking-at "\\([<[]\\(%%\\)?.*?\\)[]>]\\(?:--\\([<[].*?[]>]\\)\\)?") - (match-string-no-properties 0))) - (date-start (match-string-no-properties 1)) - (date-end (match-string 3)) - (diaryp (match-beginning 2)) - (post-blank (progn (goto-char (match-end 0)) - (skip-chars-forward " \t"))) - (end (point)) - (time-range - (and (not diaryp) - (string-match - "[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)" - date-start) - (cons (string-to-number (match-string 2 date-start)) - (string-to-number (match-string 3 date-start))))) - (type (cond (diaryp 'diary) - ((and activep (or date-end time-range)) 'active-range) - (activep 'active) - ((or date-end time-range) 'inactive-range) - (t 'inactive))) - (repeater-props - (and (not diaryp) - (string-match "\\([.+]?\\+\\)\\([0-9]+\\)\\([hdwmy]\\)" - raw-value) - (list - :repeater-type - (let ((type (match-string 1 raw-value))) - (cond ((equal "++" type) 'catch-up) - ((equal ".+" type) 'restart) - (t 'cumulate))) - :repeater-value (string-to-number (match-string 2 raw-value)) - :repeater-unit - (case (string-to-char (match-string 3 raw-value)) - (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (t 'year))))) - (warning-props - (and (not diaryp) - (string-match "\\(-\\)?-\\([0-9]+\\)\\([hdwmy]\\)" raw-value) - (list - :warning-type (if (match-string 1 raw-value) 'first 'all) - :warning-value (string-to-number (match-string 2 raw-value)) - :warning-unit - (case (string-to-char (match-string 3 raw-value)) - (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (t 'year))))) - year-start month-start day-start hour-start minute-start year-end - month-end day-end hour-end minute-end) - ;; Parse date-start. - (unless diaryp - (let ((date (org-parse-time-string date-start t))) - (setq year-start (nth 5 date) - month-start (nth 4 date) - day-start (nth 3 date) - hour-start (nth 2 date) - minute-start (nth 1 date)))) - ;; Compute date-end. It can be provided directly in time-stamp, - ;; or extracted from time range. Otherwise, it defaults to the - ;; same values as date-start. - (unless diaryp - (let ((date (and date-end (org-parse-time-string date-end t)))) - (setq year-end (or (nth 5 date) year-start) - month-end (or (nth 4 date) month-start) - day-end (or (nth 3 date) day-start) - hour-end (or (nth 2 date) (car time-range) hour-start) - minute-end (or (nth 1 date) (cdr time-range) minute-start)))) - (list 'timestamp - (nconc (list :type type - :raw-value raw-value - :year-start year-start - :month-start month-start - :day-start day-start - :hour-start hour-start - :minute-start minute-start - :year-end year-end - :month-end month-end - :day-end day-end - :hour-end hour-end - :minute-end minute-end - :begin begin - :end end - :post-blank post-blank) - repeater-props - warning-props))))) - -(defun org-element-timestamp-interpreter (timestamp contents) - "Interpret TIMESTAMP object as Org syntax. -CONTENTS is nil." - ;; Use `:raw-value' if specified. - (or (org-element-property :raw-value timestamp) - ;; Otherwise, build timestamp string. - (let* ((repeat-string - (concat - (case (org-element-property :repeater-type timestamp) - (cumulate "+") (catch-up "++") (restart ".+")) - (let ((val (org-element-property :repeater-value timestamp))) - (and val (number-to-string val))) - (case (org-element-property :repeater-unit timestamp) - (hour "h") (day "d") (week "w") (month "m") (year "y")))) - (warning-string - (concat - (case (org-element-property :warning-type timestamp) - (first "--") - (all "-")) - (let ((val (org-element-property :warning-value timestamp))) - (and val (number-to-string val))) - (case (org-element-property :warning-unit timestamp) - (hour "h") (day "d") (week "w") (month "m") (year "y")))) - (build-ts-string - ;; Build an Org timestamp string from TIME. ACTIVEP is - ;; non-nil when time stamp is active. If WITH-TIME-P is - ;; non-nil, add a time part. HOUR-END and MINUTE-END - ;; specify a time range in the timestamp. REPEAT-STRING - ;; is the repeater string, if any. - (lambda (time activep &optional with-time-p hour-end minute-end) - (let ((ts (format-time-string - (funcall (if with-time-p 'cdr 'car) - org-time-stamp-formats) - time))) - (when (and hour-end minute-end) - (string-match "[012]?[0-9]:[0-5][0-9]" ts) - (setq ts - (replace-match - (format "\\&-%02d:%02d" hour-end minute-end) - nil nil ts))) - (unless activep (setq ts (format "[%s]" (substring ts 1 -1)))) - (dolist (s (list repeat-string warning-string)) - (when (org-string-nw-p s) - (setq ts (concat (substring ts 0 -1) - " " - s - (substring ts -1))))) - ;; Return value. - ts))) - (type (org-element-property :type timestamp))) - (case type - ((active inactive) - (let* ((minute-start (org-element-property :minute-start timestamp)) - (minute-end (org-element-property :minute-end timestamp)) - (hour-start (org-element-property :hour-start timestamp)) - (hour-end (org-element-property :hour-end timestamp)) - (time-range-p (and hour-start hour-end minute-start minute-end - (or (/= hour-start hour-end) - (/= minute-start minute-end))))) - (funcall - build-ts-string - (encode-time 0 - (or minute-start 0) - (or hour-start 0) - (org-element-property :day-start timestamp) - (org-element-property :month-start timestamp) - (org-element-property :year-start timestamp)) - (eq type 'active) - (and hour-start minute-start) - (and time-range-p hour-end) - (and time-range-p minute-end)))) - ((active-range inactive-range) - (let ((minute-start (org-element-property :minute-start timestamp)) - (minute-end (org-element-property :minute-end timestamp)) - (hour-start (org-element-property :hour-start timestamp)) - (hour-end (org-element-property :hour-end timestamp))) - (concat - (funcall - build-ts-string (encode-time - 0 - (or minute-start 0) - (or hour-start 0) - (org-element-property :day-start timestamp) - (org-element-property :month-start timestamp) - (org-element-property :year-start timestamp)) - (eq type 'active-range) - (and hour-start minute-start)) - "--" - (funcall build-ts-string - (encode-time 0 - (or minute-end 0) - (or hour-end 0) - (org-element-property :day-end timestamp) - (org-element-property :month-end timestamp) - (org-element-property :year-end timestamp)) - (eq type 'active-range) - (and hour-end minute-end))))))))) - -(defun org-element-timestamp-successor () - "Search for the next timestamp object. - -Return value is a cons cell whose CAR is `timestamp' and CDR is -beginning position." - (save-excursion - (when (re-search-forward - (concat org-ts-regexp-both - "\\|" - "\\(?:<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[dwmy]>\\)" - "\\|" - "\\(?:<%%\\(?:([^>\n]+)\\)>\\)") - nil t) - (cons 'timestamp (match-beginning 0))))) + (when (looking-at-p org-element--timestamp-regexp) + (save-excursion + (let* ((begin (point)) + (activep (eq (char-after) ?<)) + (raw-value + (progn + (looking-at "\\([<[]\\(%%\\)?.*?\\)[]>]\\(?:--\\([<[].*?[]>]\\)\\)?") + (match-string-no-properties 0))) + (date-start (match-string-no-properties 1)) + (date-end (match-string 3)) + (diaryp (match-beginning 2)) + (post-blank (progn (goto-char (match-end 0)) + (skip-chars-forward " \t"))) + (end (point)) + (time-range + (and (not diaryp) + (string-match + "[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)" + date-start) + (cons (string-to-number (match-string 2 date-start)) + (string-to-number (match-string 3 date-start))))) + (type (cond (diaryp 'diary) + ((and activep (or date-end time-range)) 'active-range) + (activep 'active) + ((or date-end time-range) 'inactive-range) + (t 'inactive))) + (repeater-props + (and (not diaryp) + (string-match "\\([.+]?\\+\\)\\([0-9]+\\)\\([hdwmy]\\)" + raw-value) + (list + :repeater-type + (let ((type (match-string 1 raw-value))) + (cond ((equal "++" type) 'catch-up) + ((equal ".+" type) 'restart) + (t 'cumulate))) + :repeater-value (string-to-number (match-string 2 raw-value)) + :repeater-unit + (pcase (string-to-char (match-string 3 raw-value)) + (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (_ 'year))))) + (warning-props + (and (not diaryp) + (string-match "\\(-\\)?-\\([0-9]+\\)\\([hdwmy]\\)" raw-value) + (list + :warning-type (if (match-string 1 raw-value) 'first 'all) + :warning-value (string-to-number (match-string 2 raw-value)) + :warning-unit + (pcase (string-to-char (match-string 3 raw-value)) + (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (_ 'year))))) + year-start month-start day-start hour-start minute-start year-end + month-end day-end hour-end minute-end) + ;; Parse date-start. + (unless diaryp + (let ((date (org-parse-time-string date-start t))) + (setq year-start (nth 5 date) + month-start (nth 4 date) + day-start (nth 3 date) + hour-start (nth 2 date) + minute-start (nth 1 date)))) + ;; Compute date-end. It can be provided directly in time-stamp, + ;; or extracted from time range. Otherwise, it defaults to the + ;; same values as date-start. + (unless diaryp + (let ((date (and date-end (org-parse-time-string date-end t)))) + (setq year-end (or (nth 5 date) year-start) + month-end (or (nth 4 date) month-start) + day-end (or (nth 3 date) day-start) + hour-end (or (nth 2 date) (car time-range) hour-start) + minute-end (or (nth 1 date) (cdr time-range) minute-start)))) + (list 'timestamp + (nconc (list :type type + :raw-value raw-value + :year-start year-start + :month-start month-start + :day-start day-start + :hour-start hour-start + :minute-start minute-start + :year-end year-end + :month-end month-end + :day-end day-end + :hour-end hour-end + :minute-end minute-end + :begin begin + :end end + :post-blank post-blank) + repeater-props + warning-props)))))) + +(defun org-element-timestamp-interpreter (timestamp _) + "Interpret TIMESTAMP object as Org syntax." + (let* ((repeat-string + (concat + (pcase (org-element-property :repeater-type timestamp) + (`cumulate "+") (`catch-up "++") (`restart ".+")) + (let ((val (org-element-property :repeater-value timestamp))) + (and val (number-to-string val))) + (pcase (org-element-property :repeater-unit timestamp) + (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y")))) + (warning-string + (concat + (pcase (org-element-property :warning-type timestamp) + (`first "--") (`all "-")) + (let ((val (org-element-property :warning-value timestamp))) + (and val (number-to-string val))) + (pcase (org-element-property :warning-unit timestamp) + (`hour "h") (`day "d") (`week "w") (`month "m") (`year "y")))) + (build-ts-string + ;; Build an Org timestamp string from TIME. ACTIVEP is + ;; non-nil when time stamp is active. If WITH-TIME-P is + ;; non-nil, add a time part. HOUR-END and MINUTE-END + ;; specify a time range in the timestamp. REPEAT-STRING is + ;; the repeater string, if any. + (lambda (time activep &optional with-time-p hour-end minute-end) + (let ((ts (format-time-string + (funcall (if with-time-p #'cdr #'car) + org-time-stamp-formats) + time))) + (when (and hour-end minute-end) + (string-match "[012]?[0-9]:[0-5][0-9]" ts) + (setq ts + (replace-match + (format "\\&-%02d:%02d" hour-end minute-end) + nil nil ts))) + (unless activep (setq ts (format "[%s]" (substring ts 1 -1)))) + (dolist (s (list repeat-string warning-string)) + (when (org-string-nw-p s) + (setq ts (concat (substring ts 0 -1) + " " + s + (substring ts -1))))) + ;; Return value. + ts))) + (type (org-element-property :type timestamp))) + (pcase type + ((or `active `inactive) + (let* ((minute-start (org-element-property :minute-start timestamp)) + (minute-end (org-element-property :minute-end timestamp)) + (hour-start (org-element-property :hour-start timestamp)) + (hour-end (org-element-property :hour-end timestamp)) + (time-range-p (and hour-start hour-end minute-start minute-end + (or (/= hour-start hour-end) + (/= minute-start minute-end))))) + (funcall + build-ts-string + (encode-time 0 + (or minute-start 0) + (or hour-start 0) + (org-element-property :day-start timestamp) + (org-element-property :month-start timestamp) + (org-element-property :year-start timestamp)) + (eq type 'active) + (and hour-start minute-start) + (and time-range-p hour-end) + (and time-range-p minute-end)))) + ((or `active-range `inactive-range) + (let ((minute-start (org-element-property :minute-start timestamp)) + (minute-end (org-element-property :minute-end timestamp)) + (hour-start (org-element-property :hour-start timestamp)) + (hour-end (org-element-property :hour-end timestamp))) + (concat + (funcall + build-ts-string (encode-time + 0 + (or minute-start 0) + (or hour-start 0) + (org-element-property :day-start timestamp) + (org-element-property :month-start timestamp) + (org-element-property :year-start timestamp)) + (eq type 'active-range) + (and hour-start minute-start)) + "--" + (funcall build-ts-string + (encode-time 0 + (or minute-end 0) + (or hour-end 0) + (org-element-property :day-end timestamp) + (org-element-property :month-end timestamp) + (org-element-property :year-end timestamp)) + (eq type 'active-range) + (and hour-end minute-end))))) + (_ (org-element-property :raw-value timestamp))))) ;;;; Underline (defun org-element-underline-parser () - "Parse underline object at point. + "Parse underline object at point, if any. -Return a list whose CAR is `underline' and CDR is a plist with -`:begin', `:end', `:contents-begin' and `:contents-end' and -`:post-blank' keywords. +When at an underline object, return a list whose car is +`underline' and cdr is a plist with `:begin', `:end', +`:contents-begin' and `:contents-end' and `:post-blank' keywords. +Otherwise, return nil. Assume point is at the first underscore marker." (save-excursion (unless (bolp) (backward-char 1)) - (looking-at org-emph-re) - (let ((begin (match-beginning 2)) - (contents-begin (match-beginning 4)) - (contents-end (match-end 4)) - (post-blank (progn (goto-char (match-end 2)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'underline - (list :begin begin - :end end - :contents-begin contents-begin - :contents-end contents-end - :post-blank post-blank))))) - -(defun org-element-underline-interpreter (underline contents) - "Interpret UNDERLINE object as Org syntax. + (when (looking-at org-emph-re) + (let ((begin (match-beginning 2)) + (contents-begin (match-beginning 4)) + (contents-end (match-end 4)) + (post-blank (progn (goto-char (match-end 2)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'underline + (list :begin begin + :end end + :contents-begin contents-begin + :contents-end contents-end + :post-blank post-blank)))))) + +(defun org-element-underline-interpreter (_ contents) + "Interpret underline object as Org syntax. CONTENTS is the contents of the object." (format "_%s_" contents)) @@ -3781,29 +3706,29 @@ CONTENTS is the contents of the object." ;;;; Verbatim (defun org-element-verbatim-parser () - "Parse verbatim object at point. + "Parse verbatim object at point, if any. -Return a list whose CAR is `verbatim' and CDR is a plist with -`:value', `:begin', `:end' and `:post-blank' keywords. +When at a verbatim object, return a list whose car is `verbatim' +and cdr is a plist with `:value', `:begin', `:end' and +`:post-blank' keywords. Otherwise, return nil. Assume point is at the first equal sign marker." (save-excursion (unless (bolp) (backward-char 1)) - (looking-at org-emph-re) - (let ((begin (match-beginning 2)) - (value (org-match-string-no-properties 4)) - (post-blank (progn (goto-char (match-end 2)) - (skip-chars-forward " \t"))) - (end (point))) - (list 'verbatim - (list :value value - :begin begin - :end end - :post-blank post-blank))))) - -(defun org-element-verbatim-interpreter (verbatim contents) - "Interpret VERBATIM object as Org syntax. -CONTENTS is nil." + (when (looking-at org-emph-re) + (let ((begin (match-beginning 2)) + (value (match-string-no-properties 4)) + (post-blank (progn (goto-char (match-end 2)) + (skip-chars-forward " \t"))) + (end (point))) + (list 'verbatim + (list :value value + :begin begin + :end end + :post-blank post-blank)))))) + +(defun org-element-verbatim-interpreter (verbatim _) + "Interpret VERBATIM object as Org syntax." (format "=%s=" (org-element-property :value verbatim))) @@ -3818,10 +3743,9 @@ CONTENTS is nil." ;; are activated for fixed element chaining (e.g., `plain-list' > ;; `item') or fixed conditional element chaining (e.g., `headline' > ;; `section'). Special modes are: `first-section', `item', -;; `node-property', `quote-section', `section' and `table-row'. +;; `node-property', `section' and `table-row'. -(defun org-element--current-element - (limit &optional granularity special structure) +(defun org-element--current-element (limit &optional granularity mode structure) "Parse the element starting at point. Return value is a list like (TYPE PROPS) where TYPE is the type @@ -3838,12 +3762,12 @@ recursion. Allowed values are `headline', `greater-element', nil), secondary values will not be parsed, since they only contain objects. -Optional argument SPECIAL, when non-nil, can be either -`first-section', `item', `node-property', `quote-section', -`section', and `table-row'. +Optional argument MODE, when non-nil, can be either +`first-section', `section', `planning', `item', `node-property' +and `table-row'. -If STRUCTURE isn't provided but SPECIAL is set to `item', it will -be computed. +If STRUCTURE isn't provided but MODE is set to `item', it will be +computed. This function assumes point is always at the beginning of the element it has to parse." @@ -3855,30 +3779,37 @@ element it has to parse." (raw-secondary-p (and granularity (not (eq granularity 'object))))) (cond ;; Item. - ((eq special 'item) + ((eq mode 'item) (org-element-item-parser limit structure raw-secondary-p)) ;; Table Row. - ((eq special 'table-row) (org-element-table-row-parser limit)) + ((eq mode 'table-row) (org-element-table-row-parser limit)) ;; Node Property. - ((eq special 'node-property) (org-element-node-property-parser limit)) + ((eq mode 'node-property) (org-element-node-property-parser limit)) ;; Headline. ((org-with-limited-levels (org-at-heading-p)) (org-element-headline-parser limit raw-secondary-p)) ;; Sections (must be checked after headline). - ((eq special 'section) (org-element-section-parser limit)) - ((eq special 'quote-section) (org-element-quote-section-parser limit)) - ((eq special 'first-section) + ((eq mode 'section) (org-element-section-parser limit)) + ((eq mode 'first-section) (org-element-section-parser (or (save-excursion (org-with-limited-levels (outline-next-heading))) limit))) + ;; Planning. + ((and (eq mode 'planning) + (eq ?* (char-after (line-beginning-position 0))) + (looking-at org-planning-line-re)) + (org-element-planning-parser limit)) + ;; Property drawer. + ((and (memq mode '(planning property-drawer)) + (eq ?* (char-after (line-beginning-position + (if (eq mode 'planning) 0 -1)))) + (looking-at org-property-drawer-re)) + (org-element-property-drawer-parser limit)) ;; When not at bol, point is at the beginning of an item or ;; a footnote definition: next item is always a paragraph. ((not (bolp)) (org-element-paragraph-parser limit (list (point)))) - ;; Planning and Clock. - ((looking-at org-planning-or-clock-line-re) - (if (equal (match-string 1) org-clock-string) - (org-element-clock-parser limit) - (org-element-planning-parser limit))) + ;; Clock. + ((looking-at org-clock-line-re) (org-element-clock-parser limit)) ;; Inlinetask. ((org-at-heading-p) (org-element-inlinetask-parser limit raw-secondary-p)) @@ -3891,13 +3822,11 @@ element it has to parse." (goto-char (car affiliated)) (org-element-keyword-parser limit nil)) ;; LaTeX Environment. - ((looking-at "[ \t]*\\\\begin{\\([A-Za-z0-9]+\\*?\\)}\\(\\[.*?\\]\\|{.*?}\\)*[ \t]*$") + ((looking-at org-element--latex-begin-environment) (org-element-latex-environment-parser limit affiliated)) ;; Drawer and Property Drawer. ((looking-at org-drawer-regexp) - (if (equal (match-string 1) "PROPERTIES") - (org-element-property-drawer-parser limit affiliated) - (org-element-drawer-parser limit affiliated))) + (org-element-drawer-parser limit affiliated)) ;; Fixed Width ((looking-at "[ \t]*:\\( \\|$\\)") (org-element-fixed-width-parser limit affiliated)) @@ -3905,27 +3834,35 @@ element it has to parse." ;; Keywords. ((looking-at "[ \t]*#") (goto-char (match-end 0)) - (cond ((looking-at "\\(?: \\|$\\)") - (beginning-of-line) - (org-element-comment-parser limit affiliated)) - ((looking-at "\\+BEGIN_\\(\\S-+\\)") - (beginning-of-line) - (let ((parser (assoc (upcase (match-string 1)) - org-element-block-name-alist))) - (if parser (funcall (cdr parser) limit affiliated) - (org-element-special-block-parser limit affiliated)))) - ((looking-at "\\+CALL:") - (beginning-of-line) - (org-element-babel-call-parser limit affiliated)) - ((looking-at "\\+BEGIN:? ") - (beginning-of-line) - (org-element-dynamic-block-parser limit affiliated)) - ((looking-at "\\+\\S-+:") - (beginning-of-line) - (org-element-keyword-parser limit affiliated)) - (t - (beginning-of-line) - (org-element-paragraph-parser limit affiliated)))) + (cond + ((looking-at "\\(?: \\|$\\)") + (beginning-of-line) + (org-element-comment-parser limit affiliated)) + ((looking-at "\\+BEGIN_\\(\\S-+\\)") + (beginning-of-line) + (funcall (pcase (upcase (match-string 1)) + ("CENTER" #'org-element-center-block-parser) + ("COMMENT" #'org-element-comment-block-parser) + ("EXAMPLE" #'org-element-example-block-parser) + ("EXPORT" #'org-element-export-block-parser) + ("QUOTE" #'org-element-quote-block-parser) + ("SRC" #'org-element-src-block-parser) + ("VERSE" #'org-element-verse-block-parser) + (_ #'org-element-special-block-parser)) + limit + affiliated)) + ((looking-at "\\+CALL:") + (beginning-of-line) + (org-element-babel-call-parser limit affiliated)) + ((looking-at "\\+BEGIN:? ") + (beginning-of-line) + (org-element-dynamic-block-parser limit affiliated)) + ((looking-at "\\+\\S-+:") + (beginning-of-line) + (org-element-keyword-parser limit affiliated)) + (t + (beginning-of-line) + (org-element-paragraph-parser limit affiliated)))) ;; Footnote Definition. ((looking-at org-footnote-definition-re) (org-element-footnote-definition-parser limit affiliated)) @@ -3936,7 +3873,8 @@ element it has to parse." ((looking-at "%%(") (org-element-diary-sexp-parser limit affiliated)) ;; Table. - ((org-at-table-p t) (org-element-table-parser limit affiliated)) + ((looking-at "[ \t]*\\(|\\|\\+\\(-+\\+\\)+[ \t]*$\\)") + (org-element-table-parser limit affiliated)) ;; List. ((looking-at (org-item-re)) (org-element-plain-list-parser @@ -3980,7 +3918,7 @@ position of point and CDR is nil." (save-match-data (org-trim (buffer-substring-no-properties - (match-end 0) (point-at-eol))))) + (match-end 0) (line-end-position))))) ;; PARSEDP is non-nil when keyword should have its ;; value parsed. (parsedp (member kwd org-element-parsed-keywords)) @@ -3989,14 +3927,20 @@ position of point and CDR is nil." (dualp (member kwd org-element-dual-keywords)) (dual-value (and dualp - (let ((sec (org-match-string-no-properties 2))) + (let ((sec (match-string-no-properties 2))) (if (or (not sec) (not parsedp)) sec - (org-element-parse-secondary-string sec restrict))))) + (save-match-data + (org-element--parse-objects + (match-beginning 2) (match-end 2) nil restrict)))))) ;; Attribute a property name to KWD. (kwd-sym (and kwd (intern (concat ":" (downcase kwd)))))) ;; Now set final shape for VALUE. (when parsedp - (setq value (org-element-parse-secondary-string value restrict))) + (setq value + (org-element--parse-objects + (match-end 0) + (progn (end-of-line) (skip-chars-backward " \t") (point)) + nil restrict))) (when dualp (setq value (and (or value dual-value) (cons value dual-value)))) (when (or (member kwd org-element-multiple-keywords) @@ -4037,7 +3981,7 @@ Optional argument GRANULARITY determines the depth of the recursion. It can be set to the following symbols: `headline' Only parse headlines. -`greater-element' Don't recurse into greater elements excepted +`greater-element' Don't recurse into greater elements except headlines and sections. Thus, elements parsed are the top-level ones. `element' Parse everything but objects and plain text. @@ -4046,7 +3990,7 @@ recursion. It can be set to the following symbols: When VISIBLE-ONLY is non-nil, don't parse contents of hidden elements. -An element or an objects is represented as a list with the +An element or object is represented as a list with the pattern (TYPE PROPERTIES CONTENTS), where : TYPE is a symbol describing the element or object. See @@ -4089,23 +4033,25 @@ looked after. Optional argument PARENT, when non-nil, is the element or object containing the secondary string. It is used to set correctly -`:parent' property within the string." - (let ((local-variables (buffer-local-variables))) - (with-temp-buffer - (dolist (v local-variables) - (ignore-errors - (if (symbolp v) (makunbound v) - (org-set-local (car v) (cdr v))))) - (insert string) - (restore-buffer-modified-p nil) - (let ((secondary (org-element--parse-objects - (point-min) (point-max) nil restriction))) - (when parent - (dolist (o secondary) (org-element-put-property o :parent parent))) - secondary)))) +`:parent' property within the string. + +If STRING is the empty string or nil, return nil." + (cond + ((not string) nil) + ((equal string "") nil) + (t (let ((local-variables (buffer-local-variables))) + (with-temp-buffer + (dolist (v local-variables) + (ignore-errors + (if (symbolp v) (makunbound v) + (set (make-local-variable (car v)) (cdr v))))) + (insert string) + (restore-buffer-modified-p nil) + (org-element--parse-objects + (point-min) (point-max) nil restriction parent)))))) (defun org-element-map - (data types fun &optional info first-match no-recursion with-affiliated) + (data types fun &optional info first-match no-recursion with-affiliated) "Map a function on selected elements or objects. DATA is a parse tree, an element, an object, a string, or a list @@ -4141,7 +4087,7 @@ Assuming TREE is a variable containing an Org buffer parse tree, the following example will return a flat list of all `src-block' and `example-block' elements in it: - (org-element-map tree \\='(example-block src-block) \\='identity) + (org-element-map tree \\='(example-block src-block) #\\='identity) The following snippet will find the first headline with a level of 1 and a \"phone\" tag, and will return its beginning position: @@ -4156,7 +4102,7 @@ of 1 and a \"phone\" tag, and will return its beginning position: The next example will return a flat list of all `plain-list' type elements in TREE that are not a sub-list themselves: - (org-element-map tree \\='plain-list \\='identity nil nil \\='plain-list) + (org-element-map tree \\='plain-list #\\='identity nil nil \\='plain-list) Eventually, this example will return a flat list of all `bold' type objects containing a `latex-snippet' type object, even @@ -4164,116 +4110,101 @@ looking into captions: (org-element-map tree \\='bold (lambda (b) - (and (org-element-map b \\='latex-snippet \\='identity nil t) b)) + (and (org-element-map b \\='latex-snippet #\\='identity nil t) b)) nil nil nil t)" ;; Ensure TYPES and NO-RECURSION are a list, even of one element. - (unless (listp types) (setq types (list types))) - (unless (listp no-recursion) (setq no-recursion (list no-recursion))) - ;; Recursion depth is determined by --CATEGORY. - (let* ((--category - (catch 'found - (let ((category 'greater-elements)) - (mapc (lambda (type) - (cond ((or (memq type org-element-all-objects) - (eq type 'plain-text)) - ;; If one object is found, the function - ;; has to recurse into every object. - (throw 'found 'objects)) - ((not (memq type org-element-greater-elements)) - ;; If one regular element is found, the - ;; function has to recurse, at least, - ;; into every element it encounters. - (and (not (eq category 'elements)) - (setq category 'elements))))) - types) - category))) - ;; Compute properties for affiliated keywords if necessary. - (--affiliated-alist - (and with-affiliated - (mapcar (lambda (kwd) - (cons kwd (intern (concat ":" (downcase kwd))))) - org-element-affiliated-keywords))) - --acc - --walk-tree - (--walk-tree - (function - (lambda (--data) - ;; Recursively walk DATA. INFO, if non-nil, is a plist - ;; holding contextual information. - (let ((--type (org-element-type --data))) - (cond - ((not --data)) - ;; Ignored element in an export context. - ((and info (memq --data (plist-get info :ignore-list)))) - ;; List of elements or objects. - ((not --type) (mapc --walk-tree --data)) - ;; Unconditionally enter parse trees. - ((eq --type 'org-data) - (mapc --walk-tree (org-element-contents --data))) - (t - ;; Check if TYPE is matching among TYPES. If so, - ;; apply FUN to --DATA and accumulate return value - ;; into --ACC (or exit if FIRST-MATCH is non-nil). - (when (memq --type types) - (let ((result (funcall fun --data))) - (cond ((not result)) - (first-match (throw '--map-first-match result)) - (t (push result --acc))))) - ;; If --DATA has a secondary string that can contain - ;; objects with their type among TYPES, look into it. - (when (and (eq --category 'objects) (not (stringp --data))) - (let ((sec-prop - (assq --type org-element-secondary-value-alist))) - (when sec-prop - (funcall --walk-tree - (org-element-property (cdr sec-prop) --data))))) - ;; If --DATA has any affiliated keywords and - ;; WITH-AFFILIATED is non-nil, look for objects in - ;; them. - (when (and with-affiliated - (eq --category 'objects) - (memq --type org-element-all-elements)) - (mapc (lambda (kwd-pair) - (let ((kwd (car kwd-pair)) - (value (org-element-property - (cdr kwd-pair) --data))) - ;; Pay attention to the type of value. - ;; Preserve order for multiple keywords. - (cond - ((not value)) - ((and (member kwd org-element-multiple-keywords) - (member kwd org-element-dual-keywords)) - (mapc (lambda (line) - (funcall --walk-tree (cdr line)) - (funcall --walk-tree (car line))) - (reverse value))) - ((member kwd org-element-multiple-keywords) - (mapc (lambda (line) (funcall --walk-tree line)) - (reverse value))) - ((member kwd org-element-dual-keywords) - (funcall --walk-tree (cdr value)) - (funcall --walk-tree (car value))) - (t (funcall --walk-tree value))))) - --affiliated-alist)) - ;; Determine if a recursion into --DATA is possible. - (cond - ;; --TYPE is explicitly removed from recursion. - ((memq --type no-recursion)) - ;; --DATA has no contents. - ((not (org-element-contents --data))) - ;; Looking for greater elements but --DATA is simply - ;; an element or an object. - ((and (eq --category 'greater-elements) - (not (memq --type org-element-greater-elements)))) - ;; Looking for elements but --DATA is an object. - ((and (eq --category 'elements) - (memq --type org-element-all-objects))) - ;; In any other case, map contents. - (t (mapc --walk-tree (org-element-contents --data))))))))))) - (catch '--map-first-match - (funcall --walk-tree data) - ;; Return value in a proper order. - (nreverse --acc)))) + (let* ((types (if (listp types) types (list types))) + (no-recursion (if (listp no-recursion) no-recursion + (list no-recursion))) + ;; Recursion depth is determined by --CATEGORY. + (--category + (catch :--found + (let ((category 'greater-elements) + (all-objects (cons 'plain-text org-element-all-objects))) + (dolist (type types category) + (cond ((memq type all-objects) + ;; If one object is found, the function has + ;; to recurse into every object. + (throw :--found 'objects)) + ((not (memq type org-element-greater-elements)) + ;; If one regular element is found, the + ;; function has to recurse, at least, into + ;; every element it encounters. + (and (not (eq category 'elements)) + (setq category 'elements)))))))) + --acc) + (letrec ((--walk-tree + (lambda (--data) + ;; Recursively walk DATA. INFO, if non-nil, is a plist + ;; holding contextual information. + (let ((--type (org-element-type --data))) + (cond + ((not --data)) + ;; Ignored element in an export context. + ((and info (memq --data (plist-get info :ignore-list)))) + ;; List of elements or objects. + ((not --type) (mapc --walk-tree --data)) + ;; Unconditionally enter parse trees. + ((eq --type 'org-data) + (mapc --walk-tree (org-element-contents --data))) + (t + ;; Check if TYPE is matching among TYPES. If so, + ;; apply FUN to --DATA and accumulate return value + ;; into --ACC (or exit if FIRST-MATCH is non-nil). + (when (memq --type types) + (let ((result (funcall fun --data))) + (cond ((not result)) + (first-match (throw :--map-first-match result)) + (t (push result --acc))))) + ;; If --DATA has a secondary string that can contain + ;; objects with their type among TYPES, look inside. + (when (and (eq --category 'objects) (not (stringp --data))) + (dolist (p (cdr (assq --type + org-element-secondary-value-alist))) + (funcall --walk-tree (org-element-property p --data)))) + ;; If --DATA has any parsed affiliated keywords and + ;; WITH-AFFILIATED is non-nil, look for objects in + ;; them. + (when (and with-affiliated + (eq --category 'objects) + (eq (org-element-class --data) 'element)) + (dolist (kwd-pair org-element--parsed-properties-alist) + (let ((kwd (car kwd-pair)) + (value (org-element-property (cdr kwd-pair) --data))) + ;; Pay attention to the type of parsed + ;; keyword. In particular, preserve order for + ;; multiple keywords. + (cond + ((not value)) + ((member kwd org-element-dual-keywords) + (if (member kwd org-element-multiple-keywords) + (dolist (line (reverse value)) + (funcall --walk-tree (cdr line)) + (funcall --walk-tree (car line))) + (funcall --walk-tree (cdr value)) + (funcall --walk-tree (car value)))) + ((member kwd org-element-multiple-keywords) + (mapc --walk-tree (reverse value))) + (t (funcall --walk-tree value)))))) + ;; Determine if a recursion into --DATA is possible. + (cond + ;; --TYPE is explicitly removed from recursion. + ((memq --type no-recursion)) + ;; --DATA has no contents. + ((not (org-element-contents --data))) + ;; Looking for greater elements but --DATA is + ;; simply an element or an object. + ((and (eq --category 'greater-elements) + (not (memq --type org-element-greater-elements)))) + ;; Looking for elements but --DATA is an object. + ((and (eq --category 'elements) + (eq (org-element-class --data) 'object))) + ;; In any other case, map contents. + (t (mapc --walk-tree (org-element-contents --data)))))))))) + (catch :--map-first-match + (funcall --walk-tree data) + ;; Return value in a proper order. + (nreverse --acc))))) (put 'org-element-map 'lisp-indent-function 2) ;; The following functions are internal parts of the parser. @@ -4282,24 +4213,38 @@ looking into captions: ;; level. ;; ;; The second one, `org-element--parse-objects' applies on all objects -;; of a paragraph or a secondary string. It uses -;; `org-element--get-next-object-candidates' to optimize the search of -;; the next object in the buffer. -;; -;; More precisely, that function looks for every allowed object type -;; first. Then, it discards failed searches, keeps further matches, -;; and searches again types matched behind point, for subsequent -;; calls. Thus, searching for a given type fails only once, and every -;; object is searched only once at top level (but sometimes more for -;; nested types). +;; of a paragraph or a secondary string. It calls +;; `org-element--object-lex' to find the next object in the current +;; container. + +(defsubst org-element--next-mode (type parentp) + "Return next special mode according to TYPE, or nil. +TYPE is a symbol representing the type of an element or object +containing next element if PARENTP is non-nil, or before it +otherwise. Modes can be either `first-section', `item', +`node-property', `planning', `property-drawer', `section', +`table-row' or nil." + (if parentp + (pcase type + (`headline 'section) + (`inlinetask 'planning) + (`plain-list 'item) + (`property-drawer 'node-property) + (`section 'planning) + (`table 'table-row)) + (pcase type + (`item 'item) + (`node-property 'node-property) + (`planning 'property-drawer) + (`table-row 'table-row)))) (defun org-element--parse-elements - (beg end special structure granularity visible-only acc) + (beg end mode structure granularity visible-only acc) "Parse elements between BEG and END positions. -SPECIAL prioritize some elements over the others. It can be set -to `first-section', `quote-section', `section' `item' or -`table-row'. +MODE prioritizes some elements over the others. It can be set to +`first-section', `section', `planning', `item', `node-property' +or `table-row'. When value is `item', STRUCTURE will be used as the current list structure. @@ -4320,140 +4265,205 @@ Elements are accumulated into ACC." ;; When parsing only headlines, skip any text before first one. (when (and (eq granularity 'headline) (not (org-at-heading-p))) (org-with-limited-levels (outline-next-heading))) - ;; Main loop start. - (while (< (point) end) - ;; Find current element's type and parse it accordingly to - ;; its category. - (let* ((element (org-element--current-element - end granularity special structure)) - (type (org-element-type element)) - (cbeg (org-element-property :contents-begin element))) - (goto-char (org-element-property :end element)) - ;; Visible only: skip invisible parts between siblings. - (when (and visible-only (org-invisible-p2)) - (goto-char (min (1+ (org-find-visible)) end))) - ;; Fill ELEMENT contents by side-effect. - (cond - ;; If element has no contents, don't modify it. - ((not cbeg)) - ;; Greater element: parse it between `contents-begin' and - ;; `contents-end'. Make sure GRANULARITY allows the - ;; recursion, or ELEMENT is a headline, in which case going - ;; inside is mandatory, in order to get sub-level headings. - ((and (memq type org-element-greater-elements) - (or (memq granularity '(element object nil)) - (and (eq granularity 'greater-element) - (eq type 'section)) - (eq type 'headline))) - (org-element--parse-elements - cbeg (org-element-property :contents-end element) - ;; Possibly switch to a special mode. - (case type - (headline - (if (org-element-property :quotedp element) 'quote-section - 'section)) - (plain-list 'item) - (property-drawer 'node-property) - (table 'table-row)) - (and (memq type '(item plain-list)) - (org-element-property :structure element)) - granularity visible-only element)) - ;; ELEMENT has contents. Parse objects inside, if - ;; GRANULARITY allows it. - ((memq granularity '(object nil)) - (org-element--parse-objects - cbeg (org-element-property :contents-end element) element - (org-element-restriction type)))) - (org-element-adopt-elements acc element))) - ;; Return result. - acc)) - -(defun org-element--parse-objects (beg end acc restriction) + (let (elements) + (while (< (point) end) + ;; Find current element's type and parse it accordingly to + ;; its category. + (let* ((element (org-element--current-element + end granularity mode structure)) + (type (org-element-type element)) + (cbeg (org-element-property :contents-begin element))) + (goto-char (org-element-property :end element)) + ;; Visible only: skip invisible parts between siblings. + (when (and visible-only (org-invisible-p2)) + (goto-char (min (1+ (org-find-visible)) end))) + ;; Fill ELEMENT contents by side-effect. + (cond + ;; If element has no contents, don't modify it. + ((not cbeg)) + ;; Greater element: parse it between `contents-begin' and + ;; `contents-end'. Make sure GRANULARITY allows the + ;; recursion, or ELEMENT is a headline, in which case going + ;; inside is mandatory, in order to get sub-level headings. + ((and (memq type org-element-greater-elements) + (or (memq granularity '(element object nil)) + (and (eq granularity 'greater-element) + (eq type 'section)) + (eq type 'headline))) + (org-element--parse-elements + cbeg (org-element-property :contents-end element) + ;; Possibly switch to a special mode. + (org-element--next-mode type t) + (and (memq type '(item plain-list)) + (org-element-property :structure element)) + granularity visible-only element)) + ;; ELEMENT has contents. Parse objects inside, if + ;; GRANULARITY allows it. + ((memq granularity '(object nil)) + (org-element--parse-objects + cbeg (org-element-property :contents-end element) element + (org-element-restriction type)))) + (push (org-element-put-property element :parent acc) elements) + ;; Update mode. + (setq mode (org-element--next-mode type nil)))) + ;; Return result. + (apply #'org-element-set-contents acc (nreverse elements))))) + +(defun org-element--object-lex (restriction) + "Return next object in current buffer or nil. +RESTRICTION is a list of object types, as symbols, that should be +looked after. This function assumes that the buffer is narrowed +to an appropriate container (e.g., a paragraph)." + (if (memq 'table-cell restriction) (org-element-table-cell-parser) + (let* ((start (point)) + (limit + ;; Object regexp sometimes needs to have a peek at + ;; a character ahead. Therefore, when there is a hard + ;; limit, make it one more than the true beginning of the + ;; radio target. + (save-excursion + (cond ((not org-target-link-regexp) nil) + ((not (memq 'link restriction)) nil) + ((progn + (unless (bolp) (forward-char -1)) + (not (re-search-forward org-target-link-regexp nil t))) + nil) + ;; Since we moved backward, we do not want to + ;; match again an hypothetical 1-character long + ;; radio link before us. Realizing that this can + ;; only happen if such a radio link starts at + ;; beginning of line, we prevent this here. + ((and (= start (1+ (line-beginning-position))) + (= start (match-end 1))) + (and (re-search-forward org-target-link-regexp nil t) + (1+ (match-beginning 1)))) + (t (1+ (match-beginning 1)))))) + found) + (save-excursion + (while (and (not found) + (re-search-forward org-element--object-regexp limit 'move)) + (goto-char (match-beginning 0)) + (let ((result (match-string 0))) + (setq found + (cond + ((string-prefix-p "call_" result t) + (and (memq 'inline-babel-call restriction) + (org-element-inline-babel-call-parser))) + ((string-prefix-p "src_" result t) + (and (memq 'inline-src-block restriction) + (org-element-inline-src-block-parser))) + (t + (pcase (char-after) + (?^ (and (memq 'superscript restriction) + (org-element-superscript-parser))) + (?_ (or (and (memq 'subscript restriction) + (org-element-subscript-parser)) + (and (memq 'underline restriction) + (org-element-underline-parser)))) + (?* (and (memq 'bold restriction) + (org-element-bold-parser))) + (?/ (and (memq 'italic restriction) + (org-element-italic-parser))) + (?~ (and (memq 'code restriction) + (org-element-code-parser))) + (?= (and (memq 'verbatim restriction) + (org-element-verbatim-parser))) + (?+ (and (memq 'strike-through restriction) + (org-element-strike-through-parser))) + (?@ (and (memq 'export-snippet restriction) + (org-element-export-snippet-parser))) + (?{ (and (memq 'macro restriction) + (org-element-macro-parser))) + (?$ (and (memq 'latex-fragment restriction) + (org-element-latex-fragment-parser))) + (?< + (if (eq (aref result 1) ?<) + (or (and (memq 'radio-target restriction) + (org-element-radio-target-parser)) + (and (memq 'target restriction) + (org-element-target-parser))) + (or (and (memq 'timestamp restriction) + (org-element-timestamp-parser)) + (and (or (memq 'link restriction) + (memq 'simple-link restriction)) + (org-element-link-parser))))) + (?\\ + (if (eq (aref result 1) ?\\) + (and (memq 'line-break restriction) + (org-element-line-break-parser)) + (or (and (memq 'entity restriction) + (org-element-entity-parser)) + (and (memq 'latex-fragment restriction) + (org-element-latex-fragment-parser))))) + (?\[ + (if (eq (aref result 1) ?\[) + (and (memq 'link restriction) + (org-element-link-parser)) + (or (and (memq 'footnote-reference restriction) + (org-element-footnote-reference-parser)) + (and (memq 'timestamp restriction) + (org-element-timestamp-parser)) + (and (memq 'statistics-cookie restriction) + (org-element-statistics-cookie-parser))))) + ;; This is probably a plain link. + (_ (and (or (memq 'link restriction) + (memq 'simple-link restriction)) + (org-element-link-parser))))))) + (or (eobp) (forward-char)))) + (cond (found) + (limit (forward-char -1) + (org-element-link-parser)) ;radio link + (t nil)))))) + +(defun org-element--parse-objects (beg end acc restriction &optional parent) "Parse objects between BEG and END and return recursive structure. -Objects are accumulated in ACC. +Objects are accumulated in ACC. RESTRICTION is a list of object +successors which are allowed in the current object. -RESTRICTION is a list of object successors which are allowed in -the current object." - (let ((candidates 'initial)) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) +ACC becomes the parent for all parsed objects. However, if ACC +is nil (i.e., a secondary string is being parsed) and optional +argument PARENT is non-nil, use it as the parent for all objects. +Eventually, if both ACC and PARENT are nil, the common parent is +the list of objects itself." + (save-excursion + (save-restriction + (narrow-to-region beg end) + (goto-char (point-min)) + (let (next-object contents) (while (and (not (eobp)) - (setq candidates - (org-element--get-next-object-candidates - restriction candidates))) - (let ((next-object - (let ((pos (apply 'min (mapcar 'cdr candidates)))) - (save-excursion - (goto-char pos) - (funcall (intern (format "org-element-%s-parser" - (car (rassq pos candidates))))))))) - ;; 1. Text before any object. Untabify it. - (let ((obj-beg (org-element-property :begin next-object))) - (unless (= (point) obj-beg) - (setq acc - (org-element-adopt-elements - acc - (replace-regexp-in-string - "\t" (make-string tab-width ? ) - (buffer-substring-no-properties (point) obj-beg)))))) - ;; 2. Object... - (let ((obj-end (org-element-property :end next-object)) - (cont-beg (org-element-property :contents-begin next-object))) - ;; Fill contents of NEXT-OBJECT by side-effect, if it has - ;; a recursive type. - (when (and cont-beg - (memq (car next-object) org-element-recursive-objects)) - (org-element--parse-objects - cont-beg (org-element-property :contents-end next-object) - next-object (org-element-restriction next-object))) - (setq acc (org-element-adopt-elements acc next-object)) - (goto-char obj-end)))) - ;; 3. Text after last object. Untabify it. + (setq next-object (org-element--object-lex restriction))) + ;; Text before any object. + (let ((obj-beg (org-element-property :begin next-object))) + (unless (= (point) obj-beg) + (let ((text (buffer-substring-no-properties (point) obj-beg))) + (push (if acc (org-element-put-property text :parent acc) text) + contents)))) + ;; Object... + (let ((obj-end (org-element-property :end next-object)) + (cont-beg (org-element-property :contents-begin next-object))) + (when acc (org-element-put-property next-object :parent acc)) + (push (if cont-beg + ;; Fill contents of NEXT-OBJECT if possible. + (org-element--parse-objects + cont-beg + (org-element-property :contents-end next-object) + next-object + (org-element-restriction next-object)) + next-object) + contents) + (goto-char obj-end))) + ;; Text after last object. (unless (eobp) - (setq acc - (org-element-adopt-elements - acc - (replace-regexp-in-string - "\t" (make-string tab-width ? ) - (buffer-substring-no-properties (point) end))))) - ;; Result. - acc)))) - -(defun org-element--get-next-object-candidates (restriction objects) - "Return an alist of candidates for the next object. - -RESTRICTION is a list of object types, as symbols. Only -candidates with such types are looked after. - -OBJECTS is the previous candidates alist. If it is set to -`initial', no search has been done before, and all symbols in -RESTRICTION should be looked after. - -Return value is an alist whose CAR is the object type and CDR its -beginning position." - (delq - nil - (if (eq objects 'initial) - ;; When searching for the first time, look for every successor - ;; allowed in RESTRICTION. - (mapcar - (lambda (res) - (funcall (intern (format "org-element-%s-successor" res)))) - restriction) - ;; Focus on objects returned during last search. Keep those - ;; still after point. Search again objects before it. - (mapcar - (lambda (obj) - (if (>= (cdr obj) (point)) obj - (let* ((type (car obj)) - (succ (or (cdr (assq type org-element-object-successor-alist)) - type))) - (and succ - (funcall (intern (format "org-element-%s-successor" succ))))))) - objects)))) + (let ((text (buffer-substring-no-properties (point) end))) + (push (if acc (org-element-put-property text :parent acc) text) + contents))) + ;; Result. Set appropriate parent. + (if acc (apply #'org-element-set-contents acc (nreverse contents)) + (let* ((contents (nreverse contents)) + (parent (or parent contents))) + (dolist (datum contents contents) + (org-element-put-property datum :parent parent)))))))) @@ -4468,71 +4478,74 @@ beginning position." ;; `org-element--interpret-affiliated-keywords'. ;;;###autoload -(defun org-element-interpret-data (data &optional parent) +(defun org-element-interpret-data (data) "Interpret DATA as Org syntax. - DATA is a parse tree, an element, an object or a secondary string -to interpret. - -Optional argument PARENT is used for recursive calls. It contains -the element or object containing data, or nil. - -Return Org syntax as a string." - (let* ((type (org-element-type data)) - (results - (cond - ;; Secondary string. - ((not type) - (mapconcat - (lambda (obj) (org-element-interpret-data obj parent)) - data "")) - ;; Full Org document. - ((eq type 'org-data) - (mapconcat - (lambda (obj) (org-element-interpret-data obj parent)) - (org-element-contents data) "")) - ;; Plain text: return it. - ((stringp data) data) - ;; Element/Object without contents. - ((not (org-element-contents data)) - (funcall (intern (format "org-element-%s-interpreter" type)) - data nil)) - ;; Element/Object with contents. - (t - (let* ((greaterp (memq type org-element-greater-elements)) - (objectp (and (not greaterp) - (memq type org-element-recursive-objects))) - (contents - (mapconcat - (lambda (obj) (org-element-interpret-data obj data)) - (org-element-contents - (if (or greaterp objectp) data - ;; Elements directly containing objects must - ;; have their indentation normalized first. - (org-element-normalize-contents +to interpret. Return Org syntax as a string." + (letrec ((fun + (lambda (data parent) + (let* ((type (org-element-type data)) + ;; Find interpreter for current object or + ;; element. If it doesn't exist (e.g. this is + ;; a pseudo object or element), return contents, + ;; if any. + (interpret + (let ((fun (intern + (format "org-element-%s-interpreter" type)))) + (if (fboundp fun) fun (lambda (_ contents) contents)))) + (results + (cond + ;; Secondary string. + ((not type) + (mapconcat (lambda (obj) (funcall fun obj parent)) + data + "")) + ;; Full Org document. + ((eq type 'org-data) + (mapconcat (lambda (obj) (funcall fun obj parent)) + (org-element-contents data) + "")) + ;; Plain text: return it. + ((stringp data) data) + ;; Element or object without contents. + ((not (org-element-contents data)) + (funcall interpret data nil)) + ;; Element or object with contents. + (t + (funcall + interpret data - ;; When normalizing first paragraph of an - ;; item or a footnote-definition, ignore - ;; first line's indentation. - (and (eq type 'paragraph) - (equal data (car (org-element-contents parent))) - (memq (org-element-type parent) - '(footnote-definition item)))))) - ""))) - (funcall (intern (format "org-element-%s-interpreter" type)) - data - (if greaterp (org-element-normalize-contents contents) - contents))))))) - (if (memq type '(org-data plain-text nil)) results - ;; Build white spaces. If no `:post-blank' property is - ;; specified, assume its value is 0. - (let ((post-blank (or (org-element-property :post-blank data) 0))) - (if (memq type org-element-all-objects) - (concat results (make-string post-blank 32)) - (concat - (org-element--interpret-affiliated-keywords data) - (org-element-normalize-string results) - (make-string post-blank 10))))))) + ;; Recursively interpret contents. + (mapconcat + (lambda (datum) (funcall fun datum data)) + (org-element-contents + (if (not (memq type '(paragraph verse-block))) + data + ;; Fix indentation of elements containing + ;; objects. We ignore `table-row' + ;; elements as they are one line long + ;; anyway. + (org-element-normalize-contents + data + ;; When normalizing first paragraph of + ;; an item or a footnote-definition, + ;; ignore first line's indentation. + (and (eq type 'paragraph) + (memq (org-element-type parent) + '(footnote-definition item)) + (eq data + (car (org-element-contents parent))))))) + "")))))) + (if (memq type '(org-data plain-text nil)) results + ;; Build white spaces. If no `:post-blank' property + ;; is specified, assume its value is 0. + (let ((blank (or (org-element-property :post-blank data) 0))) + (if (eq (org-element-class data parent) 'object) + (concat results (make-string blank ?\s)) + (concat (org-element--interpret-affiliated-keywords data) + (org-element-normalize-string results) + (make-string blank ?\n))))))))) + (funcall fun data nil))) (defun org-element--interpret-affiliated-keywords (element) "Return ELEMENT's affiliated keywords as Org syntax. @@ -4566,14 +4579,14 @@ If there is no affiliated keyword, return the empty string." ;; List all ELEMENT's properties matching an attribute line or an ;; affiliated keyword, but ignore translated keywords since they ;; cannot belong to the property list. - (loop for prop in (nth 1 element) by 'cddr - when (let ((keyword (upcase (substring (symbol-name prop) 1)))) - (or (string-match "^ATTR_" keyword) - (and - (member keyword org-element-affiliated-keywords) - (not (assoc keyword - org-element-keyword-translation-alist))))) - collect prop) + (cl-loop for prop in (nth 1 element) by 'cddr + when (let ((keyword (upcase (substring (symbol-name prop) 1)))) + (or (string-match "^ATTR_" keyword) + (and + (member keyword org-element-affiliated-keywords) + (not (assoc keyword + org-element-keyword-translation-alist))))) + collect prop) ""))) ;; Because interpretation of the parse tree must return the same @@ -4609,67 +4622,1109 @@ If optional argument IGNORE-FIRST is non-nil, ignore first line's indentation to compute maximal common indentation. Return the normalized element that is element with global -indentation removed from its contents. The function assumes that -indentation is not done with TAB characters." - (let* ((min-ind most-positive-fixnum) - find-min-ind ; For byte-compiler. - (find-min-ind - ;; Return minimal common indentation within BLOB. This is - ;; done by walking recursively BLOB and updating MIN-IND - ;; along the way. FIRST-FLAG is non-nil when the first - ;; string hasn't been seen yet. It is required as this - ;; string is the only one whose indentation doesn't happen - ;; after a newline character. - (lambda (blob first-flag) - (dolist (object (org-element-contents blob)) - (when (and first-flag (stringp object)) - (setq first-flag nil) - (string-match "\\` *" object) - (let ((len (match-end 0))) - ;; An indentation of zero means no string will be - ;; modified. Quit the process. - (if (zerop len) (throw 'zero (setq min-ind 0)) - (setq min-ind (min len min-ind))))) - (cond - ((stringp object) - (dolist (line (cdr (org-split-string object " *\n"))) - (unless (string= line "") - (setq min-ind (min (org-get-indentation line) min-ind))))) - ((memq (org-element-type object) org-element-recursive-objects) - (funcall find-min-ind object first-flag))))))) - ;; Find minimal indentation in ELEMENT. - (catch 'zero (funcall find-min-ind element (not ignore-first))) +indentation removed from its contents." + (letrec ((find-min-ind + ;; Return minimal common indentation within BLOB. This is + ;; done by walking recursively BLOB and updating MIN-IND + ;; along the way. FIRST-FLAG is non-nil when the next + ;; object is expected to be a string that doesn't start + ;; with a newline character. It happens for strings at + ;; the beginnings of the contents or right after a line + ;; break. + (lambda (blob first-flag min-ind) + (dolist (datum (org-element-contents blob) min-ind) + (when first-flag + (setq first-flag nil) + (cond + ;; Objects cannot start with spaces: in this + ;; case, indentation is 0. + ((not (stringp datum)) (throw :zero 0)) + ((not (string-match + "\\`\\([ \t]+\\)\\([^ \t\n]\\|\n\\|\\'\\)" datum)) + (throw :zero 0)) + ((equal (match-string 2 datum) "\n") + (put-text-property + (match-beginning 1) (match-end 1) 'org-ind 'empty datum)) + (t + (let ((i (string-width (match-string 1 datum)))) + (put-text-property + (match-beginning 1) (match-end 1) 'org-ind i datum) + (setq min-ind (min i min-ind)))))) + (cond + ((stringp datum) + (let ((s 0)) + (while (string-match + "\n\\([ \t]*\\)\\([^ \t\n]\\|\n\\|\\'\\)" datum s) + (setq s (match-end 1)) + (cond + ((equal (match-string 1 datum) "") + (unless (member (match-string 2 datum) '("" "\n")) + (throw :zero 0))) + ((equal (match-string 2 datum) "\n") + (put-text-property (match-beginning 1) (match-end 1) + 'org-ind 'empty datum)) + (t + (let ((i (string-width (match-string 1 datum)))) + (put-text-property (match-beginning 1) (match-end 1) + 'org-ind i datum) + (setq min-ind (min i min-ind)))))))) + ((eq (org-element-type datum) 'line-break) + (setq first-flag t)) + ((memq (org-element-type datum) org-element-recursive-objects) + (setq min-ind + (funcall find-min-ind datum first-flag min-ind))))))) + (min-ind + (catch :zero + (funcall find-min-ind + element (not ignore-first) most-positive-fixnum)))) (if (or (zerop min-ind) (= min-ind most-positive-fixnum)) element ;; Build ELEMENT back, replacing each string with the same ;; string minus common indentation. - (let* (build ; For byte compiler. - (build - (function - (lambda (blob first-flag) - ;; Return BLOB with all its strings indentation - ;; shortened from MIN-IND white spaces. FIRST-FLAG - ;; is non-nil when the first string hasn't been seen - ;; yet. - (setcdr (cdr blob) - (mapcar - #'(lambda (object) - (when (and first-flag (stringp object)) - (setq first-flag nil) - (setq object - (replace-regexp-in-string - (format "\\` \\{%d\\}" min-ind) - "" object))) - (cond - ((stringp object) - (replace-regexp-in-string - (format "\n \\{%d\\}" min-ind) "\n" object)) - ((memq (org-element-type object) - org-element-recursive-objects) - (funcall build object first-flag)) - (t object))) - (org-element-contents blob))) - blob)))) - (funcall build element (not ignore-first)))))) + (letrec ((build + (lambda (datum) + ;; Return DATUM with all its strings indentation + ;; shortened from MIN-IND white spaces. + (setcdr + (cdr datum) + (mapcar + (lambda (object) + (cond + ((stringp object) + (with-temp-buffer + (insert object) + (let ((s (point-min))) + (while (setq s (text-property-not-all + s (point-max) 'org-ind nil)) + (goto-char s) + (let ((i (get-text-property s 'org-ind))) + (delete-region s (progn + (skip-chars-forward " \t") + (point))) + (when (integerp i) (indent-to (- i min-ind)))))) + (buffer-string))) + ((memq (org-element-type object) + org-element-recursive-objects) + (funcall build object)) + (t object))) + (org-element-contents datum))) + datum))) + (funcall build element))))) + + + +;;; Cache +;; +;; Implement a caching mechanism for `org-element-at-point' and +;; `org-element-context', which see. +;; +;; A single public function is provided: `org-element-cache-reset'. +;; +;; Cache is enabled by default, but can be disabled globally with +;; `org-element-use-cache'. `org-element-cache-sync-idle-time', +;; org-element-cache-sync-duration' and `org-element-cache-sync-break' +;; can be tweaked to control caching behaviour. +;; +;; Internally, parsed elements are stored in an AVL tree, +;; `org-element--cache'. This tree is updated lazily: whenever +;; a change happens to the buffer, a synchronization request is +;; registered in `org-element--cache-sync-requests' (see +;; `org-element--cache-submit-request'). During idle time, requests +;; are processed by `org-element--cache-sync'. Synchronization also +;; happens when an element is required from the cache. In this case, +;; the process stops as soon as the needed element is up-to-date. +;; +;; A synchronization request can only apply on a synchronized part of +;; the cache. Therefore, the cache is updated at least to the +;; location where the new request applies. Thus, requests are ordered +;; from left to right and all elements starting before the first +;; request are correct. This property is used by functions like +;; `org-element--cache-find' to retrieve elements in the part of the +;; cache that can be trusted. +;; +;; A request applies to every element, starting from its original +;; location (or key, see below). When a request is processed, it +;; moves forward and may collide the next one. In this case, both +;; requests are merged into a new one that starts from that element. +;; As a consequence, the whole synchronization complexity does not +;; depend on the number of pending requests, but on the number of +;; elements the very first request will be applied on. +;; +;; Elements cannot be accessed through their beginning position, which +;; may or may not be up-to-date. Instead, each element in the tree is +;; associated to a key, obtained with `org-element--cache-key'. This +;; mechanism is robust enough to preserve total order among elements +;; even when the tree is only partially synchronized. +;; +;; Objects contained in an element are stored in a hash table, +;; `org-element--cache-objects'. + + +(defvar org-element-use-cache nil + "Non-nil when Org parser should cache its results. + +WARNING: for the time being, using cache sometimes triggers +freezes. Therefore, it is disabled by default. Activate it if +you want to help debugging the issue.") + +(defvar org-element-cache-sync-idle-time 0.6 + "Length, in seconds, of idle time before syncing cache.") + +(defvar org-element-cache-sync-duration (seconds-to-time 0.04) + "Maximum duration, as a time value, for a cache synchronization. +If the synchronization is not over after this delay, the process +pauses and resumes after `org-element-cache-sync-break' +seconds.") + +(defvar org-element-cache-sync-break (seconds-to-time 0.3) + "Duration, as a time value, of the pause between synchronizations. +See `org-element-cache-sync-duration' for more information.") + + +;;;; Data Structure + +(defvar org-element--cache nil + "AVL tree used to cache elements. +Each node of the tree contains an element. Comparison is done +with `org-element--cache-compare'. This cache is used in +`org-element-at-point'.") + +(defvar org-element--cache-objects nil + "Hash table used as to cache objects. +Key is an element, as returned by `org-element-at-point', and +value is an alist where each association is: + + (PARENT COMPLETEP . OBJECTS) + +where PARENT is an element or object, COMPLETEP is a boolean, +non-nil when all direct children of parent are already cached and +OBJECTS is a list of such children, as objects, from farthest to +closest. + +In the following example, \\alpha, bold object and \\beta are +contained within a paragraph + + \\alpha *\\beta* + +If the paragraph is completely parsed, OBJECTS-DATA will be + + ((PARAGRAPH t BOLD-OBJECT ENTITY-OBJECT) + (BOLD-OBJECT t ENTITY-OBJECT)) + +whereas in a partially parsed paragraph, it could be + + ((PARAGRAPH nil ENTITY-OBJECT)) + +This cache is used in `org-element-context'.") + +(defvar org-element--cache-sync-requests nil + "List of pending synchronization requests. + +A request is a vector with the following pattern: + + \[NEXT BEG END OFFSET PARENT PHASE] + +Processing a synchronization request consists of three phases: + + 0. Delete modified elements, + 1. Fill missing area in cache, + 2. Shift positions and re-parent elements after the changes. + +During phase 0, NEXT is the key of the first element to be +removed, BEG and END is buffer position delimiting the +modifications. Elements starting between them (inclusive) are +removed. So are elements whose parent is removed. PARENT, when +non-nil, is the parent of the first element to be removed. + +During phase 1, NEXT is the key of the next known element in +cache and BEG its beginning position. Parse buffer between that +element and the one before it in order to determine the parent of +the next element. Set PARENT to the element containing NEXT. + +During phase 2, NEXT is the key of the next element to shift in +the parse tree. All elements starting from this one have their +properties relatives to buffer positions shifted by integer +OFFSET and, if they belong to element PARENT, are adopted by it. + +PHASE specifies the phase number, as an integer.") + +(defvar org-element--cache-sync-timer nil + "Timer used for cache synchronization.") + +(defvar org-element--cache-sync-keys nil + "Hash table used to store keys during synchronization. +See `org-element--cache-key' for more information.") + +(defsubst org-element--cache-key (element) + "Return a unique key for ELEMENT in cache tree. + +Keys are used to keep a total order among elements in the cache. +Comparison is done with `org-element--cache-key-less-p'. + +When no synchronization is taking place, a key is simply the +beginning position of the element, or that position plus one in +the case of an first item (respectively row) in +a list (respectively a table). + +During a synchronization, the key is the one the element had when +the cache was synchronized for the last time. Elements added to +cache during the synchronization get a new key generated with +`org-element--cache-generate-key'. + +Such keys are stored in `org-element--cache-sync-keys'. The hash +table is cleared once the synchronization is complete." + (or (gethash element org-element--cache-sync-keys) + (let* ((begin (org-element-property :begin element)) + ;; Increase beginning position of items (respectively + ;; table rows) by one, so the first item can get + ;; a different key from its parent list (respectively + ;; table). + (key (if (memq (org-element-type element) '(item table-row)) + (1+ begin) + begin))) + (if org-element--cache-sync-requests + (puthash element key org-element--cache-sync-keys) + key)))) + +(defun org-element--cache-generate-key (lower upper) + "Generate a key between LOWER and UPPER. + +LOWER and UPPER are integers or lists, possibly empty. + +If LOWER and UPPER are equals, return LOWER. Otherwise, return +a unique key, as an integer or a list of integers, according to +the following rules: + + - LOWER and UPPER are compared level-wise until values differ. + + - If, at a given level, LOWER and UPPER differ from more than + 2, the new key shares all the levels above with LOWER and + gets a new level. Its value is the mean between LOWER and + UPPER: + + (1 2) + (1 4) --> (1 3) + + - If LOWER has no value to compare with, it is assumed that its + value is `most-negative-fixnum'. E.g., + + (1 1) + (1 1 2) + + is equivalent to + + (1 1 m) + (1 1 2) + + where m is `most-negative-fixnum'. Likewise, if UPPER is + short of levels, the current value is `most-positive-fixnum'. + + - If they differ from only one, the new key inherits from + current LOWER level and fork it at the next level. E.g., + + (2 1) + (3 3) + + is equivalent to + + (2 1) + (2 M) + + where M is `most-positive-fixnum'. + + - If the key is only one level long, it is returned as an + integer: + + (1 2) + (3 2) --> 2 + +When they are not equals, the function assumes that LOWER is +lesser than UPPER, per `org-element--cache-key-less-p'." + (if (equal lower upper) lower + (let ((lower (if (integerp lower) (list lower) lower)) + (upper (if (integerp upper) (list upper) upper)) + skip-upper key) + (catch 'exit + (while t + (let ((min (or (car lower) most-negative-fixnum)) + (max (cond (skip-upper most-positive-fixnum) + ((car upper)) + (t most-positive-fixnum)))) + (if (< (1+ min) max) + (let ((mean (+ (ash min -1) (ash max -1) (logand min max 1)))) + (throw 'exit (if key (nreverse (cons mean key)) mean))) + (when (and (< min max) (not skip-upper)) + ;; When at a given level, LOWER and UPPER differ from + ;; 1, ignore UPPER altogether. Instead create a key + ;; between LOWER and the greatest key with the same + ;; prefix as LOWER so far. + (setq skip-upper t)) + (push min key) + (setq lower (cdr lower) upper (cdr upper))))))))) + +(defsubst org-element--cache-key-less-p (a b) + "Non-nil if key A is less than key B. +A and B are either integers or lists of integers, as returned by +`org-element--cache-key'." + (if (integerp a) (if (integerp b) (< a b) (<= a (car b))) + (if (integerp b) (< (car a) b) + (catch 'exit + (while (and a b) + (cond ((car-less-than-car a b) (throw 'exit t)) + ((car-less-than-car b a) (throw 'exit nil)) + (t (setq a (cdr a) b (cdr b))))) + ;; If A is empty, either keys are equal (B is also empty) and + ;; we return nil, or A is lesser than B (B is longer) and we + ;; return a non-nil value. + ;; + ;; If A is not empty, B is necessarily empty and A is greater + ;; than B (A is longer). Therefore, return nil. + (and (null a) b))))) + +(defun org-element--cache-compare (a b) + "Non-nil when element A is located before element B." + (org-element--cache-key-less-p (org-element--cache-key a) + (org-element--cache-key b))) + +(defsubst org-element--cache-root () + "Return root value in cache. +This function assumes `org-element--cache' is a valid AVL tree." + (avl-tree--node-left (avl-tree--dummyroot org-element--cache))) + + +;;;; Tools + +(defsubst org-element--cache-active-p () + "Non-nil when cache is active in current buffer." + (and org-element-use-cache + org-element--cache + (derived-mode-p 'org-mode))) + +(defun org-element--cache-find (pos &optional side) + "Find element in cache starting at POS or before. + +POS refers to a buffer position. + +When optional argument SIDE is non-nil, the function checks for +elements starting at or past POS instead. If SIDE is `both', the +function returns a cons cell where car is the first element +starting at or before POS and cdr the first element starting +after POS. + +The function can only find elements in the synchronized part of +the cache." + (let ((limit (and org-element--cache-sync-requests + (aref (car org-element--cache-sync-requests) 0))) + (node (org-element--cache-root)) + lower upper) + (while node + (let* ((element (avl-tree--node-data node)) + (begin (org-element-property :begin element))) + (cond + ((and limit + (not (org-element--cache-key-less-p + (org-element--cache-key element) limit))) + (setq node (avl-tree--node-left node))) + ((> begin pos) + (setq upper element + node (avl-tree--node-left node))) + ((< begin pos) + (setq lower element + node (avl-tree--node-right node))) + ;; We found an element in cache starting at POS. If `side' + ;; is `both' we also want the next one in order to generate + ;; a key in-between. + ;; + ;; If the element is the first row or item in a table or + ;; a plain list, we always return the table or the plain + ;; list. + ;; + ;; In any other case, we return the element found. + ((eq side 'both) + (setq lower element) + (setq node (avl-tree--node-right node))) + ((and (memq (org-element-type element) '(item table-row)) + (let ((parent (org-element-property :parent element))) + (and (= (org-element-property :begin element) + (org-element-property :contents-begin parent)) + (setq node nil + lower parent + upper parent))))) + (t + (setq node nil + lower element + upper element))))) + (pcase side + (`both (cons lower upper)) + (`nil lower) + (_ upper)))) + +(defun org-element--cache-put (element &optional data) + "Store ELEMENT in current buffer's cache, if allowed. +When optional argument DATA is non-nil, assume is it object data +relative to ELEMENT and store it in the objects cache." + (cond ((not (org-element--cache-active-p)) nil) + ((not data) + (when org-element--cache-sync-requests + ;; During synchronization, first build an appropriate key + ;; for the new element so `avl-tree-enter' can insert it at + ;; the right spot in the cache. + (let ((keys (org-element--cache-find + (org-element-property :begin element) 'both))) + (puthash element + (org-element--cache-generate-key + (and (car keys) (org-element--cache-key (car keys))) + (cond ((cdr keys) (org-element--cache-key (cdr keys))) + (org-element--cache-sync-requests + (aref (car org-element--cache-sync-requests) 0)))) + org-element--cache-sync-keys))) + (avl-tree-enter org-element--cache element)) + ;; Headlines are not stored in cache, so objects in titles are + ;; not stored either. + ((eq (org-element-type element) 'headline) nil) + (t (puthash element data org-element--cache-objects)))) + +(defsubst org-element--cache-remove (element) + "Remove ELEMENT from cache. +Assume ELEMENT belongs to cache and that a cache is active." + (avl-tree-delete org-element--cache element) + (remhash element org-element--cache-objects)) + + +;;;; Synchronization + +(defsubst org-element--cache-set-timer (buffer) + "Set idle timer for cache synchronization in BUFFER." + (when org-element--cache-sync-timer + (cancel-timer org-element--cache-sync-timer)) + (setq org-element--cache-sync-timer + (run-with-idle-timer + (let ((idle (current-idle-time))) + (if idle (time-add idle org-element-cache-sync-break) + org-element-cache-sync-idle-time)) + nil + #'org-element--cache-sync + buffer))) + +(defsubst org-element--cache-interrupt-p (time-limit) + "Non-nil when synchronization process should be interrupted. +TIME-LIMIT is a time value or nil." + (and time-limit + (or (input-pending-p) + (time-less-p time-limit (current-time))))) + +(defsubst org-element--cache-shift-positions (element offset &optional props) + "Shift ELEMENT properties relative to buffer positions by OFFSET. + +Properties containing buffer positions are `:begin', `:end', +`:contents-begin', `:contents-end' and `:structure'. When +optional argument PROPS is a list of keywords, only shift +properties provided in that list. + +Properties are modified by side-effect." + (let ((properties (nth 1 element))) + ;; Shift `:structure' property for the first plain list only: it + ;; is the only one that really matters and it prevents from + ;; shifting it more than once. + (when (and (or (not props) (memq :structure props)) + (eq (org-element-type element) 'plain-list) + (not (eq (org-element-type (plist-get properties :parent)) + 'item))) + (dolist (item (plist-get properties :structure)) + (cl-incf (car item) offset) + (cl-incf (nth 6 item) offset))) + (dolist (key '(:begin :contents-begin :contents-end :end :post-affiliated)) + (let ((value (and (or (not props) (memq key props)) + (plist-get properties key)))) + (and value (plist-put properties key (+ offset value))))))) + +(defun org-element--cache-sync (buffer &optional threshold future-change) + "Synchronize cache with recent modification in BUFFER. + +When optional argument THRESHOLD is non-nil, do the +synchronization for all elements starting before or at threshold, +then exit. Otherwise, synchronize cache for as long as +`org-element-cache-sync-duration' or until Emacs leaves idle +state. + +FUTURE-CHANGE, when non-nil, is a buffer position where changes +not registered yet in the cache are going to happen. It is used +in `org-element--cache-submit-request', where cache is partially +updated before current modification are actually submitted." + (when (buffer-live-p buffer) + (with-current-buffer buffer + (let ((inhibit-quit t) request next) + (when org-element--cache-sync-timer + (cancel-timer org-element--cache-sync-timer)) + (catch 'interrupt + (while org-element--cache-sync-requests + (setq request (car org-element--cache-sync-requests) + next (nth 1 org-element--cache-sync-requests)) + (org-element--cache-process-request + request + (and next (aref next 0)) + threshold + (and (not threshold) + (time-add (current-time) + org-element-cache-sync-duration)) + future-change) + ;; Request processed. Merge current and next offsets and + ;; transfer ending position. + (when next + (cl-incf (aref next 3) (aref request 3)) + (aset next 2 (aref request 2))) + (setq org-element--cache-sync-requests + (cdr org-element--cache-sync-requests)))) + ;; If more requests are awaiting, set idle timer accordingly. + ;; Otherwise, reset keys. + (if org-element--cache-sync-requests + (org-element--cache-set-timer buffer) + (clrhash org-element--cache-sync-keys)))))) + +(defun org-element--cache-process-request + (request next threshold time-limit future-change) + "Process synchronization REQUEST for all entries before NEXT. + +REQUEST is a vector, built by `org-element--cache-submit-request'. + +NEXT is a cache key, as returned by `org-element--cache-key'. + +When non-nil, THRESHOLD is a buffer position. Synchronization +stops as soon as a shifted element begins after it. + +When non-nil, TIME-LIMIT is a time value. Synchronization stops +after this time or when Emacs exits idle state. + +When non-nil, FUTURE-CHANGE is a buffer position where changes +not registered yet in the cache are going to happen. See +`org-element--cache-submit-request' for more information. + +Throw `interrupt' if the process stops before completing the +request." + (catch 'quit + (when (= (aref request 5) 0) + ;; Phase 0. + ;; + ;; Delete all elements starting after BEG, but not after buffer + ;; position END or past element with key NEXT. Also delete + ;; elements contained within a previously removed element + ;; (stored in `last-container'). + ;; + ;; At each iteration, we start again at tree root since + ;; a deletion modifies structure of the balanced tree. + (catch 'end-phase + (while t + (when (org-element--cache-interrupt-p time-limit) + (throw 'interrupt nil)) + ;; Find first element in cache with key BEG or after it. + (let ((beg (aref request 0)) + (end (aref request 2)) + (node (org-element--cache-root)) + data data-key last-container) + (while node + (let* ((element (avl-tree--node-data node)) + (key (org-element--cache-key element))) + (cond + ((org-element--cache-key-less-p key beg) + (setq node (avl-tree--node-right node))) + ((org-element--cache-key-less-p beg key) + (setq data element + data-key key + node (avl-tree--node-left node))) + (t (setq data element + data-key key + node nil))))) + (if data + (let ((pos (org-element-property :begin data))) + (if (if (or (not next) + (org-element--cache-key-less-p data-key next)) + (<= pos end) + (and last-container + (let ((up data)) + (while (and up (not (eq up last-container))) + (setq up (org-element-property :parent up))) + up))) + (progn (when (and (not last-container) + (> (org-element-property :end data) + end)) + (setq last-container data)) + (org-element--cache-remove data)) + (aset request 0 data-key) + (aset request 1 pos) + (aset request 5 1) + (throw 'end-phase nil))) + ;; No element starting after modifications left in + ;; cache: further processing is futile. + (throw 'quit t)))))) + (when (= (aref request 5) 1) + ;; Phase 1. + ;; + ;; Phase 0 left a hole in the cache. Some elements after it + ;; could have parents within. For example, in the following + ;; buffer: + ;; + ;; - item + ;; + ;; + ;; Paragraph1 + ;; + ;; Paragraph2 + ;; + ;; if we remove a blank line between "item" and "Paragraph1", + ;; everything down to "Paragraph2" is removed from cache. But + ;; the paragraph now belongs to the list, and its `:parent' + ;; property no longer is accurate. + ;; + ;; Therefore we need to parse again elements in the hole, or at + ;; least in its last section, so that we can re-parent + ;; subsequent elements, during phase 2. + ;; + ;; Note that we only need to get the parent from the first + ;; element in cache after the hole. + ;; + ;; When next key is lesser or equal to the current one, delegate + ;; phase 1 processing to next request in order to preserve key + ;; order among requests. + (let ((key (aref request 0))) + (when (and next (not (org-element--cache-key-less-p key next))) + (let ((next-request (nth 1 org-element--cache-sync-requests))) + (aset next-request 0 key) + (aset next-request 1 (aref request 1)) + (aset next-request 5 1)) + (throw 'quit t))) + ;; Next element will start at its beginning position plus + ;; offset, since it hasn't been shifted yet. Therefore, LIMIT + ;; contains the real beginning position of the first element to + ;; shift and re-parent. + (let ((limit (+ (aref request 1) (aref request 3)))) + (cond ((and threshold (> limit threshold)) (throw 'interrupt nil)) + ((and future-change (>= limit future-change)) + ;; Changes are going to happen around this element and + ;; they will trigger another phase 1 request. Skip the + ;; current one. + (aset request 5 2)) + (t + (let ((parent (org-element--parse-to limit t time-limit))) + (aset request 4 parent) + (aset request 5 2)))))) + ;; Phase 2. + ;; + ;; Shift all elements starting from key START, but before NEXT, by + ;; OFFSET, and re-parent them when appropriate. + ;; + ;; Elements are modified by side-effect so the tree structure + ;; remains intact. + ;; + ;; Once THRESHOLD, if any, is reached, or once there is an input + ;; pending, exit. Before leaving, the current synchronization + ;; request is updated. + (let ((start (aref request 0)) + (offset (aref request 3)) + (parent (aref request 4)) + (node (org-element--cache-root)) + (stack (list nil)) + (leftp t) + exit-flag) + ;; No re-parenting nor shifting planned: request is over. + (when (and (not parent) (zerop offset)) (throw 'quit t)) + (while node + (let* ((data (avl-tree--node-data node)) + (key (org-element--cache-key data))) + (if (and leftp (avl-tree--node-left node) + (not (org-element--cache-key-less-p key start))) + (progn (push node stack) + (setq node (avl-tree--node-left node))) + (unless (org-element--cache-key-less-p key start) + ;; We reached NEXT. Request is complete. + (when (equal key next) (throw 'quit t)) + ;; Handle interruption request. Update current request. + (when (or exit-flag (org-element--cache-interrupt-p time-limit)) + (aset request 0 key) + (aset request 4 parent) + (throw 'interrupt nil)) + ;; Shift element. + (unless (zerop offset) + (org-element--cache-shift-positions data offset) + ;; Shift associated objects data, if any. + (dolist (object-data (gethash data org-element--cache-objects)) + (dolist (object (cddr object-data)) + (org-element--cache-shift-positions object offset)))) + (let ((begin (org-element-property :begin data))) + ;; Update PARENT and re-parent DATA, only when + ;; necessary. Propagate new structures for lists. + (while (and parent + (<= (org-element-property :end parent) begin)) + (setq parent (org-element-property :parent parent))) + (cond ((and (not parent) (zerop offset)) (throw 'quit nil)) + ((and parent + (let ((p (org-element-property :parent data))) + (or (not p) + (< (org-element-property :begin p) + (org-element-property :begin parent))))) + (org-element-put-property data :parent parent) + (let ((s (org-element-property :structure parent))) + (when (and s (org-element-property :structure data)) + (org-element-put-property data :structure s))))) + ;; Cache is up-to-date past THRESHOLD. Request + ;; interruption. + (when (and threshold (> begin threshold)) (setq exit-flag t)))) + (setq node (if (setq leftp (avl-tree--node-right node)) + (avl-tree--node-right node) + (pop stack)))))) + ;; We reached end of tree: synchronization complete. + t))) + +(defun org-element--parse-to (pos &optional syncp time-limit) + "Parse elements in current section, down to POS. + +Start parsing from the closest between the last known element in +cache or headline above. Return the smallest element containing +POS. + +When optional argument SYNCP is non-nil, return the parent of the +element containing POS instead. In that case, it is also +possible to provide TIME-LIMIT, which is a time value specifying +when the parsing should stop. The function throws `interrupt' if +the process stopped before finding the expected result." + (catch 'exit + (org-with-wide-buffer + (goto-char pos) + (let* ((cached (and (org-element--cache-active-p) + (org-element--cache-find pos nil))) + (begin (org-element-property :begin cached)) + element next mode) + (cond + ;; Nothing in cache before point: start parsing from first + ;; element following headline above, or first element in + ;; buffer. + ((not cached) + (when (org-with-limited-levels (outline-previous-heading)) + (setq mode 'planning) + (forward-line)) + (skip-chars-forward " \r\t\n") + (beginning-of-line)) + ;; Cache returned exact match: return it. + ((= pos begin) + (throw 'exit (if syncp (org-element-property :parent cached) cached))) + ;; There's a headline between cached value and POS: cached + ;; value is invalid. Start parsing from first element + ;; following the headline. + ((re-search-backward + (org-with-limited-levels org-outline-regexp-bol) begin t) + (forward-line) + (skip-chars-forward " \r\t\n") + (beginning-of-line) + (setq mode 'planning)) + ;; Check if CACHED or any of its ancestors contain point. + ;; + ;; If there is such an element, we inspect it in order to know + ;; if we return it or if we need to parse its contents. + ;; Otherwise, we just start parsing from current location, + ;; which is right after the top-most element containing + ;; CACHED. + ;; + ;; As a special case, if POS is at the end of the buffer, we + ;; want to return the innermost element ending there. + ;; + ;; Also, if we find an ancestor and discover that we need to + ;; parse its contents, make sure we don't start from + ;; `:contents-begin', as we would otherwise go past CACHED + ;; again. Instead, in that situation, we will resume parsing + ;; from NEXT, which is located after CACHED or its higher + ;; ancestor not containing point. + (t + (let ((up cached) + (pos (if (= (point-max) pos) (1- pos) pos))) + (goto-char (or (org-element-property :contents-begin cached) begin)) + (while (let ((end (org-element-property :end up))) + (and (<= end pos) + (goto-char end) + (setq up (org-element-property :parent up))))) + (cond ((not up)) + ((eobp) (setq element up)) + (t (setq element up next (point))))))) + ;; Parse successively each element until we reach POS. + (let ((end (or (org-element-property :end element) + (save-excursion + (org-with-limited-levels (outline-next-heading)) + (point)))) + (parent element)) + (while t + (when syncp + (cond ((= (point) pos) (throw 'exit parent)) + ((org-element--cache-interrupt-p time-limit) + (throw 'interrupt nil)))) + (unless element + (setq element (org-element--current-element + end 'element mode + (org-element-property :structure parent))) + (org-element-put-property element :parent parent) + (org-element--cache-put element)) + (let ((elem-end (org-element-property :end element)) + (type (org-element-type element))) + (cond + ;; Skip any element ending before point. Also skip + ;; element ending at point (unless it is also the end of + ;; buffer) since we're sure that another element begins + ;; after it. + ((and (<= elem-end pos) (/= (point-max) elem-end)) + (goto-char elem-end) + (setq mode (org-element--next-mode type nil))) + ;; A non-greater element contains point: return it. + ((not (memq type org-element-greater-elements)) + (throw 'exit element)) + ;; Otherwise, we have to decide if ELEMENT really + ;; contains POS. In that case we start parsing from + ;; contents' beginning. + ;; + ;; If POS is at contents' beginning but it is also at + ;; the beginning of the first item in a list or a table. + ;; In that case, we need to create an anchor for that + ;; list or table, so return it. + ;; + ;; Also, if POS is at the end of the buffer, no element + ;; can start after it, but more than one may end there. + ;; Arbitrarily, we choose to return the innermost of + ;; such elements. + ((let ((cbeg (org-element-property :contents-begin element)) + (cend (org-element-property :contents-end element))) + (when (or syncp + (and cbeg cend + (or (< cbeg pos) + (and (= cbeg pos) + (not (memq type '(plain-list table))))) + (or (> cend pos) + (and (= cend pos) (= (point-max) pos))))) + (goto-char (or next cbeg)) + (setq next nil + mode (org-element--next-mode type t) + parent element + end cend)))) + ;; Otherwise, return ELEMENT as it is the smallest + ;; element containing POS. + (t (throw 'exit element)))) + (setq element nil))))))) + + +;;;; Staging Buffer Changes + +(defconst org-element--cache-sensitive-re + (concat + org-outline-regexp-bol "\\|" + "\\\\end{[A-Za-z0-9*]+}[ \t]*$" "\\|" + "^[ \t]*\\(?:" + "#\\+\\(?:BEGIN[:_]\\|END\\(?:_\\|:?[ \t]*$\\)\\)" "\\|" + "\\\\begin{[A-Za-z0-9*]+}" "\\|" + ":\\(?:\\w\\|[-_]\\)+:[ \t]*$" + "\\)") + "Regexp matching a sensitive line, structure wise. +A sensitive line is a headline, inlinetask, block, drawer, or +latex-environment boundary. When such a line is modified, +structure changes in the document may propagate in the whole +section, possibly making cache invalid.") + +(defvar org-element--cache-change-warning nil + "Non-nil when a sensitive line is about to be changed. +It is a symbol among nil, t and `headline'.") + +(defun org-element--cache-before-change (beg end) + "Request extension of area going to be modified if needed. +BEG and END are the beginning and end of the range of changed +text. See `before-change-functions' for more information." + (when (org-element--cache-active-p) + (org-with-wide-buffer + (goto-char beg) + (beginning-of-line) + (let ((bottom (save-excursion (goto-char end) (line-end-position)))) + (setq org-element--cache-change-warning + (save-match-data + (if (and (org-with-limited-levels (org-at-heading-p)) + (= (line-end-position) bottom)) + 'headline + (let ((case-fold-search t)) + (re-search-forward + org-element--cache-sensitive-re bottom t))))))))) + +(defun org-element--cache-after-change (beg end pre) + "Update buffer modifications for current buffer. +BEG and END are the beginning and end of the range of changed +text, and the length in bytes of the pre-change text replaced by +that range. See `after-change-functions' for more information." + (when (org-element--cache-active-p) + (org-with-wide-buffer + (goto-char beg) + (beginning-of-line) + (save-match-data + (let ((top (point)) + (bottom (save-excursion (goto-char end) (line-end-position)))) + ;; Determine if modified area needs to be extended, according + ;; to both previous and current state. We make a special + ;; case for headline editing: if a headline is modified but + ;; not removed, do not extend. + (when (pcase org-element--cache-change-warning + (`t t) + (`headline + (not (and (org-with-limited-levels (org-at-heading-p)) + (= (line-end-position) bottom)))) + (_ + (let ((case-fold-search t)) + (re-search-forward + org-element--cache-sensitive-re bottom t)))) + ;; Effectively extend modified area. + (org-with-limited-levels + (setq top (progn (goto-char top) + (when (outline-previous-heading) (forward-line)) + (point))) + (setq bottom (progn (goto-char bottom) + (if (outline-next-heading) (1- (point)) + (point)))))) + ;; Store synchronization request. + (let ((offset (- end beg pre))) + (org-element--cache-submit-request top (- bottom offset) offset))))) + ;; Activate a timer to process the request during idle time. + (org-element--cache-set-timer (current-buffer)))) + +(defun org-element--cache-for-removal (beg end offset) + "Return first element to remove from cache. + +BEG and END are buffer positions delimiting buffer modifications. +OFFSET is the size of the changes. + +Returned element is usually the first element in cache containing +any position between BEG and END. As an exception, greater +elements around the changes that are robust to contents +modifications are preserved and updated according to the +changes." + (let* ((elements (org-element--cache-find (1- beg) 'both)) + (before (car elements)) + (after (cdr elements))) + (if (not before) after + (let ((up before) + (robust-flag t)) + (while up + (if (let ((type (org-element-type up))) + (and (or (memq type '(center-block dynamic-block quote-block + special-block)) + ;; Drawers named "PROPERTIES" are probably + ;; a properties drawer being edited. Force + ;; parsing to check if editing is over. + (and (eq type 'drawer) + (not (string= + (org-element-property :drawer-name up) + "PROPERTIES")))) + (let ((cbeg (org-element-property :contents-begin up))) + (and cbeg + (<= cbeg beg) + (> (org-element-property :contents-end up) end))))) + ;; UP is a robust greater element containing changes. + ;; We only need to extend its ending boundaries. + (org-element--cache-shift-positions + up offset '(:contents-end :end)) + (setq before up) + (when robust-flag (setq robust-flag nil))) + (setq up (org-element-property :parent up))) + ;; We're at top level element containing ELEMENT: if it's + ;; altered by buffer modifications, it is first element in + ;; cache to be removed. Otherwise, that first element is the + ;; following one. + ;; + ;; As a special case, do not remove BEFORE if it is a robust + ;; container for current changes. + (if (or (< (org-element-property :end before) beg) robust-flag) after + before))))) + +(defun org-element--cache-submit-request (beg end offset) + "Submit a new cache synchronization request for current buffer. +BEG and END are buffer positions delimiting the minimal area +where cache data should be removed. OFFSET is the size of the +change, as an integer." + (let ((next (car org-element--cache-sync-requests)) + delete-to delete-from) + (if (and next + (zerop (aref next 5)) + (> (setq delete-to (+ (aref next 2) (aref next 3))) end) + (<= (setq delete-from (aref next 1)) end)) + ;; Current changes can be merged with first sync request: we + ;; can save a partial cache synchronization. + (progn + (cl-incf (aref next 3) offset) + ;; If last change happened within area to be removed, extend + ;; boundaries of robust parents, if any. Otherwise, find + ;; first element to remove and update request accordingly. + (if (> beg delete-from) + (let ((up (aref next 4))) + (while up + (org-element--cache-shift-positions + up offset '(:contents-end :end)) + (setq up (org-element-property :parent up)))) + (let ((first (org-element--cache-for-removal beg delete-to offset))) + (when first + (aset next 0 (org-element--cache-key first)) + (aset next 1 (org-element-property :begin first)) + (aset next 4 (org-element-property :parent first)))))) + ;; Ensure cache is correct up to END. Also make sure that NEXT, + ;; if any, is no longer a 0-phase request, thus ensuring that + ;; phases are properly ordered. We need to provide OFFSET as + ;; optional parameter since current modifications are not known + ;; yet to the otherwise correct part of the cache (i.e, before + ;; the first request). + (when next (org-element--cache-sync (current-buffer) end beg)) + (let ((first (org-element--cache-for-removal beg end offset))) + (if first + (push (let ((beg (org-element-property :begin first)) + (key (org-element--cache-key first))) + (cond + ;; When changes happen before the first known + ;; element, re-parent and shift the rest of the + ;; cache. + ((> beg end) (vector key beg nil offset nil 1)) + ;; Otherwise, we find the first non robust + ;; element containing END. All elements between + ;; FIRST and this one are to be removed. + ((let ((first-end (org-element-property :end first))) + (and (> first-end end) + (vector key beg first-end offset first 0)))) + (t + (let* ((element (org-element--cache-find end)) + (end (org-element-property :end element)) + (up element)) + (while (and (setq up (org-element-property :parent up)) + (>= (org-element-property :begin up) beg)) + (setq end (org-element-property :end up) + element up)) + (vector key beg end offset element 0))))) + org-element--cache-sync-requests) + ;; No element to remove. No need to re-parent either. + ;; Simply shift additional elements, if any, by OFFSET. + (when org-element--cache-sync-requests + (cl-incf (aref (car org-element--cache-sync-requests) 3) + offset))))))) + + +;;;; Public Functions + +;;;###autoload +(defun org-element-cache-reset (&optional all) + "Reset cache in current buffer. +When optional argument ALL is non-nil, reset cache in all Org +buffers." + (interactive "P") + (dolist (buffer (if all (buffer-list) (list (current-buffer)))) + (with-current-buffer buffer + (when (and org-element-use-cache (derived-mode-p 'org-mode)) + (setq-local org-element--cache + (avl-tree-create #'org-element--cache-compare)) + (setq-local org-element--cache-objects (make-hash-table :test #'eq)) + (setq-local org-element--cache-sync-keys + (make-hash-table :weakness 'key :test #'eq)) + (setq-local org-element--cache-change-warning nil) + (setq-local org-element--cache-sync-requests nil) + (setq-local org-element--cache-sync-timer nil) + (add-hook 'before-change-functions + #'org-element--cache-before-change nil t) + (add-hook 'after-change-functions + #'org-element--cache-after-change nil t))))) + +;;;###autoload +(defun org-element-cache-refresh (pos) + "Refresh cache at position POS." + (when (org-element--cache-active-p) + (org-element--cache-sync (current-buffer) pos) + (org-element--cache-submit-request pos pos 0) + (org-element--cache-set-timer (current-buffer)))) @@ -4678,7 +5733,7 @@ indentation is not done with TAB characters." ;; The first move is to implement a way to obtain the smallest element ;; containing point. This is the job of `org-element-at-point'. It ;; basically jumps back to the beginning of section containing point -;; and moves, element after element, with +;; and proceed, one element after the other, with ;; `org-element--current-element' until the container is found. Note: ;; When using `org-element-at-point', secondary values are never ;; parsed since the function focuses on elements, not on objects. @@ -4689,8 +5744,9 @@ indentation is not done with TAB characters." ;; `org-element-nested-p' and `org-element-swap-A-B' may be used ;; internally by navigation and manipulation tools. + ;;;###autoload -(defun org-element-at-point (&optional keep-trail) +(defun org-element-at-point () "Determine closest element around point. Return value is a list like (TYPE PROPS) where TYPE is the type @@ -4701,118 +5757,36 @@ Possible types are defined in `org-element-all-elements'. Properties depend on element or object type, but always include `:begin', `:end', `:parent' and `:post-blank' properties. -As a special case, if point is at the very beginning of a list or -sub-list, returned element will be that list instead of the first -item. In the same way, if point is at the beginning of the first -row of a table, returned element will be the table instead of the -first row. - -If optional argument KEEP-TRAIL is non-nil, the function returns -a list of elements leading to element at point. The list's CAR -is always the element at point. The following positions contain -element's siblings, then parents, siblings of parents, until the -first element of current section." +As a special case, if point is at the very beginning of the first +item in a list or sub-list, returned element will be that list +instead of the item. Likewise, if point is at the beginning of +the first row of a table, returned element will be the table +instead of the first row. + +When point is at the end of the buffer, return the innermost +element ending there." (org-with-wide-buffer - ;; If at a headline, parse it. It is the sole element that - ;; doesn't require to know about context. Be sure to disallow - ;; secondary string parsing, though. - (if (org-with-limited-levels (org-at-heading-p)) - (progn - (beginning-of-line) - (if (not keep-trail) (org-element-headline-parser (point-max) t) - (list (org-element-headline-parser (point-max) t)))) - ;; Otherwise move at the beginning of the section containing - ;; point. - (catch 'exit - (let ((origin (point)) - (end (save-excursion - (org-with-limited-levels (outline-next-heading)) (point))) - element type special-flag trail struct prevs parent) - (org-with-limited-levels - (if (org-before-first-heading-p) - ;; In empty lines at buffer's beginning, return nil. - (progn (goto-char (point-min)) - (org-skip-whitespace) - (when (or (eobp) (> (line-beginning-position) origin)) - (throw 'exit nil))) - (org-back-to-heading) - (forward-line) - (org-skip-whitespace) - (when (or (eobp) (> (line-beginning-position) origin)) - ;; In blank lines just after the headline, point still - ;; belongs to the headline. - (throw 'exit - (progn (skip-chars-backward " \r\t\n") - (beginning-of-line) - (if (not keep-trail) - (org-element-headline-parser (point-max) t) - (list (org-element-headline-parser - (point-max) t)))))))) - (beginning-of-line) - ;; Parse successively each element, skipping those ending - ;; before original position. - (while t - (setq element - (org-element--current-element end 'element special-flag struct) - type (car element)) - (org-element-put-property element :parent parent) - (when keep-trail (push element trail)) - (cond - ;; 1. Skip any element ending before point. Also skip - ;; element ending at point when we're sure that another - ;; element has started. - ((let ((elem-end (org-element-property :end element))) - (when (or (< elem-end origin) - (and (= elem-end origin) (/= elem-end end))) - (goto-char elem-end)))) - ;; 2. An element containing point is always the element at - ;; point. - ((not (memq type org-element-greater-elements)) - (throw 'exit (if keep-trail trail element))) - ;; 3. At any other greater element type, if point is - ;; within contents, move into it. - (t - (let ((cbeg (org-element-property :contents-begin element)) - (cend (org-element-property :contents-end element))) - (if (or (not cbeg) (not cend) (> cbeg origin) (< cend origin) - ;; Create an anchor for tables and plain lists: - ;; when point is at the very beginning of these - ;; elements, ignoring affiliated keywords, - ;; target them instead of their contents. - (and (= cbeg origin) (memq type '(plain-list table))) - ;; When point is at contents end, do not move - ;; into elements with an explicit ending, but - ;; return that element instead. - (and (= cend origin) - (or (memq type - '(center-block - drawer dynamic-block inlinetask - property-drawer quote-block - special-block)) - ;; Corner case: if a list ends at the - ;; end of a buffer without a final new - ;; line, return last element in last - ;; item instead. - (and (memq type '(item plain-list)) - (progn (goto-char cend) - (or (bolp) (not (eobp)))))))) - (throw 'exit (if keep-trail trail element)) - (setq parent element) - (case type - (plain-list - (setq special-flag 'item - struct (org-element-property :structure element))) - (item (setq special-flag nil)) - (property-drawer - (setq special-flag 'node-property struct nil)) - (table (setq special-flag 'table-row struct nil)) - (otherwise (setq special-flag nil struct nil))) - (setq end cend) - (goto-char cbeg))))))))))) + (let ((origin (point))) + (end-of-line) + (skip-chars-backward " \r\t\n") + (cond + ;; Within blank lines at the beginning of buffer, return nil. + ((bobp) nil) + ;; Within blank lines right after a headline, return that + ;; headline. + ((org-with-limited-levels (org-at-heading-p)) + (beginning-of-line) + (org-element-headline-parser (point-max) t)) + ;; Otherwise parse until we find element containing ORIGIN. + (t + (when (org-element--cache-active-p) + (if (not org-element--cache) (org-element-cache-reset) + (org-element--cache-sync (current-buffer) origin))) + (org-element--parse-to origin)))))) ;;;###autoload (defun org-element-context (&optional element) - "Return closest element or object around point. + "Return smallest element or object around point. Return value is a list like (TYPE PROPS) where TYPE is the type of the element or object and PROPS a plist of properties @@ -4823,34 +5797,36 @@ Possible types are defined in `org-element-all-elements' and object type, but always include `:begin', `:end', `:parent' and `:post-blank'. +As a special case, if point is right after an object and not at +the beginning of any other object, return that object. + Optional argument ELEMENT, when non-nil, is the closest element containing point, as returned by `org-element-at-point'. Providing it allows for quicker computation." (catch 'objects-forbidden (org-with-wide-buffer - (let* ((origin (point)) - (element (or element (org-element-at-point))) - (type (org-element-type element)) - context) - ;; Check if point is inside an element containing objects or at - ;; a secondary string. In that case, narrow buffer to the - ;; containing area. Otherwise, return ELEMENT. + (let* ((pos (point)) + (element (or element (org-element-at-point))) + (type (org-element-type element)) + (post (org-element-property :post-affiliated element))) + ;; If point is inside an element containing objects or + ;; a secondary string, narrow buffer to the container and + ;; proceed with parsing. Otherwise, return ELEMENT. (cond ;; At a parsed affiliated keyword, check if we're inside main ;; or dual value. - ((let ((post (org-element-property :post-affiliated element))) - (and post (< origin post))) + ((and post (< pos post)) (beginning-of-line) (let ((case-fold-search t)) (looking-at org-element--affiliated-re)) (cond ((not (member-ignore-case (match-string 1) org-element-parsed-keywords)) (throw 'objects-forbidden element)) - ((< (match-end 0) origin) + ((< (match-end 0) pos) (narrow-to-region (match-end 0) (line-end-position))) ((and (match-beginning 2) - (>= origin (match-beginning 2)) - (< origin (match-end 2))) + (>= pos (match-beginning 2)) + (< pos (match-end 2))) (narrow-to-region (match-beginning 2) (match-end 2))) (t (throw 'objects-forbidden element))) ;; Also change type to retrieve correct restrictions. @@ -4858,88 +5834,168 @@ Providing it allows for quicker computation." ;; At an item, objects can only be located within tag, if any. ((eq type 'item) (let ((tag (org-element-property :tag element))) - (if (not tag) (throw 'objects-forbidden element) + (if (or (not tag) (/= (line-beginning-position) post)) + (throw 'objects-forbidden element) (beginning-of-line) (search-forward tag (line-end-position)) (goto-char (match-beginning 0)) - (if (and (>= origin (point)) (< origin (match-end 0))) + (if (and (>= pos (point)) (< pos (match-end 0))) (narrow-to-region (point) (match-end 0)) (throw 'objects-forbidden element))))) - ;; At an headline or inlinetask, objects are located within - ;; their title. + ;; At an headline or inlinetask, objects are in title. ((memq type '(headline inlinetask)) - (goto-char (org-element-property :begin element)) - (skip-chars-forward "*") - (if (and (> origin (point)) (< origin (line-end-position))) - (narrow-to-region (point) (line-end-position)) - (throw 'objects-forbidden element))) + (let ((case-fold-search nil)) + (goto-char (org-element-property :begin element)) + (looking-at org-complex-heading-regexp) + (let ((end (match-end 4))) + (if (not end) (throw 'objects-forbidden element) + (goto-char (match-beginning 4)) + (when (looking-at org-comment-string) + (goto-char (match-end 0))) + (if (>= (point) end) (throw 'objects-forbidden element) + (narrow-to-region (point) end)))))) ;; At a paragraph, a table-row or a verse block, objects are ;; located within their contents. ((memq type '(paragraph table-row verse-block)) (let ((cbeg (org-element-property :contents-begin element)) (cend (org-element-property :contents-end element))) ;; CBEG is nil for table rules. - (if (and cbeg cend (>= origin cbeg) (< origin cend)) + (if (and cbeg cend (>= pos cbeg) + (or (< pos cend) (and (= pos cend) (eobp)))) (narrow-to-region cbeg cend) (throw 'objects-forbidden element)))) - ;; At a parsed keyword, objects are located within value. - ((eq type 'keyword) - (if (not (member (org-element-property :key element) - org-element-document-properties)) - (throw 'objects-forbidden element) - (beginning-of-line) - (search-forward ":") - (if (and (>= origin (point)) (< origin (line-end-position))) - (narrow-to-region (point) (line-end-position)) - (throw 'objects-forbidden element)))) ;; At a planning line, if point is at a timestamp, return it, ;; otherwise, return element. ((eq type 'planning) (dolist (p '(:closed :deadline :scheduled)) (let ((timestamp (org-element-property p element))) (when (and timestamp - (<= (org-element-property :begin timestamp) origin) - (> (org-element-property :end timestamp) origin)) + (<= (org-element-property :begin timestamp) pos) + (> (org-element-property :end timestamp) pos)) (throw 'objects-forbidden timestamp)))) + ;; All other locations cannot contain objects: bail out. (throw 'objects-forbidden element)) (t (throw 'objects-forbidden element))) (goto-char (point-min)) (let ((restriction (org-element-restriction type)) - (parent element) - (candidates 'initial)) - (catch 'exit - (while (setq candidates - (org-element--get-next-object-candidates - restriction candidates)) - (let ((closest-cand (rassq (apply 'min (mapcar 'cdr candidates)) - candidates))) - ;; If ORIGIN is before next object in element, there's - ;; no point in looking further. - (if (> (cdr closest-cand) origin) (throw 'exit parent) - (let* ((object - (progn (goto-char (cdr closest-cand)) - (funcall (intern (format "org-element-%s-parser" - (car closest-cand)))))) - (cbeg (org-element-property :contents-begin object)) - (cend (org-element-property :contents-end object)) - (obj-end (org-element-property :end object))) - (cond - ;; ORIGIN is after OBJECT, so skip it. - ((<= obj-end origin) (goto-char obj-end)) - ;; ORIGIN is within a non-recursive object or at - ;; an object boundaries: Return that object. - ((or (not cbeg) (< origin cbeg) (>= origin cend)) - (throw 'exit - (org-element-put-property object :parent parent))) - ;; Otherwise, move within current object and - ;; restrict search to the end of its contents. - (t (goto-char cbeg) - (narrow-to-region (point) cend) - (org-element-put-property object :parent parent) - (setq parent object - restriction (org-element-restriction object) - candidates 'initial))))))) - parent)))))) + (parent element) + (cache (cond ((not (org-element--cache-active-p)) nil) + (org-element--cache-objects + (gethash element org-element--cache-objects)) + (t (org-element-cache-reset) nil))) + next object-data last) + (prog1 + (catch 'exit + (while t + ;; When entering PARENT for the first time, get list + ;; of objects within known so far. Store it in + ;; OBJECT-DATA. + (unless next + (let ((data (assq parent cache))) + (if data (setq object-data data) + (push (setq object-data (list parent nil)) cache)))) + ;; Find NEXT object for analysis. + (catch 'found + ;; If NEXT is non-nil, we already exhausted the + ;; cache so we can parse buffer to find the object + ;; after it. + (if next (setq next (org-element--object-lex restriction)) + ;; Otherwise, check if cache can help us. + (let ((objects (cddr object-data)) + (completep (nth 1 object-data))) + (cond + ((and (not objects) completep) (throw 'exit parent)) + ((not objects) + (setq next (org-element--object-lex restriction))) + (t + (let ((cache-limit + (org-element-property :end (car objects)))) + (if (>= cache-limit pos) + ;; Cache contains the information needed. + (dolist (object objects (throw 'exit parent)) + (when (<= (org-element-property :begin object) + pos) + (if (>= (org-element-property :end object) + pos) + (throw 'found (setq next object)) + (throw 'exit parent)))) + (goto-char cache-limit) + (setq next + (org-element--object-lex restriction)))))))) + ;; If we have a new object to analyze, store it in + ;; cache. Otherwise record that there is nothing + ;; more to parse in this element at this depth. + (if next + (progn (org-element-put-property next :parent parent) + (push next (cddr object-data))) + (setcar (cdr object-data) t))) + ;; Process NEXT, if any, in order to know if we need + ;; to skip it, return it or move into it. + (if (or (not next) (> (org-element-property :begin next) pos)) + (throw 'exit (or last parent)) + (let ((end (org-element-property :end next)) + (cbeg (org-element-property :contents-begin next)) + (cend (org-element-property :contents-end next))) + (cond + ;; Skip objects ending before point. Also skip + ;; objects ending at point unless it is also the + ;; end of buffer, since we want to return the + ;; innermost object. + ((and (<= end pos) (/= (point-max) end)) + (goto-char end) + ;; For convenience, when object ends at POS, + ;; without any space, store it in LAST, as we + ;; will return it if no object starts here. + (when (and (= end pos) + (not (memq (char-before) '(?\s ?\t)))) + (setq last next))) + ;; If POS is within a container object, move + ;; into that object. + ((and cbeg cend + (>= pos cbeg) + (or (< pos cend) + ;; At contents' end, if there is no + ;; space before point, also move into + ;; object, for consistency with + ;; convenience feature above. + (and (= pos cend) + (or (= (point-max) pos) + (not (memq (char-before pos) + '(?\s ?\t))))))) + (goto-char cbeg) + (narrow-to-region (point) cend) + (setq parent next + restriction (org-element-restriction next) + next nil + object-data nil)) + ;; Otherwise, return NEXT. + (t (throw 'exit next))))))) + ;; Store results in cache, if applicable. + (org-element--cache-put element cache))))))) + +(defun org-element-lineage (blob &optional types with-self) + "List all ancestors of a given element or object. + +BLOB is an object or element. + +When optional argument TYPES is a list of symbols, return the +first element or object in the lineage whose type belongs to that +list. + +When optional argument WITH-SELF is non-nil, lineage includes +BLOB itself as the first element, and TYPES, if provided, also +apply to it. + +When BLOB is obtained through `org-element-context' or +`org-element-at-point', only ancestors from its section can be +found. There is no such limitation when BLOB belongs to a full +parse tree." + (let ((up (if with-self blob (org-element-property :parent blob))) + ancestors) + (while (and up (not (memq (org-element-type up) types))) + (unless types (push up ancestors)) + (setq up (org-element-property :parent up))) + (if types up (nreverse ancestors)))) (defun org-element-nested-p (elem-A elem-B) "Non-nil when elements ELEM-A and ELEM-B are nested." @@ -4982,39 +6038,44 @@ end of ELEM-A." (goto-char (org-element-property :end elem-B)) (skip-chars-backward " \r\t\n") (point-at-eol))) - ;; Store overlays responsible for visibility status. We - ;; also need to store their boundaries as they will be + ;; Store inner overlays responsible for visibility status. + ;; We also need to store their boundaries as they will be ;; removed from buffer. (overlays (cons - (mapcar (lambda (ov) (list ov (overlay-start ov) (overlay-end ov))) - (overlays-in beg-A end-A)) - (mapcar (lambda (ov) (list ov (overlay-start ov) (overlay-end ov))) - (overlays-in beg-B end-B)))) + (delq nil + (mapcar (lambda (o) + (and (>= (overlay-start o) beg-A) + (<= (overlay-end o) end-A) + (list o (overlay-start o) (overlay-end o)))) + (overlays-in beg-A end-A))) + (delq nil + (mapcar (lambda (o) + (and (>= (overlay-start o) beg-B) + (<= (overlay-end o) end-B) + (list o (overlay-start o) (overlay-end o)))) + (overlays-in beg-B end-B))))) ;; Get contents. (body-A (buffer-substring beg-A end-A)) (body-B (delete-and-extract-region beg-B end-B))) (goto-char beg-B) (when specialp (setq body-B (replace-regexp-in-string "\\`[ \t]*" "" body-B)) - (org-indent-to-column ind-B)) + (indent-to-column ind-B)) (insert body-A) ;; Restore ex ELEM-A overlays. (let ((offset (- beg-B beg-A))) - (mapc (lambda (ov) - (move-overlay - (car ov) (+ (nth 1 ov) offset) (+ (nth 2 ov) offset))) - (car overlays)) + (dolist (o (car overlays)) + (move-overlay (car o) (+ (nth 1 o) offset) (+ (nth 2 o) offset))) (goto-char beg-A) (delete-region beg-A end-A) (insert body-B) ;; Restore ex ELEM-B overlays. - (mapc (lambda (ov) - (move-overlay - (car ov) (- (nth 1 ov) offset) (- (nth 2 ov) offset))) - (cdr overlays))) + (dolist (o (cdr overlays)) + (move-overlay (car o) (- (nth 1 o) offset) (- (nth 2 o) offset)))) (goto-char (org-element-property :end elem-B))))) + (provide 'org-element) ;; Local variables: diff --git a/lisp/org/org-entities.el b/lisp/org/org-entities.el index 3ca2cceea7..05ccf0cf5b 100644 --- a/lisp/org/org-entities.el +++ b/lisp/org/org-entities.el @@ -1,4 +1,4 @@ -;;; org-entities.el --- Support for special entities in Org-mode +;;; org-entities.el --- Support for Special Entities -*- lexical-binding: t; -*- ;; Copyright (C) 2010-2017 Free Software Foundation, Inc. @@ -30,38 +30,36 @@ (declare-function org-toggle-pretty-entities "org" ()) (declare-function org-table-align "org-table" ()) -(eval-when-compile - (require 'cl)) - (defgroup org-entities nil - "Options concerning entities in Org-mode." + "Options concerning entities in Org mode." :tag "Org Entities" :group 'org) -(defcustom org-entities-ascii-explanatory nil - "Non-nil means replace special entities in ASCII. -For example, this will replace \"\\nsup\" with \"[not a superset of]\" -in backends where the corresponding character is not available." - :group 'org-entities - :version "24.1" - :type 'boolean) +(defun org-entities--user-safe-p (v) + "Non-nil if V is a safe value for `org-entities-user'." + (pcase v + (`nil t) + (`(,(and (pred stringp) + (pred (string-match-p "\\`[a-zA-Z][a-zA-Z0-9]*\\'"))) + ,(pred stringp) ,(pred booleanp) ,(pred stringp) + ,(pred stringp) ,(pred stringp) ,(pred stringp)) + t) + (_ nil))) (defcustom org-entities-user nil - "User-defined entities used in Org-mode to produce special characters. + "User-defined entities used in Org to produce special characters. Each entry in this list is a list of strings. It associates the name of the entity that can be inserted into an Org file as \\name with the appropriate replacements for the different export backends. The order of the fields is the following -name As a string, without the leading backslash -LaTeX replacement In ready LaTeX, no further processing will take place -LaTeX mathp A Boolean, either t or nil. t if this entity needs - to be in math mode. +name As a string, without the leading backslash. +LaTeX replacement In ready LaTeX, no further processing will take place. +LaTeX mathp Either t or nil. When t this entity needs to be in + math mode. HTML replacement In ready HTML, no further processing will take place. Usually this will be an &...; entity. -ASCII replacement Plain ASCII, no extensions. Symbols that cannot be - represented will be left as they are, but see the. - variable `org-entities-ascii-explanatory'. +ASCII replacement Plain ASCII, no extensions. Latin1 replacement Use the special characters available in latin1. utf-8 replacement Use the special characters available in utf-8. @@ -77,439 +75,454 @@ packages to be loaded, add these packages to `org-latex-packages-alist'." (string :tag "HTML ") (string :tag "ASCII ") (string :tag "Latin1") - (string :tag "utf-8 ")))) + (string :tag "utf-8 "))) + :safe #'org-entities--user-safe-p) (defconst org-entities - '( - "* Letters" - "** Latin" - ("Agrave" "\\`{A}" nil "À" "A" "À" "À") - ("agrave" "\\`{a}" nil "à" "a" "à" "à") - ("Aacute" "\\'{A}" nil "Á" "A" "Á" "Á") - ("aacute" "\\'{a}" nil "á" "a" "á" "á") - ("Acirc" "\\^{A}" nil "Â" "A" "Â" "Â") - ("acirc" "\\^{a}" nil "â" "a" "â" "â") - ("Atilde" "\\~{A}" nil "Ã" "A" "Ã" "Ã") - ("atilde" "\\~{a}" nil "ã" "a" "ã" "ã") - ("Auml" "\\\"{A}" nil "Ä" "Ae" "Ä" "Ä") - ("auml" "\\\"{a}" nil "ä" "ae" "ä" "ä") - ("Aring" "\\AA{}" nil "Å" "A" "Å" "Å") - ("AA" "\\AA{}" nil "Å" "A" "Å" "Å") - ("aring" "\\aa{}" nil "å" "a" "å" "å") - ("AElig" "\\AE{}" nil "Æ" "AE" "Æ" "Æ") - ("aelig" "\\ae{}" nil "æ" "ae" "æ" "æ") - ("Ccedil" "\\c{C}" nil "Ç" "C" "Ç" "Ç") - ("ccedil" "\\c{c}" nil "ç" "c" "ç" "ç") - ("Egrave" "\\`{E}" nil "È" "E" "È" "È") - ("egrave" "\\`{e}" nil "è" "e" "è" "è") - ("Eacute" "\\'{E}" nil "É" "E" "É" "É") - ("eacute" "\\'{e}" nil "é" "e" "é" "é") - ("Ecirc" "\\^{E}" nil "Ê" "E" "Ê" "Ê") - ("ecirc" "\\^{e}" nil "ê" "e" "ê" "ê") - ("Euml" "\\\"{E}" nil "Ë" "E" "Ë" "Ë") - ("euml" "\\\"{e}" nil "ë" "e" "ë" "ë") - ("Igrave" "\\`{I}" nil "Ì" "I" "Ì" "Ì") - ("igrave" "\\`{i}" nil "ì" "i" "ì" "ì") - ("Iacute" "\\'{I}" nil "Í" "I" "Í" "Í") - ("iacute" "\\'{i}" nil "í" "i" "í" "í") - ("Icirc" "\\^{I}" nil "Î" "I" "Î" "Î") - ("icirc" "\\^{i}" nil "î" "i" "î" "î") - ("Iuml" "\\\"{I}" nil "Ï" "I" "Ï" "Ï") - ("iuml" "\\\"{i}" nil "ï" "i" "ï" "ï") - ("Ntilde" "\\~{N}" nil "Ñ" "N" "Ñ" "Ñ") - ("ntilde" "\\~{n}" nil "ñ" "n" "ñ" "ñ") - ("Ograve" "\\`{O}" nil "Ò" "O" "Ò" "Ò") - ("ograve" "\\`{o}" nil "ò" "o" "ò" "ò") - ("Oacute" "\\'{O}" nil "Ó" "O" "Ó" "Ó") - ("oacute" "\\'{o}" nil "ó" "o" "ó" "ó") - ("Ocirc" "\\^{O}" nil "Ô" "O" "Ô" "Ô") - ("ocirc" "\\^{o}" nil "ô" "o" "ô" "ô") - ("Otilde" "\\~{O}" nil "Õ" "O" "Õ" "Õ") - ("otilde" "\\~{o}" nil "õ" "o" "õ" "õ") - ("Ouml" "\\\"{O}" nil "Ö" "Oe" "Ö" "Ö") - ("ouml" "\\\"{o}" nil "ö" "oe" "ö" "ö") - ("Oslash" "\\O" nil "Ø" "O" "Ø" "Ø") - ("oslash" "\\o{}" nil "ø" "o" "ø" "ø") - ("OElig" "\\OE{}" nil "Œ" "OE" "OE" "Œ") - ("oelig" "\\oe{}" nil "œ" "oe" "oe" "œ") - ("Scaron" "\\v{S}" nil "Š" "S" "S" "Š") - ("scaron" "\\v{s}" nil "š" "s" "s" "š") - ("szlig" "\\ss{}" nil "ß" "ss" "ß" "ß") - ("Ugrave" "\\`{U}" nil "Ù" "U" "Ù" "Ù") - ("ugrave" "\\`{u}" nil "ù" "u" "ù" "ù") - ("Uacute" "\\'{U}" nil "Ú" "U" "Ú" "Ú") - ("uacute" "\\'{u}" nil "ú" "u" "ú" "ú") - ("Ucirc" "\\^{U}" nil "Û" "U" "Û" "Û") - ("ucirc" "\\^{u}" nil "û" "u" "û" "û") - ("Uuml" "\\\"{U}" nil "Ü" "Ue" "Ü" "Ü") - ("uuml" "\\\"{u}" nil "ü" "ue" "ü" "ü") - ("Yacute" "\\'{Y}" nil "Ý" "Y" "Ý" "Ý") - ("yacute" "\\'{y}" nil "ý" "y" "ý" "ý") - ("Yuml" "\\\"{Y}" nil "Ÿ" "Y" "Y" "Ÿ") - ("yuml" "\\\"{y}" nil "ÿ" "y" "ÿ" "ÿ") - - "** Latin (special face)" - ("fnof" "\\textit{f}" nil "ƒ" "f" "f" "ƒ") - ("real" "\\Re" t "ℜ" "R" "R" "ℜ") - ("image" "\\Im" t "ℑ" "I" "I" "ℑ") - ("weierp" "\\wp" t "℘" "P" "P" "℘") - ("ell" "\\ell" t "ℓ" "ell" "ell" "ℓ") - ("imath" "\\imath" t "ı" "[dotless i]" "dotless i" "ı") - ("jmath" "\\jmath" t "ȷ" "[dotless j]" "dotless j" "ȷ") - - "** Greek" - ("Alpha" "A" nil "Α" "Alpha" "Alpha" "Α") - ("alpha" "\\alpha" t "α" "alpha" "alpha" "α") - ("Beta" "B" nil "Β" "Beta" "Beta" "Β") - ("beta" "\\beta" t "β" "beta" "beta" "β") - ("Gamma" "\\Gamma" t "Γ" "Gamma" "Gamma" "Γ") - ("gamma" "\\gamma" t "γ" "gamma" "gamma" "γ") - ("Delta" "\\Delta" t "Δ" "Delta" "Gamma" "Δ") - ("delta" "\\delta" t "δ" "delta" "delta" "δ") - ("Epsilon" "E" nil "Ε" "Epsilon" "Epsilon" "Ε") - ("epsilon" "\\epsilon" t "ε" "epsilon" "epsilon" "ε") - ("varepsilon" "\\varepsilon" t "ε" "varepsilon" "varepsilon" "ε") - ("Zeta" "Z" nil "Ζ" "Zeta" "Zeta" "Ζ") - ("zeta" "\\zeta" t "ζ" "zeta" "zeta" "ζ") - ("Eta" "H" nil "Η" "Eta" "Eta" "Η") - ("eta" "\\eta" t "η" "eta" "eta" "η") - ("Theta" "\\Theta" t "Θ" "Theta" "Theta" "Θ") - ("theta" "\\theta" t "θ" "theta" "theta" "θ") - ("thetasym" "\\vartheta" t "ϑ" "theta" "theta" "ϑ") - ("vartheta" "\\vartheta" t "ϑ" "theta" "theta" "ϑ") - ("Iota" "I" nil "Ι" "Iota" "Iota" "Ι") - ("iota" "\\iota" t "ι" "iota" "iota" "ι") - ("Kappa" "K" nil "Κ" "Kappa" "Kappa" "Κ") - ("kappa" "\\kappa" t "κ" "kappa" "kappa" "κ") - ("Lambda" "\\Lambda" t "Λ" "Lambda" "Lambda" "Λ") - ("lambda" "\\lambda" t "λ" "lambda" "lambda" "λ") - ("Mu" "M" nil "Μ" "Mu" "Mu" "Μ") - ("mu" "\\mu" t "μ" "mu" "mu" "μ") - ("nu" "\\nu" t "ν" "nu" "nu" "ν") - ("Nu" "N" nil "Ν" "Nu" "Nu" "Ν") - ("Xi" "\\Xi" t "Ξ" "Xi" "Xi" "Ξ") - ("xi" "\\xi" t "ξ" "xi" "xi" "ξ") - ("Omicron" "O" nil "Ο" "Omicron" "Omicron" "Ο") - ("omicron" "\\textit{o}" nil "ο" "omicron" "omicron" "ο") - ("Pi" "\\Pi" t "Π" "Pi" "Pi" "Π") - ("pi" "\\pi" t "π" "pi" "pi" "π") - ("Rho" "P" nil "Ρ" "Rho" "Rho" "Ρ") - ("rho" "\\rho" t "ρ" "rho" "rho" "ρ") - ("Sigma" "\\Sigma" t "Σ" "Sigma" "Sigma" "Σ") - ("sigma" "\\sigma" t "σ" "sigma" "sigma" "σ") - ("sigmaf" "\\varsigma" t "ς" "sigmaf" "sigmaf" "ς") - ("varsigma" "\\varsigma" t "ς" "varsigma" "varsigma" "ς") - ("Tau" "T" nil "Τ" "Tau" "Tau" "Τ") - ("Upsilon" "\\Upsilon" t "Υ" "Upsilon" "Upsilon" "Υ") - ("upsih" "\\Upsilon" t "ϒ" "upsilon" "upsilon" "ϒ") - ("upsilon" "\\upsilon" t "υ" "upsilon" "upsilon" "υ") - ("Phi" "\\Phi" t "Φ" "Phi" "Phi" "Φ") - ("phi" "\\phi" t "φ" "phi" "phi" "φ") - ("varphi" "\\varphi" t "ϕ" "varphi" "varphi" "ɸ") - ("Chi" "X" nil "Χ" "Chi" "Chi" "Χ") - ("chi" "\\chi" t "χ" "chi" "chi" "χ") - ("acutex" "\\acute x" t "´x" "'x" "'x" "𝑥́") - ("Psi" "\\Psi" t "Ψ" "Psi" "Psi" "Ψ") - ("psi" "\\psi" t "ψ" "psi" "psi" "ψ") - ("tau" "\\tau" t "τ" "tau" "tau" "τ") - ("Omega" "\\Omega" t "Ω" "Omega" "Omega" "Ω") - ("omega" "\\omega" t "ω" "omega" "omega" "ω") - ("piv" "\\varpi" t "ϖ" "omega-pi" "omega-pi" "ϖ") - ("varpi" "\\varpi" t "ϖ" "omega-pi" "omega-pi" "ϖ") - ("partial" "\\partial" t "∂" "[partial differential]" "[partial differential]" "∂") - - "** Hebrew" - ("alefsym" "\\aleph" t "ℵ" "aleph" "aleph" "ℵ") - ("aleph" "\\aleph" t "ℵ" "aleph" "aleph" "ℵ") - ("gimel" "\\gimel" t "ℷ" "gimel" "gimel" "ℷ") - ("beth" "\\beth" t "ℶ" "beth" "beth" "ב") - ("dalet" "\\daleth" t "ℸ" "dalet" "dalet" "ד") - - "** Dead languages" - ("ETH" "\\DH{}" nil "Ð" "D" "Ð" "Ð") - ("eth" "\\dh{}" nil "ð" "dh" "ð" "ð") - ("THORN" "\\TH{}" nil "Þ" "TH" "Þ" "Þ") - ("thorn" "\\th{}" nil "þ" "th" "þ" "þ") - - "* Punctuation" - "** Dots and Marks" - ("dots" "\\dots{}" nil "…" "..." "..." "…") - ("cdots" "\\cdots{}" t "⋯" "..." "..." "⋯") - ("hellip" "\\dots{}" nil "…" "..." "..." "…") - ("middot" "\\textperiodcentered{}" nil "·" "." "·" "·") - ("iexcl" "!`" nil "¡" "!" "¡" "¡") - ("iquest" "?`" nil "¿" "?" "¿" "¿") - - "** Dash-like" - ("shy" "\\-" nil "­" "" "" "") - ("ndash" "--" nil "–" "-" "-" "–") - ("mdash" "---" nil "—" "--" "--" "—") - - "** Quotations" - ("quot" "\\textquotedbl{}" nil """ "\"" "\"" "\"") - ("acute" "\\textasciiacute{}" nil "´" "'" "´" "´") - ("ldquo" "\\textquotedblleft{}" nil "“" "\"" "\"" "“") - ("rdquo" "\\textquotedblright{}" nil "”" "\"" "\"" "”") - ("bdquo" "\\quotedblbase{}" nil "„" "\"" "\"" "„") - ("lsquo" "\\textquoteleft{}" nil "‘" "`" "`" "‘") - ("rsquo" "\\textquoteright{}" nil "’" "'" "'" "’") - ("sbquo" "\\quotesinglbase{}" nil "‚" "," "," "‚") - ("laquo" "\\guillemotleft{}" nil "«" "<<" "«" "«") - ("raquo" "\\guillemotright{}" nil "»" ">>" "»" "»") - ("lsaquo" "\\guilsinglleft{}" nil "‹" "<" "<" "‹") - ("rsaquo" "\\guilsinglright{}" nil "›" ">" ">" "›") - - "* Other" - "** Misc. (often used)" - ("circ" "\\^{}" nil "ˆ" "^" "^" "ˆ") - ("vert" "\\vert{}" t "|" "|" "|" "|") - ("brvbar" "\\textbrokenbar{}" nil "¦" "|" "¦" "¦") - ("S" "\\S" nil "§" "paragraph" "§" "§") - ("sect" "\\S" nil "§" "paragraph" "§" "§") - ("amp" "\\&" nil "&" "&" "&" "&") - ("lt" "\\textless{}" nil "<" "<" "<" "<") - ("gt" "\\textgreater{}" nil ">" ">" ">" ">") - ("tilde" "\\textasciitilde{}" nil "~" "~" "~" "~") - ("slash" "/" nil "/" "/" "/" "/") - ("plus" "+" nil "+" "+" "+" "+") - ("under" "\\_" nil "_" "_" "_" "_") - ("equal" "=" nil "=" "=" "=" "=") - ("asciicirc" "\\textasciicircum{}" nil "^" "^" "^" "^") - ("dagger" "\\textdagger{}" nil "†" "[dagger]" "[dagger]" "†") - ("dag" "\\dag{}" nil "†" "[dagger]" "[dagger]" "†") - ("Dagger" "\\textdaggerdbl{}" nil "‡" "[doubledagger]" "[doubledagger]" "‡") - ("ddag" "\\ddag{}" nil "‡" "[doubledagger]" "[doubledagger]" "‡") - - "** Whitespace" - ("nbsp" "~" nil " " " " " " " ") - ("ensp" "\\hspace*{.5em}" nil " " " " " " " ") - ("emsp" "\\hspace*{1em}" nil " " " " " " " ") - ("thinsp" "\\hspace*{.2em}" nil " " " " " " " ") - - "** Currency" - ("curren" "\\textcurrency{}" nil "¤" "curr." "¤" "¤") - ("cent" "\\textcent{}" nil "¢" "cent" "¢" "¢") - ("pound" "\\pounds{}" nil "£" "pound" "£" "£") - ("yen" "\\textyen{}" nil "¥" "yen" "¥" "¥") - ("euro" "\\texteuro{}" nil "€" "EUR" "EUR" "€") - ("EUR" "\\EUR{}" nil "€" "EUR" "EUR" "€") - ("EURdig" "\\EURdig{}" nil "€" "EUR" "EUR" "€") - ("EURhv" "\\EURhv{}" nil "€" "EUR" "EUR" "€") - ("EURcr" "\\EURcr{}" nil "€" "EUR" "EUR" "€") - ("EURtm" "\\EURtm{}" nil "€" "EUR" "EUR" "€") - - "** Property Marks" - ("copy" "\\textcopyright{}" nil "©" "(c)" "©" "©") - ("reg" "\\textregistered{}" nil "®" "(r)" "®" "®") - ("trade" "\\texttrademark{}" nil "™" "TM" "TM" "™") - - "** Science et al." - ("minus" "\\minus" t "−" "-" "-" "−") - ("pm" "\\textpm{}" nil "±" "+-" "±" "±") - ("plusmn" "\\textpm{}" nil "±" "+-" "±" "±") - ("times" "\\texttimes{}" nil "×" "*" "×" "×") - ("frasl" "/" nil "⁄" "/" "/" "⁄") - ("colon" "\\colon" t ":" ":" ":" ":") - ("div" "\\textdiv{}" nil "÷" "/" "÷" "÷") - ("frac12" "\\textonehalf{}" nil "½" "1/2" "½" "½") - ("frac14" "\\textonequarter{}" nil "¼" "1/4" "¼" "¼") - ("frac34" "\\textthreequarters{}" nil "¾" "3/4" "¾" "¾") - ("permil" "\\textperthousand{}" nil "‰" "per thousand" "per thousand" "‰") - ("sup1" "\\textonesuperior{}" nil "¹" "^1" "¹" "¹") - ("sup2" "\\texttwosuperior{}" nil "²" "^2" "²" "²") - ("sup3" "\\textthreesuperior{}" nil "³" "^3" "³" "³") - ("radic" "\\sqrt{\\,}" t "√" "[square root]" "[square root]" "√") - ("sum" "\\sum" t "∑" "[sum]" "[sum]" "∑") - ("prod" "\\prod" t "∏" "[product]" "[n-ary product]" "∏") - ("micro" "\\textmu{}" nil "µ" "micro" "µ" "µ") - ("macr" "\\textasciimacron{}" nil "¯" "[macron]" "¯" "¯") - ("deg" "\\textdegree{}" nil "°" "degree" "°" "°") - ("prime" "\\prime" t "′" "'" "'" "′") - ("Prime" "\\prime{}\\prime" t "″" "''" "''" "″") - ("infin" "\\propto" t "∞" "[infinity]" "[infinity]" "∞") - ("infty" "\\infty" t "∞" "[infinity]" "[infinity]" "∞") - ("prop" "\\propto" t "∝" "[proportional to]" "[proportional to]" "∝") - ("propto" "\\propto" t "∝" "[proportional to]" "[proportional to]" "∝") - ("not" "\\textlnot{}" nil "¬" "[angled dash]" "¬" "¬") - ("neg" "\\neg{}" t "¬" "[angled dash]" "¬" "¬") - ("land" "\\land" t "∧" "[logical and]" "[logical and]" "∧") - ("wedge" "\\wedge" t "∧" "[logical and]" "[logical and]" "∧") - ("lor" "\\lor" t "∨" "[logical or]" "[logical or]" "∨") - ("vee" "\\vee" t "∨" "[logical or]" "[logical or]" "∨") - ("cap" "\\cap" t "∩" "[intersection]" "[intersection]" "∩") - ("cup" "\\cup" t "∪" "[union]" "[union]" "∪") - ("int" "\\int" t "∫" "[integral]" "[integral]" "∫") - ("therefore" "\\therefore" t "∴" "[therefore]" "[therefore]" "∴") - ("there4" "\\therefore" t "∴" "[therefore]" "[therefore]" "∴") - ("because" "\\because" t "∵" "[because]" "[because]" "∵") - ("sim" "\\sim" t "∼" "~" "~" "∼") - ("cong" "\\cong" t "≅" "[approx. equal to]" "[approx. equal to]" "≅") - ("simeq" "\\simeq" t "≅" "[approx. equal to]" "[approx. equal to]" "≅") - ("asymp" "\\asymp" t "≈" "[almost equal to]" "[almost equal to]" "≈") - ("approx" "\\approx" t "≈" "[almost equal to]" "[almost equal to]" "≈") - ("ne" "\\ne" t "≠" "[not equal to]" "[not equal to]" "≠") - ("neq" "\\neq" t "≠" "[not equal to]" "[not equal to]" "≠") - ("equiv" "\\equiv" t "≡" "[identical to]" "[identical to]" "≡") - - ("triangleq" "\\triangleq" t "≜" "[defined to]" "[defined to]" "≜") - ("le" "\\le" t "≤" "<=" "<=" "≤") - ("leq" "\\le" t "≤" "<=" "<=" "≤") - ("ge" "\\ge" t "≥" ">=" ">=" "≥") - ("geq" "\\ge" t "≥" ">=" ">=" "≥") - ("lessgtr" "\\lessgtr" t "≶" "[less than or greater than]" "[less than or greater than]" "≶") - ("lesseqgtr" "\\lesseqgtr" t "⋚" "[less than or equal or greater than or equal]" "[less than or equal or greater than or equal]" "⋚") - ("ll" "\\ll" t "≪" "<<" "<<" "≪") - ("Ll" "\\lll" t "⋘" "<<<" "<<<" "⋘") - ("lll" "\\lll" t "⋘" "<<<" "<<<" "⋘") - ("gg" "\\gg" t "≫" ">>" ">>" "≫") - ("Gg" "\\ggg" t "⋙" ">>>" ">>>" "⋙") - ("ggg" "\\ggg" t "⋙" ">>>" ">>>" "⋙") - ("prec" "\\prec" t "≺" "[precedes]" "[precedes]" "≺") - ("preceq" "\\preceq" t "≼" "[precedes or equal]" "[precedes or equal]" "≼") - ("preccurlyeq" "\\preccurlyeq" t "≼" "[precedes or equal]" "[precedes or equal]" "≼") - ("succ" "\\succ" t "≻" "[succeeds]" "[succeeds]" "≻") - ("succeq" "\\succeq" t "≽" "[succeeds or equal]" "[succeeds or equal]" "≽") - ("succcurlyeq" "\\succcurlyeq" t "≽" "[succeeds or equal]" "[succeeds or equal]" "≽") - ("sub" "\\subset" t "⊂" "[subset of]" "[subset of]" "⊂") - ("subset" "\\subset" t "⊂" "[subset of]" "[subset of]" "⊂") - ("sup" "\\supset" t "⊃" "[superset of]" "[superset of]" "⊃") - ("supset" "\\supset" t "⊃" "[superset of]" "[superset of]" "⊃") - ("nsub" "\\not\\subset" t "⊄" "[not a subset of]" "[not a subset of" "⊄") - ("sube" "\\subseteq" t "⊆" "[subset of or equal to]" "[subset of or equal to]" "⊆") - ("nsup" "\\not\\supset" t "⊅" "[not a superset of]" "[not a superset of]" "⊅") - ("supe" "\\supseteq" t "⊇" "[superset of or equal to]" "[superset of or equal to]" "⊇") - ("setminus" "\\setminus" t "∖" "\" "\" "⧵") - ("forall" "\\forall" t "∀" "[for all]" "[for all]" "∀") - ("exist" "\\exists" t "∃" "[there exists]" "[there exists]" "∃") - ("exists" "\\exists" t "∃" "[there exists]" "[there exists]" "∃") - ("nexist" "\\nexists" t "∃" "[there does not exists]" "[there does not exists]" "∄") - ("nexists" "\\nexists" t "∃" "[there does not exists]" "[there does not exists]" "∄") - ("empty" "\\empty" t "∅" "[empty set]" "[empty set]" "∅") - ("emptyset" "\\emptyset" t "∅" "[empty set]" "[empty set]" "∅") - ("isin" "\\in" t "∈" "[element of]" "[element of]" "∈") - ("in" "\\in" t "∈" "[element of]" "[element of]" "∈") - ("notin" "\\notin" t "∉" "[not an element of]" "[not an element of]" "∉") - ("ni" "\\ni" t "∋" "[contains as member]" "[contains as member]" "∋") - ("nabla" "\\nabla" t "∇" "[nabla]" "[nabla]" "∇") - ("ang" "\\angle" t "∠" "[angle]" "[angle]" "∠") - ("angle" "\\angle" t "∠" "[angle]" "[angle]" "∠") - ("perp" "\\perp" t "⊥" "[up tack]" "[up tack]" "⊥") - ("sdot" "\\cdot" t "⋅" "[dot]" "[dot]" "⋅") - ("cdot" "\\cdot" t "⋅" "[dot]" "[dot]" "⋅") - ("lceil" "\\lceil" t "⌈" "[left ceiling]" "[left ceiling]" "⌈") - ("rceil" "\\rceil" t "⌉" "[right ceiling]" "[right ceiling]" "⌉") - ("lfloor" "\\lfloor" t "⌊" "[left floor]" "[left floor]" "⌊") - ("rfloor" "\\rfloor" t "⌋" "[right floor]" "[right floor]" "⌋") - ("lang" "\\langle" t "⟨" "<" "<" "⟨") - ("rang" "\\rangle" t "⟩" ">" ">" "⟩") - ("hbar" "\\hbar" t "ℏ" "hbar" "hbar" "ℏ") - ("mho" "\\mho" t "℧" "mho" "mho" "℧") - - "** Arrows" - ("larr" "\\leftarrow" t "←" "<-" "<-" "←") - ("leftarrow" "\\leftarrow" t "←" "<-" "<-" "←") - ("gets" "\\gets" t "←" "<-" "<-" "←") - ("lArr" "\\Leftarrow" t "⇐" "<=" "<=" "⇐") - ("Leftarrow" "\\Leftarrow" t "⇐" "<=" "<=" "⇐") - ("uarr" "\\uparrow" t "↑" "[uparrow]" "[uparrow]" "↑") - ("uparrow" "\\uparrow" t "↑" "[uparrow]" "[uparrow]" "↑") - ("uArr" "\\Uparrow" t "⇑" "[dbluparrow]" "[dbluparrow]" "⇑") - ("Uparrow" "\\Uparrow" t "⇑" "[dbluparrow]" "[dbluparrow]" "⇑") - ("rarr" "\\rightarrow" t "→" "->" "->" "→") - ("to" "\\to" t "→" "->" "->" "→") - ("rightarrow" "\\rightarrow" t "→" "->" "->" "→") - ("rArr" "\\Rightarrow" t "⇒" "=>" "=>" "⇒") - ("Rightarrow" "\\Rightarrow" t "⇒" "=>" "=>" "⇒") - ("darr" "\\downarrow" t "↓" "[downarrow]" "[downarrow]" "↓") - ("downarrow" "\\downarrow" t "↓" "[downarrow]" "[downarrow]" "↓") - ("dArr" "\\Downarrow" t "⇓" "[dbldownarrow]" "[dbldownarrow]" "⇓") - ("Downarrow" "\\Downarrow" t "⇓" "[dbldownarrow]" "[dbldownarrow]" "⇓") - ("harr" "\\leftrightarrow" t "↔" "<->" "<->" "↔") - ("leftrightarrow" "\\leftrightarrow" t "↔" "<->" "<->" "↔") - ("hArr" "\\Leftrightarrow" t "⇔" "<=>" "<=>" "⇔") - ("Leftrightarrow" "\\Leftrightarrow" t "⇔" "<=>" "<=>" "⇔") - ("crarr" "\\hookleftarrow" t "↵" "<-'" "<-'" "↵") - ("hookleftarrow" "\\hookleftarrow" t "↵" "<-'" "<-'" "↵") - - "** Function names" - ("arccos" "\\arccos" t "arccos" "arccos" "arccos" "arccos") - ("arcsin" "\\arcsin" t "arcsin" "arcsin" "arcsin" "arcsin") - ("arctan" "\\arctan" t "arctan" "arctan" "arctan" "arctan") - ("arg" "\\arg" t "arg" "arg" "arg" "arg") - ("cos" "\\cos" t "cos" "cos" "cos" "cos") - ("cosh" "\\cosh" t "cosh" "cosh" "cosh" "cosh") - ("cot" "\\cot" t "cot" "cot" "cot" "cot") - ("coth" "\\coth" t "coth" "coth" "coth" "coth") - ("csc" "\\csc" t "csc" "csc" "csc" "csc") - ("deg" "\\deg" t "°" "deg" "deg" "deg") - ("det" "\\det" t "det" "det" "det" "det") - ("dim" "\\dim" t "dim" "dim" "dim" "dim") - ("exp" "\\exp" t "exp" "exp" "exp" "exp") - ("gcd" "\\gcd" t "gcd" "gcd" "gcd" "gcd") - ("hom" "\\hom" t "hom" "hom" "hom" "hom") - ("inf" "\\inf" t "inf" "inf" "inf" "inf") - ("ker" "\\ker" t "ker" "ker" "ker" "ker") - ("lg" "\\lg" t "lg" "lg" "lg" "lg") - ("lim" "\\lim" t "lim" "lim" "lim" "lim") - ("liminf" "\\liminf" t "liminf" "liminf" "liminf" "liminf") - ("limsup" "\\limsup" t "limsup" "limsup" "limsup" "limsup") - ("ln" "\\ln" t "ln" "ln" "ln" "ln") - ("log" "\\log" t "log" "log" "log" "log") - ("max" "\\max" t "max" "max" "max" "max") - ("min" "\\min" t "min" "min" "min" "min") - ("Pr" "\\Pr" t "Pr" "Pr" "Pr" "Pr") - ("sec" "\\sec" t "sec" "sec" "sec" "sec") - ("sin" "\\sin" t "sin" "sin" "sin" "sin") - ("sinh" "\\sinh" t "sinh" "sinh" "sinh" "sinh") - ("sup" "\\sup" t "⊃" "sup" "sup" "sup") - ("tan" "\\tan" t "tan" "tan" "tan" "tan") - ("tanh" "\\tanh" t "tanh" "tanh" "tanh" "tanh") - - "** Signs & Symbols" - ("bull" "\\textbullet{}" nil "•" "*" "*" "•") - ("bullet" "\\textbullet{}" nil "•" "*" "*" "•") - ("star" "\\star" t "*" "*" "*" "⋆") - ("lowast" "\\ast" t "∗" "*" "*" "∗") - ("ast" "\\ast" t "∗" "*" "*" "*") - ("odot" "\\odot" t "o" "[circled dot]" "[circled dot]" "ʘ") - ("oplus" "\\oplus" t "⊕" "[circled plus]" "[circled plus]" "⊕") - ("otimes" "\\otimes" t "⊗" "[circled times]" "[circled times]" "⊗") - ("check" "\\checkmark" t "✓" "[checkmark]" "[checkmark]" "✓") - ("checkmark" "\\checkmark" t "✓" "[checkmark]" "[checkmark]" "✓") - - "** Miscellaneous (seldom used)" - ("para" "\\P{}" nil "¶" "[pilcrow]" "¶" "¶") - ("ordf" "\\textordfeminine{}" nil "ª" "_a_" "ª" "ª") - ("ordm" "\\textordmasculine{}" nil "º" "_o_" "º" "º") - ("cedil" "\\c{}" nil "¸" "[cedilla]" "¸" "¸") - ("oline" "\\overline{~}" t "‾" "[overline]" "¯" "‾") - ("uml" "\\textasciidieresis{}" nil "¨" "[diaeresis]" "¨" "¨") - ("zwnj" "\\/{}" nil "‌" "" "" "‌") - ("zwj" "" nil "‍" "" "" "‍") - ("lrm" "" nil "‎" "" "" "‎") - ("rlm" "" nil "‏" "" "" "‏") - - "** Smilies" - ("smile" "\\smile" t "⌣" ":-)" ":-)" "⌣") - ("frown" "\\frown" t "⌢" ":-(" ":-(" "⌢") - ("smiley" "\\smiley{}" nil "☺" ":-)" ":-)" "☺") - ("blacksmile" "\\blacksmiley{}" nil "☻" ":-)" ":-)" "☻") - ("sad" "\\frownie{}" nil "☹" ":-(" ":-(" "☹") - - "** Suits" - ("clubs" "\\clubsuit" t "♣" "[clubs]" "[clubs]" "♣") - ("clubsuit" "\\clubsuit" t "♣" "[clubs]" "[clubs]" "♣") - ("spades" "\\spadesuit" t "♠" "[spades]" "[spades]" "♠") - ("spadesuit" "\\spadesuit" t "♠" "[spades]" "[spades]" "♠") - ("hearts" "\\heartsuit" t "♥" "[hearts]" "[hearts]" "♥") - ("heartsuit" "\\heartsuit" t "♥" "[hearts]" "[hearts]" "♥") - ("diams" "\\diamondsuit" t "♦" "[diamonds]" "[diamonds]" "◆") - ("diamondsuit" "\\diamondsuit" t "♦" "[diamonds]" "[diamonds]" "◆") - ("diamond" "\\diamondsuit" t "⋄" "[diamond]" "[diamond]" "◆") - ("Diamond" "\\diamondsuit" t "⋄" "[diamond]" "[diamond]" "◆") - ("loz" "\\lozenge" t "◊" "[lozenge]" "[lozenge]" "⧫") - ) - "Default entities used in Org-mode to produce special characters. + (append + '("* Letters" + "** Latin" + ("Agrave" "\\`{A}" nil "À" "A" "À" "À") + ("agrave" "\\`{a}" nil "à" "a" "à" "à") + ("Aacute" "\\'{A}" nil "Á" "A" "Á" "Á") + ("aacute" "\\'{a}" nil "á" "a" "á" "á") + ("Acirc" "\\^{A}" nil "Â" "A" "Â" "Â") + ("acirc" "\\^{a}" nil "â" "a" "â" "â") + ("Amacr" "\\bar{A}" nil "Ā" "A" "Ã" "Ã") + ("amacr" "\\bar{a}" nil "ā" "a" "ã" "ã") + ("Atilde" "\\~{A}" nil "Ã" "A" "Ã" "Ã") + ("atilde" "\\~{a}" nil "ã" "a" "ã" "ã") + ("Auml" "\\\"{A}" nil "Ä" "Ae" "Ä" "Ä") + ("auml" "\\\"{a}" nil "ä" "ae" "ä" "ä") + ("Aring" "\\AA{}" nil "Å" "A" "Å" "Å") + ("AA" "\\AA{}" nil "Å" "A" "Å" "Å") + ("aring" "\\aa{}" nil "å" "a" "å" "å") + ("AElig" "\\AE{}" nil "Æ" "AE" "Æ" "Æ") + ("aelig" "\\ae{}" nil "æ" "ae" "æ" "æ") + ("Ccedil" "\\c{C}" nil "Ç" "C" "Ç" "Ç") + ("ccedil" "\\c{c}" nil "ç" "c" "ç" "ç") + ("Egrave" "\\`{E}" nil "È" "E" "È" "È") + ("egrave" "\\`{e}" nil "è" "e" "è" "è") + ("Eacute" "\\'{E}" nil "É" "E" "É" "É") + ("eacute" "\\'{e}" nil "é" "e" "é" "é") + ("Ecirc" "\\^{E}" nil "Ê" "E" "Ê" "Ê") + ("ecirc" "\\^{e}" nil "ê" "e" "ê" "ê") + ("Euml" "\\\"{E}" nil "Ë" "E" "Ë" "Ë") + ("euml" "\\\"{e}" nil "ë" "e" "ë" "ë") + ("Igrave" "\\`{I}" nil "Ì" "I" "Ì" "Ì") + ("igrave" "\\`{i}" nil "ì" "i" "ì" "ì") + ("Iacute" "\\'{I}" nil "Í" "I" "Í" "Í") + ("iacute" "\\'{i}" nil "í" "i" "í" "í") + ("Icirc" "\\^{I}" nil "Î" "I" "Î" "Î") + ("icirc" "\\^{i}" nil "î" "i" "î" "î") + ("Iuml" "\\\"{I}" nil "Ï" "I" "Ï" "Ï") + ("iuml" "\\\"{i}" nil "ï" "i" "ï" "ï") + ("Ntilde" "\\~{N}" nil "Ñ" "N" "Ñ" "Ñ") + ("ntilde" "\\~{n}" nil "ñ" "n" "ñ" "ñ") + ("Ograve" "\\`{O}" nil "Ò" "O" "Ò" "Ò") + ("ograve" "\\`{o}" nil "ò" "o" "ò" "ò") + ("Oacute" "\\'{O}" nil "Ó" "O" "Ó" "Ó") + ("oacute" "\\'{o}" nil "ó" "o" "ó" "ó") + ("Ocirc" "\\^{O}" nil "Ô" "O" "Ô" "Ô") + ("ocirc" "\\^{o}" nil "ô" "o" "ô" "ô") + ("Otilde" "\\~{O}" nil "Õ" "O" "Õ" "Õ") + ("otilde" "\\~{o}" nil "õ" "o" "õ" "õ") + ("Ouml" "\\\"{O}" nil "Ö" "Oe" "Ö" "Ö") + ("ouml" "\\\"{o}" nil "ö" "oe" "ö" "ö") + ("Oslash" "\\O" nil "Ø" "O" "Ø" "Ø") + ("oslash" "\\o{}" nil "ø" "o" "ø" "ø") + ("OElig" "\\OE{}" nil "Œ" "OE" "OE" "Œ") + ("oelig" "\\oe{}" nil "œ" "oe" "oe" "œ") + ("Scaron" "\\v{S}" nil "Š" "S" "S" "Š") + ("scaron" "\\v{s}" nil "š" "s" "s" "š") + ("szlig" "\\ss{}" nil "ß" "ss" "ß" "ß") + ("Ugrave" "\\`{U}" nil "Ù" "U" "Ù" "Ù") + ("ugrave" "\\`{u}" nil "ù" "u" "ù" "ù") + ("Uacute" "\\'{U}" nil "Ú" "U" "Ú" "Ú") + ("uacute" "\\'{u}" nil "ú" "u" "ú" "ú") + ("Ucirc" "\\^{U}" nil "Û" "U" "Û" "Û") + ("ucirc" "\\^{u}" nil "û" "u" "û" "û") + ("Uuml" "\\\"{U}" nil "Ü" "Ue" "Ü" "Ü") + ("uuml" "\\\"{u}" nil "ü" "ue" "ü" "ü") + ("Yacute" "\\'{Y}" nil "Ý" "Y" "Ý" "Ý") + ("yacute" "\\'{y}" nil "ý" "y" "ý" "ý") + ("Yuml" "\\\"{Y}" nil "Ÿ" "Y" "Y" "Ÿ") + ("yuml" "\\\"{y}" nil "ÿ" "y" "ÿ" "ÿ") + + "** Latin (special face)" + ("fnof" "\\textit{f}" nil "ƒ" "f" "f" "ƒ") + ("real" "\\Re" t "ℜ" "R" "R" "ℜ") + ("image" "\\Im" t "ℑ" "I" "I" "ℑ") + ("weierp" "\\wp" t "℘" "P" "P" "℘") + ("ell" "\\ell" t "ℓ" "ell" "ell" "ℓ") + ("imath" "\\imath" t "ı" "[dotless i]" "dotless i" "ı") + ("jmath" "\\jmath" t "ȷ" "[dotless j]" "dotless j" "ȷ") + + "** Greek" + ("Alpha" "A" nil "Α" "Alpha" "Alpha" "Α") + ("alpha" "\\alpha" t "α" "alpha" "alpha" "α") + ("Beta" "B" nil "Β" "Beta" "Beta" "Β") + ("beta" "\\beta" t "β" "beta" "beta" "β") + ("Gamma" "\\Gamma" t "Γ" "Gamma" "Gamma" "Γ") + ("gamma" "\\gamma" t "γ" "gamma" "gamma" "γ") + ("Delta" "\\Delta" t "Δ" "Delta" "Delta" "Δ") + ("delta" "\\delta" t "δ" "delta" "delta" "δ") + ("Epsilon" "E" nil "Ε" "Epsilon" "Epsilon" "Ε") + ("epsilon" "\\epsilon" t "ε" "epsilon" "epsilon" "ε") + ("varepsilon" "\\varepsilon" t "ε" "varepsilon" "varepsilon" "ε") + ("Zeta" "Z" nil "Ζ" "Zeta" "Zeta" "Ζ") + ("zeta" "\\zeta" t "ζ" "zeta" "zeta" "ζ") + ("Eta" "H" nil "Η" "Eta" "Eta" "Η") + ("eta" "\\eta" t "η" "eta" "eta" "η") + ("Theta" "\\Theta" t "Θ" "Theta" "Theta" "Θ") + ("theta" "\\theta" t "θ" "theta" "theta" "θ") + ("thetasym" "\\vartheta" t "ϑ" "theta" "theta" "ϑ") + ("vartheta" "\\vartheta" t "ϑ" "theta" "theta" "ϑ") + ("Iota" "I" nil "Ι" "Iota" "Iota" "Ι") + ("iota" "\\iota" t "ι" "iota" "iota" "ι") + ("Kappa" "K" nil "Κ" "Kappa" "Kappa" "Κ") + ("kappa" "\\kappa" t "κ" "kappa" "kappa" "κ") + ("Lambda" "\\Lambda" t "Λ" "Lambda" "Lambda" "Λ") + ("lambda" "\\lambda" t "λ" "lambda" "lambda" "λ") + ("Mu" "M" nil "Μ" "Mu" "Mu" "Μ") + ("mu" "\\mu" t "μ" "mu" "mu" "μ") + ("nu" "\\nu" t "ν" "nu" "nu" "ν") + ("Nu" "N" nil "Ν" "Nu" "Nu" "Ν") + ("Xi" "\\Xi" t "Ξ" "Xi" "Xi" "Ξ") + ("xi" "\\xi" t "ξ" "xi" "xi" "ξ") + ("Omicron" "O" nil "Ο" "Omicron" "Omicron" "Ο") + ("omicron" "\\textit{o}" nil "ο" "omicron" "omicron" "ο") + ("Pi" "\\Pi" t "Π" "Pi" "Pi" "Π") + ("pi" "\\pi" t "π" "pi" "pi" "π") + ("Rho" "P" nil "Ρ" "Rho" "Rho" "Ρ") + ("rho" "\\rho" t "ρ" "rho" "rho" "ρ") + ("Sigma" "\\Sigma" t "Σ" "Sigma" "Sigma" "Σ") + ("sigma" "\\sigma" t "σ" "sigma" "sigma" "σ") + ("sigmaf" "\\varsigma" t "ς" "sigmaf" "sigmaf" "ς") + ("varsigma" "\\varsigma" t "ς" "varsigma" "varsigma" "ς") + ("Tau" "T" nil "Τ" "Tau" "Tau" "Τ") + ("Upsilon" "\\Upsilon" t "Υ" "Upsilon" "Upsilon" "Υ") + ("upsih" "\\Upsilon" t "ϒ" "upsilon" "upsilon" "ϒ") + ("upsilon" "\\upsilon" t "υ" "upsilon" "upsilon" "υ") + ("Phi" "\\Phi" t "Φ" "Phi" "Phi" "Φ") + ("phi" "\\phi" t "φ" "phi" "phi" "ɸ") + ("varphi" "\\varphi" t "ϕ" "varphi" "varphi" "φ") + ("Chi" "X" nil "Χ" "Chi" "Chi" "Χ") + ("chi" "\\chi" t "χ" "chi" "chi" "χ") + ("acutex" "\\acute x" t "´x" "'x" "'x" "𝑥́") + ("Psi" "\\Psi" t "Ψ" "Psi" "Psi" "Ψ") + ("psi" "\\psi" t "ψ" "psi" "psi" "ψ") + ("tau" "\\tau" t "τ" "tau" "tau" "τ") + ("Omega" "\\Omega" t "Ω" "Omega" "Omega" "Ω") + ("omega" "\\omega" t "ω" "omega" "omega" "ω") + ("piv" "\\varpi" t "ϖ" "omega-pi" "omega-pi" "ϖ") + ("varpi" "\\varpi" t "ϖ" "omega-pi" "omega-pi" "ϖ") + ("partial" "\\partial" t "∂" "[partial differential]" "[partial differential]" "∂") + + "** Hebrew" + ("alefsym" "\\aleph" t "ℵ" "aleph" "aleph" "ℵ") + ("aleph" "\\aleph" t "ℵ" "aleph" "aleph" "ℵ") + ("gimel" "\\gimel" t "ℷ" "gimel" "gimel" "ℷ") + ("beth" "\\beth" t "ℶ" "beth" "beth" "ב") + ("dalet" "\\daleth" t "ℸ" "dalet" "dalet" "ד") + + "** Dead languages" + ("ETH" "\\DH{}" nil "Ð" "D" "Ð" "Ð") + ("eth" "\\dh{}" nil "ð" "dh" "ð" "ð") + ("THORN" "\\TH{}" nil "Þ" "TH" "Þ" "Þ") + ("thorn" "\\th{}" nil "þ" "th" "þ" "þ") + + "* Punctuation" + "** Dots and Marks" + ("dots" "\\dots{}" nil "…" "..." "..." "…") + ("cdots" "\\cdots{}" t "⋯" "..." "..." "⋯") + ("hellip" "\\dots{}" nil "…" "..." "..." "…") + ("middot" "\\textperiodcentered{}" nil "·" "." "·" "·") + ("iexcl" "!`" nil "¡" "!" "¡" "¡") + ("iquest" "?`" nil "¿" "?" "¿" "¿") + + "** Dash-like" + ("shy" "\\-" nil "­" "" "" "") + ("ndash" "--" nil "–" "-" "-" "–") + ("mdash" "---" nil "—" "--" "--" "—") + + "** Quotations" + ("quot" "\\textquotedbl{}" nil """ "\"" "\"" "\"") + ("acute" "\\textasciiacute{}" nil "´" "'" "´" "´") + ("ldquo" "\\textquotedblleft{}" nil "“" "\"" "\"" "“") + ("rdquo" "\\textquotedblright{}" nil "”" "\"" "\"" "”") + ("bdquo" "\\quotedblbase{}" nil "„" "\"" "\"" "„") + ("lsquo" "\\textquoteleft{}" nil "‘" "`" "`" "‘") + ("rsquo" "\\textquoteright{}" nil "’" "'" "'" "’") + ("sbquo" "\\quotesinglbase{}" nil "‚" "," "," "‚") + ("laquo" "\\guillemotleft{}" nil "«" "<<" "«" "«") + ("raquo" "\\guillemotright{}" nil "»" ">>" "»" "»") + ("lsaquo" "\\guilsinglleft{}" nil "‹" "<" "<" "‹") + ("rsaquo" "\\guilsinglright{}" nil "›" ">" ">" "›") + + "* Other" + "** Misc. (often used)" + ("circ" "\\^{}" nil "ˆ" "^" "^" "∘") + ("vert" "\\vert{}" t "|" "|" "|" "|") + ("vbar" "|" nil "|" "|" "|" "|") + ("brvbar" "\\textbrokenbar{}" nil "¦" "|" "¦" "¦") + ("S" "\\S" nil "§" "paragraph" "§" "§") + ("sect" "\\S" nil "§" "paragraph" "§" "§") + ("amp" "\\&" nil "&" "&" "&" "&") + ("lt" "\\textless{}" nil "<" "<" "<" "<") + ("gt" "\\textgreater{}" nil ">" ">" ">" ">") + ("tilde" "\\textasciitilde{}" nil "~" "~" "~" "~") + ("slash" "/" nil "/" "/" "/" "/") + ("plus" "+" nil "+" "+" "+" "+") + ("under" "\\_" nil "_" "_" "_" "_") + ("equal" "=" nil "=" "=" "=" "=") + ("asciicirc" "\\textasciicircum{}" nil "^" "^" "^" "^") + ("dagger" "\\textdagger{}" nil "†" "[dagger]" "[dagger]" "†") + ("dag" "\\dag{}" nil "†" "[dagger]" "[dagger]" "†") + ("Dagger" "\\textdaggerdbl{}" nil "‡" "[doubledagger]" "[doubledagger]" "‡") + ("ddag" "\\ddag{}" nil "‡" "[doubledagger]" "[doubledagger]" "‡") + + "** Whitespace" + ("nbsp" "~" nil " " " " "\x00A0" "\x00A0") + ("ensp" "\\hspace*{.5em}" nil " " " " " " " ") + ("emsp" "\\hspace*{1em}" nil " " " " " " " ") + ("thinsp" "\\hspace*{.2em}" nil " " " " " " " ") + + "** Currency" + ("curren" "\\textcurrency{}" nil "¤" "curr." "¤" "¤") + ("cent" "\\textcent{}" nil "¢" "cent" "¢" "¢") + ("pound" "\\pounds{}" nil "£" "pound" "£" "£") + ("yen" "\\textyen{}" nil "¥" "yen" "¥" "¥") + ("euro" "\\texteuro{}" nil "€" "EUR" "EUR" "€") + ("EUR" "\\texteuro{}" nil "€" "EUR" "EUR" "€") + + "** Property Marks" + ("copy" "\\textcopyright{}" nil "©" "(c)" "©" "©") + ("reg" "\\textregistered{}" nil "®" "(r)" "®" "®") + ("trade" "\\texttrademark{}" nil "™" "TM" "TM" "™") + + "** Science et al." + ("minus" "\\minus" t "−" "-" "-" "−") + ("pm" "\\textpm{}" nil "±" "+-" "±" "±") + ("plusmn" "\\textpm{}" nil "±" "+-" "±" "±") + ("times" "\\texttimes{}" nil "×" "*" "×" "×") + ("frasl" "/" nil "⁄" "/" "/" "⁄") + ("colon" "\\colon" t ":" ":" ":" ":") + ("div" "\\textdiv{}" nil "÷" "/" "÷" "÷") + ("frac12" "\\textonehalf{}" nil "½" "1/2" "½" "½") + ("frac14" "\\textonequarter{}" nil "¼" "1/4" "¼" "¼") + ("frac34" "\\textthreequarters{}" nil "¾" "3/4" "¾" "¾") + ("permil" "\\textperthousand{}" nil "‰" "per thousand" "per thousand" "‰") + ("sup1" "\\textonesuperior{}" nil "¹" "^1" "¹" "¹") + ("sup2" "\\texttwosuperior{}" nil "²" "^2" "²" "²") + ("sup3" "\\textthreesuperior{}" nil "³" "^3" "³" "³") + ("radic" "\\sqrt{\\,}" t "√" "[square root]" "[square root]" "√") + ("sum" "\\sum" t "∑" "[sum]" "[sum]" "∑") + ("prod" "\\prod" t "∏" "[product]" "[n-ary product]" "∏") + ("micro" "\\textmu{}" nil "µ" "micro" "µ" "µ") + ("macr" "\\textasciimacron{}" nil "¯" "[macron]" "¯" "¯") + ("deg" "\\textdegree{}" nil "°" "degree" "°" "°") + ("prime" "\\prime" t "′" "'" "'" "′") + ("Prime" "\\prime{}\\prime" t "″" "''" "''" "″") + ("infin" "\\infty" t "∞" "[infinity]" "[infinity]" "∞") + ("infty" "\\infty" t "∞" "[infinity]" "[infinity]" "∞") + ("prop" "\\propto" t "∝" "[proportional to]" "[proportional to]" "∝") + ("propto" "\\propto" t "∝" "[proportional to]" "[proportional to]" "∝") + ("not" "\\textlnot{}" nil "¬" "[angled dash]" "¬" "¬") + ("neg" "\\neg{}" t "¬" "[angled dash]" "¬" "¬") + ("land" "\\land" t "∧" "[logical and]" "[logical and]" "∧") + ("wedge" "\\wedge" t "∧" "[logical and]" "[logical and]" "∧") + ("lor" "\\lor" t "∨" "[logical or]" "[logical or]" "∨") + ("vee" "\\vee" t "∨" "[logical or]" "[logical or]" "∨") + ("cap" "\\cap" t "∩" "[intersection]" "[intersection]" "∩") + ("cup" "\\cup" t "∪" "[union]" "[union]" "∪") + ("smile" "\\smile" t "⌣" "[cup product]" "[cup product]" "⌣") + ("frown" "\\frown" t "⌢" "[Cap product]" "[cap product]" "⌢") + ("int" "\\int" t "∫" "[integral]" "[integral]" "∫") + ("therefore" "\\therefore" t "∴" "[therefore]" "[therefore]" "∴") + ("there4" "\\therefore" t "∴" "[therefore]" "[therefore]" "∴") + ("because" "\\because" t "∵" "[because]" "[because]" "∵") + ("sim" "\\sim" t "∼" "~" "~" "∼") + ("cong" "\\cong" t "≅" "[approx. equal to]" "[approx. equal to]" "≅") + ("simeq" "\\simeq" t "≅" "[approx. equal to]" "[approx. equal to]" "≅") + ("asymp" "\\asymp" t "≈" "[almost equal to]" "[almost equal to]" "≈") + ("approx" "\\approx" t "≈" "[almost equal to]" "[almost equal to]" "≈") + ("ne" "\\ne" t "≠" "[not equal to]" "[not equal to]" "≠") + ("neq" "\\neq" t "≠" "[not equal to]" "[not equal to]" "≠") + ("equiv" "\\equiv" t "≡" "[identical to]" "[identical to]" "≡") + + ("triangleq" "\\triangleq" t "≜" "[defined to]" "[defined to]" "≜") + ("le" "\\le" t "≤" "<=" "<=" "≤") + ("leq" "\\le" t "≤" "<=" "<=" "≤") + ("ge" "\\ge" t "≥" ">=" ">=" "≥") + ("geq" "\\ge" t "≥" ">=" ">=" "≥") + ("lessgtr" "\\lessgtr" t "≶" "[less than or greater than]" "[less than or greater than]" "≶") + ("lesseqgtr" "\\lesseqgtr" t "⋚" "[less than or equal or greater than or equal]" "[less than or equal or greater than or equal]" "⋚") + ("ll" "\\ll" t "≪" "<<" "<<" "≪") + ("Ll" "\\lll" t "⋘" "<<<" "<<<" "⋘") + ("lll" "\\lll" t "⋘" "<<<" "<<<" "⋘") + ("gg" "\\gg" t "≫" ">>" ">>" "≫") + ("Gg" "\\ggg" t "⋙" ">>>" ">>>" "⋙") + ("ggg" "\\ggg" t "⋙" ">>>" ">>>" "⋙") + ("prec" "\\prec" t "≺" "[precedes]" "[precedes]" "≺") + ("preceq" "\\preceq" t "≼" "[precedes or equal]" "[precedes or equal]" "≼") + ("preccurlyeq" "\\preccurlyeq" t "≼" "[precedes or equal]" "[precedes or equal]" "≼") + ("succ" "\\succ" t "≻" "[succeeds]" "[succeeds]" "≻") + ("succeq" "\\succeq" t "≽" "[succeeds or equal]" "[succeeds or equal]" "≽") + ("succcurlyeq" "\\succcurlyeq" t "≽" "[succeeds or equal]" "[succeeds or equal]" "≽") + ("sub" "\\subset" t "⊂" "[subset of]" "[subset of]" "⊂") + ("subset" "\\subset" t "⊂" "[subset of]" "[subset of]" "⊂") + ("sup" "\\supset" t "⊃" "[superset of]" "[superset of]" "⊃") + ("supset" "\\supset" t "⊃" "[superset of]" "[superset of]" "⊃") + ("nsub" "\\not\\subset" t "⊄" "[not a subset of]" "[not a subset of" "⊄") + ("sube" "\\subseteq" t "⊆" "[subset of or equal to]" "[subset of or equal to]" "⊆") + ("nsup" "\\not\\supset" t "⊅" "[not a superset of]" "[not a superset of]" "⊅") + ("supe" "\\supseteq" t "⊇" "[superset of or equal to]" "[superset of or equal to]" "⊇") + ("setminus" "\\setminus" t "∖" "\" "\" "⧵") + ("forall" "\\forall" t "∀" "[for all]" "[for all]" "∀") + ("exist" "\\exists" t "∃" "[there exists]" "[there exists]" "∃") + ("exists" "\\exists" t "∃" "[there exists]" "[there exists]" "∃") + ("nexist" "\\nexists" t "∃" "[there does not exists]" "[there does not exists]" "∄") + ("nexists" "\\nexists" t "∃" "[there does not exists]" "[there does not exists]" "∄") + ("empty" "\\empty" t "∅" "[empty set]" "[empty set]" "∅") + ("emptyset" "\\emptyset" t "∅" "[empty set]" "[empty set]" "∅") + ("isin" "\\in" t "∈" "[element of]" "[element of]" "∈") + ("in" "\\in" t "∈" "[element of]" "[element of]" "∈") + ("notin" "\\notin" t "∉" "[not an element of]" "[not an element of]" "∉") + ("ni" "\\ni" t "∋" "[contains as member]" "[contains as member]" "∋") + ("nabla" "\\nabla" t "∇" "[nabla]" "[nabla]" "∇") + ("ang" "\\angle" t "∠" "[angle]" "[angle]" "∠") + ("angle" "\\angle" t "∠" "[angle]" "[angle]" "∠") + ("perp" "\\perp" t "⊥" "[up tack]" "[up tack]" "⊥") + ("parallel" "\\parallel" t "∥" "||" "||" "∥") + ("sdot" "\\cdot" t "⋅" "[dot]" "[dot]" "⋅") + ("cdot" "\\cdot" t "⋅" "[dot]" "[dot]" "⋅") + ("lceil" "\\lceil" t "⌈" "[left ceiling]" "[left ceiling]" "⌈") + ("rceil" "\\rceil" t "⌉" "[right ceiling]" "[right ceiling]" "⌉") + ("lfloor" "\\lfloor" t "⌊" "[left floor]" "[left floor]" "⌊") + ("rfloor" "\\rfloor" t "⌋" "[right floor]" "[right floor]" "⌋") + ("lang" "\\langle" t "⟨" "<" "<" "⟨") + ("rang" "\\rangle" t "⟩" ">" ">" "⟩") + ("langle" "\\langle" t "⟨" "<" "<" "⟨") + ("rangle" "\\rangle" t "⟩" ">" ">" "⟩") + ("hbar" "\\hbar" t "ℏ" "hbar" "hbar" "ℏ") + ("mho" "\\mho" t "℧" "mho" "mho" "℧") + + "** Arrows" + ("larr" "\\leftarrow" t "←" "<-" "<-" "←") + ("leftarrow" "\\leftarrow" t "←" "<-" "<-" "←") + ("gets" "\\gets" t "←" "<-" "<-" "←") + ("lArr" "\\Leftarrow" t "⇐" "<=" "<=" "⇐") + ("Leftarrow" "\\Leftarrow" t "⇐" "<=" "<=" "⇐") + ("uarr" "\\uparrow" t "↑" "[uparrow]" "[uparrow]" "↑") + ("uparrow" "\\uparrow" t "↑" "[uparrow]" "[uparrow]" "↑") + ("uArr" "\\Uparrow" t "⇑" "[dbluparrow]" "[dbluparrow]" "⇑") + ("Uparrow" "\\Uparrow" t "⇑" "[dbluparrow]" "[dbluparrow]" "⇑") + ("rarr" "\\rightarrow" t "→" "->" "->" "→") + ("to" "\\to" t "→" "->" "->" "→") + ("rightarrow" "\\rightarrow" t "→" "->" "->" "→") + ("rArr" "\\Rightarrow" t "⇒" "=>" "=>" "⇒") + ("Rightarrow" "\\Rightarrow" t "⇒" "=>" "=>" "⇒") + ("darr" "\\downarrow" t "↓" "[downarrow]" "[downarrow]" "↓") + ("downarrow" "\\downarrow" t "↓" "[downarrow]" "[downarrow]" "↓") + ("dArr" "\\Downarrow" t "⇓" "[dbldownarrow]" "[dbldownarrow]" "⇓") + ("Downarrow" "\\Downarrow" t "⇓" "[dbldownarrow]" "[dbldownarrow]" "⇓") + ("harr" "\\leftrightarrow" t "↔" "<->" "<->" "↔") + ("leftrightarrow" "\\leftrightarrow" t "↔" "<->" "<->" "↔") + ("hArr" "\\Leftrightarrow" t "⇔" "<=>" "<=>" "⇔") + ("Leftrightarrow" "\\Leftrightarrow" t "⇔" "<=>" "<=>" "⇔") + ("crarr" "\\hookleftarrow" t "↵" "<-'" "<-'" "↵") + ("hookleftarrow" "\\hookleftarrow" t "↵" "<-'" "<-'" "↵") + + "** Function names" + ("arccos" "\\arccos" t "arccos" "arccos" "arccos" "arccos") + ("arcsin" "\\arcsin" t "arcsin" "arcsin" "arcsin" "arcsin") + ("arctan" "\\arctan" t "arctan" "arctan" "arctan" "arctan") + ("arg" "\\arg" t "arg" "arg" "arg" "arg") + ("cos" "\\cos" t "cos" "cos" "cos" "cos") + ("cosh" "\\cosh" t "cosh" "cosh" "cosh" "cosh") + ("cot" "\\cot" t "cot" "cot" "cot" "cot") + ("coth" "\\coth" t "coth" "coth" "coth" "coth") + ("csc" "\\csc" t "csc" "csc" "csc" "csc") + ("deg" "\\deg" t "°" "deg" "deg" "deg") + ("det" "\\det" t "det" "det" "det" "det") + ("dim" "\\dim" t "dim" "dim" "dim" "dim") + ("exp" "\\exp" t "exp" "exp" "exp" "exp") + ("gcd" "\\gcd" t "gcd" "gcd" "gcd" "gcd") + ("hom" "\\hom" t "hom" "hom" "hom" "hom") + ("inf" "\\inf" t "inf" "inf" "inf" "inf") + ("ker" "\\ker" t "ker" "ker" "ker" "ker") + ("lg" "\\lg" t "lg" "lg" "lg" "lg") + ("lim" "\\lim" t "lim" "lim" "lim" "lim") + ("liminf" "\\liminf" t "liminf" "liminf" "liminf" "liminf") + ("limsup" "\\limsup" t "limsup" "limsup" "limsup" "limsup") + ("ln" "\\ln" t "ln" "ln" "ln" "ln") + ("log" "\\log" t "log" "log" "log" "log") + ("max" "\\max" t "max" "max" "max" "max") + ("min" "\\min" t "min" "min" "min" "min") + ("Pr" "\\Pr" t "Pr" "Pr" "Pr" "Pr") + ("sec" "\\sec" t "sec" "sec" "sec" "sec") + ("sin" "\\sin" t "sin" "sin" "sin" "sin") + ("sinh" "\\sinh" t "sinh" "sinh" "sinh" "sinh") + ("sup" "\\sup" t "⊃" "sup" "sup" "sup") + ("tan" "\\tan" t "tan" "tan" "tan" "tan") + ("tanh" "\\tanh" t "tanh" "tanh" "tanh" "tanh") + + "** Signs & Symbols" + ("bull" "\\textbullet{}" nil "•" "*" "*" "•") + ("bullet" "\\textbullet{}" nil "•" "*" "*" "•") + ("star" "\\star" t "*" "*" "*" "⋆") + ("lowast" "\\ast" t "∗" "*" "*" "∗") + ("ast" "\\ast" t "∗" "*" "*" "*") + ("odot" "\\odot" t "o" "[circled dot]" "[circled dot]" "ʘ") + ("oplus" "\\oplus" t "⊕" "[circled plus]" "[circled plus]" "⊕") + ("otimes" "\\otimes" t "⊗" "[circled times]" "[circled times]" "⊗") + ("check" "\\checkmark" t "✓" "[checkmark]" "[checkmark]" "✓") + ("checkmark" "\\checkmark" t "✓" "[checkmark]" "[checkmark]" "✓") + + "** Miscellaneous (seldom used)" + ("para" "\\P{}" nil "¶" "[pilcrow]" "¶" "¶") + ("ordf" "\\textordfeminine{}" nil "ª" "_a_" "ª" "ª") + ("ordm" "\\textordmasculine{}" nil "º" "_o_" "º" "º") + ("cedil" "\\c{}" nil "¸" "[cedilla]" "¸" "¸") + ("oline" "\\overline{~}" t "‾" "[overline]" "¯" "‾") + ("uml" "\\textasciidieresis{}" nil "¨" "[diaeresis]" "¨" "¨") + ("zwnj" "\\/{}" nil "‌" "" "" "‌") + ("zwj" "" nil "‍" "" "" "‍") + ("lrm" "" nil "‎" "" "" "‎") + ("rlm" "" nil "‏" "" "" "‏") + + "** Smilies" + ("smiley" "\\ddot\\smile" t "☺" ":-)" ":-)" "☺") + ("blacksmile" "\\ddot\\smile" t "☻" ":-)" ":-)" "☻") + ("sad" "\\ddot\\frown" t "☹" ":-(" ":-(" "☹") + ("frowny" "\\ddot\\frown" t "☹" ":-(" ":-(" "☹") + + "** Suits" + ("clubs" "\\clubsuit" t "♣" "[clubs]" "[clubs]" "♣") + ("clubsuit" "\\clubsuit" t "♣" "[clubs]" "[clubs]" "♣") + ("spades" "\\spadesuit" t "♠" "[spades]" "[spades]" "♠") + ("spadesuit" "\\spadesuit" t "♠" "[spades]" "[spades]" "♠") + ("hearts" "\\heartsuit" t "♥" "[hearts]" "[hearts]" "♥") + ("heartsuit" "\\heartsuit" t "♥" "[hearts]" "[hearts]" "♥") + ("diams" "\\diamondsuit" t "♦" "[diamonds]" "[diamonds]" "◆") + ("diamondsuit" "\\diamondsuit" t "♦" "[diamonds]" "[diamonds]" "◆") + ("diamond" "\\diamondsuit" t "⋄" "[diamond]" "[diamond]" "◆") + ("Diamond" "\\diamondsuit" t "⋄" "[diamond]" "[diamond]" "◆") + ("loz" "\\lozenge" t "◊" "[lozenge]" "[lozenge]" "⧫")) + ;; Add "\_ "-entity family for spaces. + (let (space-entities html-spaces (entity "_")) + (dolist (n (number-sequence 1 20) (nreverse space-entities)) + (let ((spaces (make-string n ?\s))) + (push (list (setq entity (concat entity " ")) + (format "\\hspace*{%sem}" (* n .5)) + nil + (setq html-spaces (concat " " html-spaces)) + spaces + spaces + (make-string n ?\x2002)) + space-entities))))) + "Default entities used in Org mode to produce special characters. For details see `org-entities-user'.") (defsubst org-entity-get (name) @@ -518,52 +531,27 @@ This first checks the user list, then the built-in list." (or (assoc name org-entities-user) (assoc name org-entities))) -(defun org-entity-get-representation (name kind) - "Get the correct representation of entity NAME for export type KIND. -Kind can be any of `latex', `html', `ascii', `latin1', or `utf8'." - (let* ((e (org-entity-get name)) - (n (cdr (assq kind '((latex . 1) (html . 3) (ascii . 4) - (latin1 . 5) (utf8 . 6))))) - (r (and e n (nth n e)))) - (if (and e r - (not org-entities-ascii-explanatory) - (memq kind '(ascii latin1 utf8)) - (= (string-to-char r) ?\[)) - (concat "\\" name) - r))) - -(defsubst org-entity-latex-math-p (name) - "Does entity NAME require math mode in LaTeX?" - (nth 2 (org-entity-get name))) - ;; Helpfunctions to create a table for orgmode.org/worg/org-symbols.org (defun org-entities-create-table () "Create an Org mode table with all entities." (interactive) - (let ((pos (point)) e latex mathp html latin utf8 name ascii) + (let ((pos (point))) (insert "|Name|LaTeX code|LaTeX|HTML code |HTML|ASCII|Latin1|UTF-8\n|-\n") - (mapc (lambda (e) (when (listp e) - (setq name (car e) - latex (nth 1 e) - mathp (nth 2 e) - html (nth 3 e) - ascii (nth 4 e) - latin (nth 5 e) - utf8 (nth 6 e)) - (if (equal ascii "|") (setq ascii "\\vert")) - (if (equal latin "|") (setq latin "\\vert")) - (if (equal utf8 "|") (setq utf8 "\\vert")) - (if (equal ascii "=>") (setq ascii "= >")) - (if (equal latin "=>") (setq latin "= >")) - (insert "|" name - "|" (format "=%s=" latex) - "|" (format (if mathp "$%s$" "$\\mbox{%s}$") - latex) - "|" (format "=%s=" html) "|" html - "|" ascii "|" latin "|" utf8 - "|\n"))) - org-entities) + (dolist (e org-entities) + (pcase e + (`(,name ,latex ,mathp ,html ,ascii ,latin ,utf8) + (if (equal ascii "|") (setq ascii "\\vert")) + (if (equal latin "|") (setq latin "\\vert")) + (if (equal utf8 "|") (setq utf8 "\\vert")) + (if (equal ascii "=>") (setq ascii "= >")) + (if (equal latin "=>") (setq latin "= >")) + (insert "|" name + "|" (format "=%s=" latex) + "|" (format (if mathp "$%s$" "$\\mbox{%s}$") latex) + "|" (format "=%s=" html) "|" html + "|" ascii "|" latin "|" utf8 + "|\n")))) (goto-char pos) (org-table-align))) @@ -572,31 +560,27 @@ Kind can be any of `latex', `html', `ascii', `latin1', or `utf8'." "Create a Help buffer with all available entities." (interactive) (with-output-to-temp-buffer "*Org Entity Help*" - (princ "Org-mode entities\n=================\n\n") + (princ "Org mode entities\n=================\n\n") (let ((ll (append '("* User-defined additions (variable org-entities-user)") org-entities-user org-entities)) - e latex mathp html latin utf8 name ascii (lastwasstring t) (head (concat "\n" " Symbol Org entity LaTeX code HTML code\n" " -----------------------------------------------------------\n"))) - (while ll - (setq e (pop ll)) - (if (stringp e) - (progn - (princ e) - (princ "\n") - (setq lastwasstring t)) - (if lastwasstring (princ head)) - (setq lastwasstring nil) - (setq name (car e) - latex (nth 1 e) - html (nth 3 e) - utf8 (nth 6 e)) - (princ (format " %-8s \\%-16s %-22s %-13s\n" - utf8 name latex html)))))) + (dolist (e ll) + (pcase e + (`(,name ,latex ,_ ,html ,_ ,_ ,utf8) + (when lastwasstring + (princ head) + (setq lastwasstring nil)) + (princ (format " %-8s \\%-16s %-22s %-13s\n" + utf8 name latex html))) + ((pred stringp) + (princ e) + (princ "\n") + (setq lastwasstring t)))))) (with-current-buffer "*Org Entity Help*" (org-mode) (when org-pretty-entities @@ -604,12 +588,6 @@ Kind can be any of `latex', `html', `ascii', `latin1', or `utf8'." (select-window (get-buffer-window "*Org Entity Help*"))) -(defun replace-amp () - "Postprocess HTML file to unescape the ampersand." - (interactive) - (while (re-search-forward "&\\([^<;]+;\\)" nil t) - (replace-match (concat "&" (match-string 1)) t t))) - (provide 'org-entities) ;; Local variables: diff --git a/lisp/org/org-eshell.el b/lisp/org/org-eshell.el index 9eddd3fcf4..34cc4ffbb8 100644 --- a/lisp/org/org-eshell.el +++ b/lisp/org/org-eshell.el @@ -1,4 +1,4 @@ -;;; org-eshell.el - Support for links to working directories in eshell +;;; org-eshell.el - Support for Links to Working Directories in Eshell -*- lexical-binding: t; -*- ;; Copyright (C) 2011-2017 Free Software Foundation, Inc. @@ -27,8 +27,9 @@ (require 'eshell) (require 'esh-mode) -(org-add-link-type "eshell" 'org-eshell-open) -(add-hook 'org-store-link-functions 'org-eshell-store-link) +(org-link-set-parameters "eshell" + :follow #'org-eshell-open + :store #'org-eshell-store-link) (defun org-eshell-open (link) "Switch to am eshell buffer and execute a command line. @@ -43,7 +44,7 @@ (eshell-buffer-name (car buffer-and-command)) (command (cadr buffer-and-command))) (if (get-buffer eshell-buffer-name) - (org-pop-to-buffer-same-window eshell-buffer-name) + (pop-to-buffer-same-window eshell-buffer-name) (eshell)) (goto-char (point-max)) (eshell-kill-input) diff --git a/lisp/org/org-eww.el b/lisp/org/org-eww.el new file mode 100644 index 0000000000..7bc248d4df --- /dev/null +++ b/lisp/org/org-eww.el @@ -0,0 +1,175 @@ +;;; org-eww.el --- Store url and kill from Eww mode for Org -*- lexical-binding: t -*- + +;; Copyright (C) 2014-2017 Free Software Foundation, Inc. + +;; Author: Marco Wahl a +;; Keywords: link, eww +;; Homepage: http://orgmode.org +;; +;; This file is part of GNU Emacs. +;; +;; 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 GNU Emacs. If not, see . + + +;;; Commentary: + +;; When this module is active `org-store-link' (often on key C-c l) in +;; a eww buffer stores a link to the current url of the eww buffer. + +;; In an eww buffer function `org-eww-copy-for-org-mode' kills either +;; a region or the whole buffer if no region is set and transforms the +;; text on the fly so that it can be pasted into an Org buffer with +;; hot links. + +;; C-c C-x C-w (and also C-c C-x M-w) trigger +;; `org-eww-copy-for-org-mode'. + +;; Hint: A lot of code of this module comes from module org-w3m which +;; has been written by Andy Steward based on the idea of Richard +;; Riley. Thanks! + +;; Potential: Since the code for w3m and eww is so similar one could +;; try to refactor. + + +;;; Code: +(require 'org) +(require 'cl-lib) + +(defvar eww-current-title) +(defvar eww-current-url) +(defvar eww-data) +(defvar eww-mode-map) + +(declare-function eww-current-url "eww") + + +;; Store Org-link in eww-mode buffer +(org-link-set-parameters "eww" :follow #'eww :store #'org-eww-store-link) +(defun org-eww-store-link () + "Store a link to the url of a Eww buffer." + (when (eq major-mode 'eww-mode) + (org-store-link-props + :type "eww" + :link (if (< emacs-major-version 25) + eww-current-url + (eww-current-url)) + :url (url-view-url t) + :description (if (< emacs-major-version 25) + (or eww-current-title eww-current-url) + (or (plist-get eww-data :title) + (eww-current-url)))))) + + +;; Some auxiliary functions concerning links in eww buffers +(defun org-eww-goto-next-url-property-change () + "Move to the start of next link if exists. +Otherwise point is not moved. Return point." + (goto-char + (or (next-single-property-change (point) 'shr-url) + (point)))) + +(defun org-eww-has-further-url-property-change-p () + "Non-nil if there is a next url property change." + (save-excursion + (not (eq (point) (org-eww-goto-next-url-property-change))))) + +(defun org-eww-url-below-point () + "Return the url below point if there is an url; otherwise, return nil." + (get-text-property (point) 'shr-url)) + + +(defun org-eww-copy-for-org-mode () + "Copy current buffer content or active region with `org-mode' style links. +This will encode `link-title' and `link-location' with +`org-make-link-string', and insert the transformed test into the kill ring, +so that it can be yanked into an Org mode buffer with links working correctly. + +Further lines starting with a star get quoted with a comma to keep +the structure of the Org file." + (interactive) + (let* ((regionp (org-region-active-p)) + (transform-start (point-min)) + (transform-end (point-max)) + return-content + link-location link-title + temp-position out-bound) + (when regionp + (setq transform-start (region-beginning)) + (setq transform-end (region-end)) + ;; Deactivate mark if current mark is activate. + (when (fboundp 'deactivate-mark) (deactivate-mark))) + (message "Transforming links...") + (save-excursion + (goto-char transform-start) + (while (and (not out-bound) ; still inside region to copy + (org-eww-has-further-url-property-change-p)) ; there is a next link + ;; Store current point before jump next anchor. + (setq temp-position (point)) + ;; Move to next anchor when current point is not at anchor. + (or (org-eww-url-below-point) + (org-eww-goto-next-url-property-change)) + (cl-assert + (org-eww-url-below-point) t + "program logic error: point must have an url below but it hasn't") + (if (<= (point) transform-end) ; if point is inside transform bound + (progn + ;; Get content between two links. + (when (< temp-position (point)) + (setq return-content (concat return-content + (buffer-substring + temp-position (point))))) + ;; Get link location at current point. + (setq link-location (org-eww-url-below-point)) + ;; Get link title at current point. + (setq link-title + (buffer-substring + (point) + (org-eww-goto-next-url-property-change))) + ;; concat `org-mode' style url to `return-content'. + (setq return-content + (concat return-content + (if (stringp link-location) + ;; hint: link-location is different for form-elements. + (org-make-link-string link-location link-title) + link-title)))) + (goto-char temp-position) ; reset point before jump next anchor + (setq out-bound t) ; for break out `while' loop + )) + ;; Add the rest until end of the region to be copied. + (when (< (point) transform-end) + (setq return-content + (concat return-content + (buffer-substring (point) transform-end)))) + ;; Quote lines starting with *. + (org-kill-new (replace-regexp-in-string "^\\*" ",*" return-content)) + (message "Transforming links...done, use C-y to insert text into Org mode file")))) + + +;; Additional keys for eww-mode + +(defun org-eww-extend-eww-keymap () + (define-key eww-mode-map "\C-c\C-x\M-w" 'org-eww-copy-for-org-mode) + (define-key eww-mode-map "\C-c\C-x\C-w" 'org-eww-copy-for-org-mode)) + +(when (and (boundp 'eww-mode-map) + (keymapp eww-mode-map)) ; eww is already up. + (org-eww-extend-eww-keymap)) + +(add-hook 'eww-mode-hook #'org-eww-extend-eww-keymap) + + +(provide 'org-eww) + +;;; org-eww.el ends here diff --git a/lisp/org/org-faces.el b/lisp/org/org-faces.el index c340aca73a..cd43d37178 100644 --- a/lisp/org/org-faces.el +++ b/lisp/org/org-faces.el @@ -1,4 +1,4 @@ -;;; org-faces.el --- Face definitions for Org-mode. +;;; org-faces.el --- Face definitions -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -28,32 +28,12 @@ ;;; Code: -(require 'org-macs) -(require 'org-compat) - -(defun org-copy-face (old-face new-face docstring &rest attributes) - (unless (facep new-face) - (if (fboundp 'set-face-attribute) - (progn - (make-face new-face) - (set-face-attribute new-face nil :inherit old-face) - (apply 'set-face-attribute new-face nil attributes) - (set-face-doc-string new-face docstring)) - (copy-face old-face new-face) - (if (fboundp 'set-face-doc-string) - (set-face-doc-string new-face docstring))))) -(put 'org-copy-face 'lisp-indent-function 2) - -(when (featurep 'xemacs) - (put 'mode-line 'face-alias 'modeline)) - (defgroup org-faces nil - "Faces in Org-mode." + "Faces in Org mode." :tag "Org Faces" :group 'org-appearance) -(defface org-default - (org-compatible-face 'default nil) +(defface org-default '((t :inherit default)) "Face used for default text." :group 'org-faces) @@ -65,99 +45,49 @@ The foreground color of this face should be equal to the background color of the frame." :group 'org-faces) -(defface org-level-1 ;; originally copied from font-lock-function-name-face - (org-compatible-face 'outline-1 - '((((class color) (min-colors 88) (background light)) (:foreground "Blue1")) - (((class color) (min-colors 88) (background dark)) (:foreground "LightSkyBlue")) - (((class color) (min-colors 16) (background light)) (:foreground "Blue")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightSkyBlue")) - (((class color) (min-colors 8)) (:foreground "blue" :bold t)) - (t (:bold t)))) +(defface org-level-1 '((t :inherit outline-1)) "Face used for level 1 headlines." :group 'org-faces) -(defface org-level-2 ;; originally copied from font-lock-variable-name-face - (org-compatible-face 'outline-2 - '((((class color) (min-colors 16) (background light)) (:foreground "DarkGoldenrod")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightGoldenrod")) - (((class color) (min-colors 8) (background light)) (:foreground "yellow")) - (((class color) (min-colors 8) (background dark)) (:foreground "yellow" :bold t)) - (t (:bold t)))) +(defface org-level-2 '((t :inherit outline-2)) "Face used for level 2 headlines." :group 'org-faces) -(defface org-level-3 ;; originally copied from font-lock-keyword-face - (org-compatible-face 'outline-3 - '((((class color) (min-colors 88) (background light)) (:foreground "Purple")) - (((class color) (min-colors 88) (background dark)) (:foreground "Cyan1")) - (((class color) (min-colors 16) (background light)) (:foreground "Purple")) - (((class color) (min-colors 16) (background dark)) (:foreground "Cyan")) - (((class color) (min-colors 8) (background light)) (:foreground "purple" :bold t)) - (((class color) (min-colors 8) (background dark)) (:foreground "cyan" :bold t)) - (t (:bold t)))) +(defface org-level-3 '((t :inherit outline-3)) "Face used for level 3 headlines." :group 'org-faces) -(defface org-level-4 ;; originally copied from font-lock-comment-face - (org-compatible-face 'outline-4 - '((((class color) (min-colors 88) (background light)) (:foreground "Firebrick")) - (((class color) (min-colors 88) (background dark)) (:foreground "chocolate1")) - (((class color) (min-colors 16) (background light)) (:foreground "red")) - (((class color) (min-colors 16) (background dark)) (:foreground "red1")) - (((class color) (min-colors 8) (background light)) (:foreground "red" :bold t)) - (((class color) (min-colors 8) (background dark)) (:foreground "red" :bold t)) - (t (:bold t)))) +(defface org-level-4 '((t :inherit outline-4)) "Face used for level 4 headlines." :group 'org-faces) -(defface org-level-5 ;; originally copied from font-lock-type-face - (org-compatible-face 'outline-5 - '((((class color) (min-colors 16) (background light)) (:foreground "ForestGreen")) - (((class color) (min-colors 16) (background dark)) (:foreground "PaleGreen")) - (((class color) (min-colors 8)) (:foreground "green")))) +(defface org-level-5 '((t :inherit outline-5)) "Face used for level 5 headlines." :group 'org-faces) -(defface org-level-6 ;; originally copied from font-lock-constant-face - (org-compatible-face 'outline-6 - '((((class color) (min-colors 16) (background light)) (:foreground "CadetBlue")) - (((class color) (min-colors 16) (background dark)) (:foreground "Aquamarine")) - (((class color) (min-colors 8)) (:foreground "magenta")))) +(defface org-level-6 '((t :inherit outline-6)) "Face used for level 6 headlines." :group 'org-faces) -(defface org-level-7 ;; originally copied from font-lock-builtin-face - (org-compatible-face 'outline-7 - '((((class color) (min-colors 16) (background light)) (:foreground "Orchid")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightSteelBlue")) - (((class color) (min-colors 8)) (:foreground "blue")))) +(defface org-level-7 '((t :inherit outline-7)) "Face used for level 7 headlines." :group 'org-faces) -(defface org-level-8 ;; originally copied from font-lock-string-face - (org-compatible-face 'outline-8 - '((((class color) (min-colors 16) (background light)) (:foreground "RosyBrown")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightSalmon")) - (((class color) (min-colors 8)) (:foreground "green")))) +(defface org-level-8 '((t :inherit outline-8)) "Face used for level 8 headlines." :group 'org-faces) -(defface org-special-keyword ;; originally copied from font-lock-string-face - (org-compatible-face 'font-lock-keyword-face - '((((class color) (min-colors 16) (background light)) (:foreground "RosyBrown")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightSalmon")) - (t (:italic t)))) +(defface org-special-keyword '((t :inherit font-lock-keyword-face)) "Face used for special keywords." :group 'org-faces) -(defface org-drawer ;; originally copied from font-lock-function-name-face - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) (:foreground "Blue1")) - (((class color) (min-colors 88) (background dark)) (:foreground "LightSkyBlue")) - (((class color) (min-colors 16) (background light)) (:foreground "Blue")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightSkyBlue")) - (((class color) (min-colors 8)) (:foreground "blue" :bold t)) - (t (:bold t)))) +(defface org-drawer ;Copied from `font-lock-function-name-face' + '((((class color) (min-colors 88) (background light)) (:foreground "Blue1")) + (((class color) (min-colors 88) (background dark)) (:foreground "LightSkyBlue")) + (((class color) (min-colors 16) (background light)) (:foreground "Blue")) + (((class color) (min-colors 16) (background dark)) (:foreground "LightSkyBlue")) + (((class color) (min-colors 8)) (:foreground "blue" :bold t)) + (t (:bold t))) "Face used for drawers." :group 'org-faces) @@ -166,18 +96,17 @@ color of the frame." :group 'org-faces) (defface org-column - (org-compatible-face nil - '((((class color) (min-colors 16) (background light)) - (:background "grey90" :weight normal :slant normal :strike-through nil - :underline nil)) - (((class color) (min-colors 16) (background dark)) - (:background "grey30" :weight normal :slant normal :strike-through nil - :underline nil)) - (((class color) (min-colors 8)) - (:background "cyan" :foreground "black" - :weight normal :slant normal :strike-through nil - :underline nil)) - (t (:inverse-video t)))) + '((((class color) (min-colors 16) (background light)) + (:background "grey90" :weight normal :slant normal :strike-through nil + :underline nil)) + (((class color) (min-colors 16) (background dark)) + (:background "grey30" :weight normal :slant normal :strike-through nil + :underline nil)) + (((class color) (min-colors 8)) + (:background "cyan" :foreground "black" + :weight normal :slant normal :strike-through nil + :underline nil)) + (t (:inverse-video t))) "Face for column display of entry properties. This is actually only part of the face definition for the text in column view. The following faces apply, with this priority. @@ -198,59 +127,33 @@ character (this might for example be the a TODO keyword) might still shine through in some properties. So when your column view looks funny, with \"random\" colors, weight, strike-through, try to explicitly set the properties in the `org-column' face. For example, set -:underline to nil, or the :slant to `normal'. - -Under XEmacs, the rules are simpler, because the XEmacs version of -column view defines special faces for each outline level. See the file -`org-colview-xemacs.el' in Org's contrib/ directory for details." +:underline to nil, or the :slant to `normal'." :group 'org-faces) (defface org-column-title - (org-compatible-face nil - '((((class color) (min-colors 16) (background light)) - (:background "grey90" :underline t :weight bold)) - (((class color) (min-colors 16) (background dark)) - (:background "grey30" :underline t :weight bold)) - (((class color) (min-colors 8)) - (:background "cyan" :foreground "black" :underline t :weight bold)) - (t (:inverse-video t)))) + '((((class color) (min-colors 16) (background light)) + (:background "grey90" :underline t :weight bold)) + (((class color) (min-colors 16) (background dark)) + (:background "grey30" :underline t :weight bold)) + (((class color) (min-colors 8)) + (:background "cyan" :foreground "black" :underline t :weight bold)) + (t (:inverse-video t))) "Face for column display of entry properties." :group 'org-faces) -(defface org-agenda-column-dateline - (org-compatible-face 'org-column - '((t nil))) +(defface org-agenda-column-dateline '((t :inherit org-column)) "Face used in agenda column view for datelines with summaries." :group 'org-faces) -(defface org-warning - (org-compatible-face 'font-lock-warning-face - '((((class color) (min-colors 16) (background light)) (:foreground "Red1" :bold t)) - (((class color) (min-colors 16) (background dark)) (:foreground "Pink" :bold t)) - (((class color) (min-colors 8) (background light)) (:foreground "red" :bold t)) - (((class color) (min-colors 8) (background dark)) (:foreground "red" :bold t)) - (t (:bold t)))) +(defface org-warning '((t :inherit font-lock-warning-face)) "Face for deadlines and TODO keywords." :group 'org-faces) -(defface org-archived ; similar to shadow - (org-compatible-face 'shadow - '((((class color grayscale) (min-colors 88) (background light)) - (:foreground "grey50")) - (((class color grayscale) (min-colors 88) (background dark)) - (:foreground "grey70")) - (((class color) (min-colors 8) (background light)) - (:foreground "green")) - (((class color) (min-colors 8) (background dark)) - (:foreground "yellow")))) +(defface org-archived '((t :inherit shadow)) "Face for headline with the ARCHIVE tag." :group 'org-faces) -(defface org-link - (org-compatible-face 'link - '((((class color) (background light)) (:foreground "Purple" :underline t)) - (((class color) (background dark)) (:foreground "Cyan" :underline t)) - (t (:underline t)))) +(defface org-link '((t :inherit link)) "Face for links." :group 'org-faces) @@ -283,12 +186,11 @@ column view defines special faces for each outline level. See the file :group 'org-faces) (defface org-date-selected - (org-compatible-face nil - '((((class color) (min-colors 16) (background light)) (:foreground "Red1" :inverse-video t)) - (((class color) (min-colors 16) (background dark)) (:foreground "Pink" :inverse-video t)) - (((class color) (min-colors 8) (background light)) (:foreground "red" :inverse-video t)) - (((class color) (min-colors 8) (background dark)) (:foreground "red" :inverse-video t)) - (t (:inverse-video t)))) + '((((class color) (min-colors 16) (background light)) (:foreground "Red1" :inverse-video t)) + (((class color) (min-colors 16) (background dark)) (:foreground "Pink" :inverse-video t)) + (((class color) (min-colors 8) (background light)) (:foreground "red" :inverse-video t)) + (((class color) (min-colors 8) (background dark)) (:foreground "red" :inverse-video t)) + (t (:inverse-video t))) "Face for highlighting the calendar day when using `org-read-date'. Using a bold face here might cause discrepancies while displaying the calendar." @@ -301,43 +203,38 @@ calendar." "Face for diary-like sexp date specifications." :group 'org-faces) -(defface org-tag - '((t (:bold t))) +(defface org-tag '((t (:bold t))) "Default face for tags. Note that the variable `org-tag-faces' can be used to overrule this face for specific tags." :group 'org-faces) -(defface org-list-dt - '((t (:bold t))) +(defface org-list-dt '((t (:bold t))) "Default face for definition terms in lists." :group 'org-faces) -(defface org-todo ; font-lock-warning-face - (org-compatible-face nil - '((((class color) (min-colors 16) (background light)) (:foreground "Red1" :bold t)) - (((class color) (min-colors 16) (background dark)) (:foreground "Pink" :bold t)) - (((class color) (min-colors 8) (background light)) (:foreground "red" :bold t)) - (((class color) (min-colors 8) (background dark)) (:foreground "red" :bold t)) - (t (:inverse-video t :bold t)))) +(defface org-todo ;Copied from `font-lock-warning-face' + '((((class color) (min-colors 16) (background light)) (:foreground "Red1" :bold t)) + (((class color) (min-colors 16) (background dark)) (:foreground "Pink" :bold t)) + (((class color) (min-colors 8) (background light)) (:foreground "red" :bold t)) + (((class color) (min-colors 8) (background dark)) (:foreground "red" :bold t)) + (t (:inverse-video t :bold t))) "Face for TODO keywords." :group 'org-faces) -(defface org-done ;; originally copied from font-lock-type-face - (org-compatible-face nil - '((((class color) (min-colors 16) (background light)) (:foreground "ForestGreen" :bold t)) - (((class color) (min-colors 16) (background dark)) (:foreground "PaleGreen" :bold t)) - (((class color) (min-colors 8)) (:foreground "green")) - (t (:bold t)))) +(defface org-done ;Copied from `font-lock-type-face' + '((((class color) (min-colors 16) (background light)) (:foreground "ForestGreen" :bold t)) + (((class color) (min-colors 16) (background dark)) (:foreground "PaleGreen" :bold t)) + (((class color) (min-colors 8)) (:foreground "green")) + (t (:bold t))) "Face used for todo keywords that indicate DONE items." :group 'org-faces) -(defface org-agenda-done ;; originally copied from font-lock-type-face - (org-compatible-face nil - '((((class color) (min-colors 16) (background light)) (:foreground "ForestGreen")) - (((class color) (min-colors 16) (background dark)) (:foreground "PaleGreen")) - (((class color) (min-colors 8)) (:foreground "green")) - (t (:bold nil)))) +(defface org-agenda-done ;Copied from `font-lock-type-face' + '((((class color) (min-colors 16) (background light)) (:foreground "ForestGreen")) + (((class color) (min-colors 16) (background dark)) (:foreground "PaleGreen")) + (((class color) (min-colors 8)) (:foreground "green")) + (t (:bold nil))) "Face used in agenda, to indicate lines switched to DONE. This face is used to de-emphasize items that where brightly colored in the agenda because they were things to do, or overdue. The DONE state itself @@ -346,11 +243,10 @@ is of course immediately visible, but for example a passed deadline is of the frame, for example." :group 'org-faces) -(defface org-headline-done ;; originally copied from font-lock-string-face - (org-compatible-face nil - '((((class color) (min-colors 16) (background light)) (:foreground "RosyBrown")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightSalmon")) - (((class color) (min-colors 8) (background light)) (:bold nil)))) +(defface org-headline-done ;Copied from `font-lock-string-face' + '((((class color) (min-colors 16) (background light)) (:foreground "RosyBrown")) + (((class color) (min-colors 16) (background dark)) (:foreground "LightSalmon")) + (((class color) (min-colors 8) (background light)) (:bold nil))) "Face used to indicate that a headline is DONE. This face is only used if `org-fontify-done-headline' is set. If applies to the part of the headline after the DONE keyword." @@ -388,11 +284,7 @@ determines if it is a foreground or a background color." (string :tag "Color") (sexp :tag "Face"))))) -(defface org-priority ;; originally copied from font-lock-string-face - (org-compatible-face 'font-lock-keyword-face - '((((class color) (min-colors 16) (background light)) (:foreground "RosyBrown")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightSalmon")) - (t (:italic t)))) +(defface org-priority '((t :inherit font-lock-keyword-face)) "Face used for priority cookies." :group 'org-faces) @@ -421,18 +313,17 @@ determines if it is a foreground or a background color." (setq org-tags-special-faces-re (concat ":\\(" (mapconcat 'car value "\\|") "\\):")))) -(defface org-checkbox - (org-compatible-face 'bold - '((t (:bold t)))) +(defface org-checkbox '((t :inherit bold)) "Face for checkboxes." :group 'org-faces) +(defface org-checkbox-statistics-todo '((t (:inherit org-todo))) + "Face used for unfinished checkbox statistics." + :group 'org-faces) -(org-copy-face 'org-todo 'org-checkbox-statistics-todo - "Face used for unfinished checkbox statistics.") - -(org-copy-face 'org-done 'org-checkbox-statistics-done - "Face used for finished checkbox statistics.") +(defface org-checkbox-statistics-done '((t (:inherit org-done))) + "Face used for finished checkbox statistics." + :group 'org-faces) (defcustom org-tag-faces nil "Faces for specific tags. @@ -454,44 +345,32 @@ changes." (string :tag "Foreground color") (sexp :tag "Face"))))) -(defface org-table ;; originally copied from font-lock-function-name-face - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) (:foreground "Blue1")) - (((class color) (min-colors 88) (background dark)) (:foreground "LightSkyBlue")) - (((class color) (min-colors 16) (background light)) (:foreground "Blue")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightSkyBlue")) - (((class color) (min-colors 8) (background light)) (:foreground "blue")) - (((class color) (min-colors 8) (background dark))))) +(defface org-table ;Copied from `font-lock-function-name-face' + '((((class color) (min-colors 88) (background light)) (:foreground "Blue1")) + (((class color) (min-colors 88) (background dark)) (:foreground "LightSkyBlue")) + (((class color) (min-colors 16) (background light)) (:foreground "Blue")) + (((class color) (min-colors 16) (background dark)) (:foreground "LightSkyBlue")) + (((class color) (min-colors 8) (background light)) (:foreground "blue")) + (((class color) (min-colors 8) (background dark)))) "Face used for tables." :group 'org-faces) (defface org-formula - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) (:foreground "Firebrick")) - (((class color) (min-colors 88) (background dark)) (:foreground "chocolate1")) - (((class color) (min-colors 8) (background light)) (:foreground "red")) - (((class color) (min-colors 8) (background dark)) (:foreground "red")) - (t (:bold t :italic t)))) + '((((class color) (min-colors 88) (background light)) (:foreground "Firebrick")) + (((class color) (min-colors 88) (background dark)) (:foreground "chocolate1")) + (((class color) (min-colors 8) (background light)) (:foreground "red")) + (((class color) (min-colors 8) (background dark)) (:foreground "red")) + (t (:bold t :italic t))) "Face for formulas." :group 'org-faces) -(defface org-code - (org-compatible-face 'shadow - '((((class color grayscale) (min-colors 88) (background light)) - (:foreground "grey50")) - (((class color grayscale) (min-colors 88) (background dark)) - (:foreground "grey70")) - (((class color) (min-colors 8) (background light)) - (:foreground "green")) - (((class color) (min-colors 8) (background dark)) - (:foreground "yellow")))) +(defface org-code '((t :inherit shadow)) "Face for fixed-width text like code snippets." :group 'org-faces :version "22.1") -(defface org-meta-line - (org-compatible-face 'font-lock-comment-face nil) - "Face for meta lines startin with \"#+\"." +(defface org-meta-line '((t :inherit font-lock-comment-face)) + "Face for meta lines starting with \"#+\"." :group 'org-faces :version "22.1") @@ -510,60 +389,37 @@ changes." follows a #+DATE:, #+AUTHOR: or #+EMAIL: keyword." :group 'org-faces) -(defface org-document-info-keyword - (org-compatible-face 'shadow - '((((class color grayscale) (min-colors 88) (background light)) - (:foreground "grey50")) - (((class color grayscale) (min-colors 88) (background dark)) - (:foreground "grey70")) - (((class color) (min-colors 8) (background light)) - (:foreground "green")) - (((class color) (min-colors 8) (background dark)) - (:foreground "yellow")))) +(defface org-document-info-keyword '((t :inherit shadow)) "Face for #+TITLE:, #+AUTHOR:, #+EMAIL: and #+DATE: keywords." :group 'org-faces) -(defface org-block - (org-compatible-face 'shadow - '((((class color grayscale) (min-colors 88) (background light)) - (:foreground "grey50")) - (((class color grayscale) (min-colors 88) (background dark)) - (:foreground "grey70")) - (((class color) (min-colors 8) (background light)) - (:foreground "green")) - (((class color) (min-colors 8) (background dark)) - (:foreground "yellow")))) - "Face text in #+begin ... #+end blocks." +(defface org-block '((t :inherit shadow)) + "Face text in #+begin ... #+end blocks. +For source-blocks `org-src-block-faces' takes precedence. +See also `org-fontify-quote-and-verse-blocks'." :group 'org-faces - :version "22.1") + :version "26.1") -(defface org-block-background '((t ())) - "Face used for the source block background.") - -(org-copy-face 'org-meta-line 'org-block-begin-line - "Face used for the line delimiting the begin of source blocks.") - -(org-copy-face 'org-meta-line 'org-block-end-line - "Face used for the line delimiting the end of source blocks.") - -(defface org-verbatim - (org-compatible-face 'shadow - '((((class color grayscale) (min-colors 88) (background light)) - (:foreground "grey50" :underline t)) - (((class color grayscale) (min-colors 88) (background dark)) - (:foreground "grey70" :underline t)) - (((class color) (min-colors 8) (background light)) - (:foreground "green" :underline t)) - (((class color) (min-colors 8) (background dark)) - (:foreground "yellow" :underline t)))) - "Face for fixed-with text like code snippets." +(defface org-block-begin-line '((t (:inherit org-meta-line))) + "Face used for the line delimiting the begin of source blocks." + :group 'org-faces) + +(defface org-block-end-line '((t (:inherit org-block-begin-line))) + "Face used for the line delimiting the end of source blocks." + :group 'org-faces) + +(defface org-verbatim '((t (:inherit shadow))) + "Face for fixed-with text like code snippets" :group 'org-faces :version "22.1") -(org-copy-face 'org-block 'org-quote - "Face for #+BEGIN_QUOTE ... #+END_QUOTE blocks.") -(org-copy-face 'org-block 'org-verse - "Face for #+BEGIN_VERSE ... #+END_VERSE blocks.") +(defface org-quote '((t (:inherit org-block))) + "Face for #+BEGIN_QUOTE ... #+END_QUOTE blocks." + :group 'org-faces) + +(defface org-verse '((t (:inherit org-block))) + "Face for #+BEGIN_VERSE ... #+END_VERSE blocks." + :group 'org-faces) (defcustom org-fontify-quote-and-verse-blocks nil "Non-nil means, add a special face to #+begin_quote and #+begin_verse block. @@ -573,64 +429,64 @@ content of these blocks will still be treated as Org syntax." :version "24.1" :type 'boolean) -(defface org-clock-overlay ;; copied from secondary-selection - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) - (:background "yellow1")) - (((class color) (min-colors 88) (background dark)) - (:background "SkyBlue4")) - (((class color) (min-colors 16) (background light)) - (:background "yellow")) - (((class color) (min-colors 16) (background dark)) - (:background "SkyBlue4")) - (((class color) (min-colors 8)) - (:background "cyan" :foreground "black")) - (t (:inverse-video t)))) +(defface org-clock-overlay ;Copied from `secondary-selection' + '((((class color) (min-colors 88) (background light)) + (:background "LightGray" :foreground "black")) + (((class color) (min-colors 88) (background dark)) + (:background "SkyBlue4" :foreground "white")) + (((class color) (min-colors 16) (background light)) + (:background "gray" :foreground "black")) + (((class color) (min-colors 16) (background dark)) + (:background "SkyBlue4" :foreground "white")) + (((class color) (min-colors 8)) + (:background "cyan" :foreground "black")) + (t (:inverse-video t))) "Basic face for displaying the secondary selection." :group 'org-faces) -(defface org-agenda-structure ;; originally copied from font-lock-function-name-face - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) (:foreground "Blue1")) - (((class color) (min-colors 88) (background dark)) (:foreground "LightSkyBlue")) - (((class color) (min-colors 16) (background light)) (:foreground "Blue")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightSkyBlue")) - (((class color) (min-colors 8)) (:foreground "blue" :bold t)) - (t (:bold t)))) +(defface org-agenda-structure ;Copied from `font-lock-function-name-face' + '((((class color) (min-colors 88) (background light)) (:foreground "Blue1")) + (((class color) (min-colors 88) (background dark)) (:foreground "LightSkyBlue")) + (((class color) (min-colors 16) (background light)) (:foreground "Blue")) + (((class color) (min-colors 16) (background dark)) (:foreground "LightSkyBlue")) + (((class color) (min-colors 8)) (:foreground "blue" :bold t)) + (t (:bold t))) "Face used in agenda for captions and dates." :group 'org-faces) -(org-copy-face 'org-agenda-structure 'org-agenda-date - "Face used in agenda for normal days.") +(defface org-agenda-date '((t (:inherit org-agenda-structure))) + "Face used in agenda for normal days." + :group 'org-faces) -(org-copy-face 'org-agenda-date 'org-agenda-date-today +(defface org-agenda-date-today + '((t (:inherit org-agenda-date :weight bold :italic t))) "Face used in agenda for today." - :weight 'bold :italic 't) + :group 'org-faces) -(org-copy-face 'secondary-selection 'org-agenda-clocking - "Face marking the current clock item in the agenda.") +(defface org-agenda-clocking '((t (:inherit secondary-selection))) + "Face marking the current clock item in the agenda." + :group 'org-faces) -(org-copy-face 'org-agenda-date 'org-agenda-date-weekend +(defface org-agenda-date-weekend '((t (:inherit org-agenda-date :weight bold))) "Face used in agenda for weekend days. -See the variable `org-agenda-weekend-days' for a definition of which days -belong to the weekend." - :weight 'bold) + +See the variable `org-agenda-weekend-days' for a definition of +which days belong to the weekend." + :group 'org-faces) (defface org-scheduled - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) (:foreground "DarkGreen")) - (((class color) (min-colors 88) (background dark)) (:foreground "PaleGreen")) - (((class color) (min-colors 8)) (:foreground "green")) - (t (:bold t :italic t)))) + '((((class color) (min-colors 88) (background light)) (:foreground "DarkGreen")) + (((class color) (min-colors 88) (background dark)) (:foreground "PaleGreen")) + (((class color) (min-colors 8)) (:foreground "green")) + (t (:bold t :italic t))) "Face for items scheduled for a certain day." :group 'org-faces) (defface org-scheduled-today - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) (:foreground "DarkGreen")) - (((class color) (min-colors 88) (background dark)) (:foreground "PaleGreen")) - (((class color) (min-colors 8)) (:foreground "green")) - (t (:bold t :italic t)))) + '((((class color) (min-colors 88) (background light)) (:foreground "DarkGreen")) + (((class color) (min-colors 88) (background dark)) (:foreground "PaleGreen")) + (((class color) (min-colors 8)) (:foreground "green")) + (t (:bold t :italic t))) "Face for items scheduled for a certain day." :group 'org-faces) @@ -641,22 +497,20 @@ belong to the weekend." :group 'org-faces) (defface org-scheduled-previously - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) (:foreground "Firebrick")) - (((class color) (min-colors 88) (background dark)) (:foreground "chocolate1")) - (((class color) (min-colors 8) (background light)) (:foreground "red")) - (((class color) (min-colors 8) (background dark)) (:foreground "red" :bold t)) - (t (:bold t)))) + '((((class color) (min-colors 88) (background light)) (:foreground "Firebrick")) + (((class color) (min-colors 88) (background dark)) (:foreground "chocolate1")) + (((class color) (min-colors 8) (background light)) (:foreground "red")) + (((class color) (min-colors 8) (background dark)) (:foreground "red" :bold t)) + (t (:bold t))) "Face for items scheduled previously, and not yet done." :group 'org-faces) (defface org-upcoming-deadline - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) (:foreground "Firebrick")) - (((class color) (min-colors 88) (background dark)) (:foreground "chocolate1")) - (((class color) (min-colors 8) (background light)) (:foreground "red")) - (((class color) (min-colors 8) (background dark)) (:foreground "red" :bold t)) - (t (:bold t)))) + '((((class color) (min-colors 88) (background light)) (:foreground "Firebrick")) + (((class color) (min-colors 88) (background dark)) (:foreground "chocolate1")) + (((class color) (min-colors 8) (background light)) (:foreground "red")) + (((class color) (min-colors 8) (background dark)) (:foreground "red" :bold t)) + (t (:bold t))) "Face for items scheduled previously, and not yet done." :group 'org-faces) @@ -666,7 +520,7 @@ belong to the weekend." (0.0 . default)) "Faces for showing deadlines in the agenda. This is a list of cons cells. The cdr of each cell is a face to be used, -and it can also just be like (:foreground \"yellow\"). +and it can also just be like \\='(:foreground \"yellow\"). Each car is a fraction of the head-warning time that must have passed for this the face in the cdr to be used for display. The numbers must be given in descending order. The head-warning time is normally taken @@ -686,65 +540,61 @@ month and 365.24 days for a year)." (sexp :tag "Face")))) (defface org-agenda-restriction-lock - (org-compatible-face nil - '((((class color) (min-colors 88) (background light)) (:background "#eeeeee")) - (((class color) (min-colors 88) (background dark)) (:background "#1C1C1C")) - (((class color) (min-colors 16) (background light)) (:background "#eeeeee")) - (((class color) (min-colors 16) (background dark)) (:background "#1C1C1C")) - (((class color) (min-colors 8)) (:background "cyan" :foreground "black")) - (t (:inverse-video t)))) + '((((class color) (min-colors 88) (background light)) (:background "#eeeeee")) + (((class color) (min-colors 88) (background dark)) (:background "#1C1C1C")) + (((class color) (min-colors 16) (background light)) (:background "#eeeeee")) + (((class color) (min-colors 16) (background dark)) (:background "#1C1C1C")) + (((class color) (min-colors 8)) (:background "cyan" :foreground "black")) + (t (:inverse-video t))) "Face for showing the agenda restriction lock." :group 'org-faces) -(defface org-agenda-filter-tags - (org-compatible-face 'mode-line nil) +(defface org-agenda-filter-tags '((t :inherit mode-line)) "Face for tag(s) in the mode-line when filtering the agenda." :group 'org-faces) -(defface org-agenda-filter-regexp - (org-compatible-face 'mode-line nil) +(defface org-agenda-filter-regexp '((t :inherit mode-line)) "Face for regexp(s) in the mode-line when filtering the agenda." :group 'org-faces) -(defface org-agenda-filter-category - (org-compatible-face 'mode-line nil) - "Face for categories(s) in the mode-line when filtering the agenda." +(defface org-agenda-filter-category '((t :inherit mode-line)) + "Face for categories in the mode-line when filtering the agenda." :group 'org-faces) -(defface org-time-grid ;; originally copied from font-lock-variable-name-face - (org-compatible-face nil - '((((class color) (min-colors 16) (background light)) (:foreground "DarkGoldenrod")) - (((class color) (min-colors 16) (background dark)) (:foreground "LightGoldenrod")) - (((class color) (min-colors 8)) (:foreground "yellow" :weight light)))) +(defface org-agenda-filter-effort '((t :inherit mode-line)) + "Face for effort in the mode-line when filtering the agenda." + :group 'org-faces) + +(defface org-time-grid ;Copied from `font-lock-variable-name-face' + '((((class color) (min-colors 16) (background light)) (:foreground "DarkGoldenrod")) + (((class color) (min-colors 16) (background dark)) (:foreground "LightGoldenrod")) + (((class color) (min-colors 8)) (:foreground "yellow" :weight light))) "Face used for time grids." :group 'org-faces) -(org-copy-face 'org-time-grid 'org-agenda-current-time - "Face used to show the current time in the time grid.") +(defface org-agenda-current-time '((t (:inherit org-time-grid))) + "Face used to show the current time in the time grid." + :group 'org-faces) -(defface org-agenda-diary - (org-compatible-face 'default nil) +(defface org-agenda-diary '((t :inherit default)) "Face used for agenda entries that come from the Emacs diary." :group 'org-faces) -(defface org-agenda-calendar-event - (org-compatible-face 'default nil) +(defface org-agenda-calendar-event '((t :inherit default)) "Face used to show events and appointments in the agenda." :group 'org-faces) -(defface org-agenda-calendar-sexp - (org-compatible-face 'default nil) +(defface org-agenda-calendar-sexp '((t :inherit default)) "Face used to show events computed from a S-expression." :group 'org-faces) (defconst org-level-faces '(org-level-1 org-level-2 org-level-3 org-level-4 - org-level-5 org-level-6 org-level-7 org-level-8 - )) + org-level-5 org-level-6 org-level-7 org-level-8)) (defcustom org-n-level-faces (length org-level-faces) "The number of different faces to be used for headlines. -Org-mode defines 8 different headline faces, so this can be at most 8. +Org mode defines 8 different headline faces, so this can be at most 8. If it is less than 8, the level-1 face gets re-used for level N+1 etc." :type 'integer :group 'org-faces) @@ -777,25 +627,26 @@ level org-n-level-faces" :version "24.4" :package-version '(Org . "8.0")) -(defface org-macro - (org-compatible-face 'org-latex-and-related nil) +(defface org-macro '((t :inherit org-latex-and-related)) "Face for macros." :group 'org-faces :version "24.4" :package-version '(Org . "8.0")) -(defface org-tag-group - (org-compatible-face 'org-tag nil) +(defface org-tag-group '((t :inherit org-tag)) "Face for group tags." :group 'org-faces :version "24.4" :package-version '(Org . "8.0")) -(org-copy-face 'mode-line 'org-mode-line-clock - "Face used for clock display in mode line.") -(org-copy-face 'mode-line 'org-mode-line-clock-overrun +(defface org-mode-line-clock '((t (:inherit mode-line))) + "Face used for clock display in mode line." + :group 'org-faces) + +(defface org-mode-line-clock-overrun + '((t (:inherit mode-line :background "red"))) "Face used for clock display for overrun tasks in mode line." - :background "red") + :group 'org-faces) (provide 'org-faces) diff --git a/lisp/org/org-feed.el b/lisp/org/org-feed.el index cfb4b4f7e3..6ebe5ecf5d 100644 --- a/lisp/org/org-feed.el +++ b/lisp/org/org-feed.el @@ -1,4 +1,4 @@ -;;; org-feed.el --- Add RSS feed items to Org files +;;; org-feed.el --- Add RSS feed items to Org files -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. ;; @@ -24,11 +24,11 @@ ;; ;;; Commentary: ;; -;; This module allows entries to be created and changed in an Org-mode -;; file triggered by items in an RSS feed. The basic functionality is -;; geared toward simply adding new items found in a feed as outline nodes -;; to an Org file. Using hooks, arbitrary actions can be triggered for -;; new or changed items. +;; This module allows entries to be created and changed in an Org mode +;; file triggered by items in an RSS feed. The basic functionality +;; is geared toward simply adding new items found in a feed as +;; outline nodes to an Org file. Using hooks, arbitrary actions can +;; be triggered for new or changed items. ;; ;; Selecting feeds and target locations ;; ------------------------------------ @@ -77,10 +77,8 @@ ;; org-feed.el needs to keep track of which feed items have been handled ;; before, so that they will not be handled again. For this, org-feed.el ;; stores information in a special drawer, FEEDSTATUS, under the heading -;; that received the input of the feed. You should add FEEDSTATUS -;; to your list of drawers in the files that receive feed input: +;; that received the input of the feed. ;; -;; #+DRAWERS: PROPERTIES CLOCK LOGBOOK RESULTS FEEDSTATUS ;; ;; Acknowledgments ;; --------------- @@ -102,8 +100,8 @@ (declare-function xml-substitute-special "xml" (string)) (declare-function org-capture-escaped-% "org-capture" ()) +(declare-function org-capture-expand-embedded-elisp "org-capture" (&optional mark)) (declare-function org-capture-inside-embedded-elisp-p "org-capture" ()) -(declare-function org-capture-expand-embedded-elisp "org-capture" ()) (defgroup org-feed nil "Options concerning RSS feeds as inputs for Org files." @@ -117,7 +115,9 @@ to create inbox items in Org. Each entry is a list with the following items: name a custom name for this feed URL the Feed URL -file the target Org file where entries should be listed +file the target Org file where entries should be listed, when + nil the target becomes the current buffer (may be an + indirect buffer) each time the feed update is invoked headline the headline under which entries should be listed Additional arguments can be given using keyword-value pairs. Many of these @@ -216,10 +216,7 @@ Here are the keyword-value pair allows in `org-feed-alist'. (defcustom org-feed-drawer "FEEDSTATUS" "The name of the drawer for feed status information. Each feed may also specify its own drawer name using the `:drawer' -parameter in `org-feed-alist'. -Note that in order to make these drawers behave like drawers, they must -be added to the variable `org-drawers' or configured with a #+DRAWERS -line." +parameter in `org-feed-alist'." :group 'org-feed :type '(string :tag "Drawer Name")) @@ -300,7 +297,8 @@ it can be a list structured like an entry in `org-feed-alist'." (catch 'exit (let ((name (car feed)) (url (nth 1 feed)) - (file (nth 2 feed)) + (file (or (nth 2 feed) (buffer-file-name (or (buffer-base-buffer) + (current-buffer))))) (headline (nth 3 feed)) (filter (nth 1 (memq :filter feed))) (formatter (nth 1 (memq :formatter feed))) @@ -315,7 +313,7 @@ it can be a list structured like an entry in `org-feed-alist'." (parse-entry (or (nth 1 (memq :parse-entry feed)) 'org-feed-parse-rss-entry)) feed-buffer inbox-pos new-formatted - entries old-status status new changed guid-alist e guid olds) + entries old-status status new changed guid-alist guid olds) (setq feed-buffer (org-feed-get-feed url)) (unless (and feed-buffer (bufferp (get-buffer feed-buffer))) (error "Cannot get feed %s" name)) @@ -407,8 +405,8 @@ it can be a list structured like an entry in `org-feed-alist'." ;; Normalize the visibility of the inbox tree (goto-char inbox-pos) - (hide-subtree) - (show-children) + (outline-hide-subtree) + (org-show-children) (org-cycle-hide-drawers 'children) ;; Hooks and messages @@ -442,7 +440,7 @@ it can be a list structured like an entry in `org-feed-alist'." (if (stringp feed) (setq feed (assoc feed org-feed-alist))) (unless feed (error "No such feed in `org-feed-alist")) - (org-pop-to-buffer-same-window + (pop-to-buffer-same-window (org-feed-update feed 'retrieve-only)) (goto-char (point-min))) @@ -477,8 +475,7 @@ This will find DRAWER and extract the alist." "Write the feed STATUS to DRAWER in entry at POS." (save-excursion (goto-char pos) - (let ((end (save-excursion (org-end-of-subtree t t))) - guid) + (let ((end (save-excursion (org-end-of-subtree t t)))) (if (re-search-forward (concat "^[ \t]*:" drawer ":[ \t]*\n") end t) (progn @@ -514,66 +511,77 @@ ENTRY is a property list. This function adds a `:formatted-for-org' property and returns the full property list. If that property is already present, nothing changes." (require 'org-capture) - (if formatter - (funcall formatter entry) - (let (dlines time escape name tmp - v-h v-t v-T v-u v-U v-a) - (setq dlines (org-split-string (or (plist-get entry :description) "???") - "\n") - v-h (or (plist-get entry :title) (car dlines) "???") - time (or (if (plist-get entry :pubDate) - (org-read-date t t (plist-get entry :pubDate))) - (current-time)) - v-t (format-time-string (org-time-stamp-format nil nil) time) - v-T (format-time-string (org-time-stamp-format t nil) time) - v-u (format-time-string (org-time-stamp-format nil t) time) - v-U (format-time-string (org-time-stamp-format t t) time) - v-a (if (setq tmp (or (and (plist-get entry :guid-permalink) - (plist-get entry :guid)) - (plist-get entry :link))) - (concat "[[" tmp "]]\n") - "")) + (if formatter (funcall formatter entry) + (let* ((dlines + (org-split-string (or (plist-get entry :description) "???") + "\n")) + (time (or (if (plist-get entry :pubDate) + (org-read-date t t (plist-get entry :pubDate))) + (current-time))) + (v-h (or (plist-get entry :title) (car dlines) "???")) + (v-t (format-time-string (org-time-stamp-format nil nil) time)) + (v-T (format-time-string (org-time-stamp-format t nil) time)) + (v-u (format-time-string (org-time-stamp-format nil t) time)) + (v-U (format-time-string (org-time-stamp-format t t) time)) + (v-a (let ((tmp (or (and (plist-get entry :guid-permalink) + (plist-get entry :guid)) + (plist-get entry :link)))) + (if tmp (format "[[%s]]\n" tmp ) "")))) (with-temp-buffer - (insert template) - - ;; Simple %-escapes - ;; before embedded elisp to support simple %-escapes as - ;; arguments for embedded elisp - (goto-char (point-min)) - (while (re-search-forward "%\\([a-zA-Z]+\\)" nil t) - (unless (org-capture-escaped-%) - (setq name (match-string 1) - escape (org-capture-inside-embedded-elisp-p)) - (cond - ((member name '("h" "t" "T" "u" "U" "a")) - (setq tmp (symbol-value (intern (concat "v-" name))))) - ((setq tmp (plist-get entry (intern (concat ":" name)))) - (save-excursion - (save-match-data - (beginning-of-line 1) - (when (looking-at - (concat "^\\([ \t]*\\)%" name "[ \t]*$")) - (setq tmp (org-feed-make-indented-block - tmp (org-get-indentation)))))))) - (when tmp - ;; escape string delimiters `"' when inside %() embedded lisp - (when escape - (setq tmp (replace-regexp-in-string "\"" "\\\\\"" tmp))) - (replace-match tmp t t)))) - - ;; %() embedded elisp - (org-capture-expand-embedded-elisp) - - (decode-coding-string - (buffer-string) (detect-coding-region (point-min) (point-max) t)))))) + (insert template) + (goto-char (point-min)) + + ;; Mark %() embedded elisp for later evaluation. + (org-capture-expand-embedded-elisp 'mark) + + ;; Simple %-escapes. `org-capture-escaped-%' may modify + ;; buffer and cripple match-data. Use markers instead. + (while (re-search-forward "%\\([a-zA-Z]+\\)" nil t) + (let ((key (match-string 1)) + (beg (copy-marker (match-beginning 0))) + (end (copy-marker (match-end 0)))) + (unless (org-capture-escaped-%) + (delete-region beg end) + (set-marker beg nil) + (set-marker end nil) + (let ((replacement + (pcase key + ("h" v-h) + ("t" v-t) + ("T" v-T) + ("u" v-u) + ("U" v-U) + ("a" v-a) + (name + (let ((v (plist-get entry (intern (concat ":" name))))) + (save-excursion + (save-match-data + (beginning-of-line) + (if (looking-at + (concat "^\\([ \t]*\\)%" name "[ \t]*$")) + (org-feed-make-indented-block + v (org-get-indentation)) + v)))))))) + (when replacement + (insert + ;; Escape string delimiters within embedded lisp. + (if (org-capture-inside-embedded-elisp-p) + (replace-regexp-in-string "\"" "\\\\\"" replacement) + replacement))))))) + + ;; %() embedded elisp + (org-capture-expand-embedded-elisp) + + (decode-coding-string + (buffer-string) (detect-coding-region (point-min) (point-max) t)))))) (defun org-feed-make-indented-block (s n) "Add indentation of N spaces to a multiline string S." (if (not (string-match "\n" s)) s (mapconcat 'identity - (org-split-string s "\n") - (concat "\n" (make-string n ?\ ))))) + (org-split-string s "\n") + (concat "\n" (make-string n ?\ ))))) (defun org-feed-skip-http-headers (buffer) "Remove HTTP headers from BUFFER, and return it. @@ -605,6 +613,7 @@ Assumes headers are indeed present!" "Parse BUFFER for RSS feed entries. Returns a list of entries, with each entry a property list, containing the properties `:guid' and `:item-full-text'." + (require 'xml) (let ((case-fold-search t) entries beg end item guid entry) (with-current-buffer buffer @@ -616,7 +625,7 @@ containing the properties `:guid' and `:item-full-text'." (match-beginning 0))) (setq item (buffer-substring beg end) guid (if (string-match ".*?>\\(.*?\\)" item) - (org-match-string-no-properties 1 item))) + (xml-substitute-special (match-string-no-properties 1 item)))) (setq entry (list :guid guid :item-full-text item)) (push entry entries) (widen) diff --git a/lisp/org/org-footnote.el b/lisp/org/org-footnote.el index 553f124042..af03fbfe7b 100644 --- a/lisp/org/org-footnote.el +++ b/lisp/org/org-footnote.el @@ -1,4 +1,4 @@ -;;; org-footnote.el --- Footnote support in Org and elsewhere +;;; org-footnote.el --- Footnote support in Org -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. ;; @@ -24,72 +24,68 @@ ;; ;;; Commentary: -;; This file contains the code dealing with footnotes in Org-mode. -;; The code can also be used in arbitrary text modes to provide -;; footnotes. Compared to Steven L Baur's footnote.el it provides -;; better support for resuming editing. It is less configurable than -;; Steve's code, though. +;; This file contains the code dealing with footnotes in Org mode. ;;; Code: -(eval-when-compile - (require 'cl)) +;;;; Declarations + +(require 'cl-lib) (require 'org-macs) (require 'org-compat) -(declare-function message-point-in-header-p "message" ()) +(declare-function org-at-comment-p "org" ()) +(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-edit-footnote-reference "org-src" ()) +(declare-function org-element-at-point "org-element" ()) +(declare-function org-element-class "org-element" (datum &optional parent)) +(declare-function org-element-context "org-element" (&optional element)) +(declare-function org-element-lineage "org-element" (blob &optional types with-self)) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-type "org-element" (element)) (declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading)) (declare-function org-fill-paragraph "org" (&optional justify)) -(declare-function org-icompleting-read "org" (&rest args)) -(declare-function org-id-uuid "org-id" ()) (declare-function org-in-block-p "org" (names)) -(declare-function org-in-commented-line "org" ()) -(declare-function org-in-indented-comment-line "org" ()) (declare-function org-in-regexp "org" (re &optional nlines visually)) (declare-function org-in-verbatim-emphasis "org" ()) (declare-function org-inside-LaTeX-fragment-p "org" ()) (declare-function org-inside-latex-macro-p "org" ()) (declare-function org-mark-ring-push "org" (&optional pos buffer)) (declare-function org-show-context "org" (&optional key)) -(declare-function org-trim "org" (s)) -(declare-function org-skip-whitespace "org" ()) +(declare-function org-trim "org" (s &optional keep-lead)) (declare-function outline-next-heading "outline") -(declare-function org-skip-whitespace "org" ()) -(defvar org-outline-regexp-bol) ; defined in org.el -(defvar org-odd-levels-only) ; defined in org.el +(defvar electric-indent-mode) +(defvar org-blank-before-new-entry) ; defined in org.el (defvar org-bracket-link-regexp) ; defined in org.el -(defvar message-cite-prefix-regexp) ; defined in message.el -(defvar message-signature-separator) ; defined in message.el +(defvar org-complex-heading-regexp) ; defined in org.el +(defvar org-odd-levels-only) ; defined in org.el +(defvar org-outline-regexp) ; defined in org.el +(defvar org-outline-regexp-bol) ; defined in org.el + + +;;;; Constants (defconst org-footnote-re - ;; Only [1]-like footnotes are closed in this regexp, as footnotes - ;; from other types might contain square brackets (i.e. links) in - ;; their definition. - ;; - ;; `org-re' is used for regexp compatibility with XEmacs. - (concat "\\[\\(?:" - ;; Match inline footnotes. - (org-re "fn:\\([-_[:word:]]+\\)?:\\|") - ;; Match other footnotes. - "\\(?:\\([0-9]+\\)\\]\\)\\|" - (org-re "\\(fn:[-_[:word:]]+\\)") - "\\)") - "Regular expression for matching footnotes.") - -(defconst org-footnote-definition-re - (org-re "^\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]") - "Regular expression matching the definition of a footnote.") - -(defconst org-footnote-forbidden-blocks - '("ascii" "beamer" "comment" "example" "html" "latex" "odt" "src") + "\\[fn:\\(?:\\(?1:[-_[:word:]]+\\)?\\(:\\)\\|\\(?1:[-_[:word:]]+\\)\\]\\)" + "Regular expression for matching footnotes. +Match group 1 contains footnote's label. It is nil for anonymous +footnotes. Match group 2 is non-nil only when footnote is +inline, i.e., it contains its own definition.") + +(defconst org-footnote-definition-re "^\\[fn:\\([-_[:word:]]+\\)\\]" + "Regular expression matching the definition of a footnote. +Match group 1 contains definition's label.") + +(defconst org-footnote-forbidden-blocks '("comment" "example" "export" "src") "Names of blocks where footnotes are not allowed.") + +;;;; Customization + (defgroup org-footnote nil - "Footnotes in Org-mode." + "Footnotes in Org mode." :tag "Org Footnote" :group 'org) @@ -106,25 +102,21 @@ the notes. However, by hand you may place definitions *anywhere*. If this is a string, during export, all subtrees starting with -this heading will be ignored." - :group 'org-footnote - :type '(choice - (string :tag "Collect footnotes under heading") - (const :tag "Define footnotes locally" nil))) +this heading will be ignored. -(defcustom org-footnote-tag-for-non-org-mode-files "Footnotes:" - "Tag marking the beginning of footnote section. -The Org footnote engine can be used in arbitrary text files as well -as in Org-mode. Outside Org mode, new footnotes are always placed at -the end of the file. When you normalize the notes, any line containing -only this tag will be removed, a new one will be inserted at the end -of the file, followed by the collected and normalized footnotes. +If you don't use the customize interface to change this variable, +you will need to run the following command after the change: -If you don't want any tag in such buffers, set this variable to nil." + `\\[universal-argument] \\[org-element-cache-reset]'" :group 'org-footnote + :initialize 'custom-initialize-default + :set (lambda (var val) + (set var val) + (when (fboundp 'org-element-cache-reset) + (org-element-cache-reset 'all))) :type '(choice - (string :tag "Collect footnotes under tag") - (const :tag "Don't use a tag" nil))) + (string :tag "Collect footnotes under heading") + (const :tag "Define footnotes locally" nil))) (defcustom org-footnote-define-inline nil "Non-nil means define footnotes inline, at reference location. @@ -143,15 +135,13 @@ t Create unique labels of the form [fn:1], [fn:2], etc. confirm Like t, but let the user edit the created value. The label can be removed from the minibuffer to create an anonymous footnote. -random Automatically generate a unique, random label. -plain Automatically create plain number labels like [1]." +random Automatically generate a unique, random label." :group 'org-footnote :type '(choice (const :tag "Prompt for label" nil) (const :tag "Create automatic [fn:N]" t) (const :tag "Offer automatic [fn:N] for editing" confirm) - (const :tag "Create a random label" random) - (const :tag "Create automatic [N]" plain))) + (const :tag "Create a random label" random))) (defcustom org-footnote-auto-adjust nil "Non-nil means automatically adjust footnotes after insert/delete. @@ -179,23 +169,19 @@ extracted will be filled again." :group 'org-footnote :type 'boolean) + +;;;; Predicates + (defun org-footnote-in-valid-context-p () "Is point in a context where footnotes are allowed?" (save-match-data - (not (or (org-in-commented-line) - (org-in-indented-comment-line) + (not (or (org-at-comment-p) (org-inside-LaTeX-fragment-p) ;; Avoid literal example. (org-in-verbatim-emphasis) (save-excursion (beginning-of-line) (looking-at "[ \t]*:[ \t]+")) - ;; Avoid cited text and headers in message-mode. - (and (derived-mode-p 'message-mode) - (or (save-excursion - (beginning-of-line) - (looking-at message-cite-prefix-regexp)) - (message-point-in-header-p))) ;; Avoid forbidden blocks. (org-in-block-p org-footnote-forbidden-blocks))))) @@ -208,13 +194,9 @@ positions, and the definition, when inlined." (or (looking-at org-footnote-re) (org-in-regexp org-footnote-re) (save-excursion (re-search-backward org-footnote-re nil t))) - (/= (match-beginning 0) (point-at-bol))) + (/= (match-beginning 0) (line-beginning-position))) (let* ((beg (match-beginning 0)) - (label (or (org-match-string-no-properties 2) - (org-match-string-no-properties 3) - ;; Anonymous footnotes don't have labels - (and (match-string 1) - (concat "fn:" (org-match-string-no-properties 1))))) + (label (match-string-no-properties 1)) ;; Inline footnotes don't end at (match-end 0) as ;; `org-footnote-re' stops just after the second colon. ;; Find the real ending with `scan-sexps', so Org doesn't @@ -222,7 +204,8 @@ positions, and the definition, when inlined." (end (ignore-errors (scan-sexps beg 1)))) ;; Point is really at a reference if it's located before true ;; ending of the footnote. - (when (and end (< (point) end) + (when (and end + (< (point) end) ;; Verify match isn't a part of a link. (not (save-excursion (goto-char beg) @@ -234,16 +217,17 @@ positions, and the definition, when inlined." (not (org-inside-latex-macro-p))) (list label beg end ;; Definition: ensure this is an inline footnote first. - (and (or (not label) (match-string 1)) - (org-trim (buffer-substring-no-properties - (match-end 0) (1- end))))))))) + (and (match-end 2) + (org-trim + (buffer-substring-no-properties + (match-end 0) (1- end))))))))) (defun org-footnote-at-definition-p () "Is point within a footnote definition? This matches only pure definitions like [1] or [fn:name] at the beginning of a line. It does not match references like -[fn:name:definition], where the footnote text is included and +\[fn:name:definition], where the footnote text is included and defined locally. The return value will be nil if not at a footnote definition, and @@ -259,26 +243,224 @@ otherwise." (concat org-outline-regexp-bol "\\|^\\([ \t]*\n\\)\\{2,\\}") nil t)))) (when (re-search-backward org-footnote-definition-re lim t) - (let ((label (org-match-string-no-properties 1)) + (let ((label (match-string-no-properties 1)) (beg (match-beginning 0)) (beg-def (match-end 0)) - ;; In message-mode, do not search after signature. - (end (let ((bound (and (derived-mode-p 'message-mode) - (save-excursion - (goto-char (point-max)) - (re-search-backward - message-signature-separator nil t))))) - (if (progn - (end-of-line) - (re-search-forward - (concat org-outline-regexp-bol "\\|" - org-footnote-definition-re "\\|" - "^\\([ \t]*\n\\)\\{2,\\}") bound 'move)) - (match-beginning 0) - (point))))) + (end (if (progn + (end-of-line) + (re-search-forward + (concat org-outline-regexp-bol "\\|" + org-footnote-definition-re "\\|" + "^\\([ \t]*\n\\)\\{2,\\}") nil 'move)) + (match-beginning 0) + (point)))) (list label beg end (org-trim (buffer-substring-no-properties beg-def end))))))))) + +;;;; Internal functions + +(defun org-footnote--allow-reference-p () + "Non-nil when a footnote reference can be inserted at point." + ;; XXX: This is similar to `org-footnote-in-valid-context-p' but + ;; more accurate and usually faster, except in some corner cases. + ;; It may replace it after doing proper benchmarks as it would be + ;; used in fontification. + (unless (bolp) + (let* ((context (org-element-context)) + (type (org-element-type context))) + (cond + ;; No footnote reference in attributes. + ((let ((post (org-element-property :post-affiliated context))) + (and post (< (point) post))) + nil) + ;; Paragraphs and blank lines at top of document are fine. + ((memq type '(nil paragraph))) + ;; So are contents of verse blocks. + ((eq type 'verse-block) + (and (>= (point) (org-element-property :contents-begin context)) + (< (point) (org-element-property :contents-end context)))) + ;; In an headline or inlinetask, point must be either on the + ;; heading itself or on the blank lines below. + ((memq type '(headline inlinetask)) + (or (not (org-at-heading-p)) + (and (save-excursion + (beginning-of-line) + (and (let ((case-fold-search t)) + (not (looking-at-p "\\*+ END[ \t]*$"))) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp)))) + (match-beginning 4) + (>= (point) (match-beginning 4)) + (or (not (match-beginning 5)) + (< (point) (match-beginning 5)))))) + ;; White spaces after an object or blank lines after an element + ;; are OK. + ((>= (point) + (save-excursion (goto-char (org-element-property :end context)) + (skip-chars-backward " \r\t\n") + (if (eq (org-element-class context) 'object) (point) + (1+ (line-beginning-position 2)))))) + ;; Other elements are invalid. + ((eq (org-element-class context) 'element) nil) + ;; Just before object is fine. + ((= (point) (org-element-property :begin context))) + ;; Within recursive object too, but not in a link. + ((eq type 'link) nil) + ((let ((cbeg (org-element-property :contents-begin context)) + (cend (org-element-property :contents-end context))) + (and cbeg (>= (point) cbeg) (<= (point) cend)))))))) + +(defun org-footnote--clear-footnote-section () + "Remove all footnote sections in buffer and create a new one. +New section is created at the end of the buffer, before any file +local variable definition. Leave point within the new section." + (when org-footnote-section + (goto-char (point-min)) + (let ((regexp + (format "^\\*+ +%s[ \t]*$" + (regexp-quote org-footnote-section)))) + (while (re-search-forward regexp nil t) + (delete-region + (match-beginning 0) + (progn (org-end-of-subtree t t) + (if (not (eobp)) (point) + (org-footnote--goto-local-insertion-point) + (skip-chars-forward " \t\n") + (if (eobp) (point) (line-beginning-position))))))) + (goto-char (point-max)) + (org-footnote--goto-local-insertion-point) + (when (and (cdr (assq 'heading org-blank-before-new-entry)) + (zerop (save-excursion (org-back-over-empty-lines)))) + (insert "\n")) + (insert "* " org-footnote-section "\n"))) + +(defun org-footnote--set-label (label) + "Set label of footnote at point to string LABEL. +Assume point is at the beginning of the reference or definition +to rename." + (forward-char 4) + (cond ((eq (char-after) ?:) (insert label)) + ((looking-at "\\([-_[:word:]]+\\)") (replace-match label nil nil nil 1)) + (t nil))) + +(defun org-footnote--collect-references (&optional anonymous) + "Collect all labelled footnote references in current buffer. + +Return an alist where associations follow the pattern + + (LABEL MARKER TOP-LEVEL SIZE) + +with + + LABEL the label of the of the definition, + MARKER a marker pointing to its beginning, + TOP-LEVEL a boolean, nil when the footnote is contained within + another one, + SIZE the length of the inline definition, in characters, + or nil for non-inline references. + +When optional ANONYMOUS is non-nil, also collect anonymous +references. In such cases, LABEL is nil. + +References are sorted according to a deep-reading order." + (org-with-wide-buffer + (goto-char (point-min)) + (let ((regexp (if anonymous org-footnote-re "\\[fn:[-_[:word:]]+[]:]")) + references nested) + (save-excursion + (while (re-search-forward regexp nil t) + ;; Ignore definitions. + (unless (and (eq (char-before) ?\]) + (= (line-beginning-position) (match-beginning 0))) + ;; Ensure point is within the reference before parsing it. + (backward-char) + (let ((object (org-element-context))) + (when (eq (org-element-type object) 'footnote-reference) + (let* ((label (org-element-property :label object)) + (begin (org-element-property :begin object)) + (size + (and (eq (org-element-property :type object) 'inline) + (- (org-element-property :contents-end object) + (org-element-property :contents-begin object))))) + (let ((d (org-element-lineage object '(footnote-definition)))) + (push (list label (copy-marker begin) (not d) size) + references) + (when d + ;; Nested references are stored in alist NESTED. + ;; Associations there follow the pattern + ;; + ;; (DEFINITION-LABEL . REFERENCES) + (let* ((def-label (org-element-property :label d)) + (labels (assoc def-label nested))) + (if labels (push label (cdr labels)) + (push (list def-label label) nested))))))))))) + ;; Sort the list of references. Nested footnotes have priority + ;; over top-level ones. + (letrec ((ordered nil) + (add-reference + (lambda (ref allow-nested) + (when (or allow-nested (nth 2 ref)) + (push ref ordered) + (dolist (r (mapcar (lambda (l) (assoc l references)) + (reverse + (cdr (assoc (nth 0 ref) nested))))) + (funcall add-reference r t)))))) + (dolist (r (reverse references) (nreverse ordered)) + (funcall add-reference r nil)))))) + +(defun org-footnote--collect-definitions (&optional delete) + "Collect all footnote definitions in current buffer. + +Return an alist where associations follow the pattern + + (LABEL . DEFINITION) + +with LABEL and DEFINITION being, respectively, the label and the +definition of the footnote, as strings. + +When optional argument DELETE is non-nil, delete the definition +while collecting them." + (org-with-wide-buffer + (goto-char (point-min)) + (let (definitions seen) + (while (re-search-forward org-footnote-definition-re nil t) + (backward-char) + (let ((element (org-element-at-point))) + (let ((label (org-element-property :label element))) + (when (and (eq (org-element-type element) 'footnote-definition) + (not (member label seen))) + (push label seen) + (let* ((beg (progn + (goto-char (org-element-property :begin element)) + (skip-chars-backward " \r\t\n") + (if (bobp) (point) (line-beginning-position 2)))) + (end (progn + (goto-char (org-element-property :end element)) + (skip-chars-backward " \r\t\n") + (line-beginning-position 2))) + (def (org-trim (buffer-substring-no-properties beg end)))) + (push (cons label def) definitions) + (when delete (delete-region beg end))))))) + definitions))) + +(defun org-footnote--goto-local-insertion-point () + "Find insertion point for footnote, just before next outline heading. +Assume insertion point is within currently accessible part of the buffer." + (org-with-limited-levels (outline-next-heading)) + ;; Skip file local variables. See `modify-file-local-variable'. + (when (eobp) + (let ((case-fold-search t)) + (re-search-backward "^[ \t]*# +Local Variables:" + (max (- (point-max) 3000) (point-min)) + t))) + (skip-chars-backward " \t\n") + (forward-line) + (unless (bolp) (insert "\n"))) + + +;;;; Navigation + (defun org-footnote-get-next-reference (&optional label backward limit) "Return complete reference of the next footnote. @@ -289,7 +471,7 @@ the buffer position bounding the search. Return value is a list like those provided by `org-footnote-at-reference-p'. If no footnote is found, return nil." (save-excursion - (let* ((label-fmt (if label (format "\\[%s[]:]" label) org-footnote-re))) + (let* ((label-fmt (if label (format "\\[fn:%s[]:]" label) org-footnote-re))) (catch 'exit (while t (unless (funcall (if backward #'re-search-backward #'re-search-forward) @@ -313,59 +495,54 @@ If no footnote is found, return nil." (unless (re-search-forward org-footnote-re limit t) (goto-char origin) (throw 'exit nil)) - ;; Beware: with [1]-like footnotes point will be just after + ;; Beware: with non-inline footnotes point will be just after ;; the closing square bracket. (backward-char) (cond ((setq ref (org-footnote-at-reference-p)) (throw 'exit ref)) - ;; Definition: also grab the last square bracket, only - ;; matched in `org-footnote-re' for [1]-like footnotes. + ;; Definition: also grab the last square bracket, matched in + ;; `org-footnote-re' for non-inline footnotes. ((save-match-data (org-footnote-at-definition-p)) (let ((end (match-end 0))) (throw 'exit (list nil (match-beginning 0) - (if (eq (char-before end) 93) end (1+ end))))))))))) + (if (eq (char-before end) ?\]) end (1+ end))))))))))) -(defun org-footnote-get-definition (label) - "Return label, boundaries and definition of the footnote LABEL." - (let* ((label (regexp-quote (org-footnote-normalize-label label))) - (re (format "^\\[%s\\]\\|.\\[%s:" label label)) - pos) - (save-excursion - (save-restriction - (when (or (re-search-forward re nil t) - (and (goto-char (point-min)) - (re-search-forward re nil t)) - (and (progn (widen) t) - (goto-char (point-min)) - (re-search-forward re nil t))) - (let ((refp (org-footnote-at-reference-p))) - (cond - ((and (nth 3 refp) refp)) - ((org-footnote-at-definition-p))))))))) - -(defun org-footnote-goto-definition (label) +(defun org-footnote-goto-definition (label &optional location) "Move point to the definition of the footnote LABEL. -Return a non-nil value when a definition has been found." + +LOCATION, when non-nil specifies the buffer position of the +definition. + +Throw an error if there is no definition or if it cannot be +reached from current narrowed part of buffer. Return a non-nil +value if point was successfully moved." (interactive "sLabel: ") - (org-mark-ring-push) - (let ((def (org-footnote-get-definition label))) - (if (not def) - (error "Cannot find definition of footnote %s" label) - (goto-char (nth 1 def)) - (looking-at (format "\\[%s\\]\\|\\[%s:" label label)) - (goto-char (match-end 0)) - (org-show-context 'link-search) - (when (derived-mode-p 'org-mode) - (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'.")) - t))) + (let* ((label (org-footnote-normalize-label label)) + (def-start (or location (nth 1 (org-footnote-get-definition label))))) + (cond + ((not def-start) + (user-error "Cannot find definition of footnote %s" label)) + ((or (> def-start (point-max)) (< def-start (point-min))) + (user-error "Definition is outside narrowed part of buffer"))) + (org-mark-ring-push) + (goto-char def-start) + (looking-at (format "\\[fn:%s[]:] ?" (regexp-quote label))) + (goto-char (match-end 0)) + (org-show-context 'link-search) + (when (derived-mode-p 'org-mode) + (message "%s" (substitute-command-keys + "Edit definition and go back with \ +`\\[org-mark-ring-goto]' or, if unique, with `\\[org-ctrl-c-ctrl-c]'."))) + t)) (defun org-footnote-goto-previous-reference (label) "Find the first closest (to point) reference of footnote with label LABEL." (interactive "sLabel: ") (org-mark-ring-push) - (let* ((label (org-footnote-normalize-label label)) ref) + (let ((label (org-footnote-normalize-label label)) + ref) (save-excursion (setq ref (or (org-footnote-get-next-reference label t) (org-footnote-get-next-reference label) @@ -379,62 +556,74 @@ Return a non-nil value when a definition has been found." (goto-char (nth 1 ref)) (org-show-context 'link-search)))) + +;;;; Getters + (defun org-footnote-normalize-label (label) - "Return LABEL as an appropriate string." - (cond - ((numberp label) (number-to-string label)) - ((equal "" label) nil) - ((not (string-match "^[0-9]+$\\|^fn:" label)) - (concat "fn:" label)) - (t label))) - -(defun org-footnote-all-labels (&optional with-defs) - "Return list with all defined foot labels used in the buffer. - -If WITH-DEFS is non-nil, also associate the definition to each -label. The function will then return an alist whose key is label -and value definition." - (let* (rtn - (push-to-rtn - (function - ;; Depending on WITH-DEFS, store label or (label . def) of - ;; footnote reference/definition given as argument in RTN. - (lambda (el) - (let ((lbl (car el))) - (push (if with-defs (cons lbl (nth 3 el)) lbl) rtn)))))) - (save-excursion - (save-restriction - (widen) - ;; Find all labels found in definitions. - (goto-char (point-min)) - (let (def) - (while (re-search-forward org-footnote-definition-re nil t) - (when (setq def (org-footnote-at-definition-p)) - (funcall push-to-rtn def)))) - ;; Find all labels found in references. - (goto-char (point-min)) - (let (ref) - (while (setq ref (org-footnote-get-next-reference)) - (goto-char (nth 2 ref)) - (and (car ref) ; ignore anonymous footnotes - (not (funcall (if with-defs #'assoc #'member) (car ref) rtn)) - (funcall push-to-rtn ref)))))) - rtn)) + "Return LABEL without \"fn:\" prefix. +If LABEL is the empty string or constituted of white spaces only, +return nil instead." + (pcase (org-trim label) + ("" nil) + ((pred (string-prefix-p "fn:")) (substring label 3)) + (_ label))) + +(defun org-footnote-get-definition (label) + "Return label, boundaries and definition of the footnote LABEL." + (let* ((label (regexp-quote (org-footnote-normalize-label label))) + (re (format "^\\[fn:%s\\]\\|.\\[fn:%s:" label label))) + (org-with-wide-buffer + (goto-char (point-min)) + (catch 'found + (while (re-search-forward re nil t) + (let* ((datum (progn (backward-char) (org-element-context))) + (type (org-element-type datum))) + (when (memq type '(footnote-definition footnote-reference)) + (throw 'found + (list + label + (org-element-property :begin datum) + (org-element-property :end datum) + (let ((cbeg (org-element-property :contents-begin datum))) + (if (not cbeg) "" + (replace-regexp-in-string + "[ \t\n]*\\'" + "" + (buffer-substring-no-properties + cbeg + (org-element-property :contents-end datum)))))))))) + nil)))) + +(defun org-footnote-all-labels () + "List all defined footnote labels used throughout the buffer. +This function ignores narrowing, if any." + (org-with-wide-buffer + (goto-char (point-min)) + (let (all) + (while (re-search-forward org-footnote-re nil t) + (backward-char) + (let ((context (org-element-context))) + (when (memq (org-element-type context) + '(footnote-definition footnote-reference)) + (let ((label (org-element-property :label context))) + (when label (cl-pushnew label all :test #'equal)))))) + all))) (defun org-footnote-unique-label (&optional current) "Return a new unique footnote label. -The function returns the first \"fn:N\" or \"N\" label that is -currently not used. +The function returns the first numeric label currently unused. Optional argument CURRENT is the list of labels active in the buffer." - (unless current (setq current (org-footnote-all-labels))) - (let ((fmt (if (eq org-footnote-auto-label 'plain) "%d" "fn:%d")) - (cnt 1)) - (while (member (format fmt cnt) current) - (incf cnt)) - (format fmt cnt))) + (let ((current (or current (org-footnote-all-labels)))) + (let ((count 1)) + (while (member (number-to-string count) current) + (cl-incf count)) + (number-to-string count)))) + + +;;;; Adding, Deleting Footnotes (defun org-footnote-new () "Insert a new footnote. @@ -442,343 +631,66 @@ This command prompts for a label. If this is a label referencing an existing label, only insert the label. If the footnote label is empty or new, let the user edit the definition of the footnote." (interactive) - (unless (org-footnote-in-valid-context-p) - (error "Cannot insert a footnote here")) - (let* ((lbls (and (not (equal org-footnote-auto-label 'random)) - (org-footnote-all-labels))) - (propose (and (not (equal org-footnote-auto-label 'random)) - (org-footnote-unique-label lbls))) + (unless (org-footnote--allow-reference-p) + (user-error "Cannot insert a footnote here")) + (let* ((all (org-footnote-all-labels)) (label - (org-footnote-normalize-label - (cond - ((member org-footnote-auto-label '(t plain)) - propose) - ((equal org-footnote-auto-label 'random) - (require 'org-id) - (substring (org-id-uuid) 0 8)) - (t - (org-icompleting-read - "Label (leave empty for anonymous): " - (mapcar 'list lbls) nil nil - (if (eq org-footnote-auto-label 'confirm) propose nil))))))) - (cond - ((bolp) (error "Cannot create a footnote reference at left margin")) - ((not label) - (insert "[fn:: ]") - (backward-char 1)) - ((member label lbls) - (insert "[" label "]") - (message "New reference to existing note")) - (org-footnote-define-inline - (insert "[" label ": ]") - (backward-char 1) - (org-footnote-auto-adjust-maybe)) - (t - (insert "[" label "]") - (org-footnote-create-definition label) - (org-footnote-auto-adjust-maybe))))) - -(defvar org-blank-before-new-entry) ; silence byte-compiler + (if (eq org-footnote-auto-label 'random) + (format "%x" (random most-positive-fixnum)) + (org-footnote-normalize-label + (let ((propose (org-footnote-unique-label all))) + (if (eq org-footnote-auto-label t) propose + (completing-read + "Label (leave empty for anonymous): " + (mapcar #'list all) nil nil + (and (eq org-footnote-auto-label 'confirm) propose)))))))) + (cond ((not label) + (insert "[fn::]") + (backward-char 1)) + ((member label all) + (insert "[fn:" label "]") + (message "New reference to existing note")) + (org-footnote-define-inline + (insert "[fn:" label ":]") + (backward-char 1) + (org-footnote-auto-adjust-maybe)) + (t + (insert "[fn:" label "]") + (let ((p (org-footnote-create-definition label))) + ;; `org-footnote-goto-definition' needs to be called + ;; after `org-footnote-auto-adjust-maybe'. Otherwise + ;; both label and location of the definition are lost. + ;; On the contrary, it needs to be called before + ;; `org-edit-footnote-reference' so that the remote + ;; editing buffer can display the correct label. + (if (ignore-errors (org-footnote-goto-definition label p)) + (org-footnote-auto-adjust-maybe) + ;; Definition was created outside current scope: edit + ;; it remotely. + (org-footnote-auto-adjust-maybe) + (org-edit-footnote-reference))))))) + (defun org-footnote-create-definition (label) - "Start the definition of a footnote with label LABEL." - (interactive "sLabel: ") + "Start the definition of a footnote with label LABEL. +Return buffer position at the beginning of the definition. This +function doesn't move point." (let ((label (org-footnote-normalize-label label)) - electric-indent-mode) ;; Prevent wrong indentation - (cond - ;; In an Org file. - ((derived-mode-p 'org-mode) - ;; If `org-footnote-section' is defined, find it, or create it - ;; at the end of the buffer. - (when org-footnote-section - (goto-char (point-min)) - (let ((re (concat "^\\*+[ \t]+" org-footnote-section "[ \t]*$"))) - (unless (or (re-search-forward re nil t) - (and (progn (widen) t) - (re-search-forward re nil t))) - (goto-char (point-max)) - (skip-chars-backward " \t\r\n") - (unless (bolp) (newline)) - ;; Insert new section. Separate it from the previous one - ;; with a blank line, unless `org-blank-before-new-entry' - ;; explicitly says no. - (when (and (cdr (assq 'heading org-blank-before-new-entry)) - (zerop (save-excursion (org-back-over-empty-lines)))) - (insert "\n")) - (insert "* " org-footnote-section "\n")))) - ;; Move to the end of this entry (which may be - ;; `org-footnote-section' or the current one). - (org-footnote-goto-local-insertion-point) - (org-show-context 'link-search)) - (t - ;; In a non-Org file. Search for footnote tag, or create it if - ;; specified (at the end of buffer, or before signature if in - ;; Message mode). Set point after any definition already there. - (let ((tag (and org-footnote-tag-for-non-org-mode-files - (concat "^" (regexp-quote - org-footnote-tag-for-non-org-mode-files) - "[ \t]*$"))) - (max (if (and (derived-mode-p 'message-mode) - (goto-char (point-max)) - (re-search-backward - message-signature-separator nil t)) - (progn - ;; Ensure one blank line separates last - ;; footnote from signature. - (beginning-of-line) - (open-line 2) - (point-marker)) - (point-max-marker)))) - (set-marker-insertion-type max t) - (goto-char max) - ;; Check if the footnote tag is defined but missing. In this - ;; case, insert it, before any footnote or one blank line - ;; after any previous text. - (when (and tag (not (re-search-backward tag nil t))) - (skip-chars-backward " \t\r\n") - (while (re-search-backward org-footnote-definition-re nil t)) - (unless (bolp) (newline 2)) - (insert org-footnote-tag-for-non-org-mode-files "\n\n")) - ;; Remove superfluous white space and clear marker. - (goto-char max) - (skip-chars-backward " \t\r\n") - (delete-region (point) max) - (unless (bolp) (newline)) - (set-marker max nil)))) - ;; Insert footnote label. - (when (zerop (org-back-over-empty-lines)) (newline)) - (insert "[" label "] \n") - (backward-char) - ;; Only notify user about next possible action when in an Org - ;; buffer, as the bindings may have different meanings otherwise. - (when (derived-mode-p 'org-mode) - (message - "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'.")))) - -;;;###autoload -(defun org-footnote-action (&optional special) - "Do the right thing for footnotes. - -When at a footnote reference, jump to the definition. - -When at a definition, jump to the references if they exist, offer -to create them otherwise. - -When neither at definition or reference, create a new footnote, -interactively. - -With prefix arg SPECIAL, offer additional commands in a menu." - (interactive "P") - (let (tmp c) - (cond - (special - (message "Footnotes: [s]ort | [r]enumber fn:N | [S]=r+s |->[n]umeric | [d]elete") - (setq c (read-char-exclusive)) - (cond - ((eq c ?s) (org-footnote-normalize 'sort)) - ((eq c ?r) (org-footnote-renumber-fn:N)) - ((eq c ?S) - (org-footnote-renumber-fn:N) - (org-footnote-normalize 'sort)) - ((eq c ?n) (org-footnote-normalize)) - ((eq c ?d) (org-footnote-delete)) - (t (error "No such footnote command %c" c)))) - ((setq tmp (org-footnote-at-reference-p)) - (cond - ;; Anonymous footnote: move point at the beginning of its - ;; definition. - ((not (car tmp)) - (goto-char (nth 1 tmp)) - (forward-char 5)) - ;; A definition exists: move to it. - ((ignore-errors (org-footnote-goto-definition (car tmp)))) - ;; No definition exists: offer to create it. - ((yes-or-no-p (format "No definition for %s. Create one? " (car tmp))) - (org-footnote-create-definition (car tmp))))) - ((setq tmp (org-footnote-at-definition-p)) - (org-footnote-goto-previous-reference (car tmp))) - (t (org-footnote-new))))) - -;;;###autoload -(defun org-footnote-normalize (&optional sort-only) - "Collect the footnotes in various formats and normalize them. - -This finds the different sorts of footnotes allowed in Org, and -normalizes them to the usual [N] format. - -When SORT-ONLY is set, only sort the footnote definitions into the -referenced sequence." - ;; This is based on Paul's function, but rewritten. - ;; - ;; Re-create `org-with-limited-levels', but not limited to Org - ;; buffers. - (let* ((limit-level - (and (boundp 'org-inlinetask-min-level) - org-inlinetask-min-level - (1- org-inlinetask-min-level))) - (nstars (and limit-level - (if org-odd-levels-only (1- (* limit-level 2)) - limit-level))) - (org-outline-regexp - (concat "\\*" (if nstars (format "\\{1,%d\\} " nstars) "+ "))) - (count 0) - ins-point ref ref-table) - (save-excursion - ;; 1. Find every footnote reference, extract the definition, and - ;; collect that data in REF-TABLE. If SORT-ONLY is nil, also - ;; normalize references. - (goto-char (point-min)) - (while (setq ref (org-footnote-get-next-reference)) - (let* ((lbl (car ref)) - (pos (nth 1 ref)) - ;; When footnote isn't anonymous, check if it's label - ;; (REF) is already stored in REF-TABLE. In that case, - ;; extract number used to identify it (MARKER). If - ;; footnote is unknown, increment the global counter - ;; (COUNT) to create an unused identifier. - (a (and lbl (assoc lbl ref-table))) - (marker (or (nth 1 a) (incf count))) - ;; Is the reference inline or pointing to an inline - ;; footnote? - (inlinep (or (stringp (nth 3 ref)) (nth 3 a)))) - ;; Replace footnote reference with [MARKER]. Maybe fill - ;; paragraph once done. If SORT-ONLY is non-nil, only move - ;; to the end of reference found to avoid matching it twice. - (if sort-only (goto-char (nth 2 ref)) - (delete-region (nth 1 ref) (nth 2 ref)) - (goto-char (nth 1 ref)) - (insert (format "[%d]" marker)) - (and inlinep - org-footnote-fill-after-inline-note-extraction - (org-fill-paragraph))) - ;; Add label (REF), identifier (MARKER), definition (DEF) - ;; type (INLINEP) and position (POS) to REF-TABLE if data - ;; was unknown. - (unless a - (let ((def (or (nth 3 ref) ; Inline definition. - (nth 3 (org-footnote-get-definition lbl))))) - (push (list lbl marker def - ;; Reference beginning position is a marker - ;; to preserve it during further buffer - ;; modifications. - inlinep (copy-marker pos)) ref-table))))) - ;; 2. Find and remove the footnote section, if any. Also - ;; determine where footnotes shall be inserted (INS-POINT). - (cond - ((and org-footnote-section (derived-mode-p 'org-mode)) - (goto-char (point-min)) - (if (re-search-forward - (concat "^\\*[ \t]+" (regexp-quote org-footnote-section) - "[ \t]*$") nil t) - (delete-region (match-beginning 0) (org-end-of-subtree t t))) - ;; A new footnote section is inserted by default at the end of - ;; the buffer. - (goto-char (point-max)) - (skip-chars-backward " \r\t\n") - (forward-line) - (unless (bolp) (newline))) - ;; No footnote section set: Footnotes will be added at the end - ;; of the section containing their first reference. - ((derived-mode-p 'org-mode)) - (t - ;; Remove any left-over tag in the buffer, if one is set up. - (when org-footnote-tag-for-non-org-mode-files - (let ((tag (concat "^" (regexp-quote - org-footnote-tag-for-non-org-mode-files) - "[ \t]*$"))) - (goto-char (point-min)) - (while (re-search-forward tag nil t) - (replace-match "") - (delete-region (point) (progn (forward-line) (point)))))) - ;; In Message mode, ensure footnotes are inserted before the - ;; signature. - (if (and (derived-mode-p 'message-mode) - (goto-char (point-max)) - (re-search-backward message-signature-separator nil t)) - (beginning-of-line) - (goto-char (point-max))))) - (setq ins-point (point-marker)) - ;; 3. Clean-up REF-TABLE. - (setq ref-table - (delq nil - (mapcar - (lambda (x) - (cond - ;; When only sorting, ignore inline footnotes. - ;; Also clear position marker. - ((and sort-only (nth 3 x)) - (set-marker (nth 4 x) nil) nil) - ;; No definition available: provide one. - ((not (nth 2 x)) - (append - (list (car x) (nth 1 x) - (format "DEFINITION NOT FOUND: %s" (car x))) - (nthcdr 3 x))) - (t x))) - ref-table))) - (setq ref-table (nreverse ref-table)) - ;; 4. Remove left-over definitions in the buffer. - (mapc (lambda (x) - (unless (nth 3 x) (org-footnote-delete-definitions (car x)))) - ref-table) - ;; 5. Insert the footnotes again in the buffer, at the - ;; appropriate spot. - (goto-char ins-point) - (cond - ;; No footnote: exit. - ((not ref-table)) - ;; Cases when footnotes should be inserted in one place. - ((or (not (derived-mode-p 'org-mode)) org-footnote-section) - ;; Insert again the section title, if any. Ensure that title, - ;; or the subsequent footnotes, will be separated by a blank - ;; lines from the rest of the document. In an Org buffer, - ;; separate section with a blank line, unless explicitly - ;; stated in `org-blank-before-new-entry'. - (if (not (derived-mode-p 'org-mode)) - (progn (skip-chars-backward " \t\n\r") - (delete-region (point) ins-point) - (unless (bolp) (newline)) - (when org-footnote-tag-for-non-org-mode-files - (insert "\n" org-footnote-tag-for-non-org-mode-files "\n"))) - (when (and (cdr (assq 'heading org-blank-before-new-entry)) - (zerop (save-excursion (org-back-over-empty-lines)))) - (insert "\n")) - (insert "* " org-footnote-section "\n")) - (set-marker ins-point nil) - ;; Insert the footnotes, separated by a blank line. - (insert - (mapconcat - (lambda (x) - ;; Clean markers. - (set-marker (nth 4 x) nil) - (format "\n[%s] %s" (nth (if sort-only 0 1) x) (nth 2 x))) - ref-table "\n")) - (unless (eobp) (insert "\n\n"))) - ;; Each footnote definition has to be inserted at the end of - ;; the section where its first reference belongs. - (t - (mapc - (lambda (x) - (let ((pos (nth 4 x))) - (goto-char pos) - ;; Clean marker. - (set-marker pos nil)) - (org-footnote-goto-local-insertion-point) - (insert (format "\n[%s] %s\n" - (if sort-only (car x) (nth 1 x)) - (nth 2 x)))) - ref-table)))))) - -(defun org-footnote-goto-local-insertion-point () - "Find insertion point for footnote, just before next outline heading." - (org-with-limited-levels (outline-next-heading)) - (or (bolp) (newline)) - (beginning-of-line 0) - (while (and (not (bobp)) (= (char-after) ?#)) - (beginning-of-line 0)) - (if (let ((case-fold-search t)) (looking-at "[ \t]*#\\+tblfm:")) (beginning-of-line 2)) - (end-of-line 1) - (skip-chars-backward "\n\r\t ") - (forward-line)) + electric-indent-mode) ; Prevent wrong indentation. + (org-with-wide-buffer + (cond + ((not org-footnote-section) (org-footnote--goto-local-insertion-point)) + ((save-excursion + (goto-char (point-min)) + (re-search-forward + (concat "^\\*+[ \t]+" (regexp-quote org-footnote-section) "[ \t]*$") + nil t)) + (goto-char (match-end 0)) + (forward-line) + (unless (bolp) (insert "\n"))) + (t (org-footnote--clear-footnote-section))) + (when (zerop (org-back-over-empty-lines)) (insert "\n")) + (insert "[fn:" label "] \n") + (line-beginning-position 0)))) (defun org-footnote-delete-references (label) "Delete every reference to footnote LABEL. @@ -789,7 +701,7 @@ Return the number of footnotes removed." (while (setq ref (org-footnote-get-next-reference label)) (goto-char (nth 1 ref)) (delete-region (nth 1 ref) (nth 2 ref)) - (incf nref)) + (cl-incf nref)) nref))) (defun org-footnote-delete-definitions (label) @@ -797,17 +709,21 @@ Return the number of footnotes removed." Return the number of footnotes removed." (save-excursion (goto-char (point-min)) - (let ((def-re (concat "^\\[" (regexp-quote label) "\\]")) + (let ((def-re (format "^\\[fn:%s\\]" (regexp-quote label))) (ndef 0)) (while (re-search-forward def-re nil t) - (let ((full-def (org-footnote-at-definition-p))) - (when full-def - ;; Remove the footnote, and all blank lines before it. - (goto-char (nth 1 full-def)) - (skip-chars-backward " \r\t\n") - (unless (bolp) (forward-line)) - (delete-region (point) (nth 2 full-def)) - (incf ndef)))) + (pcase (org-footnote-at-definition-p) + (`(,_ ,start ,end ,_) + ;; Remove the footnote, and all blank lines before it. + (delete-region (progn + (goto-char start) + (skip-chars-backward " \r\t\n") + (if (bobp) (point) (line-beginning-position 2))) + (progn + (goto-char end) + (skip-chars-backward " \r\t\n") + (if (bobp) (point) (line-beginning-position 2)))) + (cl-incf ndef)))) ndef))) (defun org-footnote-delete (&optional label) @@ -843,24 +759,165 @@ If LABEL is non-nil, delete that footnote instead." (message "%d definition(s) of and %d reference(s) of footnote %s removed" ndef nref label)))) + +;;;; Sorting, Renumbering, Normalizing + (defun org-footnote-renumber-fn:N () - "Renumber the simple footnotes like fn:17 into a sequence in the document." + "Order numbered footnotes into a sequence in the document." (interactive) - (let (map (n 0)) - (org-with-wide-buffer - (goto-char (point-min)) - (while (re-search-forward "\\[fn:\\([0-9]+\\)[]:]" nil t) - (save-excursion - (goto-char (match-beginning 0)) - ;; Ensure match is a footnote reference or definition. - (when (save-match-data (if (bolp) - (org-footnote-at-definition-p) - (org-footnote-at-reference-p))) - (let ((new-val (or (cdr (assoc (match-string 1) map)) - (number-to-string (incf n))))) - (unless (assoc (match-string 1) map) - (push (cons (match-string 1) new-val) map)) - (replace-match new-val nil nil nil 1)))))))) + (let ((references (org-footnote--collect-references))) + (unwind-protect + (let* ((c 0) + (references (cl-remove-if-not + (lambda (r) (string-match-p "\\`[0-9]+\\'" (car r))) + references)) + (alist (mapcar (lambda (l) (cons l (number-to-string (cl-incf c)))) + (delete-dups (mapcar #'car references))))) + (org-with-wide-buffer + ;; Re-number references. + (dolist (ref references) + (goto-char (nth 1 ref)) + (org-footnote--set-label (cdr (assoc (nth 0 ref) alist)))) + ;; Re-number definitions. + (goto-char (point-min)) + (while (re-search-forward "^\\[fn:\\([0-9]+\\)\\]" nil t) + (replace-match (or (cdr (assoc (match-string 1) alist)) + ;; Un-referenced definitions get + ;; higher numbers. + (number-to-string (cl-incf c))) + nil nil nil 1)))) + (dolist (r references) (set-marker (nth 1 r) nil))))) + +(defun org-footnote-sort () + "Rearrange footnote definitions in the current buffer. +Sort footnote definitions so they match order of footnote +references. Also relocate definitions at the end of their +relative section or within a single footnote section, according +to `org-footnote-section'. Inline definitions are ignored." + (let ((references (org-footnote--collect-references))) + (unwind-protect + (let ((definitions (org-footnote--collect-definitions 'delete))) + (org-with-wide-buffer + (org-footnote--clear-footnote-section) + ;; Insert footnote definitions at the appropriate location, + ;; separated by a blank line. Each definition is inserted + ;; only once throughout the buffer. + (let (inserted) + (dolist (cell references) + (let ((label (car cell)) + (nested (not (nth 2 cell))) + (inline (nth 3 cell))) + (unless (or (member label inserted) inline) + (push label inserted) + (unless (or org-footnote-section nested) + ;; If `org-footnote-section' is non-nil, or + ;; reference is nested, point is already at the + ;; correct position. Otherwise, move at the + ;; appropriate location within the section + ;; containing the reference. + (goto-char (nth 1 cell)) + (org-footnote--goto-local-insertion-point)) + (insert "\n" + (or (cdr (assoc label definitions)) + (format "[fn:%s] DEFINITION NOT FOUND." label)) + "\n")))) + ;; Insert un-referenced footnote definitions at the end. + (let ((unreferenced + (cl-remove-if (lambda (d) (member (car d) inserted)) + definitions))) + (dolist (d unreferenced) (insert "\n" (cdr d) "\n")))))) + ;; Clear dangling markers in the buffer. + (dolist (r references) (set-marker (nth 1 r) nil))))) + +(defun org-footnote-normalize () + "Turn every footnote in buffer into a numbered one." + (interactive) + (let ((references (org-footnote--collect-references 'anonymous))) + (unwind-protect + (let ((n 0) + (translations nil) + (definitions nil)) + (org-with-wide-buffer + ;; Update label for reference. We need to do this before + ;; clearing definitions in order to rename nested footnotes + ;; before they are deleted. + (dolist (cell references) + (let* ((label (car cell)) + (anonymous (not label)) + (new + (cond + ;; In order to differentiate anonymous + ;; references from regular ones, set their + ;; labels to integers, not strings. + (anonymous (setcar cell (cl-incf n))) + ((cdr (assoc label translations))) + (t (let ((l (number-to-string (cl-incf n)))) + (push (cons label l) translations) + l))))) + (goto-char (nth 1 cell)) ; Move to reference's start. + (org-footnote--set-label + (if anonymous (number-to-string new) new)) + (let ((size (nth 3 cell))) + ;; Transform inline footnotes into regular references + ;; and retain their definition for later insertion as + ;; a regular footnote definition. + (when size + (let ((def (concat + (format "[fn:%s] " new) + (org-trim + (substring + (delete-and-extract-region + (point) (+ (point) size 1)) + 1))))) + (push (cons (if anonymous new label) def) definitions) + (when org-footnote-fill-after-inline-note-extraction + (org-fill-paragraph))))))) + ;; Collect definitions. Update labels according to ALIST. + (let ((definitions + (nconc definitions + (org-footnote--collect-definitions 'delete))) + (inserted)) + (org-footnote--clear-footnote-section) + (dolist (cell references) + (let* ((label (car cell)) + (anonymous (integerp label)) + (pos (nth 1 cell))) + ;; Move to appropriate location, if required. When + ;; there is a footnote section or reference is + ;; nested, point is already at the expected location. + (unless (or org-footnote-section (not (nth 2 cell))) + (goto-char pos) + (org-footnote--goto-local-insertion-point)) + ;; Insert new definition once label is updated. + (unless (member label inserted) + (push label inserted) + (let ((stored (cdr (assoc label definitions))) + ;; Anonymous footnotes' label is already + ;; up-to-date. + (new (if anonymous label + (cdr (assoc label translations))))) + (insert "\n" + (cond + ((not stored) + (format "[fn:%s] DEFINITION NOT FOUND." new)) + (anonymous stored) + (t + (replace-regexp-in-string + "\\`\\[fn:\\(.*?\\)\\]" new stored nil nil 1))) + "\n"))))) + ;; Insert un-referenced footnote definitions at the end. + (let ((unreferenced + (cl-remove-if (lambda (d) (member (car d) inserted)) + definitions))) + (dolist (d unreferenced) + (insert "\n" + (replace-regexp-in-string + org-footnote-definition-re + (format "[fn:%d]" (cl-incf n)) + (cdr d)) + "\n")))))) + ;; Clear dangling markers. + (dolist (r references) (set-marker (nth 1 r) nil))))) (defun org-footnote-auto-adjust-maybe () "Renumber and/or sort footnotes according to user settings." @@ -868,14 +925,77 @@ If LABEL is non-nil, delete that footnote instead." (org-footnote-renumber-fn:N)) (when (memq org-footnote-auto-adjust '(t sort)) (let ((label (car (org-footnote-at-definition-p)))) - (org-footnote-normalize 'sort) + (org-footnote-sort) (when label (goto-char (point-min)) - (and (re-search-forward (concat "^\\[" (regexp-quote label) "\\]") + (and (re-search-forward (format "^\\[fn:%s\\]" (regexp-quote label)) nil t) (progn (insert " ") (just-one-space))))))) + +;;;; End-user interface + +;;;###autoload +(defun org-footnote-action (&optional special) + "Do the right thing for footnotes. + +When at a footnote reference, jump to the definition. + +When at a definition, jump to the references if they exist, offer +to create them otherwise. + +When neither at definition or reference, create a new footnote, +interactively if possible. + +With prefix arg SPECIAL, or when no footnote can be created, +offer additional commands in a menu." + (interactive "P") + (let* ((context (and (not special) (org-element-context))) + (type (org-element-type context))) + (cond + ;; On white space after element, insert a new footnote. + ((and context + (> (point) + (save-excursion + (goto-char (org-element-property :end context)) + (skip-chars-backward " \t") + (point)))) + (org-footnote-new)) + ((eq type 'footnote-reference) + (let ((label (org-element-property :label context))) + (cond + ;; Anonymous footnote: move point at the beginning of its + ;; definition. + ((not label) + (goto-char (org-element-property :contents-begin context))) + ;; Check if a definition exists: then move to it. + ((let ((p (nth 1 (org-footnote-get-definition label)))) + (when p (org-footnote-goto-definition label p)))) + ;; No definition exists: offer to create it. + ((yes-or-no-p (format "No definition for %s. Create one? " label)) + (let ((p (org-footnote-create-definition label))) + (or (ignore-errors (org-footnote-goto-definition label p)) + ;; Since definition was created outside current scope, + ;; edit it remotely. + (org-edit-footnote-reference))))))) + ((eq type 'footnote-definition) + (org-footnote-goto-previous-reference + (org-element-property :label context))) + ((or special (not (org-footnote--allow-reference-p))) + (message "Footnotes: [s]ort | [r]enumber fn:N | [S]=r+s | [n]ormalize | \ +\[d]elete") + (pcase (read-char-exclusive) + (?s (org-footnote-sort)) + (?r (org-footnote-renumber-fn:N)) + (?S (org-footnote-renumber-fn:N) + (org-footnote-sort)) + (?n (org-footnote-normalize)) + (?d (org-footnote-delete)) + (char (error "No such footnote command %c" char)))) + (t (org-footnote-new))))) + + (provide 'org-footnote) ;; Local variables: diff --git a/lisp/org/org-gnus.el b/lisp/org/org-gnus.el index 1d287a740b..b9d098957c 100644 --- a/lisp/org/org-gnus.el +++ b/lisp/org/org-gnus.el @@ -1,4 +1,4 @@ -;;; org-gnus.el --- Support for links to Gnus groups and messages from within Org-mode +;;; org-gnus.el --- Support for Links to Gnus Groups and Messages -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -25,8 +25,8 @@ ;; ;;; Commentary: -;; This file implements links to Gnus groups and messages from within Org-mode. -;; Org-mode loads this module by default - if this is not what you want, +;; This file implements links to Gnus groups and messages from within Org. +;; Org mode loads this module by default - if this is not what you want, ;; configure the variable `org-modules'. ;;; Code: @@ -36,18 +36,20 @@ (eval-when-compile (require 'gnus-sum)) ;; Declare external functions and variables + (declare-function message-fetch-field "message" (header &optional not-all)) (declare-function message-narrow-to-head-1 "message" nil) -;; The following line suppresses a compiler warning stemming from gnus-sum.el (declare-function gnus-summary-last-subject "gnus-sum" nil) +(declare-function nnvirtual-map-article "nnvirtual" (article)) + ;; Customization variables -(org-defvaralias 'org-usenet-links-prefer-google 'org-gnus-prefer-web-links) +(defvaralias 'org-usenet-links-prefer-google 'org-gnus-prefer-web-links) (defcustom org-gnus-prefer-web-links nil "If non-nil, `org-store-link' creates web links to Google groups or Gmane. -When nil, Gnus will be used for such links. -Using a prefix arg to the command \\[org-store-link] (`org-store-link') +\\When nil, Gnus will be used for such links. +Using a prefix argument to the command `\\[org-store-link]' (`org-store-link') negates this setting for the duration of the command." :group 'org-link-store :type 'boolean) @@ -72,20 +74,21 @@ this variable to t." :type 'boolean) ;; Install the link type -(org-add-link-type "gnus" 'org-gnus-open) -(add-hook 'org-store-link-functions 'org-gnus-store-link) +(org-link-set-parameters "gnus" :follow #'org-gnus-open :store #'org-gnus-store-link) ;; Implementation -;; FIXME: nnimap-group-overview-filename was removed from Gnus in -;; September 2010. Perhaps remove this function? (defun org-gnus-nnimap-cached-article-number (group server message-id) "Return cached article number (uid) of message in GROUP on SERVER. MESSAGE-ID is the message-id header field that identifies the message. If the uid is not cached, return nil." (with-temp-buffer - (let ((nov (nnimap-group-overview-filename group server))) - (when (file-exists-p nov) + (let ((nov (and (fboundp 'nnimap-group-overview-filename) + ;; nnimap-group-overview-filename was removed from + ;; Gnus in September 2010, and therefore should + ;; only be present in Emacs 23.1. + (nnimap-group-overview-filename group server)))) + (when (and nov (file-exists-p nov)) (mm-insert-file-contents nov) (set-buffer-modified-p nil) (goto-char (point-min)) @@ -104,7 +107,7 @@ Otherwise create a link to the group inside Gnus. If `org-store-link' was called with a prefix arg the meaning of `org-gnus-prefer-web-links' is reversed." (let ((unprefixed-group (replace-regexp-in-string "^[^:]+:" "" group))) - (if (and (string-match "^nntp" group) ;; Only for nntp groups + (if (and (string-prefix-p "nntp" group) ;; Only for nntp groups (org-xor current-prefix-arg org-gnus-prefer-web-links)) (concat (if (string-match "gmane" unprefixed-group) @@ -156,21 +159,17 @@ If `org-store-link' was called with a prefix arg the meaning of (header (with-current-buffer gnus-summary-buffer (gnus-summary-article-header))) (from (mail-header-from header)) - (message-id (org-remove-angle-brackets (mail-header-id header))) + (message-id (org-unbracket-string "<" ">" (mail-header-id header))) (date (org-trim (mail-header-date header))) - (date-ts (and date - (ignore-errors - (format-time-string - (org-time-stamp-format t) - (date-to-time date))))) - (date-ts-ia (and date - (ignore-errors - (format-time-string - (org-time-stamp-format t t) - (date-to-time date))))) (subject (copy-sequence (mail-header-subject header))) (to (cdr (assq 'To (mail-header-extra header)))) newsgroups x-no-archive desc link) + (cl-case (car (gnus-find-method-for-group gnus-newsgroup-name)) + (nnvirtual + (setq group (car (nnvirtual-map-article + (gnus-summary-article-number))))) + (nnir + (setq group (nnir-article-group (gnus-summary-article-number))))) ;; Remove text properties of subject string to avoid Emacs bug ;; #3506 (set-text-properties 0 (length subject) nil subject) @@ -183,11 +182,8 @@ If `org-store-link' was called with a prefix arg the meaning of (setq to (or to (gnus-fetch-original-field "To")) newsgroups (gnus-fetch-original-field "Newsgroups") x-no-archive (gnus-fetch-original-field "x-no-archive"))) - (org-store-link-props :type "gnus" :from from :subject subject + (org-store-link-props :type "gnus" :from from :date date :subject subject :message-id message-id :group group :to to) - (when date - (org-add-link-props :date date :date-timestamp date-ts - :date-timestamp-inactive date-ts-ia)) (setq desc (org-email-link-description) link (org-gnus-article-link group newsgroups message-id x-no-archive)) @@ -206,7 +202,7 @@ If `org-store-link' was called with a prefix arg the meaning of (let ((gcc (car (last (message-unquote-tokens (message-tokenize-header (mail-fetch-field "gcc" nil t) " ,"))))) - (id (org-remove-angle-brackets (mail-fetch-field "Message-ID"))) + (id (org-unbracket-string "<" ">" (mail-fetch-field "Message-ID"))) (to (mail-fetch-field "To")) (from (mail-fetch-field "From")) (subject (mail-fetch-field "Subject")) @@ -250,10 +246,8 @@ If `org-store-link' was called with a prefix arg the meaning of (require 'gnus) (funcall (cdr (assq 'gnus org-link-frame-setup))) (if gnus-other-frame-object (select-frame gnus-other-frame-object)) - (when group - (setq group (org-no-properties group))) - (when article - (setq article (org-no-properties article))) + (setq group (org-no-properties group)) + (setq article (org-no-properties article)) (cond ((and group article) (gnus-activate-group group) (condition-case nil diff --git a/lisp/org/org-habit.el b/lisp/org/org-habit.el index bbbf845d14..1f61565719 100644 --- a/lisp/org/org-habit.el +++ b/lisp/org/org-habit.el @@ -1,4 +1,4 @@ -;;; org-habit.el --- The habit tracking code for Org-mode +;;; org-habit.el --- The habit tracking code for Org -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. @@ -24,18 +24,16 @@ ;; ;;; Commentary: -;; This file contains the habit tracking code for Org-mode +;; This file contains the habit tracking code for Org mode ;;; Code: +(require 'cl-lib) (require 'org) (require 'org-agenda) -(eval-when-compile - (require 'cl)) - (defgroup org-habit nil - "Options concerning habit tracking in Org-mode." + "Options concerning habit tracking in Org mode." :tag "Org Habit" :group 'org-progress) @@ -165,16 +163,17 @@ Returns a list with the following elements: 2: Optional deadline (nil if not present) 3: If deadline, the repeater for the deadline, otherwise nil 4: A list of all the past dates this todo was mark closed + 5: Repeater type as a string This list represents a \"habit\" for the rest of this module." (save-excursion (if pom (goto-char pom)) - (assert (org-is-habit-p (point))) + (cl-assert (org-is-habit-p (point))) (let* ((scheduled (org-get-scheduled-time (point))) (scheduled-repeat (org-get-repeat org-scheduled-string)) (end (org-entry-end-position)) (habit-entry (org-no-properties (nth 4 (org-heading-components)))) - closed-dates deadline dr-days sr-days) + closed-dates deadline dr-days sr-days sr-type) (if scheduled (setq scheduled (time-to-days scheduled)) (error "Habit %s has no scheduled date" habit-entry)) @@ -182,7 +181,9 @@ This list represents a \"habit\" for the rest of this module." (error "Habit `%s' has no scheduled repeat period or has an incorrect one" habit-entry)) - (setq sr-days (org-habit-duration-to-days scheduled-repeat)) + (setq sr-days (org-habit-duration-to-days scheduled-repeat) + sr-type (progn (string-match "[\\.+]?\\+" scheduled-repeat) + (match-string-no-properties 0 scheduled-repeat))) (unless (> sr-days 0) (error "Habit %s scheduled repeat period is less than 1d" habit-entry)) (when (string-match "/\\([0-9]+[dwmy]\\)" scheduled-repeat) @@ -197,17 +198,33 @@ This list represents a \"habit\" for the rest of this module." (reversed org-log-states-order-reversed) (search (if reversed 're-search-forward 're-search-backward)) (limit (if reversed end (point))) - (count 0)) + (count 0) + (re (format + "^[ \t]*-[ \t]+\\(?:State \"%s\".*%s%s\\)" + (regexp-opt org-done-keywords) + org-ts-regexp-inactive + (let ((value (cdr (assq 'done org-log-note-headings)))) + (if (not value) "" + (concat "\\|" + (org-replace-escapes + (regexp-quote value) + `(("%d" . ,org-ts-regexp-inactive) + ("%D" . ,org-ts-regexp) + ("%s" . "\"\\S-+\"") + ("%S" . "\"\\S-+\"") + ("%t" . ,org-ts-regexp-inactive) + ("%T" . ,org-ts-regexp) + ("%u" . ".*?") + ("%U" . ".*?"))))))))) (unless reversed (goto-char end)) - (while (and (< count maxdays) - (funcall search (format "- State \"%s\".*\\[\\([^]]+\\)\\]" - (regexp-opt org-done-keywords)) - limit t)) + (while (and (< count maxdays) (funcall search re limit t)) (push (time-to-days - (org-time-string-to-time (match-string-no-properties 1))) + (org-time-string-to-time + (or (match-string-no-properties 1) + (match-string-no-properties 2)))) closed-dates) (setq count (1+ count)))) - (list scheduled sr-days deadline dr-days closed-dates)))) + (list scheduled sr-days deadline dr-days closed-dates sr-type)))) (defsubst org-habit-scheduled (habit) (nth 0 habit)) @@ -225,6 +242,8 @@ This list represents a \"habit\" for the rest of this module." (org-habit-scheduled-repeat habit))) (defsubst org-habit-done-dates (habit) (nth 4 habit)) +(defsubst org-habit-repeat-type (habit) + (nth 5 habit)) (defsubst org-habit-get-priority (habit &optional moment) "Determine the relative priority of a habit. @@ -265,7 +284,6 @@ Habits are assigned colors on the following basis: schedule's repeat period." (let* ((scheduled (or scheduled-days (org-habit-scheduled habit))) (s-repeat (org-habit-scheduled-repeat habit)) - (scheduled-end (+ scheduled (1- s-repeat))) (d-repeat (org-habit-deadline-repeat habit)) (deadline (if scheduled-days (+ scheduled-days (- d-repeat s-repeat)) @@ -289,13 +307,14 @@ Habits are assigned colors on the following basis: CURRENT gives the current time between STARTING and ENDING, for the purpose of drawing the graph. It need not be the actual current time." - (let* ((done-dates (sort (org-habit-done-dates habit) '<)) + (let* ((all-done-dates (sort (org-habit-done-dates habit) #'<)) + (done-dates all-done-dates) (scheduled (org-habit-scheduled habit)) (s-repeat (org-habit-scheduled-repeat habit)) (start (time-to-days starting)) (now (time-to-days current)) (end (time-to-days ending)) - (graph (make-string (1+ (- end start)) ?\ )) + (graph (make-string (1+ (- end start)) ?\s)) (index 0) last-done-date) (while (and done-dates (< (car done-dates) start)) @@ -304,18 +323,55 @@ current time." (while (< start end) (let* ((in-the-past-p (< start now)) (todayp (= start now)) - (donep (and done-dates - (= start (car done-dates)))) - (faces (if (and in-the-past-p - (not last-done-date) - (not (< scheduled now))) - '(org-habit-clear-face . org-habit-clear-future-face) - (org-habit-get-faces - habit start (and in-the-past-p - (if last-done-date - (+ last-done-date s-repeat) - scheduled)) - donep))) + (donep (and done-dates (= start (car done-dates)))) + (faces + (if (and in-the-past-p + (not last-done-date) + (not (< scheduled now))) + '(org-habit-clear-face . org-habit-clear-future-face) + (org-habit-get-faces + habit start + (and in-the-past-p + last-done-date + ;; Compute scheduled time for habit at the time + ;; START was current. + (let ((type (org-habit-repeat-type habit))) + (cond + ;; At the last done date, use current + ;; scheduling in all cases. + ((null done-dates) scheduled) + ((equal type ".+") (+ last-done-date s-repeat)) + ((equal type "+") + ;; Since LAST-DONE-DATE, each done mark + ;; shifted scheduled date by S-REPEAT. + (- scheduled (* (length done-dates) s-repeat))) + (t + ;; Compute the scheduled time after the + ;; first repeat. This is the closest time + ;; past FIRST-DONE which can reach SCHEDULED + ;; by a number of S-REPEAT hops. + ;; + ;; Then, play TODO state change history from + ;; the beginning in order to find current + ;; scheduled time. + (let* ((first-done (car all-done-dates)) + (s (let ((shift (mod (- scheduled first-done) + s-repeat))) + (+ (if (= shift 0) s-repeat shift) + first-done)))) + (if (= first-done last-done-date) s + (catch :exit + (dolist (done (cdr all-done-dates) s) + ;; Each repeat shifts S by any + ;; number of S-REPEAT hops it takes + ;; to get past DONE, with a minimum + ;; of one hop. + (cl-incf s (* (1+ (/ (max (- done s) 0) + s-repeat)) + s-repeat)) + (when (= done last-done-date) + (throw :exit s)))))))))) + donep))) markedp face) (if donep (let ((done-time (time-add @@ -348,7 +404,7 @@ current time." (defun org-habit-insert-consistency-graphs (&optional line) "Insert consistency graph for any habitual tasks." - (let ((inhibit-read-only t) l c + (let ((inhibit-read-only t) (buffer-invisibility-spec '(org-link)) (moment (time-subtract (current-time) (list 0 (* 3600 org-extend-today-until) 0)))) diff --git a/lisp/org/org-id.el b/lisp/org/org-id.el index 54fc733578..f07d243b8c 100644 --- a/lisp/org/org-id.el +++ b/lisp/org/org-id.el @@ -1,4 +1,4 @@ -;;; org-id.el --- Global identifiers for Org-mode entries +;;; org-id.el --- Global identifiers for Org entries -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2008-2017 Free Software Foundation, Inc. ;; @@ -24,7 +24,7 @@ ;; ;;; Commentary: -;; This file implements globally unique identifiers for Org-mode entries. +;; This file implements globally unique identifiers for Org entries. ;; Identifiers are stored in the entry as an :ID: property. Functions ;; are provided that create and retrieve such identifiers, and that find ;; entries based on the identifier. @@ -73,20 +73,17 @@ (require 'org) (declare-function message-make-fqdn "message" ()) -(declare-function org-pop-to-buffer-same-window - "org-compat" (&optional buffer-or-name norecord label)) ;;; Customization (defgroup org-id nil - "Options concerning global entry identifiers in Org-mode." + "Options concerning global entry identifiers in Org mode." :tag "Org ID" :group 'org) -(define-obsolete-variable-alias - 'org-link-to-org-use-id 'org-id-link-to-org-use-id "24.3") (defcustom org-id-link-to-org-use-id nil "Non-nil means storing a link to an Org file will use entry IDs. +\\\ The variable can have the following values: @@ -101,7 +98,7 @@ create-if-interactive call `org-capture' that automatically and preemptively creates a link. If you do want to get an ID link in a capture template to an entry not having an ID, create it first by explicitly creating - a link to it, using `C-c C-l' first. + a link to it, using `\\[org-store-link]' first. create-if-interactive-and-no-custom-id Like create-if-interactive, but do not create an ID if there is @@ -203,7 +200,7 @@ This variable is only relevant when `org-id-track-globally' is set." When Org reparses files to remake the list of files and IDs it is tracking, it will normally scan the agenda files, the archives related to agenda files, any files that are listed as ID containing in the current register, and -any Org-mode files currently visited by Emacs. +any Org file currently visited by Emacs. You can list additional files here. This variable is only relevant when `org-id-track-globally' is set." :group 'org-id @@ -277,7 +274,7 @@ If necessary, the ID is created." (move-marker pom nil)))) ;;;###autoload -(defun org-id-get-with-outline-drilling (&optional targets) +(defun org-id-get-with-outline-drilling () "Use an outline-cycling interface to retrieve the ID of an entry. This only finds entries in the current buffer, using `org-get-location'. It returns the ID of the entry. If necessary, the ID is created." @@ -294,7 +291,7 @@ Move the cursor to that entry in that buffer." (let ((m (org-id-find id 'marker))) (unless m (error "Cannot find entry with ID \"%s\"" id)) - (org-pop-to-buffer-same-window (marker-buffer m)) + (pop-to-buffer-same-window (marker-buffer m)) (goto-char m) (move-marker m nil) (org-show-context))) @@ -447,8 +444,7 @@ and time is the usual three-integer representation of time." Store the relation between files and corresponding IDs. This will scan all agenda files, all associated archives, and all files currently mentioned in `org-id-locations'. -When FILES is given, scan these files instead. -When CHECK is given, prepare detailed information about duplicate IDs." +When FILES is given, scan these files instead." (interactive) (if (not org-id-track-globally) (error "Please turn on `org-id-track-globally' if you want to track IDs") @@ -466,7 +462,7 @@ When CHECK is given, prepare detailed information about duplicate IDs." (if (symbolp org-id-extra-files) (symbol-value org-id-extra-files) org-id-extra-files) - ;; Files associated with live org-mode buffers + ;; Files associated with live Org buffers (delq nil (mapcar (lambda (b) (with-current-buffer b @@ -494,7 +490,7 @@ When CHECK is given, prepare detailed information about duplicate IDs." (goto-char (point-min)) (while (re-search-forward "^[ \t]*:ID:[ \t]+\\(\\S-+\\)[ \t]*$" nil t) - (setq id (org-match-string-no-properties 1)) + (setq id (match-string-no-properties 1)) (if (member id found) (progn (message "Duplicate ID \"%s\", also in file %s" @@ -678,7 +674,7 @@ optional argument MARKERP, return the position as a new marker." (move-marker m nil) (org-show-context))) -(org-add-link-type "id" 'org-id-open) +(org-link-set-parameters "id" :follow #'org-id-open) (provide 'org-id) diff --git a/lisp/org/org-indent.el b/lisp/org/org-indent.el index baaff2ff7c..10c96179b6 100644 --- a/lisp/org/org-indent.el +++ b/lisp/org/org-indent.el @@ -1,4 +1,5 @@ -;;; org-indent.el --- Dynamic indentation for Org-mode +;;; org-indent.el --- Dynamic indentation for Org -*- lexical-binding: t; -*- + ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. ;; ;; Author: Carsten Dominik @@ -39,8 +40,7 @@ (require 'org-compat) (require 'org) -(eval-when-compile - (require 'cl)) +(require 'cl-lib) (declare-function org-inlinetask-get-task-level "org-inlinetask" ()) (declare-function org-inlinetask-in-task-p "org-inlinetask" ()) @@ -52,20 +52,6 @@ :tag "Org Indent" :group 'org) -(defconst org-indent-max 40 - "Maximum indentation in characters.") -(defconst org-indent-max-levels 20 - "Maximum added level through virtual indentation, in characters. - -It is computed by multiplying `org-indent-indentation-per-level' -minus one by actual level of the headline minus one.") - -(defvar org-indent-strings nil - "Vector with all indentation strings. -It will be set in `org-indent-initialize'.") -(defvar org-indent-stars nil - "Vector with all indentation star strings. -It will be set in `org-indent-initialize'.") (defvar org-indent-inlinetask-first-star (org-add-props "*" '(face org-warning)) "First star of inline tasks, with correct face.") (defvar org-indent-agent-timer nil @@ -82,7 +68,7 @@ Delay used when the buffer to initialize is current.") Delay used when the buffer to initialize isn't current.") (defvar org-indent-agent-resume-delay '(0 0 100000) "Minimal time for other idle processes before switching back to agent.") -(defvar org-indent-initial-marker nil +(defvar org-indent--initial-marker nil "Position of initialization before interrupt. This is used locally in each buffer being initialized.") (defvar org-hide-leading-stars-before-indent-mode nil @@ -92,15 +78,12 @@ This is used locally in each buffer being initialized.") It is modified by `org-indent-notify-modified-headline'.") -(defcustom org-indent-boundary-char ?\ ; comment to protect space char +(defcustom org-indent-boundary-char ?\s "The end of the virtual indentation strings, a single-character string. The default is just a space, but if you wish, you can use \"|\" or so. This can be useful on a terminal window - under a windowing system, -it may be prettier to customize the org-indent face." +it may be prettier to customize the `org-indent' face." :group 'org-indent - :set (lambda (var val) - (set var val) - (and org-indent-strings (org-indent-initialize))) :type 'character) (defcustom org-indent-mode-turns-off-org-adapt-indentation t @@ -121,29 +104,56 @@ turn on `org-hide-leading-stars'." :group 'org-indent :type 'integer) -(defface org-indent - (org-compatible-face nil nil) +(defface org-indent '((t (:inherit org-hide))) "Face for outline indentation. The default is to make it look like whitespace. But you may find it useful to make it ever so slightly different." :group 'org-faces) -(defun org-indent-initialize () - "Initialize the indentation strings." - (setq org-indent-strings (make-vector (1+ org-indent-max) nil)) - (setq org-indent-stars (make-vector (1+ org-indent-max) nil)) - (aset org-indent-strings 0 nil) - (aset org-indent-stars 0 nil) - (loop for i from 1 to org-indent-max do - (aset org-indent-strings i - (org-add-props - (concat (make-string (1- i) ?\ ) - (char-to-string org-indent-boundary-char)) +(defvar org-indent--text-line-prefixes nil + "Vector containing line prefixes strings for regular text.") + +(defvar org-indent--heading-line-prefixes nil + "Vector containing line prefix strings for headlines.") + +(defvar org-indent--inlinetask-line-prefixes nil + "Vector containing line prefix strings for inline tasks.") + +(defconst org-indent--deepest-level 50 + "Maximum theoretical headline depth.") + +(defun org-indent--compute-prefixes () + "Compute prefix strings for regular text and headlines." + (setq org-indent--heading-line-prefixes + (make-vector org-indent--deepest-level nil)) + (setq org-indent--inlinetask-line-prefixes + (make-vector org-indent--deepest-level nil)) + (setq org-indent--text-line-prefixes + (make-vector org-indent--deepest-level nil)) + (dotimes (n org-indent--deepest-level) + (let ((indentation (if (<= n 1) 0 + (* (1- org-indent-indentation-per-level) + (1- n))))) + ;; Headlines line prefixes. + (let ((heading-prefix (make-string indentation ?*))) + (aset org-indent--heading-line-prefixes + n + (org-add-props heading-prefix nil 'face 'org-indent)) + ;; Inline tasks line prefixes + (aset org-indent--inlinetask-line-prefixes + n + (org-add-props (if (bound-and-true-p org-inlinetask-show-first-star) + (concat org-indent-inlinetask-first-star + (substring heading-prefix 1)) + heading-prefix) nil 'face 'org-indent))) - (loop for i from 1 to org-indent-max-levels do - (aset org-indent-stars i - (org-add-props (make-string i ?*) - nil 'face 'org-hide)))) + ;; Text line prefixes. + (aset org-indent--text-line-prefixes + n + (concat (org-add-props (make-string (+ n indentation) ?\s) + nil 'face 'org-indent) + (and (> n 0) + (char-to-string org-indent-boundary-char))))))) (defsubst org-indent-remove-properties (beg end) "Remove indentations between BEG and END." @@ -162,34 +172,25 @@ buffer, which can take a few seconds on large buffers, is done during idle time." nil " Ind" nil (cond - ((and org-indent-mode (featurep 'xemacs)) - (message "org-indent-mode does not work in XEmacs - refusing to turn it on") - (setq org-indent-mode nil)) - ((and org-indent-mode - (not (org-version-check "23.1.50" "Org Indent mode" :predicate))) - (message "org-indent-mode can crash Emacs 23.1 - refusing to turn it on!") - (ding) - (sit-for 1) - (setq org-indent-mode nil)) (org-indent-mode ;; mode was turned on. - (org-set-local 'indent-tabs-mode nil) - (or org-indent-strings (org-indent-initialize)) - (org-set-local 'org-indent-initial-marker (copy-marker 1)) + (setq-local indent-tabs-mode nil) + (setq-local org-indent--initial-marker (copy-marker 1)) (when org-indent-mode-turns-off-org-adapt-indentation - (org-set-local 'org-adapt-indentation nil)) + (setq-local org-adapt-indentation nil)) (when org-indent-mode-turns-on-hiding-stars - (org-set-local 'org-hide-leading-stars-before-indent-mode - org-hide-leading-stars) - (org-set-local 'org-hide-leading-stars t)) - (org-add-hook 'filter-buffer-substring-functions - (lambda (fun start end delete) - (org-indent-remove-properties-from-string - (funcall fun start end delete))) - nil t) - (org-add-hook 'after-change-functions 'org-indent-refresh-maybe nil 'local) - (org-add-hook 'before-change-functions - 'org-indent-notify-modified-headline nil 'local) + (setq-local org-hide-leading-stars-before-indent-mode + org-hide-leading-stars) + (setq-local org-hide-leading-stars t)) + (org-indent--compute-prefixes) + (add-hook 'filter-buffer-substring-functions + (lambda (fun start end delete) + (org-indent-remove-properties-from-string + (funcall fun start end delete))) + nil t) + (add-hook 'after-change-functions 'org-indent-refresh-maybe nil 'local) + (add-hook 'before-change-functions + 'org-indent-notify-modified-headline nil 'local) (and font-lock-mode (org-restart-font-lock)) (org-indent-remove-properties (point-min) (point-max)) ;; Submit current buffer to initialize agent. If it's the first @@ -205,11 +206,11 @@ during idle time." (kill-local-variable 'org-adapt-indentation) (setq org-indent-agentized-buffers (delq (current-buffer) org-indent-agentized-buffers)) - (when (markerp org-indent-initial-marker) - (set-marker org-indent-initial-marker nil)) + (when (markerp org-indent--initial-marker) + (set-marker org-indent--initial-marker nil)) (when (boundp 'org-hide-leading-stars-before-indent-mode) - (org-set-local 'org-hide-leading-stars - org-hide-leading-stars-before-indent-mode)) + (setq-local org-hide-leading-stars + org-hide-leading-stars-before-indent-mode)) (remove-hook 'filter-buffer-substring-functions (lambda (fun start end delete) (org-indent-remove-properties-from-string @@ -245,7 +246,7 @@ When no more buffer is being watched, the agent suppress itself." (when org-indent-agent-resume-timer (cancel-timer org-indent-agent-resume-timer)) (setq org-indent-agentized-buffers - (org-remove-if-not #'buffer-live-p org-indent-agentized-buffers)) + (cl-remove-if-not #'buffer-live-p org-indent-agentized-buffers)) (cond ;; Job done: kill agent. ((not org-indent-agentized-buffers) (cancel-timer org-indent-agent-timer)) @@ -269,46 +270,44 @@ a time value." (let ((interruptp ;; Always nil unless interrupted. (catch 'interrupt - (and org-indent-initial-marker - (marker-position org-indent-initial-marker) - (org-indent-add-properties org-indent-initial-marker + (and org-indent--initial-marker + (marker-position org-indent--initial-marker) + (equal (marker-buffer org-indent--initial-marker) + buffer) + (org-indent-add-properties org-indent--initial-marker (point-max) delay) nil)))) - (move-marker org-indent-initial-marker interruptp) + (move-marker org-indent--initial-marker interruptp) ;; Job is complete: un-agentize buffer. (unless interruptp (setq org-indent-agentized-buffers (delq buffer org-indent-agentized-buffers)))))))) -(defsubst org-indent-set-line-properties (l w h) +(defun org-indent-set-line-properties (level indentation &optional heading) "Set prefix properties on current line an move to next one. -Prefix properties `line-prefix' and `wrap-prefix' in current line -are set to, respectively, length L and W. - -If H is non-nil, `line-prefix' will be starred. If H is -`inline', the first star will have `org-warning' face. - -Assume point is at beginning of line." - (let ((line (cond - ((eq 'inline h) - (let ((stars (aref org-indent-stars - (min l org-indent-max-levels)))) - (and stars - (if (org-bound-and-true-p org-inlinetask-show-first-star) - (concat org-indent-inlinetask-first-star - (substring stars 1)) - stars)))) - (h (aref org-indent-stars - (min l org-indent-max-levels))) - (t (aref org-indent-strings - (min l org-indent-max))))) - (wrap (aref org-indent-strings (min w org-indent-max)))) +LEVEL is the current level of heading. INDENTATION is the +expected indentation when wrapping line. + +When optional argument HEADING is non-nil, assume line is at +a heading. Moreover, if is is `inlinetask', the first star will +have `org-warning' face." + (let* ((line (aref (pcase heading + (`nil org-indent--text-line-prefixes) + (`inlinetask org-indent--inlinetask-line-prefixes) + (_ org-indent--heading-line-prefixes)) + level)) + (wrap + (org-add-props + (concat line + (if heading (concat (make-string level ?*) " ") + (make-string indentation ?\s))) + nil 'face 'org-indent))) ;; Add properties down to the next line to indent empty lines. - (add-text-properties (point) (min (1+ (point-at-eol)) (point-max)) + (add-text-properties (line-beginning-position) (line-beginning-position 2) `(line-prefix ,line wrap-prefix ,wrap))) - (forward-line 1)) + (forward-line)) (defun org-indent-add-properties (beg end &optional delay) "Add indentation properties between BEG and END. @@ -322,26 +321,14 @@ stopped." (org-with-wide-buffer (goto-char beg) (beginning-of-line) - ;; 1. Initialize prefix at BEG. This is done by storing two - ;; variables: INLINE-PF and PF, representing respectively - ;; length of current `line-prefix' when line is inside an - ;; inline task or not. + ;; Initialize prefix at BEG, according to current entry's level. (let* ((case-fold-search t) (limited-re (org-get-limited-outline-regexp)) - (added-ind-per-lvl (abs (1- org-indent-indentation-per-level))) - (pf (save-excursion - (and (ignore-errors (let ((outline-regexp limited-re)) - (org-back-to-heading t))) - (+ (* org-indent-indentation-per-level - (- (match-end 0) (match-beginning 0) 2)) 2)))) - (pf-inline (and (featurep 'org-inlinetask) - (org-inlinetask-in-task-p) - (+ (* org-indent-indentation-per-level - (1- (org-inlinetask-get-task-level))) 2))) + (level (or (org-current-level) 0)) (time-limit (and delay (time-add (current-time) delay)))) - ;; 2. For each line, set `line-prefix' and `wrap-prefix' - ;; properties depending on the type of line (headline, - ;; inline task, item or other). + ;; For each line, set `line-prefix' and `wrap-prefix' + ;; properties depending on the type of line (headline, inline + ;; task, item or other). (org-with-silent-modifications (while (and (<= (point) end) (not (eobp))) (cond @@ -354,38 +341,23 @@ stopped." ((and delay (time-less-p time-limit (current-time))) (setq org-indent-agent-resume-timer (run-with-idle-timer - (time-add (current-idle-time) - org-indent-agent-resume-delay) + (time-add (current-idle-time) org-indent-agent-resume-delay) nil #'org-indent-initialize-agent)) (throw 'interrupt (point))) ;; Headline or inline task. ((looking-at org-outline-regexp) (let* ((nstars (- (match-end 0) (match-beginning 0) 1)) - (line (* added-ind-per-lvl (1- nstars))) - (wrap (+ line (1+ nstars)))) - (cond - ;; Headline: new value for PF. - ((looking-at limited-re) - (org-indent-set-line-properties line wrap t) - (setq pf wrap)) - ;; End of inline task: PF-INLINE is now nil. - ((looking-at "\\*+ end[ \t]*$") - (org-indent-set-line-properties line wrap 'inline) - (setq pf-inline nil)) - ;; Start of inline task. Determine if it contains - ;; text, or if it is only one line long. Set - ;; PF-INLINE accordingly. - (t (org-indent-set-line-properties line wrap 'inline) - (setq pf-inline (and (org-inlinetask-in-task-p) wrap)))))) + (type (or (looking-at-p limited-re) 'inlinetask))) + (org-indent-set-line-properties nstars 0 type) + ;; At an headline, define new value for LEVEL. + (unless (eq type 'inlinetask) (setq level nstars)))) ;; List item: `wrap-prefix' is set where body starts. ((org-at-item-p) - (let* ((line (or pf-inline pf 0)) - (wrap (+ (org-list-item-body-column (point)) line))) - (org-indent-set-line-properties line wrap nil))) - ;; Normal line: use PF-INLINE, PF or nil as prefixes. - (t (let* ((line (or pf-inline pf 0)) - (wrap (+ line (org-get-indentation)))) - (org-indent-set-line-properties line wrap nil)))))))))) + (org-indent-set-line-properties + level (org-list-item-body-column (point)))) + ;; Regular line. + (t + (org-indent-set-line-properties level (org-get-indentation)))))))))) (defun org-indent-notify-modified-headline (beg end) "Set `org-indent-modified-headline-flag' depending on context. @@ -398,13 +370,14 @@ Flag will be non-nil if command is going to modify or delete an headline." (when org-indent-mode (setq org-indent-modified-headline-flag - (save-excursion - (goto-char beg) - (save-match-data - (or (and (org-at-heading-p) (< beg (match-end 0))) - (re-search-forward org-outline-regexp-bol end t))))))) - -(defun org-indent-refresh-maybe (beg end dummy) + (org-with-wide-buffer + (goto-char beg) + (save-match-data + (or (and (org-at-heading-p) (< beg (match-end 0))) + (re-search-forward + (org-with-limited-levels org-outline-regexp-bol) end t))))))) + +(defun org-indent-refresh-maybe (beg end _) "Refresh indentation properties in an adequate portion of buffer. BEG and END are the positions of the beginning and end of the range of inserted text. DUMMY is an unused argument. @@ -414,19 +387,21 @@ This function is meant to be called by `after-change-functions'." (save-match-data ;; If a headline was modified or inserted, set properties until ;; next headline. - (if (or org-indent-modified-headline-flag - (save-excursion - (goto-char beg) - (beginning-of-line) - (re-search-forward org-outline-regexp-bol end t))) - (let ((end (save-excursion - (goto-char end) - (org-with-limited-levels (outline-next-heading)) - (point)))) - (setq org-indent-modified-headline-flag nil) - (org-indent-add-properties beg end)) - ;; Otherwise, only set properties on modified area. - (org-indent-add-properties beg end))))) + (org-with-wide-buffer + (if (or org-indent-modified-headline-flag + (save-excursion + (goto-char beg) + (beginning-of-line) + (re-search-forward + (org-with-limited-levels org-outline-regexp-bol) end t))) + (let ((end (save-excursion + (goto-char end) + (org-with-limited-levels (outline-next-heading)) + (point)))) + (setq org-indent-modified-headline-flag nil) + (org-indent-add-properties beg end)) + ;; Otherwise, only set properties on modified area. + (org-indent-add-properties beg end)))))) (provide 'org-indent) diff --git a/lisp/org/org-info.el b/lisp/org/org-info.el index c8f6f06de0..79b9bcc3d9 100644 --- a/lisp/org/org-info.el +++ b/lisp/org/org-info.el @@ -1,4 +1,4 @@ -;;; org-info.el --- Support for links to Info nodes from within Org-Mode +;;; org-info.el --- Support for Links to Info Nodes -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -24,8 +24,8 @@ ;; ;;; Commentary: -;; This file implements links to Info nodes from within Org-mode. -;; Org-mode loads this module by default - if this is not what you want, +;; This file implements links to Info nodes from within Org mode. +;; Org mode loads this module by default - if this is not what you want, ;; configure the variable `org-modules'. ;;; Code: @@ -40,19 +40,20 @@ (defvar Info-current-node) ;; Install the link type -(org-add-link-type "info" 'org-info-open) -(add-hook 'org-store-link-functions 'org-info-store-link) +(org-link-set-parameters "info" + :follow #'org-info-open + :export #'org-info-export + :store #'org-info-store-link) ;; Implementation (defun org-info-store-link () "Store a link to an Info file and node." (when (eq major-mode 'Info-mode) - (let (link desc) - (setq link (concat "info:" - (file-name-nondirectory Info-current-file) - "#" Info-current-node)) - (setq desc (concat (file-name-nondirectory Info-current-file) - "#" Info-current-node)) + (let ((link (concat "info:" + (file-name-nondirectory Info-current-file) + "#" Info-current-node)) + (desc (concat (file-name-nondirectory Info-current-file) + "#" Info-current-node))) (org-store-link-props :type "info" :file Info-current-file :node Info-current-node :link link :desc desc) @@ -67,12 +68,76 @@ "Follow an Info file and node link specified by NAME." (if (or (string-match "\\(.*\\)[#:]:?\\(.*\\)" name) (string-match "\\(.*\\)" name)) - (progn + (let ((filename (match-string 1 name)) + (nodename-or-index (or (match-string 2 name) "Top"))) (require 'info) - (if (match-string 2 name) ; If there isn't a node, choose "Top" - (Info-find-node (match-string 1 name) (match-string 2 name)) - (Info-find-node (match-string 1 name) "Top"))) - (message "Could not open: %s" name))) + ;; If nodename-or-index is invalid node name, then look it up + ;; in the index. + (condition-case nil + (Info-find-node filename nodename-or-index) + (user-error (Info-find-node filename "Top") + (condition-case nil + (Info-index nodename-or-index) + (user-error "Could not find '%s' node or index entry" + nodename-or-index))))) + (user-error "Could not open: %s" name))) + +(defconst org-info-emacs-documents + '("ada-mode" "auth" "autotype" "bovine" "calc" "ccmode" "cl" "dbus" "dired-x" + "ebrowse" "ede" "ediff" "edt" "efaq-w32" "efaq" "eieio" "eintr" "elisp" + "emacs-gnutls" "emacs-mime" "emacs" "epa" "erc" "ert" "eshell" "eudc" "eww" + "flymake" "forms" "gnus" "htmlfontify" "idlwave" "ido" "info" "mairix-el" + "message" "mh-e" "newsticker" "nxml-mode" "octave-mode" "org" "pcl-cvs" + "pgg" "rcirc" "reftex" "remember" "sasl" "sc" "semantic" "ses" "sieve" + "smtpmail" "speedbar" "srecode" "todo-mode" "tramp" "url" "vip" "viper" + "widget" "wisent" "woman") + "List of emacs documents available. +Taken from ") + +(defconst org-info-other-documents + '(("libc" . "http://www.gnu.org/software/libc/manual/html_mono/libc.html") + ("make" . "http://www.gnu.org/software/make/manual/make.html")) + "Alist of documents generated from Texinfo source. +When converting info links to HTML, links to any one of these manuals are +converted to use these URL.") + +(defun org-info-map-html-url (filename) + "Return URL or HTML file associated to Info FILENAME. +If FILENAME refers to an official GNU document, return a URL pointing to +the official page for that document, e.g., use \"gnu.org\" for all Emacs +related documents. Otherwise, append \".html\" extension to FILENAME. +See `org-info-emacs-documents' and `org-info-other-documents' for details." + (cond ((member filename org-info-emacs-documents) + (format "http://www.gnu.org/software/emacs/manual/html_mono/%s.html" + filename)) + ((cdr (assoc filename org-info-other-documents))) + (t (concat filename ".html")))) + +(defun org-info--expand-node-name (node) + "Expand Info NODE to HTML cross reference." + ;; See (info "(texinfo) HTML Xref Node Name Expansion") for the + ;; expansion rule. + (let ((node (replace-regexp-in-string + "\\([ \t\n\r]+\\)\\|\\([^a-zA-Z0-9]\\)" + (lambda (m) + (if (match-end 1) "-" (format "_%04x" (string-to-char m)))) + (org-trim node)))) + (cond ((string= node "") "") + ((string-match-p "\\`[0-9]" node) (concat "g_t" node)) + (t node)))) + +(defun org-info-export (path desc format) + "Export an info link. +See `org-link-parameters' for details about PATH, DESC and FORMAT." + (when (eq format 'html) + (or (string-match "\\(.*\\)[#:]:?\\(.*\\)" path) + (string-match "\\(.*\\)" path)) + (let ((filename (match-string 1 path)) + (node (or (match-string 2 path) "Top"))) + (format "%s" + (org-info-map-html-url filename) + (org-info--expand-node-name node) + (or desc path))))) (provide 'org-info) diff --git a/lisp/org/org-inlinetask.el b/lisp/org/org-inlinetask.el index bf4ab205a4..2918d4061d 100644 --- a/lisp/org/org-inlinetask.el +++ b/lisp/org/org-inlinetask.el @@ -1,4 +1,4 @@ -;;; org-inlinetask.el --- Tasks independent of outline hierarchy +;;; org-inlinetask.el --- Tasks Independent of Outline Hierarchy -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. ;; @@ -26,7 +26,7 @@ ;; ;;; Commentary: ;; -;; This module implements inline tasks in Org-mode. Inline tasks are +;; This module implements inline tasks in Org mode. Inline tasks are ;; tasks that have all the properties of normal outline nodes, ;; including the ability to store meta data like scheduling dates, ;; TODO state, tags and properties. However, these nodes are treated @@ -108,7 +108,6 @@ When nil, the first star is not shown." (defvar org-odd-levels-only) (defvar org-keyword-time-regexp) -(defvar org-drawer-regexp) (defvar org-complex-heading-regexp) (defvar org-property-end-re) @@ -168,9 +167,9 @@ The number of levels is controlled by `org-inlinetask-min-level'." (stars-re (org-inlinetask-outline-regexp)) (task-beg-re (concat stars-re "\\(?:.*\\)")) (task-end-re (concat stars-re "END[ \t]*$"))) - (or (org-looking-at-p task-beg-re) + (or (looking-at-p task-beg-re) (and (re-search-forward "^\\*+[ \t]+" nil t) - (progn (beginning-of-line) (org-looking-at-p task-end-re))))))) + (progn (beginning-of-line) (looking-at-p task-end-re))))))) (defun org-inlinetask-goto-beginning () "Go to the beginning of the inline task at point." @@ -178,7 +177,7 @@ The number of levels is controlled by `org-inlinetask-min-level'." (let ((case-fold-search t) (inlinetask-re (org-inlinetask-outline-regexp))) (re-search-backward inlinetask-re nil t) - (when (org-looking-at-p (concat inlinetask-re "END[ \t]*$")) + (when (looking-at-p (concat inlinetask-re "END[ \t]*$")) (re-search-backward inlinetask-re nil t)))) (defun org-inlinetask-goto-end () @@ -190,17 +189,16 @@ Return point." (inlinetask-re (org-inlinetask-outline-regexp)) (task-end-re (concat inlinetask-re "END[ \t]*$"))) (cond - ((looking-at task-end-re) (forward-line)) + ((looking-at task-end-re)) ((looking-at inlinetask-re) (forward-line) (cond - ((looking-at task-end-re) (forward-line)) + ((looking-at task-end-re)) ((looking-at inlinetask-re)) ((org-inlinetask-in-task-p) - (re-search-forward inlinetask-re nil t) - (forward-line)))) - (t (re-search-forward inlinetask-re nil t) - (forward-line))) + (re-search-forward inlinetask-re nil t)))) + (t (re-search-forward inlinetask-re nil t))) + (end-of-line) (point)))) (defun org-inlinetask-get-task-level () @@ -273,8 +271,7 @@ If the task has an end part, also demote it." (defvar org-indent-indentation-per-level) ; defined in org-indent.el -(defface org-inlinetask - (org-compatible-face 'shadow '((t (:bold t)))) +(defface org-inlinetask '((t :inherit shadow)) "Face for inlinetask headlines." :group 'org-faces) @@ -288,7 +285,7 @@ If the task has an end part, also demote it." ",\\}\\)\\(\\*\\* .*\\)")) ;; Virtual indentation will add the warning face on the first ;; star. Thus, in that case, only hide it. - (start-face (if (and (org-bound-and-true-p org-indent-mode) + (start-face (if (and (bound-and-true-p org-indent-mode) (> org-indent-indentation-per-level 1)) 'org-hide 'org-warning))) @@ -315,19 +312,36 @@ If the task has an end part, also demote it." ;; Nothing to show/hide. ((= end start)) ;; Inlinetask was folded: expand it. - ((get-char-property (1+ start) 'invisible) + ((eq (get-char-property (1+ start) 'invisible) 'outline) (outline-flag-region start end nil) (org-cycle-hide-drawers 'children)) (t (outline-flag-region start end t))))) +(defun org-inlinetask-hide-tasks (state) + "Hide inline tasks in buffer when STATE is `contents' or `children'. +This function is meant to be used in `org-cycle-hook'." + (pcase state + (`contents + (let ((regexp (org-inlinetask-outline-regexp))) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (org-inlinetask-toggle-visibility) + (org-inlinetask-goto-end))))) + (`children + (save-excursion + (while (and (outline-next-heading) (org-inlinetask-at-task-p)) + (org-inlinetask-toggle-visibility) + (org-inlinetask-goto-end)))))) + (defun org-inlinetask-remove-END-maybe () "Remove an END line when present." (when (looking-at (format "\\([ \t]*\n\\)*\\*\\{%d,\\}[ \t]+END[ \t]*$" org-inlinetask-min-level)) (replace-match ""))) -(eval-after-load "org" - '(add-hook 'org-font-lock-hook 'org-inlinetask-fontify)) +(add-hook 'org-font-lock-hook 'org-inlinetask-fontify) +(add-hook 'org-cycle-hook 'org-inlinetask-hide-tasks) (provide 'org-inlinetask) diff --git a/lisp/org/org-irc.el b/lisp/org/org-irc.el index 1243587beb..3a6a7f4db0 100644 --- a/lisp/org/org-irc.el +++ b/lisp/org/org-irc.el @@ -1,4 +1,4 @@ -;;; org-irc.el --- Store links to IRC sessions +;;; org-irc.el --- Store Links to IRC Sessions -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2008-2017 Free Software Foundation, Inc. ;; @@ -22,8 +22,8 @@ ;;; Commentary: -;; This file implements links to an IRC session from within Org-mode. -;; Org-mode loads this module by default - if this is not what you want, +;; This file implements links to an IRC session from within Org mode. +;; Org mode loads this module by default - if this is not what you want, ;; configure the variable `org-modules'. ;; ;; Please customize the variable `org-modules' to select @@ -59,8 +59,6 @@ (declare-function erc-server-buffer "erc" ()) (declare-function erc-get-server-nickname-list "erc" ()) (declare-function erc-cmd-JOIN "erc" (channel &optional key)) -(declare-function org-pop-to-buffer-same-window - "org-compat" (&optional buffer-or-name norecord label)) (defvar org-irc-client 'erc "The IRC client to act on.") @@ -73,9 +71,7 @@ ;; Generic functions/config (extend these for other clients) -(add-to-list 'org-store-link-functions 'org-irc-store-link) - -(org-add-link-type "irc" 'org-irc-visit nil) +(org-link-set-parameters "irc" :follow #'org-irc-visit :store #'org-irc-store-link) (defun org-irc-visit (link) "Parse LINK and dispatch to the correct function based on the client found." @@ -114,11 +110,9 @@ chars that the value AFTER with `...'" (cons "[ \t]*$" "") (cons (concat "^\\(.\\{" after "\\}\\).*") "\\1...")))) - (mapc (lambda (x) - (when (string-match (car x) string) - (setq string (replace-match (cdr x) nil nil string)))) - replace-map) - string)) + (dolist (x replace-map string) + (when (string-match (car x) string) + (setq string (replace-match (cdr x) nil nil string)))))) ;; ERC specific functions @@ -233,7 +227,7 @@ default." (throw 'found x)))))) (if chan-buf (progn - (org-pop-to-buffer-same-window chan-buf) + (pop-to-buffer-same-window chan-buf) ;; if we got a nick, and they're in the chan, ;; then start a chat with them (let ((nick (pop link))) @@ -244,9 +238,9 @@ default." (insert (concat nick ": "))) (error "%s not found in %s" nick chan-name))))) (progn - (org-pop-to-buffer-same-window server-buffer) + (pop-to-buffer-same-window server-buffer) (erc-cmd-JOIN chan-name)))) - (org-pop-to-buffer-same-window server-buffer))) + (pop-to-buffer-same-window server-buffer))) ;; no server match, make new connection (erc-select :server server :port port)))) diff --git a/lisp/org/org-lint.el b/lisp/org/org-lint.el new file mode 100644 index 0000000000..89aed4bbb1 --- /dev/null +++ b/lisp/org/org-lint.el @@ -0,0 +1,1225 @@ +;;; org-lint.el --- Linting for Org documents -*- lexical-binding: t; -*- + +;; Copyright (C) 2015-2017 Free Software Foundation + +;; Author: Nicolas Goaziou +;; Keywords: outlines, hypermedia, calendar, wp + +;; 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 . + +;;; Commentary: + +;; This library implements linting for Org syntax. The sole public +;; function is `org-lint', which see. + +;; Internally, the library defines a new structure: +;; `org-lint-checker', with the following slots: + +;; - NAME: Unique check identifier, as a non-nil symbol that doesn't +;; start with an hyphen. +;; +;; The check is done calling the function `org-lint-NAME' with one +;; mandatory argument, the parse tree describing the current Org +;; buffer. Such function calls are wrapped within +;; a `save-excursion' and point is always at `point-min'. Its +;; return value has to be an alist (POSITION MESSAGE) when +;; POSITION refer to the buffer position of the error, as an +;; integer, and MESSAGE is a string describing the error. + +;; - DESCRIPTION: Summary about the check, as a string. + +;; - CATEGORIES: Categories relative to the check, as a list of +;; symbol. They are used for filtering when calling `org-lint'. +;; Checkers not explicitly associated to a category are collected +;; in the `default' one. + +;; - TRUST: The trust level one can have in the check. It is either +;; `low' or `high', depending on the heuristics implemented and +;; the nature of the check. This has an indicative value only and +;; is displayed along reports. + +;; All checks have to be listed in `org-lint--checkers'. + +;; Results are displayed in a special "*Org Lint*" buffer with +;; a dedicated major mode, derived from `tabulated-list-mode'. +;; +;; In addition to the usual key-bindings inherited from it, "C-j" and +;; "TAB" display problematic line reported under point whereas "RET" +;; jumps to it. Also, "h" hides all reports similar to the current +;; one. Additionally, "i" removes them from subsequent reports. + +;; Checks currently implemented are: + +;; - duplicate CUSTOM_ID properties +;; - duplicate NAME values +;; - duplicate targets +;; - duplicate footnote definitions +;; - orphaned affiliated keywords +;; - obsolete affiliated keywords +;; - missing language in src blocks +;; - missing back-end in export blocks +;; - invalid Babel call blocks +;; - NAME values with a colon +;; - deprecated export block syntax +;; - deprecated Babel header properties +;; - wrong header arguments in src blocks +;; - misuse of CATEGORY keyword +;; - "coderef" links with unknown destination +;; - "custom-id" links with unknown destination +;; - "fuzzy" links with unknown destination +;; - "id" links with unknown destination +;; - links to non-existent local files +;; - SETUPFILE keywords with non-existent file parameter +;; - INCLUDE keywords with wrong link parameter +;; - obsolete markup in INCLUDE keyword +;; - unknown items in OPTIONS keyword +;; - spurious macro arguments or invalid macro templates +;; - special properties in properties drawer +;; - obsolete syntax for PROPERTIES drawers +;; - missing definition for footnote references +;; - missing reference for footnote definitions +;; - non-footnote definitions in footnote section +;; - probable invalid keywords +;; - invalid blocks +;; - misplaced planning info line +;; - incomplete drawers +;; - indented diary-sexps +;; - obsolete QUOTE section +;; - obsolete "file+application" link +;; - blank headlines with tags + + +;;; Code: + +(require 'cl-lib) +(require 'org-element) +(require 'org-macro) +(require 'ox) +(require 'ob) + + +;;; Checkers + +(cl-defstruct (org-lint-checker (:copier nil)) + (name 'missing-checker-name) + (description "") + (categories '(default)) + (trust 'high)) ; `low' or `high' + +(defun org-lint-missing-checker-name (_) + (error + "`A checker has no `:name' property. Please verify `org-lint--checkers'")) + +(defconst org-lint--checkers + (list + (make-org-lint-checker + :name 'duplicate-custom-id + :description "Report duplicates CUSTOM_ID properties" + :categories '(link)) + (make-org-lint-checker + :name 'duplicate-name + :description "Report duplicate NAME values" + :categories '(babel link)) + (make-org-lint-checker + :name 'duplicate-target + :description "Report duplicate targets" + :categories '(link)) + (make-org-lint-checker + :name 'duplicate-footnote-definition + :description "Report duplicate footnote definitions" + :categories '(footnote)) + (make-org-lint-checker + :name 'orphaned-affiliated-keywords + :description "Report orphaned affiliated keywords" + :trust 'low) + (make-org-lint-checker + :name 'obsolete-affiliated-keywords + :description "Report obsolete affiliated keywords" + :categories '(obsolete)) + (make-org-lint-checker + :name 'deprecated-export-blocks + :description "Report deprecated export block syntax" + :categories '(obsolete export) + :trust 'low) + (make-org-lint-checker + :name 'deprecated-header-syntax + :description "Report deprecated Babel header syntax" + :categories '(obsolete babel) + :trust 'low) + (make-org-lint-checker + :name 'missing-language-in-src-block + :description "Report missing language in src blocks" + :categories '(babel)) + (make-org-lint-checker + :name 'missing-backend-in-export-block + :description "Report missing back-end in export blocks" + :categories '(export)) + (make-org-lint-checker + :name 'invalid-babel-call-block + :description "Report invalid Babel call blocks" + :categories '(babel)) + (make-org-lint-checker + :name 'colon-in-name + :description "Report NAME values with a colon" + :categories '(babel)) + (make-org-lint-checker + :name 'wrong-header-argument + :description "Report wrong babel headers" + :categories '(babel)) + (make-org-lint-checker + :name 'wrong-header-value + :description "Report invalid value in babel headers" + :categories '(babel) + :trust 'low) + (make-org-lint-checker + :name 'deprecated-category-setup + :description "Report misuse of CATEGORY keyword" + :categories '(obsolete)) + (make-org-lint-checker + :name 'invalid-coderef-link + :description "Report \"coderef\" links with unknown destination" + :categories '(link)) + (make-org-lint-checker + :name 'invalid-custom-id-link + :description "Report \"custom-id\" links with unknown destination" + :categories '(link)) + (make-org-lint-checker + :name 'invalid-fuzzy-link + :description "Report \"fuzzy\" links with unknown destination" + :categories '(link)) + (make-org-lint-checker + :name 'invalid-id-link + :description "Report \"id\" links with unknown destination" + :categories '(link)) + (make-org-lint-checker + :name 'link-to-local-file + :description "Report links to non-existent local files" + :categories '(link) + :trust 'low) + (make-org-lint-checker + :name 'non-existent-setupfile-parameter + :description "Report SETUPFILE keywords with non-existent file parameter" + :trust 'low) + (make-org-lint-checker + :name 'wrong-include-link-parameter + :description "Report INCLUDE keywords with misleading link parameter" + :categories '(export) + :trust 'low) + (make-org-lint-checker + :name 'obsolete-include-markup + :description "Report obsolete markup in INCLUDE keyword" + :categories '(obsolete export) + :trust 'low) + (make-org-lint-checker + :name 'unknown-options-item + :description "Report unknown items in OPTIONS keyword" + :categories '(export) + :trust 'low) + (make-org-lint-checker + :name 'invalid-macro-argument-and-template + :description "Report spurious macro arguments or invalid macro templates" + :categories '(export) + :trust 'low) + (make-org-lint-checker + :name 'special-property-in-properties-drawer + :description "Report special properties in properties drawers" + :categories '(properties)) + (make-org-lint-checker + :name 'obsolete-properties-drawer + :description "Report obsolete syntax for properties drawers" + :categories '(obsolete properties)) + (make-org-lint-checker + :name 'undefined-footnote-reference + :description "Report missing definition for footnote references" + :categories '(footnote)) + (make-org-lint-checker + :name 'unreferenced-footnote-definition + :description "Report missing reference for footnote definitions" + :categories '(footnote)) + (make-org-lint-checker + :name 'extraneous-element-in-footnote-section + :description "Report non-footnote definitions in footnote section" + :categories '(footnote)) + (make-org-lint-checker + :name 'invalid-keyword-syntax + :description "Report probable invalid keywords" + :trust 'low) + (make-org-lint-checker + :name 'invalid-block + :description "Report invalid blocks" + :trust 'low) + (make-org-lint-checker + :name 'misplaced-planning-info + :description "Report misplaced planning info line" + :trust 'low) + (make-org-lint-checker + :name 'incomplete-drawer + :description "Report probable incomplete drawers" + :trust 'low) + (make-org-lint-checker + :name 'indented-diary-sexp + :description "Report probable indented diary-sexps" + :trust 'low) + (make-org-lint-checker + :name 'quote-section + :description "Report obsolete QUOTE section" + :categories '(obsolete) + :trust 'low) + (make-org-lint-checker + :name 'file-application + :description "Report obsolete \"file+application\" link" + :categories '(link obsolete)) + (make-org-lint-checker + :name 'empty-headline-with-tags + :description "Report ambiguous empty headlines with tags" + :categories '(headline) + :trust 'low)) + "List of all available checkers.") + +(defun org-lint--collect-duplicates + (ast type extract-key extract-position build-message) + "Helper function to collect duplicates in parse tree AST. + +EXTRACT-KEY is a function extracting key. It is called with +a single argument: the element or object. Comparison is done +with `equal'. + +EXTRACT-POSITION is a function returning position for the report. +It is called with two arguments, the object or element, and the +key. + +BUILD-MESSAGE is a function creating the report message. It is +called with one argument, the key used for comparison." + (let* (keys + originals + reports + (make-report + (lambda (position value) + (push (list position (funcall build-message value)) reports)))) + (org-element-map ast type + (lambda (datum) + (let ((key (funcall extract-key datum))) + (cond + ((not key)) + ((assoc key keys) (cl-pushnew (assoc key keys) originals) + (funcall make-report (funcall extract-position datum key) key)) + (t (push (cons key (funcall extract-position datum key)) keys)))))) + (dolist (e originals reports) (funcall make-report (cdr e) (car e))))) + +(defun org-lint-duplicate-custom-id (ast) + (org-lint--collect-duplicates + ast + 'node-property + (lambda (property) + (and (eq (compare-strings "CUSTOM_ID" nil nil + (org-element-property :key property) nil nil + t) + t) + (org-element-property :value property))) + (lambda (property _) (org-element-property :begin property)) + (lambda (key) (format "Duplicate CUSTOM_ID property \"%s\"" key)))) + +(defun org-lint-duplicate-name (ast) + (org-lint--collect-duplicates + ast + org-element-all-elements + (lambda (datum) (org-element-property :name datum)) + (lambda (datum name) + (goto-char (org-element-property :begin datum)) + (re-search-forward + (format "^[ \t]*#\\+[A-Za-z]+: +%s *$" (regexp-quote name))) + (match-beginning 0)) + (lambda (key) (format "Duplicate NAME \"%s\"" key)))) + +(defun org-lint-duplicate-target (ast) + (org-lint--collect-duplicates + ast + 'target + (lambda (target) (org-split-string (org-element-property :value target))) + (lambda (target _) (org-element-property :begin target)) + (lambda (key) + (format "Duplicate target <<%s>>" (mapconcat #'identity key " "))))) + +(defun org-lint-duplicate-footnote-definition (ast) + (org-lint--collect-duplicates + ast + 'footnote-definition + (lambda (definition) (org-element-property :label definition)) + (lambda (definition _) (org-element-property :post-affiliated definition)) + (lambda (key) (format "Duplicate footnote definition \"%s\"" key)))) + +(defun org-lint-orphaned-affiliated-keywords (ast) + ;; Ignore orphan RESULTS keywords, which could be generated from + ;; a source block returning no value. + (let ((keywords (cl-set-difference org-element-affiliated-keywords + '("RESULT" "RESULTS") + :test #'equal))) + (org-element-map ast 'keyword + (lambda (k) + (let ((key (org-element-property :key k))) + (and (or (let ((case-fold-search t)) + (string-match-p "\\`ATTR_[-_A-Za-z0-9]+\\'" key)) + (member key keywords)) + (list (org-element-property :post-affiliated k) + (format "Orphaned affiliated keyword: \"%s\"" key)))))))) + +(defun org-lint-obsolete-affiliated-keywords (_) + (let ((regexp (format "^[ \t]*#\\+%s:" + (regexp-opt '("DATA" "LABEL" "RESNAME" "SOURCE" + "SRCNAME" "TBLNAME" "RESULT" "HEADERS") + t))) + reports) + (while (re-search-forward regexp nil t) + (let ((key (upcase (match-string-no-properties 1)))) + (when (< (point) + (org-element-property :post-affiliated (org-element-at-point))) + (push + (list (line-beginning-position) + (format + "Obsolete affiliated keyword: \"%s\". Use \"%s\" instead" + key + (pcase key + ("HEADERS" "HEADER") + ("RESULT" "RESULTS") + (_ "NAME")))) + reports)))) + reports)) + +(defun org-lint-deprecated-export-blocks (ast) + (let ((deprecated '("ASCII" "BEAMER" "HTML" "LATEX" "MAN" "MARKDOWN" "MD" + "ODT" "ORG" "TEXINFO"))) + (org-element-map ast 'special-block + (lambda (b) + (let ((type (org-element-property :type b))) + (when (member-ignore-case type deprecated) + (list + (org-element-property :post-affiliated b) + (format + "Deprecated syntax for export block. Use \"BEGIN_EXPORT %s\" \ +instead" + type)))))))) + +(defun org-lint-deprecated-header-syntax (ast) + (let* ((deprecated-babel-properties + (mapcar (lambda (arg) (symbol-name (car arg))) + org-babel-common-header-args-w-values)) + (deprecated-re + (format "\\`%s[ \t]" (regexp-opt deprecated-babel-properties t)))) + (org-element-map ast '(keyword node-property) + (lambda (datum) + (let ((key (org-element-property :key datum))) + (pcase (org-element-type datum) + (`keyword + (let ((value (org-element-property :value datum))) + (and (string= key "PROPERTY") + (string-match deprecated-re value) + (list (org-element-property :begin datum) + (format "Deprecated syntax for \"%s\". \ +Use header-args instead" + (match-string-no-properties 1 value)))))) + (`node-property + (and (member-ignore-case key deprecated-babel-properties) + (list + (org-element-property :begin datum) + (format "Deprecated syntax for \"%s\". \ +Use :header-args: instead" + key)))))))))) + +(defun org-lint-missing-language-in-src-block (ast) + (org-element-map ast 'src-block + (lambda (b) + (unless (org-element-property :language b) + (list (org-element-property :post-affiliated b) + "Missing language in source block"))))) + +(defun org-lint-missing-backend-in-export-block (ast) + (org-element-map ast 'export-block + (lambda (b) + (unless (org-element-property :type b) + (list (org-element-property :post-affiliated b) + "Missing back-end in export block"))))) + +(defun org-lint-invalid-babel-call-block (ast) + (org-element-map ast 'babel-call + (lambda (b) + (cond + ((not (org-element-property :call b)) + (list (org-element-property :post-affiliated b) + "Invalid syntax in babel call block")) + ((let ((h (org-element-property :end-header b))) + (and h (string-match-p "\\`\\[.*\\]\\'" h))) + (list + (org-element-property :post-affiliated b) + "Babel call's end header must not be wrapped within brackets")))))) + +(defun org-lint-deprecated-category-setup (ast) + (org-element-map ast 'keyword + (let (category-flag) + (lambda (k) + (cond + ((not (string= (org-element-property :key k) "CATEGORY")) nil) + (category-flag + (list (org-element-property :post-affiliated k) + "Spurious CATEGORY keyword. Set :CATEGORY: property instead")) + (t (setf category-flag t) nil)))))) + +(defun org-lint-invalid-coderef-link (ast) + (let ((info (list :parse-tree ast))) + (org-element-map ast 'link + (lambda (link) + (let ((ref (org-element-property :path link))) + (and (equal (org-element-property :type link) "coderef") + (not (ignore-errors (org-export-resolve-coderef ref info))) + (list (org-element-property :begin link) + (format "Unknown coderef \"%s\"" ref)))))))) + +(defun org-lint-invalid-custom-id-link (ast) + (let ((info (list :parse-tree ast))) + (org-element-map ast 'link + (lambda (link) + (and (equal (org-element-property :type link) "custom-id") + (not (ignore-errors (org-export-resolve-id-link link info))) + (list (org-element-property :begin link) + (format "Unknown custom ID \"%s\"" + (org-element-property :path link)))))))) + +(defun org-lint-invalid-fuzzy-link (ast) + (let ((info (list :parse-tree ast))) + (org-element-map ast 'link + (lambda (link) + (and (equal (org-element-property :type link) "fuzzy") + (not (ignore-errors (org-export-resolve-fuzzy-link link info))) + (list (org-element-property :begin link) + (format "Unknown fuzzy location \"%s\"" + (let ((path (org-element-property :path link))) + (if (string-prefix-p "*" path) + (substring path 1) + path))))))))) + +(defun org-lint-invalid-id-link (ast) + (org-element-map ast 'link + (lambda (link) + (let ((id (org-element-property :path link))) + (and (equal (org-element-property :type link) "id") + (not (org-id-find id)) + (list (org-element-property :begin link) + (format "Unknown ID \"%s\"" id))))))) + +(defun org-lint-special-property-in-properties-drawer (ast) + (org-element-map ast 'node-property + (lambda (p) + (let ((key (org-element-property :key p))) + (and (member-ignore-case key org-special-properties) + (list (org-element-property :begin p) + (format + "Special property \"%s\" found in a properties drawer" + key))))))) + +(defun org-lint-obsolete-properties-drawer (ast) + (org-element-map ast 'drawer + (lambda (d) + (when (equal (org-element-property :drawer-name d) "PROPERTIES") + (let ((section (org-element-lineage d '(section)))) + (unless (org-element-map section 'property-drawer #'identity nil t) + (list (org-element-property :post-affiliated d) + (if (save-excursion + (goto-char (org-element-property :post-affiliated d)) + (forward-line -1) + (or (org-at-heading-p) (org-at-planning-p))) + "Incorrect contents for PROPERTIES drawer" + "Incorrect location for PROPERTIES drawer")))))))) + +(defun org-lint-link-to-local-file (ast) + (org-element-map ast 'link + (lambda (l) + (when (equal (org-element-property :type l) "file") + (let ((file (org-link-unescape (org-element-property :path l)))) + (and (not (file-remote-p file)) + (not (file-exists-p file)) + (list (org-element-property :begin l) + (format (if (org-element-lineage l '(link)) + "Link to non-existent image file \"%s\"\ + in link description" + "Link to non-existent local file \"%s\"") + file)))))))) + +(defun org-lint-non-existent-setupfile-parameter (ast) + (org-element-map ast 'keyword + (lambda (k) + (when (equal (org-element-property :key k) "SETUPFILE") + (let ((file (org-unbracket-string + "\"" "\"" + (org-element-property :value k)))) + (and (not (file-remote-p file)) + (not (file-exists-p file)) + (list (org-element-property :begin k) + (format "Non-existent setup file \"%s\"" file)))))))) + +(defun org-lint-wrong-include-link-parameter (ast) + (org-element-map ast 'keyword + (lambda (k) + (when (equal (org-element-property :key k) "INCLUDE") + (let* ((value (org-element-property :value k)) + (path + (and (string-match "^\\(\".+\"\\|\\S-+\\)[ \t]*" value) + (save-match-data + (org-unbracket-string "\"" "\"" (match-string 1 value)))))) + (if (not path) + (list (org-element-property :post-affiliated k) + "Missing location argument in INCLUDE keyword") + (let* ((file (org-string-nw-p + (if (string-match "::\\(.*\\)\\'" path) + (substring path 0 (match-beginning 0)) + path))) + (search (and (not (equal file path)) + (org-string-nw-p (match-string 1 path))))) + (if (and file + (not (file-remote-p file)) + (not (file-exists-p file))) + (list (org-element-property :post-affiliated k) + "Non-existent file argument in INCLUDE keyword") + (let* ((visiting (if file (find-buffer-visiting file) + (current-buffer))) + (buffer (or visiting (find-file-noselect file)))) + (unwind-protect + (with-current-buffer buffer + (when (and search + (not + (ignore-errors + (let ((org-link-search-inhibit-query t)) + (org-link-search search nil t))))) + (list (org-element-property :post-affiliated k) + (format + "Invalid search part \"%s\" in INCLUDE keyword" + search)))) + (unless visiting (kill-buffer buffer)))))))))))) + +(defun org-lint-obsolete-include-markup (ast) + (let ((regexp (format "\\`\\(?:\".+\"\\|\\S-+\\)[ \t]+%s" + (regexp-opt + '("ASCII" "BEAMER" "HTML" "LATEX" "MAN" "MARKDOWN" "MD" + "ODT" "ORG" "TEXINFO") + t)))) + (org-element-map ast 'keyword + (lambda (k) + (when (equal (org-element-property :key k) "INCLUDE") + (let ((case-fold-search t) + (value (org-element-property :value k))) + (when (string-match regexp value) + (let ((markup (match-string-no-properties 1 value))) + (list (org-element-property :post-affiliated k) + (format "Obsolete markup \"%s\" in INCLUDE keyword. \ +Use \"export %s\" instead" + markup + markup)))))))))) + +(defun org-lint-unknown-options-item (ast) + (let ((allowed (delq nil + (append + (mapcar (lambda (o) (nth 2 o)) org-export-options-alist) + (cl-mapcan + (lambda (b) + (mapcar (lambda (o) (nth 2 o)) + (org-export-backend-options b))) + org-export-registered-backends)))) + reports) + (org-element-map ast 'keyword + (lambda (k) + (when (string= (org-element-property :key k) "OPTIONS") + (let ((value (org-element-property :value k)) + (start 0)) + (while (string-match "\\(.+?\\):\\((.*?)\\|\\S-*\\)[ \t]*" + value + start) + (setf start (match-end 0)) + (let ((item (match-string 1 value))) + (unless (member item allowed) + (push (list (org-element-property :post-affiliated k) + (format "Unknown OPTIONS item \"%s\"" item)) + reports)))))))) + reports)) + +(defun org-lint-invalid-macro-argument-and-template (ast) + (let ((extract-placeholders + (lambda (template) + (let ((start 0) + args) + (while (string-match "\\$\\([1-9][0-9]*\\)" template start) + (setf start (match-end 0)) + (push (string-to-number (match-string 1 template)) args)) + (sort (org-uniquify args) #'<)))) + reports) + ;; Check arguments for macro templates. + (org-element-map ast 'keyword + (lambda (k) + (when (string= (org-element-property :key k) "MACRO") + (let* ((value (org-element-property :value k)) + (name (and (string-match "^\\S-+" value) + (match-string 0 value))) + (template (and name + (org-trim (substring value (match-end 0)))))) + (cond + ((not name) + (push (list (org-element-property :post-affiliated k) + "Missing name in MACRO keyword") + reports)) + ((not (org-string-nw-p template)) + (push (list (org-element-property :post-affiliated k) + "Missing template in macro \"%s\"" name) + reports)) + (t + (unless (let ((args (funcall extract-placeholders template))) + (equal (number-sequence 1 (or (org-last args) 0)) args)) + (push (list (org-element-property :post-affiliated k) + (format "Unused placeholders in macro \"%s\"" + name)) + reports)))))))) + ;; Check arguments for macros. + (org-macro-initialize-templates) + (let ((templates (append + (mapcar (lambda (m) (cons m "$1")) + '("author" "date" "email" "title" "results")) + org-macro-templates))) + (org-element-map ast 'macro + (lambda (macro) + (let* ((name (org-element-property :key macro)) + (template (cdr (assoc-string name templates t)))) + (if (not template) + (push (list (org-element-property :begin macro) + (format "Undefined macro \"%s\"" name)) + reports) + (let ((arg-numbers (funcall extract-placeholders template))) + (when arg-numbers + (let ((spurious-args + (nthcdr (apply #'max arg-numbers) + (org-element-property :args macro)))) + (when spurious-args + (push + (list (org-element-property :begin macro) + (format "Unused argument%s in macro \"%s\": %s" + (if (> (length spurious-args) 1) "s" "") + name + (mapconcat (lambda (a) (format "\"%s\"" a)) + spurious-args + ", "))) + reports)))))))))) + reports)) + +(defun org-lint-undefined-footnote-reference (ast) + (let ((definitions (org-element-map ast 'footnote-definition + (lambda (f) (org-element-property :label f))))) + (org-element-map ast 'footnote-reference + (lambda (f) + (let ((label (org-element-property :label f))) + (and label + (not (member label definitions)) + (list (org-element-property :begin f) + (format "Missing definition for footnote [%s]" + label)))))))) + +(defun org-lint-unreferenced-footnote-definition (ast) + (let ((references (org-element-map ast 'footnote-reference + (lambda (f) (org-element-property :label f))))) + (org-element-map ast 'footnote-definition + (lambda (f) + (let ((label (org-element-property :label f))) + (and label + (not (member label references)) + (list (org-element-property :post-affiliated f) + (format "No reference for footnote definition [%s]" + label)))))))) + +(defun org-lint-colon-in-name (ast) + (org-element-map ast org-element-all-elements + (lambda (e) + (let ((name (org-element-property :name e))) + (and name + (string-match-p ":" name) + (list (progn + (goto-char (org-element-property :begin e)) + (re-search-forward + (format "^[ \t]*#\\+\\w+: +%s *$" (regexp-quote name))) + (match-beginning 0)) + (format + "Name \"%s\" contains a colon; Babel cannot use it as input" + name))))))) + +(defun org-lint-misplaced-planning-info (_) + (let ((case-fold-search t) + reports) + (while (re-search-forward org-planning-line-re nil t) + (unless (memq (org-element-type (org-element-at-point)) + '(comment-block example-block export-block planning + src-block verse-block)) + (push (list (line-beginning-position) "Misplaced planning info line") + reports))) + reports)) + +(defun org-lint-incomplete-drawer (_) + (let (reports) + (while (re-search-forward org-drawer-regexp nil t) + (let ((name (org-trim (match-string-no-properties 0))) + (element (org-element-at-point))) + (pcase (org-element-type element) + ((or `drawer `property-drawer) + (goto-char (org-element-property :end element)) + nil) + ((or `comment-block `example-block `export-block `src-block + `verse-block) + nil) + (_ + (push (list (line-beginning-position) + (format "Possible incomplete drawer \"%s\"" name)) + reports))))) + reports)) + +(defun org-lint-indented-diary-sexp (_) + (let (reports) + (while (re-search-forward "^[ \t]+%%(" nil t) + (unless (memq (org-element-type (org-element-at-point)) + '(comment-block diary-sexp example-block export-block + src-block verse-block)) + (push (list (line-beginning-position) "Possible indented diary-sexp") + reports))) + reports)) + +(defun org-lint-invalid-block (_) + (let ((case-fold-search t) + (regexp "^[ \t]*#\\+\\(BEGIN\\|END\\)\\(?::\\|_[^[:space:]]*\\)?[ \t]*") + reports) + (while (re-search-forward regexp nil t) + (let ((name (org-trim (buffer-substring-no-properties + (line-beginning-position) (line-end-position))))) + (cond + ((and (string-prefix-p "END" (match-string 1) t) + (not (eolp))) + (push (list (line-beginning-position) + (format "Invalid block closing line \"%s\"" name)) + reports)) + ((not (memq (org-element-type (org-element-at-point)) + '(center-block comment-block dynamic-block example-block + export-block quote-block special-block + src-block verse-block))) + (push (list (line-beginning-position) + (format "Possible incomplete block \"%s\"" + name)) + reports))))) + reports)) + +(defun org-lint-invalid-keyword-syntax (_) + (let ((regexp "^[ \t]*#\\+\\([^[:space:]:]*\\)\\(?: \\|$\\)") + (exception-re + (format "[ \t]*#\\+%s\\(\\[.*\\]\\)?:\\(?: \\|$\\)" + (regexp-opt org-element-dual-keywords))) + reports) + (while (re-search-forward regexp nil t) + (let ((name (match-string-no-properties 1))) + (unless (or (string-prefix-p "BEGIN" name t) + (string-prefix-p "END" name t) + (save-excursion + (beginning-of-line) + (let ((case-fold-search t)) (looking-at exception-re)))) + (push (list (match-beginning 0) + (format "Possible missing colon in keyword \"%s\"" name)) + reports)))) + reports)) + +(defun org-lint-extraneous-element-in-footnote-section (ast) + (org-element-map ast 'headline + (lambda (h) + (and (org-element-property :footnote-section-p h) + (org-element-map (org-element-contents h) + (cl-remove-if + (lambda (e) + (memq e '(comment comment-block footnote-definition + property-drawer section))) + org-element-all-elements) + (lambda (e) + (not (and (eq (org-element-type e) 'headline) + (org-element-property :commentedp e)))) + nil t '(footnote-definition property-drawer)) + (list (org-element-property :begin h) + "Extraneous elements in footnote section are not exported"))))) + +(defun org-lint-quote-section (ast) + (org-element-map ast '(headline inlinetask) + (lambda (h) + (let ((title (org-element-property :raw-value h))) + (and (or (string-prefix-p "QUOTE " title) + (string-prefix-p (concat org-comment-string " QUOTE ") title)) + (list (org-element-property :begin h) + "Deprecated QUOTE section")))))) + +(defun org-lint-file-application (ast) + (org-element-map ast 'link + (lambda (l) + (let ((app (org-element-property :application l))) + (and app + (list (org-element-property :begin l) + (format "Deprecated \"file+%s\" link type" app))))))) + +(defun org-lint-wrong-header-argument (ast) + (let* ((reports) + (verify + (lambda (datum language headers) + (let ((allowed + ;; If LANGUAGE is specified, restrict allowed + ;; headers to both LANGUAGE-specific and default + ;; ones. Otherwise, accept headers from any loaded + ;; language. + (append + org-babel-header-arg-names + (cl-mapcan + (lambda (l) + (let ((v (intern (format "org-babel-header-args:%s" l)))) + (and (boundp v) (mapcar #'car (symbol-value v))))) + (if language (list language) + (mapcar #'car org-babel-load-languages)))))) + (dolist (header headers) + (let ((h (symbol-name (car header))) + (p (or (org-element-property :post-affiliated datum) + (org-element-property :begin datum)))) + (cond + ((not (string-prefix-p ":" h)) + (push + (list p + (format "Missing colon in header argument \"%s\"" h)) + reports)) + ((assoc-string (substring h 1) allowed)) + (t (push (list p (format "Unknown header argument \"%s\"" h)) + reports))))))))) + (org-element-map ast '(babel-call inline-babel-call inline-src-block keyword + node-property src-block) + (lambda (datum) + (pcase (org-element-type datum) + ((or `babel-call `inline-babel-call) + (funcall verify + datum + nil + (cl-mapcan #'org-babel-parse-header-arguments + (list + (org-element-property :inside-header datum) + (org-element-property :end-header datum))))) + (`inline-src-block + (funcall verify + datum + (org-element-property :language datum) + (org-babel-parse-header-arguments + (org-element-property :parameters datum)))) + (`keyword + (when (string= (org-element-property :key datum) "PROPERTY") + (let ((value (org-element-property :value datum))) + (when (string-match "\\`header-args\\(?::\\(\\S-+\\)\\)?\\+? *" + value) + (funcall verify + datum + (match-string 1 value) + (org-babel-parse-header-arguments + (substring value (match-end 0)))))))) + (`node-property + (let ((key (org-element-property :key datum))) + (when (let ((case-fold-search t)) + (string-match "\\`HEADER-ARGS\\(?::\\(\\S-+\\)\\)?\\+?" + key)) + (funcall verify + datum + (match-string 1 key) + (org-babel-parse-header-arguments + (org-element-property :value datum)))))) + (`src-block + (funcall verify + datum + (org-element-property :language datum) + (cl-mapcan #'org-babel-parse-header-arguments + (cons (org-element-property :parameters datum) + (org-element-property :header datum)))))))) + reports)) + +(defun org-lint-wrong-header-value (ast) + (let (reports) + (org-element-map ast + '(babel-call inline-babel-call inline-src-block src-block) + (lambda (datum) + (let* ((type (org-element-type datum)) + (language (org-element-property :language datum)) + (allowed-header-values + (append (and language + (let ((v (intern (concat "org-babel-header-args:" + language)))) + (and (boundp v) (symbol-value v)))) + org-babel-common-header-args-w-values)) + (datum-header-values + (org-babel-parse-header-arguments + (org-trim + (pcase type + (`src-block + (mapconcat + #'identity + (cons (org-element-property :parameters datum) + (org-element-property :header datum)) + " ")) + (`inline-src-block + (or (org-element-property :parameters datum) "")) + (_ + (concat + (org-element-property :inside-header datum) + " " + (org-element-property :end-header datum)))))))) + (dolist (header datum-header-values) + (let ((allowed-values + (cdr (assoc-string (substring (symbol-name (car header)) 1) + allowed-header-values)))) + (unless (memq allowed-values '(:any nil)) + (let ((values (cdr header)) + groups-alist) + (dolist (v (if (stringp values) (org-split-string values) + (list values))) + (let ((valid-value nil)) + (catch 'exit + (dolist (group allowed-values) + (cond + ((not (funcall + (if (stringp v) #'assoc-string #'assoc) + v group)) + (when (memq :any group) + (setf valid-value t) + (push (cons group v) groups-alist))) + ((assq group groups-alist) + (push + (list + (or (org-element-property :post-affiliated datum) + (org-element-property :begin datum)) + (format + "Forbidden combination in header \"%s\": %s, %s" + (car header) + (cdr (assq group groups-alist)) + v)) + reports) + (throw 'exit nil)) + (t (push (cons group v) groups-alist) + (setf valid-value t)))) + (unless valid-value + (push + (list + (or (org-element-property :post-affiliated datum) + (org-element-property :begin datum)) + (format "Unknown value \"%s\" for header \"%s\"" + v + (car header))) + reports)))))))))))) + reports)) + +(defun org-lint-empty-headline-with-tags (ast) + (org-element-map ast '(headline inlinetask) + (lambda (h) + (let ((title (org-element-property :raw-value h))) + (and (string-match-p "\\`:[[:alnum:]_@#%:]+:\\'" title) + (list (org-element-property :begin h) + (format "Headline containing only tags is ambiguous: %S" + title))))))) + + +;;; Reports UI + +(defvar org-lint--report-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map tabulated-list-mode-map) + (define-key map (kbd "RET") 'org-lint--jump-to-source) + (define-key map (kbd "TAB") 'org-lint--show-source) + (define-key map (kbd "C-j") 'org-lint--show-source) + (define-key map (kbd "h") 'org-lint--hide-checker) + (define-key map (kbd "i") 'org-lint--ignore-checker) + map) + "Local keymap for `org-lint--report-mode' buffers.") + +(define-derived-mode org-lint--report-mode tabulated-list-mode "OrgLint" + "Major mode used to display reports emitted during linting. +\\{org-lint--report-mode-map}" + (setf tabulated-list-format + `[("Line" 6 + (lambda (a b) + (< (string-to-number (aref (cadr a) 0)) + (string-to-number (aref (cadr b) 0)))) + :right-align t) + ("Trust" 5 t) + ("Warning" 0 t)]) + (tabulated-list-init-header)) + +(defun org-lint--generate-reports (buffer checkers) + "Generate linting report for BUFFER. + +CHECKERS is the list of checkers used. + +Return an alist (ID [LINE TRUST DESCRIPTION CHECKER]), suitable +for `tabulated-list-printer'." + (with-current-buffer buffer + (save-excursion + (goto-char (point-min)) + (let ((ast (org-element-parse-buffer)) + (id 0) + (last-line 1) + (last-pos 1)) + ;; Insert unique ID for each report. Replace buffer positions + ;; with line numbers. + (mapcar + (lambda (report) + (list + (cl-incf id) + (apply #'vector + (cons + (progn + (goto-char (car report)) + (beginning-of-line) + (prog1 (number-to-string + (cl-incf last-line + (count-lines last-pos (point)))) + (setf last-pos (point)))) + (cdr report))))) + ;; Insert trust level in generated reports. Also sort them + ;; by buffer position in order to optimize lines computation. + (sort (cl-mapcan + (lambda (c) + (let ((trust (symbol-name (org-lint-checker-trust c)))) + (mapcar + (lambda (report) + (list (car report) trust (nth 1 report) c)) + (save-excursion + (funcall + (intern (format "org-lint-%s" + (org-lint-checker-name c))) + ast))))) + checkers) + #'car-less-than-car)))))) + +(defvar-local org-lint--source-buffer nil + "Source buffer associated to current report buffer.") + +(defvar-local org-lint--local-checkers nil + "List of checkers used to build current report.") + +(defun org-lint--refresh-reports () + (setq tabulated-list-entries + (org-lint--generate-reports org-lint--source-buffer + org-lint--local-checkers)) + (tabulated-list-print)) + +(defun org-lint--current-line () + "Return current report line, as a number." + (string-to-number (aref (tabulated-list-get-entry) 0))) + +(defun org-lint--current-checker (&optional entry) + "Return current report checker. +When optional argument ENTRY is non-nil, use this entry instead +of current one." + (aref (if entry (nth 1 entry) (tabulated-list-get-entry)) 3)) + +(defun org-lint--display-reports (source checkers) + "Display linting reports for buffer SOURCE. +CHECKERS is the list of checkers used." + (let ((buffer (get-buffer-create "*Org Lint*"))) + (with-current-buffer buffer + (org-lint--report-mode) + (setf org-lint--source-buffer source) + (setf org-lint--local-checkers checkers) + (org-lint--refresh-reports) + (tabulated-list-print) + (add-hook 'tabulated-list-revert-hook #'org-lint--refresh-reports nil t)) + (pop-to-buffer buffer))) + +(defun org-lint--jump-to-source () + "Move to source line that generated the report at point." + (interactive) + (let ((l (org-lint--current-line))) + (switch-to-buffer-other-window org-lint--source-buffer) + (org-goto-line l) + (org-show-set-visibility 'local) + (recenter))) + +(defun org-lint--show-source () + "Show source line that generated the report at point." + (interactive) + (let ((buffer (current-buffer))) + (org-lint--jump-to-source) + (switch-to-buffer-other-window buffer))) + +(defun org-lint--hide-checker () + "Hide all reports from checker that generated the report at point." + (interactive) + (let ((c (org-lint--current-checker))) + (setf tabulated-list-entries + (cl-remove-if (lambda (e) (equal c (org-lint--current-checker e))) + tabulated-list-entries)) + (tabulated-list-print))) + +(defun org-lint--ignore-checker () + "Ignore all reports from checker that generated the report at point. +Checker will also be ignored in all subsequent reports." + (interactive) + (setf org-lint--local-checkers + (remove (org-lint--current-checker) org-lint--local-checkers)) + (org-lint--hide-checker)) + + +;;; Public function + +;;;###autoload +(defun org-lint (&optional arg) + "Check current Org buffer for syntax mistakes. + +By default, run all checkers. With a `\\[universal-argument]' prefix ARG, \ +select one +category of checkers only. With a `\\[universal-argument] \ +\\[universal-argument]' prefix, run one precise +checker by its name. + +ARG can also be a list of checker names, as symbols, to run." + (interactive "P") + (unless (derived-mode-p 'org-mode) (user-error "Not in an Org buffer")) + (when (called-interactively-p 'any) + (message "Org linting process starting...")) + (let ((checkers + (pcase arg + (`nil org-lint--checkers) + (`(4) + (let ((category + (completing-read + "Checker category: " + (mapcar #'org-lint-checker-categories org-lint--checkers) + nil t))) + (cl-remove-if-not + (lambda (c) + (assoc-string (org-lint-checker-categories c) category)) + org-lint--checkers))) + (`(16) + (list + (let ((name (completing-read + "Checker name: " + (mapcar #'org-lint-checker-name org-lint--checkers) + nil t))) + (catch 'exit + (dolist (c org-lint--checkers) + (when (string= (org-lint-checker-name c) name) + (throw 'exit c))))))) + ((pred consp) + (cl-remove-if-not (lambda (c) (memq (org-lint-checker-name c) arg)) + org-lint--checkers)) + (_ (user-error "Invalid argument `%S' for `org-lint'" arg))))) + (if (not (called-interactively-p 'any)) + (org-lint--generate-reports (current-buffer) checkers) + (org-lint--display-reports (current-buffer) checkers) + (message "Org linting process completed")))) + + +(provide 'org-lint) +;;; org-lint.el ends here diff --git a/lisp/org/org-list.el b/lisp/org/org-list.el index a24c496d72..a3e26256f9 100644 --- a/lisp/org/org-list.el +++ b/lisp/org/org-list.el @@ -1,4 +1,4 @@ -;;; org-list.el --- Plain lists for Org-mode +;;; org-list.el --- Plain lists for Org -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. ;; @@ -25,7 +25,7 @@ ;; ;;; Commentary: -;; This file contains the code dealing with plain lists in Org-mode. +;; This file contains the code dealing with plain lists in Org mode. ;; The core concept behind lists is their structure. A structure is ;; a snapshot of the list, in the shape of a data tree (see @@ -76,8 +76,7 @@ ;;; Code: -(eval-when-compile - (require 'cl)) +(require 'cl-lib) (require 'org-macs) (require 'org-compat) @@ -88,59 +87,84 @@ (defvar org-closed-string) (defvar org-deadline-string) (defvar org-description-max-indent) -(defvar org-drawers) +(defvar org-done-keywords) +(defvar org-drawer-regexp) +(defvar org-element-all-objects) +(defvar org-inhibit-startup) (defvar org-odd-levels-only) +(defvar org-outline-regexp-bol) (defvar org-scheduled-string) +(defvar org-todo-line-regexp) (defvar org-ts-regexp) (defvar org-ts-regexp-both) -(declare-function outline-invisible-p "outline" (&optional pos)) -(declare-function outline-flag-region "outline" (from to flag)) -(declare-function outline-next-heading "outline" ()) -(declare-function outline-previous-heading "outline" ()) - -(declare-function org-at-heading-p "org" (&optional ignored)) -(declare-function org-before-first-heading-p "org" ()) +(declare-function org-at-heading-p "org" (&optional invisible-ok)) (declare-function org-back-to-heading "org" (&optional invisible-ok)) +(declare-function org-before-first-heading-p "org" ()) (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-element-at-point "org-element" ()) +(declare-function org-element-context "org-element" (&optional element)) +(declare-function org-element-interpret-data "org-element" (data)) +(declare-function + org-element-lineage "org-element" (blob &optional types with-self)) +(declare-function org-element-macro-interpreter "org-element" (macro ##)) +(declare-function + org-element-map "org-element" + (data types fun &optional info first-match no-recursion with-affiliated)) +(declare-function org-element-normalize-string "org-element" (s)) +(declare-function org-element-parse-buffer "org-element" + (&optional granularity visible-only)) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-put-property "org-element" + (element property value)) +(declare-function org-element-set-element "org-element" (old new)) +(declare-function org-element-type "org-element" (element)) +(declare-function org-element-update-syntax "org-element" ()) (declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) +(declare-function org-export-create-backend "ox" (&rest rest) t) +(declare-function org-export-data-with-backend "ox" (data backend info)) +(declare-function org-export-get-backend "ox" (name)) +(declare-function org-export-get-environment "ox" + (&optional backend subtreep ext-plist)) +(declare-function org-export-get-next-element "ox" + (blob info &optional n)) +(declare-function org-export-with-backend "ox" + (backend data &optional contents info)) (declare-function org-fix-tags-on-the-fly "org" ()) (declare-function org-get-indentation "org" (&optional line)) -(declare-function org-icompleting-read "org" (&rest args)) +(declare-function org-get-todo-state "org" ()) (declare-function org-in-block-p "org" (names)) (declare-function org-in-regexp "org" (re &optional nlines visually)) +(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-level-increment "org" ()) (declare-function org-narrow-to-subtree "org" ()) -(declare-function org-at-heading-p "org" (&optional invisible-ok)) -(declare-function org-previous-line-empty-p "org" (&optional next)) -(declare-function org-remove-if "org" (predicate seq)) +(declare-function org-outline-level "org" ()) +(declare-function org-previous-line-empty-p "org" ()) (declare-function org-reduced-level "org" (L)) +(declare-function org-remove-indentation "org" (code &optional n)) (declare-function org-show-subtree "org" ()) (declare-function org-sort-remove-invisible "org" (S)) (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-trim "org" (s &optional keep-lead)) (declare-function org-uniquify "org" (list)) - -(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-export-string-as "ox" - (string backend &optional body-only ext-plist)) - +(declare-function org-invisible-p "org" (&optional pos)) +(declare-function outline-flag-region "outline" (from to flag)) +(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." + "Options concerning plain lists in Org mode." :tag "Org Plain lists" :group 'org-structure) @@ -211,14 +235,20 @@ into (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." +Valid values are ?. and ?\). To get both terminators, use t. + +This variable needs to be set before org.el is loaded. If you +need to make a change while Emacs is running, use the customize +interface or run the following code after updating it: + + `\\[org-element-update-syntax]'" :group 'org-plain-lists :type '(choice (const :tag "dot like in \"2.\"" ?.) (const :tag "paren like in \"2)\"" ?\)) - (const :tag "both" t))) + (const :tag "both" t)) + :set (lambda (var val) (set var val) + (when (featurep 'org-element) (org-element-update-syntax)))) -(define-obsolete-variable-alias 'org-alphabetical-lists - 'org-list-allow-alphabetical "24.4") ; Since 8.0 (defcustom org-list-allow-alphabetical nil "Non-nil means single character alphabetical bullets are allowed. @@ -230,13 +260,12 @@ This variable needs to be set before org.el is loaded. If you need to make a change while Emacs is running, use the customize interface or run the following code after updating it: - (when (featurep \\='org-element) (load \"org-element\" t t))" + `\\[org-element-update-syntax]'" :group 'org-plain-lists :version "24.1" :type 'boolean - :set (lambda (var val) - (when (featurep 'org-element) (load "org-element" t t)) - (set var val))) + :set (lambda (var val) (set var val) + (when (featurep 'org-element) (org-element-update-syntax)))) (defcustom org-list-two-spaces-after-bullet-regexp nil "A regular expression matching bullets that should have 2 spaces after them. @@ -250,23 +279,22 @@ spaces instead of one after the bullet in each item of the list." (const :tag "never" nil) (regexp))) -(define-obsolete-variable-alias 'org-empty-line-terminates-plain-lists - 'org-list-empty-line-terminates-plain-lists "24.4") ;; Since 8.0 -(defcustom org-list-empty-line-terminates-plain-lists nil - "Non-nil means an empty line ends all plain list levels. -Otherwise, two of them will be necessary." - :group 'org-plain-lists - :type 'boolean) - (defcustom org-list-automatic-rules '((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: + `\\[org-meta-return]', + `\\[org-metaright]', + `\\[org-metaleft]', + `\\[org-shiftmetaright]', + `\\[org-shiftmetaleft]', + `\\[org-ctrl-c-minus]', + `\\[org-toggle-checkbox]', + `\\[org-insert-todo-heading]'. + +You can disable individually these rules by setting them to nil. +Valid rules are: checkbox when non-nil, checkbox statistics is updated each time you either insert a new checkbox or toggle a checkbox. @@ -286,13 +314,15 @@ indent when non-nil, indenting or outdenting list top-item (defcustom org-list-use-circular-motion nil "Non-nil means commands implying motion in lists should be cyclic. - +\\ In that case, the item following the last item is the first one, and the item preceding the first item is the last one. -This affects the behavior of \\[org-move-item-up], - \\[org-move-item-down], \\[org-next-item] and - \\[org-previous-item]." +This affects the behavior of + `\\[org-move-item-up]', + `\\[org-move-item-down]', + `\\[org-next-item]', + `\\[org-previous-item]'." :group 'org-plain-lists :version "24.1" :type 'boolean) @@ -304,8 +334,6 @@ This hook runs even if checkbox rule in implement alternative ways of collecting statistics information.") -(define-obsolete-variable-alias 'org-hierarchical-checkbox-statistics - 'org-checkbox-hierarchical-statistics "24.4") ;; Since 8.0 (defcustom org-checkbox-hierarchical-statistics t "Non-nil means checkbox statistics counts only the state of direct children. When nil, all boxes below the cookie are counted. @@ -314,8 +342,6 @@ with the word \"recursive\" in the value." :group 'org-plain-lists :type 'boolean) -(org-defvaralias 'org-description-max-indent - 'org-list-description-max-indent) ;; Since 8.0 (defcustom org-list-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 @@ -358,8 +384,7 @@ list, obtained by prompting the user." (list (symbol :tag "Major mode") (string :tag "Format")))) -(defvar org-list-forbidden-blocks '("example" "verse" "src" "ascii" "beamer" - "html" "latex" "odt") +(defvar org-list-forbidden-blocks '("example" "verse" "src" "export") "Names of blocks where lists are not allowed. Names must be in lower case.") @@ -374,10 +399,8 @@ specifically, type `block' is determined by the variable ;;; Predicates and regexps -(defconst org-list-end-re (if org-list-empty-line-terminates-plain-lists "^[ \t]*\n" - "^[ \t]*\n[ \t]*\n") - "Regex corresponding to the end of a list. -It depends on `org-list-empty-line-terminates-plain-lists'.") +(defconst org-list-end-re "^[ \t]*\n[ \t]*\n" + "Regex matching the end of a plain list.") (defconst org-list-full-item-re (concat "^[ \t]*\\(\\(?:[-+*]\\|\\(?:[0-9]+\\|[A-Za-z]\\)[.)]\\)\\(?:[ \t]+\\|$\\)\\)" @@ -430,9 +453,6 @@ group 4: description tag") (let* ((case-fold-search t) (context (org-list-context)) (lim-up (car context)) - (drawers-re (concat "^[ \t]*:\\(" - (mapconcat #'regexp-quote org-drawers "\\|") - "\\):[ \t]*$")) (inlinetask-re (and (featurep 'org-inlinetask) (org-inlinetask-outline-regexp))) (item-re (org-item-re)) @@ -476,7 +496,7 @@ group 4: description tag") ((and (looking-at "^[ \t]*#\\+end_") (re-search-backward "^[ \t]*#\\+begin_" lim-up t))) ((and (looking-at "^[ \t]*:END:") - (re-search-backward drawers-re lim-up t)) + (re-search-backward org-drawer-regexp lim-up t)) (beginning-of-line)) ((and inlinetask-re (looking-at inlinetask-re)) (org-inlinetask-goto-beginning) @@ -547,11 +567,7 @@ Contexts `block' and `invalid' refer to `org-list-forbidden-blocks'." (lim-down (or (save-excursion (outline-next-heading)) (point-max)))) ;; Is point inside a drawer? (let ((end-re "^[ \t]*:END:") - ;; Can't use org-drawers-regexp as this function might - ;; be called in buffers not in Org mode. - (beg-re (concat "^[ \t]*:\\(" - (mapconcat #'regexp-quote org-drawers "\\|") - "\\):[ \t]*$"))) + (beg-re org-drawer-regexp)) (when (save-excursion (and (not (looking-at beg-re)) (not (looking-at end-re)) @@ -635,9 +651,6 @@ Assume point is at an item." (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))) @@ -654,7 +667,7 @@ Assume point is at an item." (match-string-no-properties 2) ; counter (match-string-no-properties 3) ; checkbox ;; Description tag. - (and (save-match-data (string-match "[-+*]" bullet)) + (and (string-match-p "[-+*]" bullet) (match-string-no-properties 4))))))) (end-before-blank (function @@ -700,7 +713,7 @@ Assume point is at an item." ((and (looking-at "^[ \t]*#\\+end_") (re-search-backward "^[ \t]*#\\+begin_" lim-up t))) ((and (looking-at "^[ \t]*:END:") - (re-search-backward drawers-re lim-up t)) + (re-search-backward org-drawer-regexp lim-up t)) (beginning-of-line)) ((and inlinetask-re (looking-at inlinetask-re)) (org-inlinetask-goto-beginning) @@ -766,7 +779,7 @@ Assume point is at an item." (cond ((and (looking-at "^[ \t]*#\\+begin_") (re-search-forward "^[ \t]*#\\+end_" lim-down t))) - ((and (looking-at drawers-re) + ((and (looking-at org-drawer-regexp) (re-search-forward "^[ \t]*:END:" lim-down t)))) (forward-line 1)))))) (setq struct (append itm-lst (cdr (nreverse itm-lst-2))) @@ -1021,7 +1034,7 @@ 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 - ((string-match "[[:alnum:]]" (org-list-get-bullet first struct)) 'ordered) + ((string-match-p "[[:alnum:]]" (org-list-get-bullet first struct)) 'ordered) ((org-list-get-tag first struct) 'descriptive) (t 'unordered)))) @@ -1043,7 +1056,7 @@ that value." (let ((seq 0) (pos item) counter) (while (and (not (setq counter (org-list-get-counter pos struct))) (setq pos (org-list-get-prev-item pos struct prevs))) - (incf seq)) + (cl-incf seq)) (if (not counter) (1+ seq) (cond ((string-match "[A-Za-z]" counter) @@ -1137,13 +1150,20 @@ This function modifies STRUCT." ;; Store overlays responsible for visibility status. We ;; also need to store their boundaries as they will be ;; removed from buffer. - (overlays (cons - (mapcar (lambda (ov) - (list ov (overlay-start ov) (overlay-end ov))) - (overlays-in beg-A end-A)) - (mapcar (lambda (ov) - (list ov (overlay-start ov) (overlay-end ov))) - (overlays-in beg-B end-B))))) + (overlays + (cons + (delq nil + (mapcar (lambda (o) + (and (>= (overlay-start o) beg-A) + (<= (overlay-end o) end-A) + (list o (overlay-start o) (overlay-end o)))) + (overlays-in beg-A end-A))) + (delq nil + (mapcar (lambda (o) + (and (>= (overlay-start o) beg-B) + (<= (overlay-end o) end-B) + (list o (overlay-start o) (overlay-end o)))) + (overlays-in beg-B end-B)))))) ;; 1. Move effectively items in buffer. (goto-char beg-A) (delete-region beg-A end-B-no-blank) @@ -1154,42 +1174,39 @@ This function modifies STRUCT." ;; 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) - (setq struct (sort struct (lambda (e1 e2) (< (car e1) (car e2))))) + (dolist (e struct) + (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)))))))) + (setq struct (sort struct #'car-less-than-car)) ;; Restore visibility status, by moving overlays to their new ;; position. - (mapc (lambda (ov) - (move-overlay - (car ov) - (+ (nth 1 ov) (- (+ beg-B (- size-B size-A)) beg-A)) - (+ (nth 2 ov) (- (+ beg-B (- size-B size-A)) beg-A)))) - (car overlays)) - (mapc (lambda (ov) - (move-overlay (car ov) - (+ (nth 1 ov) (- beg-A beg-B)) - (+ (nth 2 ov) (- beg-A beg-B)))) - (cdr overlays)) + (dolist (ov (car overlays)) + (move-overlay + (car ov) + (+ (nth 1 ov) (- (+ beg-B (- size-B size-A)) beg-A)) + (+ (nth 2 ov) (- (+ beg-B (- size-B size-A)) beg-A)))) + (dolist (ov (cdr overlays)) + (move-overlay (car ov) + (+ (nth 1 ov) (- beg-A beg-B)) + (+ (nth 2 ov) (- beg-A beg-B)))) ;; Return structure. struct))) @@ -1219,7 +1236,7 @@ some heuristics to guess the result." (point)))))))) (cond ;; Trivial cases where there should be none. - ((or org-list-empty-line-terminates-plain-lists (not insert-blank-p)) 0) + ((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 @@ -1272,12 +1289,16 @@ This function modifies STRUCT." (beforep (progn (looking-at org-list-full-item-re) - ;; Do not count tag in a non-descriptive list. - (<= pos (if (and (match-beginning 4) - (save-match-data - (string-match "[.)]" (match-string 1)))) - (match-beginning 4) - (match-end 0))))) + (<= pos + (cond + ((not (match-beginning 4)) (match-end 0)) + ;; Ignore tag in a non-descriptive list. + ((save-match-data (string-match "[.)]" (match-string 1))) + (match-beginning 4)) + (t (save-excursion + (goto-char (match-end 4)) + (skip-chars-forward " \t") + (point))))))) (split-line-p (org-get-alist-option org-M-RET-may-split-line 'item)) (blank-nb (org-list-separating-blank-lines-number pos struct prevs)) @@ -1317,7 +1338,7 @@ This function modifies STRUCT." (size-offset (- item-size (length text-cut)))) ;; 4. Insert effectively item into buffer. (goto-char item) - (org-indent-to-column ind) + (indent-to-column ind) (insert body item-sep) ;; 5. Add new item to STRUCT. (mapc (lambda (e) @@ -1459,7 +1480,7 @@ This function returns, destructively, the new list structure." (save-excursion (goto-char (org-list-get-last-item item struct prevs)) (point-at-eol))) - ((string-match "\\`[0-9]+\\'" dest) + ((string-match-p "\\`[0-9]+\\'" dest) (let* ((all (org-list-get-all-items item struct prevs)) (len (length all)) (index (mod (string-to-number dest) len))) @@ -1473,8 +1494,10 @@ This function returns, destructively, the new list structure." (point-at-eol))))) (t dest))) (org-M-RET-may-split-line nil) - ;; Store visibility. - (visibility (overlays-in item item-end))) + ;; Store inner overlays (to preserve visibility). + (overlays (cl-remove-if (lambda (o) (or (< (overlay-start o) item) + (> (overlay-end o) item))) + (overlays-in item item-end)))) (cond ((eq dest 'delete) (org-list-delete-item item struct)) ((eq dest 'kill) @@ -1509,13 +1532,12 @@ This function returns, destructively, the new list structure." new-end (+ end shift))))))) moved-items)) - (lambda (e1 e2) (< (car e1) (car e2)))))) - ;; 2. Restore visibility. - (mapc (lambda (ov) - (move-overlay ov - (+ (overlay-start ov) (- (point) item)) - (+ (overlay-end ov) (- (point) item)))) - visibility) + #'car-less-than-car))) + ;; 2. Restore inner overlays. + (dolist (o overlays) + (move-overlay o + (+ (overlay-start o) (- (point) item)) + (+ (overlay-end o) (- (point) item)))) ;; 3. Eventually delete extra copy of the item and clean marker. (prog1 (org-list-delete-item (marker-position item) struct) (move-marker item nil))) @@ -1632,7 +1654,7 @@ as returned by `org-list-prevs-alist'." (while item (let ((count (org-list-get-counter item struct))) ;; Virtually determine current bullet - (if (and count (string-match "[a-zA-Z]" count)) + (if (and count (string-match-p "[a-zA-Z]" count)) ;; Counters are not case-sensitive. (setq ascii (string-to-char (upcase count))) (setq ascii (1+ ascii))) @@ -1861,10 +1883,9 @@ Initial position of cursor is restored after the changes." (item-re (org-item-re)) (shift-body-ind (function - ;; Shift the indentation between END and BEG by DELTA. If - ;; MAX-IND is non-nil, ensure that no line will be indented - ;; more than that number. Start from the line before END. - (lambda (end beg delta max-ind) + ;; 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) @@ -1876,10 +1897,8 @@ Initial position of cursor is restored after the changes." ((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 - (if max-ind (min (+ i delta) max-ind) (+ i delta)))))) + ((looking-at-p "^[ \t]*\\S-") + (indent-line-to (+ (org-get-indentation) delta)))) (forward-line -1))))) (modify-item (function @@ -1934,37 +1953,53 @@ Initial position of cursor is restored after the changes." ;; 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 '>))) + (let ((item-up (assoc-default end-pos acc-end #'>))) (push (cons end-pos item-up) end-list))) (push (cons end-pos pos) acc-end))) ;; 2. Slice the items into parts that should be shifted by the ;; same amount of indentation. Each slice follow the pattern - ;; (END BEG DELTA MAX-IND-OR-NIL). Slices are returned in - ;; reverse order. + ;; (END BEG DELTA). Slices are returned in reverse order. (setq all-ends (sort (append (mapcar #'car itm-shift) (org-uniquify (mapcar #'car end-list))) - '<)) + #'<) + acc-end (nreverse acc-end)) (while (cdr all-ends) (let* ((up (pop all-ends)) (down (car all-ends)) (itemp (assq up struct)) - (item (if itemp up (cdr (assq up end-list)))) - (ind (cdr (assq item itm-shift))) - ;; If we're not at an item, there's a child of the item - ;; point belongs to above. Make sure this slice isn't - ;; moved within that child by specifying a maximum - ;; indentation. - (max-ind (and (not itemp) - (+ (org-list-get-ind item struct) - (length (org-list-get-bullet item struct)) - org-list-indent-offset)))) - (push (list down up ind max-ind) sliced-struct))) + (delta + (if itemp (cdr (assq up itm-shift)) + ;; If we're not at an item, there's a child of the + ;; item point belongs to above. Make sure the less + ;; indented line in this slice has the same column + ;; as that child. + (let* ((child (cdr (assq up acc-end))) + (ind (org-list-get-ind child struct)) + (min-ind most-positive-fixnum)) + (save-excursion + (goto-char up) + (while (< (point) down) + ;; Ignore empty lines. Also ignore blocks and + ;; drawers contents. + (unless (looking-at-p "[ \t]*$") + (setq min-ind (min (org-get-indentation) min-ind)) + (cond + ((and (looking-at "#\\+BEGIN\\(:\\|_\\S-+\\)") + (re-search-forward + (format "^[ \t]*#\\+END%s[ \t]*$" + (match-string 1)) + down t))) + ((and (looking-at org-drawer-regexp) + (re-search-forward "^[ \t]*:END:[ \t]*$" + down t))))) + (forward-line))) + (- ind min-ind))))) + (push (list down up delta) 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. (dolist (e sliced-struct) - (unless (and (zerop (nth 2 e)) (not (nth 3 e))) - (apply shift-body-ind 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))) @@ -2060,16 +2095,27 @@ Possible values are: `folded', `children' or `subtree'. See (defun org-list-item-body-column (item) "Return column at which 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)) + (save-excursion + (goto-char item) + (if (save-excursion + (end-of-line) + (re-search-backward + "[ \t]::\\([ \t]\\|$\\)" (line-beginning-position) t)) + ;; Descriptive list item. Body starts after item's tag, if + ;; possible. + (let ((start (1+ (- (match-beginning 1) (line-beginning-position)))) + (ind (org-get-indentation))) + (if (> start (+ ind org-list-description-max-indent)) + (+ ind 5) + start)) + ;; Regular item. Body starts after bullet. + (looking-at "[ \t]*\\(\\S-+\\)") + (+ (progn (goto-char (match-end 1)) (current-column)) + (if (and org-list-two-spaces-after-bullet-regexp + (string-match-p org-list-two-spaces-after-bullet-regexp + (match-string 1))) + 2 + 1))))) @@ -2210,7 +2256,7 @@ item is invisible." (unless (or (not itemp) (save-excursion (goto-char itemp) - (outline-invisible-p))) + (org-invisible-p))) (if (save-excursion (goto-char itemp) (org-at-item-timer-p)) @@ -2325,9 +2371,6 @@ in subtree, ignoring drawers." block-item lim-up lim-down - (drawer-re (concat "^[ \t]*:\\(" - (mapconcat #'regexp-quote org-drawers "\\|") - "\\):[ \t]*$")) (keyword-re (concat "^[ \t]*\\<\\(" org-scheduled-string "\\|" org-deadline-string "\\|" org-closed-string @@ -2349,7 +2392,8 @@ in subtree, ignoring drawers." ;; time-stamps (scheduled, etc.). (let ((limit (save-excursion (outline-next-heading) (point)))) (forward-line 1) - (while (or (looking-at drawer-re) (looking-at keyword-re)) + (while (or (looking-at org-drawer-regexp) + (looking-at keyword-re)) (if (looking-at keyword-re) (forward-line 1) (re-search-forward "^[ \t]*:END:" limit nil))) @@ -2388,7 +2432,7 @@ in subtree, ignoring drawers." (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 + (items-to-toggle (cl-remove-if (lambda (e) (or (< e lim-up) (> e lim-down))) (mapcar #'car struct)))) (mapc (lambda (e) (org-list-set-checkbox @@ -2439,130 +2483,129 @@ in subtree, ignoring drawers." (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]\\]\\)") + (org-with-wide-buffer + (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-checkbox-hierarchical-statistics) (string-match "\\" (or (org-entry-get nil "COOKIE_DATA") "")))) - (bounds (if all - (cons (point-min) (point-max)) - (cons (or (ignore-errors (org-back-to-heading t) (point)) - (point-min)) - (save-excursion (outline-next-heading) (point))))) + (within-inlinetask (and (not all) + (featurep 'org-inlinetask) + (org-inlinetask-in-task-p))) + (end (cond (all (point-max)) + (within-inlinetask + (save-excursion (outline-next-heading) (point))) + (t (save-excursion + (org-with-limited-levels (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) - (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-at-heading-p) - (string-match "\\" - (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-at-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-at-heading-p) - (setq backup-end (save-excursion - (outline-next-heading) (point)) - structs-bak nil) - (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%%]" (floor (* 100.0 checked) - (max 1 total))) - (format "[%d/%d]" checked total)))) - (goto-char beg) - (insert new) - (delete-region (point) (+ (point) (- end beg))) - (when org-auto-align-tags (org-fix-tags-on-the-fly)))) + (lambda (item structs recursivep) + ;; 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. + (let ((c-on 0) (c-all 0)) + (dolist (s structs (list c-on c-all)) + (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)))) + (cl-incf c-all (length cookies)) + (cl-incf c-on (cl-count "[X]" cookies :test #'equal))))))) + cookies-list cache) + ;; Move to start. + (cond (all (goto-char (point-min))) + (within-inlinetask (org-back-to-heading t)) + (t (org-with-limited-levels (outline-previous-heading)))) + ;; Build an alist for each cookie found. The key is the position + ;; at beginning of cookie and values ending position, format of + ;; cookie, number of checked boxes to report and total number of + ;; boxes. + (while (re-search-forward cookie-re end t) + (let ((context (save-excursion (backward-char) + (save-match-data (org-element-context))))) + (when (eq (org-element-type context) 'statistics-cookie) + (push + (append + (list (match-beginning 1) (match-end 1) (match-end 2)) + (let* ((container + (org-element-lineage + context + '(drawer center-block dynamic-block inlinetask item + quote-block special-block verse-block))) + (beg (if container + (org-element-property :contents-begin container) + (save-excursion + (org-with-limited-levels + (outline-previous-heading)) + (point))))) + (or (cdr (assq beg cache)) + (save-excursion + (goto-char beg) + (let ((end + (if container + (org-element-property :contents-end container) + (save-excursion + (org-with-limited-levels (outline-next-heading)) + (point)))) + structs) + (while (re-search-forward box-re end t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'item) + (push (org-element-property :structure element) + structs) + ;; Skip whole list since we have its + ;; structure anyway. + (while (setq element (org-element-lineage + element '(plain-list))) + (goto-char + (min (org-element-property :end element) + end)))))) + ;; Cache count for cookies applying to the same + ;; area. Then return it. + (let ((count + (funcall count-boxes + (and (eq (org-element-type container) + 'item) + (org-element-property + :begin container)) + structs + recursivep))) + (push (cons beg count) cache) + count)))))) cookies-list)))) + ;; Apply alist to buffer. + (dolist (cookie cookies-list) + (let* ((beg (car cookie)) + (end (nth 1 cookie)) + (percent (nth 2 cookie)) + (checked (nth 3 cookie)) + (total (nth 4 cookie))) + (goto-char beg) + (insert + (if percent (format "[%d%%]" (floor (* 100.0 checked) + (max 1 total))) + (format "[%d/%d]" checked total))) + (delete-region (point) (+ (point) (- end beg))) + (when org-auto-align-tags (org-fix-tags-on-the-fly))))))) (defun org-get-checkbox-statistics-face () "Select the face for checkbox statistics. @@ -2664,7 +2707,7 @@ Return t if successful." ;; of the subtree mustn't have a child. (let ((last-item (caar (reverse - (org-remove-if + (cl-remove-if (lambda (e) (>= (car e) end)) struct))))) (org-list-has-child-p last-item struct)))) @@ -2781,7 +2824,7 @@ Return t at each successful move." ((and (= ind (car org-tab-ind-state)) (ignore-errors (org-list-indent-item-generic 1 t struct)))) (t (delete-region (point-at-bol) (point-at-eol)) - (org-indent-to-column (car org-tab-ind-state)) + (indent-to-column (car org-tab-ind-state)) (insert (cdr org-tab-ind-state) " ") ;; Break cycle (setq this-command 'identity))) @@ -2794,7 +2837,8 @@ Return t at each successful move." (t (user-error "Cannot move item")))) t)))) -(defun org-sort-list (&optional with-case sorting-type getkey-func compare-func) +(defun org-sort-list + (&optional with-case sorting-type getkey-func compare-func interactive?) "Sort 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. @@ -2820,13 +2864,15 @@ 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. +record. It must return a value that is compatible with COMPARE-FUNC, +the function used to compare entries. Sorting is done against the visible part of the headlines, it -ignores hidden links." - (interactive "P") +ignores hidden links. + +A non-nil value for INTERACTIVE? is used to signal that this +function is being called interactively." + (interactive (list current-prefix-arg nil nil nil t)) (let* ((case-func (if with-case 'identity 'downcase)) (struct (org-list-struct)) (prevs (org-list-prevs-alist struct)) @@ -2838,23 +2884,31 @@ ignores hidden links." (message "Sort plain list: [a]lpha [n]umeric [t]ime [f]unc [x]checked A/N/T/F/X means reversed:") (read-char-exclusive)))) + (dcst (downcase sorting-type)) (getkey-func - (or getkey-func - (and (= (downcase sorting-type) ?f) - (intern (org-icompleting-read "Sort using function: " - obarray 'fboundp t nil nil)))))) + (and (= dcst ?f) + (or getkey-func + (and interactive? + (org-read-function "Function for extracting keys: ")) + (error "Missing key extractor")))) + (sort-func + (cond + ((= dcst ?a) #'string<) + ((= dcst ?f) + (or compare-func + (and interactive? + (org-read-function + (concat "Function for comparing keys " + "(empty for default `sort-subr' predicate): ") + 'allow-empty)))) + ((= dcst ?t) #'<) + ((= dcst ?x) #'string<)))) (message "Sorting items...") (save-restriction (narrow-to-region start end) (goto-char (point-min)) - (let* ((dcst (downcase sorting-type)) - (case-fold-search nil) + (let* ((case-fold-search nil) (now (current-time)) - (sort-func (cond - ((= dcst ?a) 'string<) - ((= dcst ?f) compare-func) - ((= dcst ?t) '<) - ((= dcst ?x) 'string<))) (next-record (lambda () (skip-chars-forward " \r\t\n") (or (eobp) (beginning-of-line)))) @@ -2908,128 +2962,249 @@ ignores hidden links." (run-hooks 'org-after-sorting-entries-or-items-hook) (message "Sorting items...done"))))) +(defun org-toggle-item (arg) + "Convert headings or normal lines to items, items to normal lines. +If there is no active region, only the current line is considered. + +If the first non blank line in the region is a headline, convert +all headlines to items, shifting text accordingly. + +If it is an item, convert all items to normal lines. + +If it is normal text, change region into a list of items. +With a prefix argument ARG, change the region in a single item." + (interactive "P") + (let ((shift-text + (lambda (ind end) + ;; Shift text in current section to IND, from point to END. + ;; The function leaves point to END line. + (let ((min-i 1000) (end (copy-marker end))) + ;; First determine the minimum indentation (MIN-I) of + ;; the text. + (save-excursion + (catch 'exit + (while (< (point) end) + (let ((i (org-get-indentation))) + (cond + ;; Skip blank lines and inline tasks. + ((looking-at "^[ \t]*$")) + ((looking-at org-outline-regexp-bol)) + ;; We can't find less than 0 indentation. + ((zerop i) (throw 'exit (setq min-i 0))) + ((< i min-i) (setq min-i i)))) + (forward-line)))) + ;; Then indent each line so that a line indented to + ;; MIN-I becomes indented to IND. Ignore blank lines + ;; and inline tasks in the process. + (let ((delta (- ind min-i))) + (while (< (point) end) + (unless (or (looking-at "^[ \t]*$") + (looking-at org-outline-regexp-bol)) + (indent-line-to (+ (org-get-indentation) delta))) + (forward-line)))))) + (skip-blanks + (lambda (pos) + ;; Return beginning of first non-blank line, starting from + ;; line at POS. + (save-excursion + (goto-char pos) + (skip-chars-forward " \r\t\n") + (point-at-bol)))) + beg end) + ;; Determine boundaries of changes. + (if (org-region-active-p) + (setq beg (funcall skip-blanks (region-beginning)) + end (copy-marker (region-end))) + (setq beg (funcall skip-blanks (point-at-bol)) + end (copy-marker (point-at-eol)))) + ;; Depending on the starting line, choose an action on the text + ;; between BEG and END. + (org-with-limited-levels + (save-excursion + (goto-char beg) + (cond + ;; Case 1. Start at an item: de-itemize. Note that it only + ;; happens when a region is active: `org-ctrl-c-minus' + ;; would call `org-cycle-list-bullet' otherwise. + ((org-at-item-p) + (while (< (point) end) + (when (org-at-item-p) + (skip-chars-forward " \t") + (delete-region (point) (match-end 0))) + (forward-line))) + ;; Case 2. Start at an heading: convert to items. + ((org-at-heading-p) + (let* ((bul (org-list-bullet-string "-")) + (bul-len (length bul)) + ;; Indentation of the first heading. It should be + ;; relative to the indentation of its parent, if any. + (start-ind (save-excursion + (cond + ((not org-adapt-indentation) 0) + ((not (outline-previous-heading)) 0) + (t (length (match-string 0)))))) + ;; Level of first heading. Further headings will be + ;; compared to it to determine hierarchy in the list. + (ref-level (org-reduced-level (org-outline-level)))) + (while (< (point) end) + (let* ((level (org-reduced-level (org-outline-level))) + (delta (max 0 (- level ref-level))) + (todo-state (org-get-todo-state))) + ;; If current headline is less indented than the first + ;; one, set it as reference, in order to preserve + ;; subtrees. + (when (< level ref-level) (setq ref-level level)) + ;; Remove stars and TODO keyword. + (let ((case-fold-search nil)) (looking-at org-todo-line-regexp)) + (delete-region (point) (or (match-beginning 3) + (line-end-position))) + (insert bul) + (indent-line-to (+ start-ind (* delta bul-len))) + ;; Turn TODO keyword into a check box. + (when todo-state + (let* ((struct (org-list-struct)) + (old (copy-tree struct))) + (org-list-set-checkbox + (line-beginning-position) + struct + (if (member todo-state org-done-keywords) + "[X]" + "[ ]")) + (org-list-write-struct struct + (org-list-parents-alist struct) + old))) + ;; Ensure all text down to END (or SECTION-END) belongs + ;; to the newly created item. + (let ((section-end (save-excursion + (or (outline-next-heading) (point))))) + (forward-line) + (funcall shift-text + (+ start-ind (* (1+ delta) bul-len)) + (min end section-end))))))) + ;; Case 3. Normal line with ARG: make the first line of region + ;; an item, and shift indentation of others lines to + ;; set them as item's body. + (arg (let* ((bul (org-list-bullet-string "-")) + (bul-len (length bul)) + (ref-ind (org-get-indentation))) + (skip-chars-forward " \t") + (insert bul) + (forward-line) + (while (< (point) end) + ;; Ensure that lines less indented than first one + ;; still get included in item body. + (funcall shift-text + (+ ref-ind bul-len) + (min end (save-excursion (or (outline-next-heading) + (point))))) + (forward-line)))) + ;; Case 4. Normal line without ARG: turn each non-item line + ;; into an item. + (t + (while (< (point) end) + (unless (or (org-at-heading-p) (org-at-item-p)) + (when (looking-at "\\([ \t]*\\)\\(\\S-\\)") + (replace-match + (concat "\\1" (org-list-bullet-string "-") "\\2")))) + (forward-line)))))))) ;;; Send and receive lists -(defun org-list-parse-list (&optional delete) +(defun org-list-to-lisp (&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, check-boxes are replaced by -\"[CBON]\", \"[CBOFF]\" and \"[CBTRANS]\". +a list of strings and other sub-lists. For example, the following list: -1. first item - + sub-item one - + [X] sub-item two - more text in first item -2. [@3] last item + 1. first item + + sub-item one + + [X] sub-item two + more text in first item + 2. [@3] last item -will be parsed as: +is 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." - (defvar parse-item) ;FIXME: Or use `cl-labels' or `letrec'. - (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 - (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 - (let ((box (match-string 1 text))) - (cond - ((equal box " ") "CBOFF") - ((equal box "-") "CBTRANS") - (t "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]+\\[@\\(start:\\)?\\([0-9]+\\|[a-zA-Z]\\)\\]\\)?[ \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)))))))) + (\"first item\" + (unordered + (\"sub-item one\") + (\"[X] sub-item two\")) + \"more text in first item\") + (\"[@3] last item\")) + +Point is left at list's end." + (letrec ((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)) + (trim + (lambda (text) + ;; Remove indentation and final newline from TEXT. + (org-remove-indentation + (if (string-match-p "\n\\'" text) + (substring text 0 -1) + text)))) + (parse-sublist + (lambda (e) + ;; Return a list whose car is list type and cdr a list + ;; of items' body. + (cons (org-list-get-list-type (car e) struct prevs) + (mapcar parse-item e)))) + (parse-item + (lambda (e) + ;; Return a list containing counter of item, if any, + ;; text and any sublist inside it. + (let* ((end (org-list-get-item-end e struct)) + (children (org-list-get-children e struct parents)) + (body + (save-excursion + (goto-char e) + (looking-at "[ \t]*\\S-+[ \t]*") + (list + (funcall + trim + (concat + (make-string (string-width (match-string 0)) ?\s) + (buffer-substring-no-properties + (match-end 0) (or (car children) end)))))))) + (while children + (let* ((child (car children)) + (sub (org-list-get-all-items child struct prevs)) + (last-in-sub (car (last sub)))) + (push (funcall parse-sublist sub) body) + ;; Remove whole sub-list from children. + (setq children (cdr (memq last-in-sub 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. + (let ((sub-end (org-list-get-item-end last-in-sub struct)) + (next (or (car children) end))) + (when (/= sub-end next) + (push (funcall + trim + (buffer-substring-no-properties sub-end next)) + body))))) + (nreverse body))))) ;; 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 (looking-at "[ \t]*$")) (looking-at org-list-end-re)) - (replace-match ""))) - out)) + (prog1 (funcall parse-sublist (org-list-get-all-items top struct prevs)) + (goto-char top) + (when delete + (delete-region top bottom) + (when (and (not (looking-at "[ \t]*$")) (looking-at org-list-end-re)) + (replace-match "")))))) (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)))) + (let ((list (save-excursion (org-list-to-lisp t)))) (insert (org-list-to-subtree list))))) (defun org-list-insert-radio-list () @@ -3055,11 +3230,13 @@ for this list." (catch 'exit (unless (org-at-item-p) (error "Not at a list item")) (save-excursion - (re-search-backward "#\\+ORGLST" nil t) - (unless (looking-at "#\\+ORGLST:[ \t]+SEND[ \t]+\\(\\S-+\\)[ \t]+\\(\\S-+\\)") - (if maybe (throw 'exit nil) - (error "Don't know how to transform this list")))) - (let* ((name (match-string 1)) + (let ((case-fold-search t)) + (re-search-backward "^[ \t]*#\\+ORGLST:" nil t) + (unless (looking-at + "[ \t]*#\\+ORGLST:[ \t]+SEND[ \t]+\\(\\S-+\\)[ \t]+\\([^ \t\n]+\\)") + (if maybe (throw 'exit nil) + (error "Don't know how to transform this list"))))) + (let* ((name (regexp-quote (match-string 1))) (transform (intern (match-string 2))) (bottom-point (save-excursion @@ -3071,220 +3248,342 @@ for this list." (re-search-backward "#\\+ORGLST" nil t) (re-search-forward (org-item-beginning-re) bottom-point t) (match-beginning 0))) - (plain-list (buffer-substring-no-properties top-point bottom-point)) - beg) + (plain-list (save-excursion + (goto-char top-point) + (org-list-to-lisp)))) (unless (fboundp transform) (error "No such transformation function %s" transform)) (let ((txt (funcall transform plain-list))) - ;; Find the insertion place + ;; Find the insertion(s) place(s). (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")))) - -(defsubst org-list-item-trim-br (item) - "Trim line breaks in a list ITEM." - (setq item (replace-regexp-in-string "\n +" " " item))) + (let ((receiver-count 0) + (begin-re (format "BEGIN +RECEIVE +ORGLST +%s\\([ \t]\\|$\\)" + name)) + (end-re (format "END +RECEIVE +ORGLST +%s\\([ \t]\\|$\\)" + name))) + (while (re-search-forward begin-re nil t) + (cl-incf receiver-count) + (let ((beg (line-beginning-position 2))) + (unless (re-search-forward end-re nil t) + (user-error "Cannot find end of receiver location at %d" beg)) + (beginning-of-line) + (delete-region beg (point)) + (insert txt "\n"))) + (cond + ((> receiver-count 1) + (message "List converted and installed at receiver locations")) + ((= receiver-count 1) + (message "List converted and installed at receiver location")) + (t (user-error "No valid receiver location found"))))))))) (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 check-box -:cbon String to insert for a checked check-box -:cbtrans String to insert for a check-box in transitional state - -:nobr Non-nil means remove line breaks in lists items. - -Alternatively, each parameter can also be a form returning -a string. These sexp can use keywords `counter' and `depth', -representing 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)) - (cbtrans (plist-get p :cbtrans)) - (nobr (plist-get p :nobr)) - 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) - ;; Stick DTSTART to ISTART by - ;; left-trimming the latter. - (concat (let ((s (eval istart))) - (or (and (string-match "[ \t\n\r]+\\'" s) - (replace-match "" t t s)) - 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 "\\[CBTRANS\\]" first) - (setq first (replace-match cbtrans t t first)))) - ;; Replace line breaks if required - (when nobr (setq first (org-list-item-trim-br first))) - ;; Insert descriptive term if TYPE is `descriptive'. - (when (eq type 'descriptive) - (let* ((complete (string-match "^\\(.*\\)[ \t]+::" first)) - (term (if complete - (save-match-data - (org-trim (match-string 1 first))) - "???")) - (desc (if complete - (org-trim (substring first (match-end 0))) - first))) - (setq first (concat (eval dtstart) term (eval dtend) - (eval ddstart) desc)))) - (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) "%s" (eval oend))) - ((eq type 'descriptive) - (concat (eval dstart) "%s" (eval dend))) - (t (concat (eval ustart) "%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 a LIST parsed through `org-list-to-lisp' to a custom format. + +LIST is a list as returned by `org-list-to-lisp', which see. +PARAMS is a property list of parameters used to tweak the output +format. + +Valid parameters are: + +:backend, :raw + + Export back-end used as a basis to transcode elements of the + list, when no specific parameter applies to it. It is also + used to translate its contents. You can prevent this by + setting :raw property to a non-nil value. + +:splice + + When non-nil, only export the contents of the top most plain + list, effectively ignoring its opening and closing lines. + +:ustart, :uend + + Strings to start and end an unordered list. They can also be + set to a function returning a string or nil, which will be + called with the depth of the list, counting from 1. + +:ostart, :oend + + Strings to start and end an ordered list. They can also be set + to a function returning a string or nil, which will be called + with the depth of the list, counting from 1. + +:dstart, :dend + + Strings to start and end a descriptive list. They can also be + set to a function returning a string or nil, which will be + called with the depth of the list, counting from 1. + +:dtstart, :dtend, :ddstart, :ddend + + Strings to start and end a descriptive term. + +:istart, :iend + + Strings to start or end a list item, and to start a list item + with a counter. They can also be set to a function returning + a string or nil, which will be called with the depth of the + item, counting from 1. + +:icount + + Strings to start a list item with a counter. It can also be + set to a function returning a string or nil, which will be + called with two arguments: the depth of the item, counting from + 1, and the counter. Its value, when non-nil, has precedence + over `:istart'. + +:isep + + String used to separate items. It can also be set to + a function returning a string or nil, which will be called with + the depth of the items, counting from 1. It always start on + a new line. + +:cbon, :cboff, :cbtrans + + String to insert, respectively, an un-checked check-box, + a checked check-box and a check-box in transitional state." + (require 'ox) + (let* ((backend (plist-get params :backend)) + (custom-backend + (org-export-create-backend + :parent (or backend 'org) + :transcoders + `((plain-list . ,(org-list--to-generic-plain-list params)) + (item . ,(org-list--to-generic-item params)) + (macro . (lambda (m c i) (org-element-macro-interpreter m nil)))))) + data info) + ;; Write LIST back into Org syntax and parse it. + (with-temp-buffer + (let ((org-inhibit-startup t)) (org-mode)) + (letrec ((insert-list + (lambda (l) + (dolist (i (cdr l)) + (funcall insert-item i (car l))))) + (insert-item + (lambda (i type) + (let ((start (point))) + (insert (if (eq type 'ordered) "1. " "- ")) + (dolist (e i) + (if (consp e) (funcall insert-list e) + (insert e) + (insert "\n"))) + (beginning-of-line) + (save-excursion + (let ((ind (if (eq type 'ordered) 3 2))) + (while (> (point) start) + (unless (looking-at-p "[ \t]*$") + (indent-to ind)) + (forward-line -1)))))))) + (funcall insert-list list)) + (setf data + (org-element-map (org-element-parse-buffer) 'plain-list + #'identity nil t)) + (setf info (org-export-get-environment backend nil params))) + (when (and backend (symbolp backend) (not (org-export-get-backend backend))) + (user-error "Unknown :backend value")) + (unless backend (require 'ox-org)) + ;; When`:raw' property has a non-nil value, turn all objects back + ;; into Org syntax. + (when (and backend (plist-get params :raw)) + (org-element-map data org-element-all-objects + (lambda (object) + (org-element-set-element + object (org-element-interpret-data object))))) + ;; We use a low-level mechanism to export DATA so as to skip all + ;; usual pre-processing and post-processing, i.e., hooks, filters, + ;; Babel code evaluation, include keywords and macro expansion, + ;; and filters. + (let ((output (org-export-data-with-backend data custom-backend info))) + ;; Remove final newline. + (if (org-string-nw-p output) (substring-no-properties output 0 -1) "")))) + +(defun org-list--depth (element) + "Return the level of ELEMENT within current plain list. +ELEMENT is either an item or a plain list." + (cl-count-if (lambda (ancestor) (eq (org-element-type ancestor) 'plain-list)) + (org-element-lineage element nil t))) + +(defun org-list--trailing-newlines (string) + "Return the number of trailing newlines in STRING." + (with-temp-buffer + (insert string) + (skip-chars-backward " \t\n") + (count-lines (line-beginning-position 2) (point-max)))) + +(defun org-list--generic-eval (value &rest args) + "Evaluate VALUE according to its type. +VALUE is either nil, a string or a function. In the latter case, +it is called with arguments ARGS." + (cond ((null value) nil) + ((stringp value) value) + ((functionp value) (apply value args)) + (t (error "Wrong value: %s" value)))) + +(defun org-list--to-generic-plain-list (params) + "Return a transcoder for `plain-list' elements. +PARAMS is a plist used to tweak the behavior of the transcoder." + (let ((ustart (plist-get params :ustart)) + (uend (plist-get params :uend)) + (ostart (plist-get params :ostart)) + (oend (plist-get params :oend)) + (dstart (plist-get params :dstart)) + (dend (plist-get params :dend)) + (splice (plist-get params :splice)) + (backend (plist-get params :backend))) + (lambda (plain-list contents info) + (let* ((type (org-element-property :type plain-list)) + (depth (org-list--depth plain-list)) + (start (and (not splice) + (org-list--generic-eval + (pcase type + (`ordered ostart) + (`unordered ustart) + (_ dstart)) + depth))) + (end (and (not splice) + (org-list--generic-eval + (pcase type + (`ordered oend) + (`unordered uend) + (_ dend)) + depth)))) + ;; Make sure trailing newlines in END appear in the output by + ;; setting `:post-blank' property to their number. + (when end + (org-element-put-property + plain-list :post-blank (org-list--trailing-newlines end))) + ;; Build output. + (concat (and start (concat start "\n")) + (if (or start end splice (not backend)) + contents + (org-export-with-backend backend plain-list contents info)) + end))))) + +(defun org-list--to-generic-item (params) + "Return a transcoder for `item' elements. +PARAMS is a plist used to tweak the behavior of the transcoder." + (let ((backend (plist-get params :backend)) + (istart (plist-get params :istart)) + (iend (plist-get params :iend)) + (isep (plist-get params :isep)) + (icount (plist-get params :icount)) + (cboff (plist-get params :cboff)) + (cbon (plist-get params :cbon)) + (cbtrans (plist-get params :cbtrans)) + (dtstart (plist-get params :dtstart)) + (dtend (plist-get params :dtend)) + (ddstart (plist-get params :ddstart)) + (ddend (plist-get params :ddend))) + (lambda (item contents info) + (let* ((type + (org-element-property :type (org-element-property :parent item))) + (tag (org-element-property :tag item)) + (depth (org-list--depth item)) + (separator (and (org-export-get-next-element item info) + (org-list--generic-eval isep depth))) + (closing (pcase (org-list--generic-eval iend depth) + ((or `nil `"") "\n") + ((and (guard separator) s) + (if (equal (substring s -1) "\n") s (concat s "\n"))) + (s s)))) + ;; When a closing line or a separator is provided, make sure + ;; its trailing newlines are taken into account when building + ;; output. This is done by setting `:post-blank' property to + ;; the number of such lines in the last line to be added. + (let ((last-string (or separator closing))) + (when last-string + (org-element-put-property + item + :post-blank + (max (1- (org-list--trailing-newlines last-string)) 0)))) + ;; Build output. + (concat + (let ((c (org-element-property :counter item))) + (if c (org-list--generic-eval icount depth c) + (org-list--generic-eval istart depth))) + (let ((body + (if (or istart iend icount cbon cboff cbtrans (not backend) + (and (eq type 'descriptive) + (or dtstart dtend ddstart ddend))) + (concat + (pcase (org-element-property :checkbox item) + (`on cbon) + (`off cboff) + (`trans cbtrans)) + (and tag + (concat dtstart + (if backend + (org-export-data-with-backend + tag backend info) + (org-element-interpret-data tag)) + dtend)) + (and tag ddstart) + (if (= (length contents) 0) "" (substring contents 0 -1)) + (and tag ddend)) + (org-export-with-backend backend item contents info)))) + ;; Remove final newline. + (if (equal body "") "" + (substring (org-element-normalize-string body) 0 -1))) + closing + separator))))) + +(defun org-list-to-latex (list &optional params) "Convert LIST into a LaTeX list. -LIST is as string representing the list to transform, as Org -syntax. Return converted list as a string." +LIST is a parsed plain list, as returned by `org-list-to-lisp'. +PARAMS is a property list with overruling parameters for +`org-list-to-generic'. Return converted list as a string." (require 'ox-latex) - (org-export-string-as list 'latex t)) + (org-list-to-generic list (org-combine-plists '(:backend latex) params))) -(defun org-list-to-html (list) +(defun org-list-to-html (list &optional params) "Convert LIST into a HTML list. -LIST is as string representing the list to transform, as Org -syntax. Return converted list as a string." +LIST is a parsed plain list, as returned by `org-list-to-lisp'. +PARAMS is a property list with overruling parameters for +`org-list-to-generic'. Return converted list as a string." (require 'ox-html) - (org-export-string-as list 'html t)) + (org-list-to-generic list (org-combine-plists '(:backend html) params))) -(defun org-list-to-texinfo (list &optional _params) +(defun org-list-to-texinfo (list &optional params) "Convert LIST into a Texinfo list. -LIST is as string representing the list to transform, as Org -syntax. Return converted list as a string." +LIST is a parsed plain list, as returned by `org-list-to-lisp'. +PARAMS is a property list with overruling parameters for +`org-list-to-generic'. Return converted list as a string." (require 'ox-texinfo) - (org-export-string-as list 'texinfo t)) + (org-list-to-generic list (org-combine-plists '(:backend texinfo) 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'." - (defvar get-stars) (defvar org--blankp) - (let* ((rule (cdr (assq 'heading org-blank-before-new-entry))) +LIST is as returned by `org-list-to-lisp'. PARAMS is a property +list with overruling parameters for `org-list-to-generic'." + (let* ((blank (pcase (cdr (assq 'heading org-blank-before-new-entry)) + (`t t) + (`auto (save-excursion + (org-with-limited-levels (outline-previous-heading)) + (org-previous-line-empty-p))))) (level (org-reduced-level (or (org-current-level) 0))) - (org--blankp (or (eq rule t) - (and (eq rule 'auto) - (save-excursion - (outline-previous-heading) - (org-previous-line-empty-p))))) - (get-stars ;FIXME: Can't rename without renaming it in org.el as well! - (function - ;; Return the string for the heading, depending on depth D - ;; of current sub-list. - (lambda (d) - (let ((oddeven-level (+ level d 1))) - (concat (make-string (if org-odd-levels-only - (1- (* 2 oddeven-level)) - oddeven-level) - ?*) - " ")))))) + (make-stars + (lambda (depth) + ;; Return the string for the heading, depending on DEPTH + ;; of current sub-list. + (let ((oddeven-level (+ level depth))) + (concat (make-string (if org-odd-levels-only + (1- (* 2 oddeven-level)) + oddeven-level) + ?*) + " "))))) (org-list-to-generic list (org-combine-plists - '(:splice t - :dtstart " " :dtend " " - :istart (funcall get-stars depth) - :icount (funcall get-stars depth) - :isep (if org--blankp "\n\n" "\n") - :csep (if org--blankp "\n\n" "\n") - :cbon "DONE" :cboff "TODO" :cbtrans "TODO") + (list :splice t + :istart make-stars + :icount make-stars + :dtstart " " :dtend " " + :isep (if blank "\n\n" "\n") + :cbon "DONE " :cboff "TODO " :cbtrans "TODO ") params)))) (provide 'org-list) diff --git a/lisp/org/org-macro.el b/lisp/org/org-macro.el index f4919d1385..3dc9c5450e 100644 --- a/lisp/org/org-macro.el +++ b/lisp/org/org-macro.el @@ -1,4 +1,4 @@ -;;; org-macro.el --- Macro Replacement Code for Org Mode +;;; org-macro.el --- Macro Replacement Code for Org -*- lexical-binding: t; -*- ;; Copyright (C) 2013-2017 Free Software Foundation, Inc. @@ -30,6 +30,10 @@ ;; `org-macro-initialize-templates', which recursively calls ;; `org-macro--collect-macros' in order to read setup files. +;; Argument in macros are separated with commas. Proper escaping rules +;; are implemented in `org-macro-escape-arguments' and arguments can +;; be extracted from a string with `org-macro-extract-arguments'. + ;; Along with macros defined through #+MACRO: keyword, default ;; templates include the following hard-coded macros: ;; {{{time(format-string)}}}, {{{property(node-property)}}}, @@ -39,19 +43,25 @@ ;; {{{email}}} and {{{title}}} macros. ;;; Code: +(require 'cl-lib) (require 'org-macs) +(require 'org-compat) -(declare-function org-element-at-point "org-element" (&optional keep-trail)) +(declare-function org-element-at-point "org-element" ()) (declare-function org-element-context "org-element" (&optional element)) +(declare-function org-element-macro-parser "org-element" ()) (declare-function org-element-property "org-element" (property element)) (declare-function org-element-type "org-element" (element)) -(declare-function org-remove-double-quotes "org" (s)) -(declare-function org-mode "org" ()) (declare-function org-file-contents "org" (file &optional noerror)) +(declare-function org-in-commented-heading-p "org" (&optional no-inheritance)) +(declare-function org-mode "org" ()) +(declare-function vc-backend "vc-hooks" (f)) +(declare-function vc-call "vc-hooks" (fun file &rest args) t) +(declare-function vc-exec-after "vc-dispatcher" (code)) ;;; Variables -(defvar org-macro-templates nil +(defvar-local org-macro-templates nil "Alist containing all macro templates in current buffer. Associations are in the shape of (NAME . TEMPLATE) where NAME stands for macro's name and template for its replacement value, @@ -59,48 +69,48 @@ both as strings. This is an internal variable. Do not set it directly, use instead: #+MACRO: name template") -(make-variable-buffer-local 'org-macro-templates) - ;;; Functions (defun org-macro--collect-macros () "Collect macro definitions in current buffer and setup files. Return an alist containing all macro templates found." - (let* (collect-macros ; For byte-compiler. - (collect-macros - (lambda (files templates) - ;; Return an alist of macro templates. FILES is a list of - ;; setup files names read so far, used to avoid circular - ;; dependencies. TEMPLATES is the alist collected so far. - (let ((case-fold-search t)) - (org-with-wide-buffer - (goto-char (point-min)) - (while (re-search-forward - "^[ \t]*#\\+\\(MACRO\\|SETUPFILE\\):" nil t) - (let ((element (org-element-at-point))) - (when (eq (org-element-type element) 'keyword) - (let ((val (org-element-property :value element))) - (if (equal (org-element-property :key element) "MACRO") - ;; Install macro in TEMPLATES. - (when (string-match - "^\\(.*?\\)\\(?:\\s-+\\(.*\\)\\)?\\s-*$" val) - (let* ((name (match-string 1 val)) - (template (or (match-string 2 val) "")) - (old-cell (assoc name templates))) - (if old-cell (setcdr old-cell template) - (push (cons name template) templates)))) - ;; Enter setup file. - (let ((file (expand-file-name - (org-remove-double-quotes val)))) - (unless (member file files) - (with-temp-buffer - (org-mode) - (insert (org-file-contents file 'noerror)) - (setq templates - (funcall collect-macros (cons file files) - templates))))))))))) - templates)))) + (letrec ((collect-macros + (lambda (files templates) + ;; Return an alist of macro templates. FILES is a list + ;; of setup files names read so far, used to avoid + ;; circular dependencies. TEMPLATES is the alist + ;; collected so far. + (let ((case-fold-search t)) + (org-with-wide-buffer + (goto-char (point-min)) + (while (re-search-forward + "^[ \t]*#\\+\\(MACRO\\|SETUPFILE\\):" nil t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'keyword) + (let ((val (org-element-property :value element))) + (if (equal (org-element-property :key element) "MACRO") + ;; Install macro in TEMPLATES. + (when (string-match + "^\\(.*?\\)\\(?:\\s-+\\(.*\\)\\)?\\s-*$" val) + (let* ((name (match-string 1 val)) + (template (or (match-string 2 val) "")) + (old-cell (assoc name templates))) + (if old-cell (setcdr old-cell template) + (push (cons name template) templates)))) + ;; Enter setup file. + (let ((file (expand-file-name + (org-unbracket-string "\"" "\"" val)))) + (unless (member file files) + (with-temp-buffer + (setq default-directory + (file-name-directory file)) + (org-mode) + (insert (org-file-contents file 'noerror)) + (setq templates + (funcall collect-macros (cons file files) + templates))))))))))) + templates)))) (funcall collect-macros nil nil))) (defun org-macro-initialize-templates () @@ -117,15 +127,26 @@ function installs the following ones: \"property\", (if old-template (setcdr old-template (cdr cell)) (push cell templates)))))) ;; Install hard-coded macros. - (mapc (lambda (cell) (funcall update-templates cell)) - (list (cons "property" "(eval (org-entry-get nil \"$1\" 'selective))") + (mapc update-templates + (list (cons "property" + "(eval (save-excursion + (let ((l \"$2\")) + (when (org-string-nw-p l) + (condition-case _ + (let ((org-link-search-must-match-exact-headline t)) + (org-link-search l nil t)) + (error + (error \"Macro property failed: cannot find location %s\" + l))))) + (org-entry-get nil \"$1\" 'selective)))") (cons "time" "(eval (format-time-string \"$1\"))"))) (let ((visited-file (buffer-file-name (buffer-base-buffer)))) (when (and visited-file (file-exists-p visited-file)) - (mapc (lambda (cell) (funcall update-templates cell)) + (mapc update-templates (list (cons "input-file" (file-name-nondirectory visited-file)) (cons "modification-time" - (format "(eval (format-time-string \"$1\" '%s))" + (format "(eval (format-time-string \"$1\" (or (and (org-string-nw-p \"$2\") (org-macro--vc-modified-time %s)) '%s)))" + (prin1-to-string visited-file) (prin1-to-string (nth 5 (file-attributes visited-file))))))))) (setq org-macro-templates templates))) @@ -154,38 +175,132 @@ default value. Return nil if no template was found." ;; Return string. (format "%s" (or value "")))))) -(defun org-macro-replace-all (templates) +(defun org-macro-replace-all (templates &optional finalize keywords) "Replace all macros in current buffer by their expansion. + TEMPLATES is an alist of templates used for expansion. See -`org-macro-templates' for a buffer-local default value." +`org-macro-templates' for a buffer-local default value. + +If optional arg FINALIZE is non-nil, raise an error if a macro is +found in the buffer with no definition in TEMPLATES. + +Optional argument KEYWORDS, when non-nil is a list of keywords, +as strings, where macro expansion is allowed." (save-excursion (goto-char (point-min)) - (let (record) + (let ((properties-regexp + (format "\\`EXPORT_%s\\+?\\'" (regexp-opt keywords))) + record) (while (re-search-forward "{{{[-A-Za-z0-9_]" nil t) - (let ((object (org-element-context))) - (when (eq (org-element-type object) 'macro) - (let* ((value (org-macro-expand object templates)) - (begin (org-element-property :begin object)) - (signature (list begin - object - (org-element-property :args object)))) - ;; Avoid circular dependencies by checking if the same - ;; macro with the same arguments is expanded at the same - ;; position twice. - (if (member signature record) - (error "Circular macro expansion: %s" - (org-element-property :key object)) - (when value - (push signature record) - (delete-region - begin - ;; Preserve white spaces after the macro. - (progn (goto-char (org-element-property :end object)) - (skip-chars-backward " \t") - (point))) - ;; Leave point before replacement in case of recursive - ;; expansions. - (save-excursion (insert value))))))))))) + (unless (save-match-data (org-in-commented-heading-p)) + (let* ((datum (save-match-data (org-element-context))) + (type (org-element-type datum)) + (macro + (cond + ((eq type 'macro) datum) + ;; In parsed keywords and associated node + ;; properties, force macro recognition. + ((or (and (eq type 'keyword) + (member (org-element-property :key datum) + keywords)) + (and (eq type 'node-property) + (string-match-p properties-regexp + (org-element-property :key + datum)))) + (save-excursion + (goto-char (match-beginning 0)) + (org-element-macro-parser)))))) + (when macro + (let* ((value (org-macro-expand macro templates)) + (begin (org-element-property :begin macro)) + (signature (list begin + macro + (org-element-property :args macro)))) + ;; Avoid circular dependencies by checking if the same + ;; macro with the same arguments is expanded at the + ;; same position twice. + (cond ((member signature record) + (error "Circular macro expansion: %s" + (org-element-property :key macro))) + (value + (push signature record) + (delete-region + begin + ;; Preserve white spaces after the macro. + (progn (goto-char (org-element-property :end macro)) + (skip-chars-backward " \t") + (point))) + ;; Leave point before replacement in case of + ;; recursive expansions. + (save-excursion (insert value))) + (finalize + (error "Undefined Org macro: %s; aborting" + (org-element-property :key macro)))))))))))) + +(defun org-macro-escape-arguments (&rest args) + "Build macro's arguments string from ARGS. +ARGS are strings. Return value is a string with arguments +properly escaped and separated with commas. This is the opposite +of `org-macro-extract-arguments'." + (let ((s "")) + (dolist (arg (reverse args) (substring s 1)) + (setq s + (concat + "," + (replace-regexp-in-string + "\\(\\\\*\\)," + (lambda (m) + (concat (make-string (1+ (* 2 (length (match-string 1 m)))) ?\\) + ",")) + ;; If a non-terminal argument ends on backslashes, make + ;; sure to also escape them as they will be followed by + ;; a comma. + (concat arg (and (not (equal s "")) + (string-match "\\\\+\\'" arg) + (match-string 0 arg))) + nil t) + s))))) + +(defun org-macro-extract-arguments (s) + "Extract macro arguments from string S. +S is a string containing comma separated values properly escaped. +Return a list of arguments, as strings. This is the opposite of +`org-macro-escape-arguments'." + ;; Do not use `org-split-string' since empty strings are + ;; meaningful here. + (split-string + (replace-regexp-in-string + "\\(\\\\*\\)," + (lambda (str) + (let ((len (length (match-string 1 str)))) + (concat (make-string (/ len 2) ?\\) + (if (zerop (mod len 2)) "\000" ",")))) + s nil t) + "\000")) + +(defun org-macro--vc-modified-time (file) + (save-window-excursion + (when (vc-backend file) + (let ((buf (get-buffer-create " *org-vc*")) + (case-fold-search t) + date) + (unwind-protect + (progn + (vc-call print-log file buf nil nil 1) + (with-current-buffer buf + (vc-exec-after + (lambda () + (goto-char (point-min)) + (when (re-search-forward "Date:?[ \t]*" nil t) + (let ((time (parse-time-string + (buffer-substring + (point) (line-end-position))))) + (when (cl-some #'identity time) + (setq date (apply #'encode-time time)))))))) + (let ((proc (get-buffer-process buf))) + (while (and proc (accept-process-output proc .5 nil t))))) + (kill-buffer buf)) + date)))) (provide 'org-macro) diff --git a/lisp/org/org-macs.el b/lisp/org/org-macs.el index 64e28cee04..ca47e5a5a3 100644 --- a/lisp/org/org-macs.el +++ b/lisp/org/org-macs.el @@ -1,4 +1,4 @@ -;;; org-macs.el --- Top-level definitions for Org-mode +;;; org-macs.el --- Top-level Definitions for Org -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -25,29 +25,12 @@ ;;; Commentary: ;; This file contains macro definitions, defsubst definitions, other -;; stuff needed for compilation and top-level forms in Org-mode, as well -;; lots of small functions that are not org-mode specific but simply -;; generally useful stuff. +;; stuff needed for compilation and top-level forms in Org mode, as +;; well lots of small functions that are not Org mode specific but +;; simply generally useful stuff. ;;; Code: -(eval-and-compile - (unless (fboundp 'declare-function) - (defmacro declare-function (fn file &optional _arglist _fileonly) - `(autoload ',fn ,file))) - - (if (>= emacs-major-version 23) - (defsubst org-char-to-string(c) - "Defsubst to decode UTF-8 character values in emacs 23 and beyond." - (char-to-string c)) - (defsubst org-char-to-string (c) - "Defsubst to decode UTF-8 character values in emacs 22." - (string (decode-char 'ucs c))))) - -(declare-function org-add-props "org-compat" (string plist &rest props)) -(declare-function org-string-match-p "org-compat" - (regexp string &optional start)) - (defmacro org-with-gensyms (symbols &rest body) (declare (debug (sexp body)) (indent 1)) `(let ,(mapcar (lambda (s) @@ -55,26 +38,11 @@ symbols) ,@body)) -(defmacro org-called-interactively-p (&optional kind) - (declare (debug (&optional ("quote" symbolp)))) ;Why not just t? - (if (featurep 'xemacs) - `(interactive-p) - (if (or (> emacs-major-version 23) - (and (>= emacs-major-version 23) - (>= emacs-minor-version 2))) - ;; defined with no argument in <=23.1 - `(with-no-warnings (called-interactively-p ,kind)) - `(interactive-p)))) - -(defmacro org-bound-and-true-p (var) - "Return the value of symbol VAR if it is bound, else nil." - (declare (debug (symbolp))) - `(and (boundp (quote ,var)) ,var)) - (defun org-string-nw-p (s) - "Is S a string with a non-white character?" + "Return S if S is a string containing a non-blank character. +Otherwise, return nil." (and (stringp s) - (org-string-match-p "\\S-" s) + (string-match-p "[^ \r\t\n]" s) s)) (defun org-not-nil (v) @@ -82,25 +50,6 @@ Otherwise return nil." (and v (not (equal v "nil")) v)) -(defun org-substitute-posix-classes (re) - "Substitute posix classes in regular expression RE." - (let ((ss re)) - (save-match-data - (while (string-match "\\[:alnum:\\]" ss) - (setq ss (replace-match "a-zA-Z0-9" t t ss))) - (while (string-match "\\[:word:\\]" ss) - (setq ss (replace-match "a-zA-Z0-9" t t ss))) - (while (string-match "\\[:alpha:\\]" ss) - (setq ss (replace-match "a-zA-Z" t t ss))) - (while (string-match "\\[:punct:\\]" ss) - (setq ss (replace-match "\001-@[-`{-~" t t ss))) - ss))) - -(defmacro org-re (s) - "Replace posix classes in regular expression." - (declare (debug (form))) - (if (featurep 'xemacs) `(org-substitute-posix-classes ,s) s)) - (defmacro org-preserve-lc (&rest body) (declare (debug (body))) (org-with-gensyms (line col) @@ -136,19 +85,6 @@ Otherwise return nil." (partial-completion-mode 1)) ,@body)) -;; FIXME: Slated for removal. Current Org mode does not support Emacs < 22 -(defmacro org-maybe-intangible (props) - "Add (intangible t) to PROPS if Emacs version is earlier than Emacs 22. -In Emacs 21, invisible text is not avoided by the command loop, so the -intangible property is needed to make sure point skips this text. -In Emacs 22, this is not necessary. The intangible text property has -led to problems with flyspell. These problems are fixed in flyspell.el, -but we still avoid setting the property in Emacs 22 and later. -We use a macro so that the test can happen at compilation time." - (if (< emacs-major-version 22) - `(append '(intangible t) ,props) - props)) - (defmacro org-with-point-at (pom &rest body) "Move to buffer and point of point-or-marker POM for the duration of BODY." (declare (debug (form body)) (indent 1)) @@ -160,10 +96,6 @@ We use a macro so that the test can happen at compilation time." (goto-char (or ,mpom (point))) ,@body))))) -(defmacro org-no-warnings (&rest body) - (declare (debug (body))) - (cons (if (fboundp 'with-no-warnings) 'with-no-warnings 'progn) body)) - (defmacro org-with-remote-undo (buffer &rest body) "Execute BODY while recording undo information in two buffers." (declare (debug (form body)) (indent 1)) @@ -199,22 +131,12 @@ We use a macro so that the test can happen at compilation time." org-emphasis t) "Properties to remove when a string without properties is wanted.") -(defsubst org-match-string-no-properties (num &optional string) - (if (featurep 'xemacs) - (let ((s (match-string num string))) - (and s (remove-text-properties 0 (length s) org-rm-props s)) - s) - (match-string-no-properties num string))) - (defsubst org-no-properties (s &optional restricted) "Remove all text properties from string S. When RESTRICTED is non-nil, only remove the properties listed in `org-rm-props'." - (if (fboundp 'set-text-properties) - (set-text-properties 0 (length s) nil s) - (if restricted - (remove-text-properties 0 (length s) org-rm-props s) - (set-text-properties 0 (length s) nil s))) + (if restricted (remove-text-properties 0 (length s) org-rm-props s) + (set-text-properties 0 (length s) nil s)) s) (defsubst org-get-alist-option (option key) @@ -236,16 +158,6 @@ program is needed for, so that the error message can be more informative." (error "Can't find `%s'%s" cmd (if use (format " (%s)" use) ""))))) -(defsubst org-inhibit-invisibility () - "Modified `buffer-invisibility-spec' for Emacs 21. -Some ops with invisible text do not work correctly on Emacs 21. For these -we turn off invisibility temporarily. Use this in a `let' form." - (if (< emacs-major-version 22) nil buffer-invisibility-spec)) - -(defsubst org-set-local (var value) - "Make VAR local in current buffer and set it to VALUE." - (set (make-local-variable var) value)) - (defsubst org-last (list) "Return the last element of LIST." (car (last list))) @@ -282,11 +194,11 @@ we turn off invisibility temporarily. Use this in a `let' form." (<= (match-beginning n) pos) (>= (match-end n) pos))) -(defun org-match-line (re) - "Looking-at at the beginning of the current line." +(defun org-match-line (regexp) + "Match REGEXP at the beginning of the current line." (save-excursion - (goto-char (point-at-bol)) - (looking-at re))) + (beginning-of-line) + (looking-at regexp))) (defun org-plist-delete (plist property) "Delete PROPERTY from PLIST. @@ -298,13 +210,6 @@ This is in contrast to merely setting it to 0." (setq plist (cddr plist))) p)) -(defun org-replace-match-keep-properties (newtext &optional fixedcase - literal string) - "Like `replace-match', but add the text properties found original text." - (setq newtext (org-add-props newtext (text-properties-at - (match-beginning 0) string))) - (replace-match newtext fixedcase literal string)) - (defmacro org-save-outline-visibility (use-markers &rest body) "Save and restore outline visibility around BODY. If USE-MARKERS is non-nil, use markers for the positions. @@ -313,19 +218,15 @@ but it also means that the buffer should stay alive during the operation, because otherwise all these markers will point nowhere." (declare (debug (form body)) (indent 1)) - (org-with-gensyms (data rtn) - `(let ((,data (org-outline-overlay-data ,use-markers)) - ,rtn) + (org-with-gensyms (data) + `(let ((,data (org-outline-overlay-data ,use-markers))) (unwind-protect - (progn - (setq ,rtn (progn ,@body)) + (prog1 (progn ,@body) (org-set-outline-overlay-data ,data)) (when ,use-markers - (mapc (lambda (c) - (and (markerp (car c)) (move-marker (car c) nil)) - (and (markerp (cdr c)) (move-marker (cdr c) nil))) - ,data))) - ,rtn))) + (dolist (c ,data) + (when (markerp (car c)) (move-marker (car c) nil)) + (when (markerp (cdr c)) (move-marker (cdr c) nil)))))))) (defmacro org-with-wide-buffer (&rest body) "Execute body while temporarily widening the buffer." @@ -355,17 +256,16 @@ point nowhere." (defun org-get-limited-outline-regexp () "Return outline-regexp with limited number of levels. The number of levels is controlled by `org-inlinetask-min-level'" - (if (or (not (derived-mode-p 'org-mode)) (not (featurep 'org-inlinetask))) - org-outline-regexp - (let* ((limit-level (1- org-inlinetask-min-level)) - (nstars (if org-odd-levels-only (1- (* limit-level 2)) limit-level))) - (format "\\*\\{1,%d\\} " nstars)))) - -(defun org-format-seconds (string seconds) - "Compatibility function replacing format-seconds." - (if (fboundp 'format-seconds) - (format-seconds string seconds) - (format-time-string string (seconds-to-time seconds)))) + (cond ((not (derived-mode-p 'org-mode)) + outline-regexp) + ((not (featurep 'org-inlinetask)) + org-outline-regexp) + (t + (let* ((limit-level (1- org-inlinetask-min-level)) + (nstars (if org-odd-levels-only + (1- (* limit-level 2)) + limit-level))) + (format "\\*\\{1,%d\\} " nstars))))) (defmacro org-eval-in-environment (environment form) (declare (debug (form form)) (indent 1)) @@ -382,10 +282,27 @@ the value in cdr." ;;;###autoload (defmacro org-load-noerror-mustsuffix (file) - "Load FILE with optional arguments NOERROR and MUSTSUFFIX. Drop the MUSTSUFFIX argument for XEmacs, which doesn't recognize it." - (if (featurep 'xemacs) - `(load ,file 'noerror) - `(load ,file 'noerror nil nil 'mustsuffix))) + "Load FILE with optional arguments NOERROR and MUSTSUFFIX." + `(load ,file 'noerror nil nil 'mustsuffix)) + +(defun org-unbracket-string (pre post string) + "Remove PRE/POST from the beginning/end of STRING. +Both PRE and POST must be pre-/suffixes of STRING, or neither is +removed." + (if (and (string-prefix-p pre string) + (string-suffix-p post string)) + (substring string (length pre) (- (length post))) + string)) + +(defun org-read-function (prompt &optional allow-empty?) + "Prompt for a function. +If ALLOW-EMPTY? is non-nil, return nil rather than raising an +error when the user input is empty." + (let ((func (completing-read prompt obarray #'fboundp t))) + (cond ((not (string= func "")) + (intern func)) + (allow-empty? nil) + (t (user-error "Empty input is not valid"))))) (provide 'org-macs) diff --git a/lisp/org/org-mhe.el b/lisp/org/org-mhe.el index d1067cd57e..4142ae45b2 100644 --- a/lisp/org/org-mhe.el +++ b/lisp/org/org-mhe.el @@ -1,4 +1,4 @@ -;;; org-mhe.el --- Support for links to MH-E messages from within Org-mode +;;; org-mhe.el --- Support for Links to MH-E Messages -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -24,8 +24,8 @@ ;; ;;; Commentary: -;; This file implements links to MH-E messages from within Org-mode. -;; Org-mode loads this module by default - if this is not what you want, +;; This file implements links to MH-E messages from within Org. +;; Org mode loads this module by default - if this is not what you want, ;; configure the variable `org-modules'. ;;; Code: @@ -74,34 +74,25 @@ supported by MH-E." (defvar mh-search-regexp-builder) ;; Install the link type -(org-add-link-type "mhe" 'org-mhe-open) -(add-hook 'org-store-link-functions 'org-mhe-store-link) +(org-link-set-parameters "mhe" :follow #'org-mhe-open :store #'org-mhe-store-link) ;; Implementation (defun org-mhe-store-link () "Store a link to an MH-E folder or message." - (when (or (equal major-mode 'mh-folder-mode) - (equal major-mode 'mh-show-mode)) + (when (or (eq major-mode 'mh-folder-mode) + (eq major-mode 'mh-show-mode)) (save-window-excursion (let* ((from (org-mhe-get-header "From:")) (to (org-mhe-get-header "To:")) (message-id (org-mhe-get-header "Message-Id:")) (subject (org-mhe-get-header "Subject:")) (date (org-mhe-get-header "Date:")) - (date-ts (and date (format-time-string - (org-time-stamp-format t) (date-to-time date)))) - (date-ts-ia (and date (format-time-string - (org-time-stamp-format t t) - (date-to-time date)))) link desc) - (org-store-link-props :type "mh" :from from :to to + (org-store-link-props :type "mh" :from from :to to :date date :subject subject :message-id message-id) - (when date - (org-add-link-props :date date :date-timestamp date-ts - :date-timestamp-inactive date-ts-ia)) (setq desc (org-email-link-description)) (setq link (concat "mhe:" (org-mhe-get-message-real-folder) "#" - (org-remove-angle-brackets message-id))) + (org-unbracket-string "<" ">" message-id))) (org-add-link-props :link link :description desc) link)))) @@ -120,7 +111,7 @@ supported by MH-E." So if you use sequences, it will now work." (save-excursion (let* ((folder - (if (equal major-mode 'mh-folder-mode) + (if (eq major-mode 'mh-folder-mode) mh-current-folder ;; Refer to the show buffer mh-show-folder-buffer)) @@ -132,7 +123,7 @@ So if you use sequences, it will now work." ;; mh-index-data is always nil in a show buffer. (if (and (boundp 'mh-index-folder) (string= mh-index-folder (substring folder 0 end-index))) - (if (equal major-mode 'mh-show-mode) + (if (eq major-mode 'mh-show-mode) (save-window-excursion (let (pop-up-frames) (when (buffer-live-p (get-buffer folder)) @@ -158,7 +149,7 @@ So if you use sequences, it will now work." "Return the name of the current message folder. Be careful if you use sequences." (save-excursion - (if (equal major-mode 'mh-folder-mode) + (if (eq major-mode 'mh-folder-mode) mh-current-folder ;; Refer to the show buffer mh-show-folder-buffer))) @@ -167,7 +158,7 @@ Be careful if you use sequences." "Return the number of the current message. Be careful if you use sequences." (save-excursion - (if (equal major-mode 'mh-folder-mode) + (if (eq major-mode 'mh-folder-mode) (mh-get-msg-num nil) ;; Refer to the show buffer (mh-show-buffer-message-number)))) @@ -182,12 +173,12 @@ you have a better idea of how to do this then please let us know." (header-field)) (with-current-buffer buffer (mh-display-msg num folder) - (if (equal major-mode 'mh-folder-mode) + (if (eq major-mode 'mh-folder-mode) (mh-header-display) (mh-show-header-display)) (set-buffer buffer) (setq header-field (mh-get-header-field header)) - (if (equal major-mode 'mh-folder-mode) + (if (eq major-mode 'mh-folder-mode) (mh-show) (mh-show-show)) (org-trim header-field)))) @@ -206,13 +197,13 @@ folders." (if (not article) (mh-visit-folder (mh-normalize-folder-name folder)) (mh-search-choose) - (if (equal mh-searcher 'pick) + (if (eq mh-searcher 'pick) (progn (setq article (org-add-angle-brackets article)) (mh-search folder (list "--message-id" article)) (when (and org-mhe-search-all-folders (not (org-mhe-get-message-real-folder))) - (kill-current-buffer) + (kill-buffer) (mh-search "+" (list "--message-id" article)))) (if mh-search-regexp-builder (mh-search "+" (funcall mh-search-regexp-builder @@ -220,7 +211,7 @@ folders." (mh-search "+" article))) (if (org-mhe-get-message-real-folder) (mh-show-msg 1) - (kill-current-buffer) + (kill-buffer) (error "Message not found")))) (provide 'org-mhe) diff --git a/lisp/org/org-mobile.el b/lisp/org/org-mobile.el index 34e6af10d8..12e6c84b3c 100644 --- a/lisp/org/org-mobile.el +++ b/lisp/org/org-mobile.el @@ -1,4 +1,4 @@ -;;; org-mobile.el --- Code for asymmetric sync with a mobile device +;;; org-mobile.el --- Code for Asymmetric Sync With a Mobile Device -*- lexical-binding: t; -*- ;; Copyright (C) 2009-2017 Free Software Foundation, Inc. ;; ;; Author: Carsten Dominik @@ -24,21 +24,20 @@ ;; ;;; Commentary: ;; -;; This file contains the code to interact with Richard Moreland's iPhone -;; application MobileOrg, as well as with the Android version by Matthew Jones. -;; This code is documented in Appendix B of the Org-mode manual. The code is -;; not specific for the iPhone and Android - any external -;; viewer/flagging/editing application that uses the same conventions could -;; be used. +;; This file contains the code to interact with Richard Moreland's +;; iPhone application MobileOrg, as well as with the Android version +;; by Matthew Jones. This code is documented in Appendix B of the Org +;; manual. The code is not specific for the iPhone and Android - any +;; external viewer/flagging/editing application that uses the same +;; conventions could be used. (require 'org) (require 'org-agenda) -;;; Code: +(require 'cl-lib) -(eval-when-compile (require 'cl)) +(defvar org-agenda-keep-restricted-file-list) -(declare-function org-pop-to-buffer-same-window - "org-compat" (&optional buffer-or-name norecord label)) +;;; Code: (defgroup org-mobile nil "Options concerning support for a viewer/editor on a mobile device." @@ -192,27 +191,6 @@ the editing types for which the mobile version should always dominate." (const heading) (const body)))) -(defcustom org-mobile-action-alist - '(("edit" . (org-mobile-edit data old new))) - "Alist with flags and actions for mobile sync. -When flagging an entry, MobileOrg will create entries that look like - - * F(action:data) [[id:entry-id][entry title]] - -This alist defines that the ACTION in the parentheses of F() should mean, -i.e. what action should be taken. The :data part in the parenthesis is -optional. If present, the string after the colon will be passed to the -action form as the `data' variable. -The car of each elements of the alist is an actions string. The cdr is -an Emacs Lisp form that will be evaluated with the cursor on the headline -of that entry. - -For now, it is not recommended to change this variable." - :group 'org-mobile - :type '(repeat - (cons (string :tag "Action flag") - (sexp :tag "Action form")))) - (defcustom org-mobile-checksum-binary (or (executable-find "shasum") (executable-find "sha1sum") (executable-find "md5sum") @@ -249,6 +227,23 @@ by the mobile device, this hook should be used to copy the emptied capture file `mobileorg.org' back to the WebDAV directory, for example using `rsync' or `scp'.") +(defconst org-mobile-action-alist '(("edit" . org-mobile-edit)) + "Alist with flags and actions for mobile sync. +When flagging an entry, MobileOrg will create entries that look like + + * F(action:data) [[id:entry-id][entry title]] + +This alist defines that the ACTION in the parentheses of F() +should mean, i.e. what action should be taken. The :data part in +the parenthesis is optional. If present, the string after the +colon will be passed to the action function as the first argument +variable. + +The car of each elements of the alist is an actions string. The +cdr is a function that is called with the cursor on the headline +of that entry. It should accept three arguments, the :data part, +the old and new values for the entry.") + (defvar org-mobile-last-flagged-files nil "List of files containing entries flagged in the latest pull.") @@ -313,40 +308,29 @@ Also exclude files matching `org-mobile-files-exclude-regexp'." This will create the index file, copy all agenda files there, and also create all custom agenda views, for upload to the mobile phone." (interactive) - (let ((a-buffer (get-buffer org-agenda-buffer-name))) - (let ((org-agenda-curbuf-name org-agenda-buffer-name) - (org-agenda-buffer-name "*SUMO*") - (org-agenda-tag-filter org-agenda-tag-filter) - (org-agenda-redo-command org-agenda-redo-command)) - (save-excursion - (save-restriction - (save-window-excursion - (run-hooks 'org-mobile-pre-push-hook) - (org-mobile-check-setup) - (org-mobile-prepare-file-lists) - (message "Creating agendas...") - (let ((inhibit-redisplay t) - (org-agenda-files (mapcar 'car org-mobile-files-alist))) - (org-mobile-create-sumo-agenda)) - (message "Creating agendas...done") - (org-save-all-org-buffers) ; to save any IDs created by this process - (message "Copying files...") - (org-mobile-copy-agenda-files) - (message "Writing index file...") - (org-mobile-create-index-file) - (message "Writing checksums...") - (org-mobile-write-checksums) - (run-hooks 'org-mobile-post-push-hook)))) - (setq org-agenda-buffer-name org-agenda-curbuf-name - org-agenda-this-buffer-name org-agenda-curbuf-name)) - (redraw-display) - (when (buffer-live-p a-buffer) - (if (not (get-buffer-window a-buffer)) - (kill-buffer a-buffer) - (let ((cw (selected-window))) - (select-window (get-buffer-window a-buffer)) - (org-agenda-redo) - (select-window cw))))) + (let ((org-agenda-buffer-name "*SUMO*") + (org-agenda-tag-filter org-agenda-tag-filter) + (org-agenda-redo-command org-agenda-redo-command)) + (save-excursion + (save-restriction + (save-window-excursion + (run-hooks 'org-mobile-pre-push-hook) + (org-mobile-check-setup) + (org-mobile-prepare-file-lists) + (message "Creating agendas...") + (let ((inhibit-redisplay t) + (org-agenda-files (mapcar 'car org-mobile-files-alist))) + (org-mobile-create-sumo-agenda)) + (message "Creating agendas...done") + (org-save-all-org-buffers) ; to save any IDs created by this process + (message "Copying files...") + (org-mobile-copy-agenda-files) + (message "Writing index file...") + (org-mobile-create-index-file) + (message "Writing checksums...") + (org-mobile-write-checksums) + (run-hooks 'org-mobile-post-push-hook))))) + (org-agenda-maybe-redo) (message "Files for mobile viewer staged")) (defvar org-mobile-before-process-capture-hook nil @@ -422,10 +406,10 @@ agenda view showing the flagged items." (let ((files-alist (sort (copy-sequence org-mobile-files-alist) (lambda (a b) (string< (cdr a) (cdr b))))) (def-todo (default-value 'org-todo-keywords)) - (def-tags (default-value 'org-tag-alist)) + (def-tags org-tag-alist) (target-file (expand-file-name org-mobile-index-file org-mobile-directory)) - file link-name todo-kwds done-kwds tags drawers entry kwds dwds twds) + todo-kwds done-kwds tags) (when (stringp (car def-todo)) (setq def-todo (list (cons 'sequence def-todo)))) (org-agenda-prepare-buffers (mapcar 'car files-alist)) @@ -433,52 +417,36 @@ agenda view showing the flagged items." (setq todo-kwds (org-delete-all done-kwds (org-uniquify org-todo-keywords-for-agenda))) - (setq drawers (org-uniquify org-drawers-for-agenda)) (setq tags (mapcar 'car (org-global-tags-completion-table (mapcar 'car files-alist)))) - (with-temp-file - (if org-mobile-use-encryption - org-mobile-encryption-tempfile - target-file) - (while (setq entry (pop def-todo)) - (insert "#+READONLY\n") - (setq kwds (mapcar (lambda (x) (if (string-match "(" x) - (substring x 0 (match-beginning 0)) - x)) - (cdr entry))) - (insert "#+TODO: " (mapconcat 'identity kwds " ") "\n") - (setq dwds (member "|" kwds) - twds (org-delete-all dwds kwds) - todo-kwds (org-delete-all twds todo-kwds) - done-kwds (org-delete-all dwds done-kwds))) + (with-temp-file (if org-mobile-use-encryption org-mobile-encryption-tempfile + target-file) + (insert "#+READONLY\n") + (dolist (entry def-todo) + (let ((kwds (mapcar (lambda (x) + (if (string-match "(" x) + (substring x 0 (match-beginning 0)) + x)) + (cdr entry)))) + (insert "#+TODO: " (mapconcat #'identity kwds " ") "\n") + (let* ((dwds (or (member "|" kwds) (last kwds))) + (twds (org-delete-all dwds kwds))) + (setq todo-kwds (org-delete-all twds todo-kwds)) + (setq done-kwds (org-delete-all dwds done-kwds))))) (when (or todo-kwds done-kwds) (insert "#+TODO: " (mapconcat 'identity todo-kwds " ") " | " (mapconcat 'identity done-kwds " ") "\n")) - (setq def-tags (mapcar - (lambda (x) - (cond ((null x) nil) - ((stringp x) x) - ((eq (car x) :startgroup) "{") - ((eq (car x) :endgroup) "}") - ((eq (car x) :grouptags) nil) - ((eq (car x) :newline) nil) - ((listp x) (car x)))) - def-tags)) - (setq def-tags (delq nil def-tags)) + (setq def-tags (split-string (org-tag-alist-to-string def-tags t))) (setq tags (org-delete-all def-tags tags)) (setq tags (sort tags (lambda (a b) (string< (downcase a) (downcase b))))) (setq tags (append def-tags tags nil)) (insert "#+TAGS: " (mapconcat 'identity tags " ") "\n") - (insert "#+DRAWERS: " (mapconcat 'identity drawers " ") "\n") (insert "#+ALLPRIORITIES: " org-mobile-allpriorities "\n") (when (file-exists-p (expand-file-name org-mobile-directory "agendas.org")) (insert "* [[file:agendas.org][Agenda Views]]\n")) - (while (setq entry (pop files-alist)) - (setq file (car entry) - link-name (cdr entry)) - (insert (format "* [[file:%s][%s]]\n" - link-name link-name))) + (pcase-dolist (`(,_ . ,link-name) files-alist) + (insert (format "* [[file:%s][%s]]\n" link-name link-name))) (push (cons org-mobile-index-file (md5 (buffer-string))) org-mobile-checksum-files)) (when org-mobile-use-encryption @@ -501,7 +469,8 @@ agenda view showing the flagged items." (org-mobile-encrypt-and-move file target-path) (copy-file file target-path 'ok-if-exists)) (setq check (shell-command-to-string - (concat org-mobile-checksum-binary " " + (concat (shell-quote-argument org-mobile-checksum-binary) + " " (shell-quote-argument (expand-file-name file))))) (when (string-match "[a-fA-F0-9]\\{30,40\\}" check) (push (cons link-name (match-string 0 check)) @@ -663,7 +632,7 @@ The table of checksums is written to the file mobile-checksums." m 10 " " 'planning) "\n") (when (setq id - (if (org-bound-and-true-p + (if (bound-and-true-p org-mobile-force-id-on-agenda-items) (org-id-get m 'create) (or (org-entry-get m "ID") @@ -679,7 +648,7 @@ The table of checksums is written to the file mobile-checksums." (org-with-point-at pom (concat "olp:" (org-mobile-escape-olp (file-name-nondirectory buffer-file-name)) - "/" + ":" (mapconcat 'org-mobile-escape-olp (org-get-outline-path) "/") @@ -823,14 +792,14 @@ If BEG and END are given, only do this in that region." (cnt-flag 0) (cnt-error 0) buf-list - id-pos org-mobile-error) + org-mobile-error) ;; Count the new captures (goto-char beg) (while (re-search-forward "^\\* \\(.*\\)" end t) (and (>= (- (match-end 1) (match-beginning 1)) 2) (not (equal (downcase (substring (match-string 1) 0 2)) "f(")) - (incf cnt-new))) + (cl-incf cnt-new))) ;; Find and apply the edits (goto-char beg) @@ -842,19 +811,21 @@ If BEG and END are given, only do this in that region." (id-pos (condition-case msg (org-mobile-locate-entry (match-string 4)) (error (nth 1 msg)))) - (bos (point-at-bol)) + (bos (line-beginning-position)) (eos (save-excursion (org-end-of-subtree t t))) (cmd (if (equal action "") - '(progn - (incf cnt-flag) - (org-toggle-tag "FLAGGED" 'on) - (and note - (org-entry-put nil "THEFLAGGINGNOTE" note))) - (incf cnt-edit) + (let ((note (buffer-substring-no-properties + (line-beginning-position 2) eos))) + (lambda (_data _old _new) + (cl-incf cnt-flag) + (org-toggle-tag "FLAGGED" 'on) + (org-entry-put + nil "THEFLAGGINGNOTE" + (replace-regexp-in-string "\n" "\\\\n" note)))) + (cl-incf cnt-edit) (cdr (assoc action org-mobile-action-alist)))) - (note (and (equal action "") - (buffer-substring (1+ (point-at-eol)) eos))) - (org-inhibit-logging 'note) ;; Do not take notes interactively + ;; Do not take notes interactively. + (org-inhibit-logging 'note) old new) (goto-char bos) @@ -867,11 +838,11 @@ If BEG and END are given, only do this in that region." (if (stringp id-pos) (insert id-pos " ") (insert "BAD REFERENCE ")) - (incf cnt-error) + (cl-incf cnt-error) (throw 'next t)) (unless cmd (insert "BAD FLAG ") - (incf cnt-error) + (cl-incf cnt-error) (throw 'next t)) (move-marker bos-marker (point)) (if (re-search-forward "^** Old value[ \t]*$" eos t) @@ -884,34 +855,28 @@ If BEG and END are given, only do this in that region." (progn (outline-next-heading) (if (eobp) (org-back-over-empty-lines)) (point))))) - (setq old (and old (if (string-match "\\S-" old) old nil))) - (setq new (and new (if (string-match "\\S-" new) new nil))) - (if (and note (> (length note) 0)) - ;; Make Note into a single line, to fit into a property - (setq note (mapconcat 'identity - (org-split-string (org-trim note) "\n") - "\\n"))) + (setq old (org-string-nw-p old)) + (setq new (org-string-nw-p new)) (unless (equal data "body") - (setq new (and new (org-trim new)) - old (and old (org-trim old)))) + (setq new (and new (org-trim new))) + (setq old (and old (org-trim old)))) (goto-char (+ 2 bos-marker)) ;; Remember this place so that we can return (move-marker marker (point)) (setq org-mobile-error nil) - (save-excursion - (condition-case msg - (org-with-point-at id-pos - (progn - (eval cmd) - (unless (member data (list "delete" "archive" "archive-sibling" "addheading")) - (if (member "FLAGGED" (org-get-tags)) - (add-to-list 'org-mobile-last-flagged-files - (buffer-file-name (current-buffer))))))) - (error (setq org-mobile-error msg)))) + (condition-case msg + (org-with-point-at id-pos + (funcall cmd data old new) + (unless (member data '("delete" "archive" "archive-sibling" + "addheading")) + (when (member "FLAGGED" (org-get-tags)) + (add-to-list 'org-mobile-last-flagged-files + (buffer-file-name))))) + (error (setq org-mobile-error msg))) (when org-mobile-error - (org-pop-to-buffer-same-window (marker-buffer marker)) + (pop-to-buffer-same-window (marker-buffer marker)) (goto-char marker) - (incf cnt-error) + (cl-incf cnt-error) (insert (if (stringp (nth 1 org-mobile-error)) (nth 1 org-mobile-error) "EXECUTION FAILED") @@ -924,8 +889,8 @@ If BEG and END are given, only do this in that region." (save-buffer) (move-marker marker nil) (move-marker end nil) - (message "%d new, %d edits, %d flags, %d errors" cnt-new - cnt-edit cnt-flag cnt-error) + (message "%d new, %d edits, %d flags, %d errors" + cnt-new cnt-edit cnt-flag cnt-error) (sit-for 1))) (defun org-mobile-timestamp-buffer (buf) @@ -1020,7 +985,7 @@ be returned that indicates what went wrong." ((equal new "DONEARCHIVE") (org-todo 'done) (org-archive-subtree-default)) - ((equal new current) t) ; nothing needs to be done + ((equal new current) t) ; nothing needs to be done ((or (equal current old) (eq org-mobile-force-mobile-change t) (memq 'todo org-mobile-force-mobile-change)) @@ -1042,33 +1007,35 @@ be returned that indicates what went wrong." (or old "") (or current ""))))) ((eq what 'priority) - (when (looking-at org-complex-heading-regexp) - (setq current (and (match-end 3) (substring (match-string 3) 2 3))) - (cond - ((equal current new) t) ; no action required - ((or (equal current old) - (eq org-mobile-force-mobile-change t) - (memq 'tags org-mobile-force-mobile-change)) - (org-priority (and new (string-to-char new)))) - (t (error "Priority was expected to be %s, but is %s" - old current))))) + (let ((case-fold-search nil)) + (when (looking-at org-complex-heading-regexp) + (let ((current (and (match-end 3) (substring (match-string 3) 2 3)))) + (cond + ((equal current new) t) ;no action required + ((or (equal current old) + (eq org-mobile-force-mobile-change t) + (memq 'tags org-mobile-force-mobile-change)) + (org-priority (and new (string-to-char new)))) + (t (error "Priority was expected to be %s, but is %s" + old current))))))) ((eq what 'heading) - (when (looking-at org-complex-heading-regexp) - (setq current (match-string 4)) - (cond - ((equal current new) t) ; no action required - ((or (equal current old) - (eq org-mobile-force-mobile-change t) - (memq 'heading org-mobile-force-mobile-change)) - (goto-char (match-beginning 4)) - (insert new) - (delete-region (point) (+ (point) (length current))) - (org-set-tags nil 'align)) - (t (error "Heading changed in MobileOrg and on the computer"))))) + (let ((case-fold-search nil)) + (when (looking-at org-complex-heading-regexp) + (let ((current (match-string 4))) + (cond + ((equal current new) t) ;no action required + ((or (equal current old) + (eq org-mobile-force-mobile-change t) + (memq 'heading org-mobile-force-mobile-change)) + (goto-char (match-beginning 4)) + (insert new) + (delete-region (point) (+ (point) (length current))) + (org-set-tags nil 'align)) + (t (error "Heading changed in MobileOrg and on the computer"))))))) ((eq what 'addheading) - (if (org-at-heading-p) ; if false we are in top-level of file + (if (org-at-heading-p) ; if false we are in top-level of file (progn ;; Workaround a `org-insert-heading-respect-content' bug ;; which prevents correct insertion when point is invisible @@ -1083,7 +1050,7 @@ be returned that indicates what went wrong." ((eq what 'refile) (org-copy-subtree) (org-with-point-at (org-mobile-locate-entry new) - (if (org-at-heading-p) ; if false we are in top-level of file + (if (org-at-heading-p) ; if false we are in top-level of file (progn (setq level (org-get-valid-level (funcall outline-level) 1)) (org-end-of-subtree t t) diff --git a/lisp/org/org-mouse.el b/lisp/org/org-mouse.el index 7eef5c6b8b..d6a472787e 100644 --- a/lisp/org/org-mouse.el +++ b/lisp/org/org-mouse.el @@ -1,4 +1,4 @@ -;;; org-mouse.el --- Better mouse support for org-mode +;;; org-mouse.el --- Better mouse support for Org -*- lexical-binding: t; -*- ;; Copyright (C) 2006-2017 Free Software Foundation, Inc. @@ -26,8 +26,8 @@ ;; ;; http://orgmode.org ;; -;; Org-mouse implements the following features: -;; * following links with the left mouse button (in Emacs 22) +;; Org mouse implements the following features: +;; * following links with the left mouse button ;; * subtree expansion/collapse (org-cycle) with the left mouse button ;; * several context menus on the right mouse button: ;; + general text @@ -66,12 +66,12 @@ ;; History: ;; -;; Since version 5.10: Changes are listed in the general org-mode docs. +;; Since version 5.10: Changes are listed in the general Org docs. ;; -;; Version 5.09;; + Version number synchronization with Org-mode. +;; Version 5.09;; + Version number synchronization with Org mode. ;; ;; Version 0.25 -;; + made compatible with org-mode 4.70 (thanks to Carsten for the patch) +;; + made compatible with Org 4.70 (thanks to Carsten for the patch) ;; ;; Version 0.24 ;; + minor changes to the table menu @@ -81,7 +81,7 @@ ;; + context menu support for org-agenda-undo & org-sort-entries ;; ;; Version 0.22 -;; + handles undo support for the agenda buffer (requires org-mode >=4.58) +;; + handles undo support for the agenda buffer (requires Org >=4.58) ;; ;; Version 0.21 ;; + selected text activates its context menu @@ -105,7 +105,7 @@ ;; + added support for checkboxes ;; ;; Version 0.15 -;; + org-mode now works with the Agenda buffer as well +;; + Org now works with the Agenda buffer as well ;; ;; Version 0.14 ;; + added a menu option that converts plain list items to outline items @@ -125,7 +125,7 @@ ;; ;; Version 0.10 ;; + added a menu option to remove highlights -;; + compatible with org-mode 4.21 now +;; + compatible with Org 4.21 now ;; ;; Version 0.08: ;; + trees can be moved/promoted/demoted by dragging with the right @@ -136,8 +136,8 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'org) +(require 'cl-lib) (defvar org-agenda-allow-remote-undo) (defvar org-agenda-undo-list) @@ -149,6 +149,8 @@ (declare-function org-agenda-earlier "org-agenda" (arg)) (declare-function org-agenda-later "org-agenda" (arg)) +(defvar org-mouse-main-buffer nil + "Active buffer for mouse operations.") (defvar org-mouse-plain-list-regexp "\\([ \t]*\\)\\([-+*]\\|[0-9]+[.)]\\) " "Regular expression that matches a plain list.") (defvar org-mouse-direct t @@ -191,15 +193,14 @@ Changing this variable requires a restart of Emacs to get activated." (interactive) (end-of-line) (skip-chars-backward "\t ") - (when (org-looking-back ":[A-Za-z]+:" (line-beginning-position)) + (when (looking-back ":[A-Za-z]+:" (line-beginning-position)) (skip-chars-backward ":A-Za-z") (skip-chars-backward "\t "))) -(defvar org-mouse-context-menu-function nil +(defvar-local org-mouse-context-menu-function nil "Function to create the context menu. The value of this variable is the function invoked by `org-mouse-context-menu' as the context menu.") -(make-variable-buffer-local 'org-mouse-context-menu-function) (defun org-mouse-show-context-menu (event prefix) "Invoke the context menu. @@ -215,13 +216,12 @@ this function is called. Otherwise, the current major mode menu is used." (when (not (org-mouse-mark-active)) (goto-char (posn-point (event-start event))) (when (not (eolp)) (save-excursion (run-hooks 'post-command-hook))) - (let ((redisplay-dont-pause t)) - (sit-for 0))) + (sit-for 0)) (if (functionp org-mouse-context-menu-function) (funcall org-mouse-context-menu-function event) (if (fboundp 'mouse-menu-major-mode-map) (popup-menu (mouse-menu-major-mode-map) event prefix) - (org-no-warnings ; don't warn about fallback, obsolete since 23.1 + (with-no-warnings ; don't warn about fallback, obsolete since 23.1 (mouse-major-mode-menu event prefix))))) (setq this-command 'mouse-save-then-kill) (mouse-save-then-kill event))) @@ -258,7 +258,7 @@ If the point is at the :beginning (`org-mouse-line-position') of the line, insert the new heading before the current line. Otherwise, insert it after the current heading." (interactive) - (case (org-mouse-line-position) + (cl-case (org-mouse-line-position) (:beginning (beginning-of-line) (org-insert-heading)) (t (org-mouse-next-heading) @@ -314,10 +314,10 @@ nor a function, elements of KEYWORDS are used directly." (just-one-space)) (defvar org-mouse-rest) -(defun org-mouse-replace-match-and-surround (newtext &optional fixedcase - literal string subexp) +(defun org-mouse-replace-match-and-surround + (_newtext &optional _fixedcase _literal _string subexp) "The same as `replace-match', but surrounds the replacement with spaces." - (apply 'replace-match org-mouse-rest) + (apply #'replace-match org-mouse-rest) (save-excursion (goto-char (match-beginning (or subexp 0))) (just-one-space) @@ -407,8 +407,8 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:" (> (match-end 0) point)))))) (defun org-mouse-priority-list () - (loop for priority from ?A to org-lowest-priority - collect (char-to-string priority))) + (cl-loop for priority from ?A to org-lowest-priority + collect (char-to-string priority))) (defun org-mouse-todo-menu (state) "Create the menu with TODO keywords." @@ -460,33 +460,33 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:" (insert " [ ] ")))) (defun org-mouse-agenda-type (type) - (case type - ('tags "Tags: ") - ('todo "TODO: ") - ('tags-tree "Tags tree: ") - ('todo-tree "TODO tree: ") - ('occur-tree "Occur tree: ") - (t "Agenda command ???"))) + (pcase type + (`tags "Tags: ") + (`todo "TODO: ") + (`tags-tree "Tags tree: ") + (`todo-tree "TODO tree: ") + (`occur-tree "Occur tree: ") + (_ "Agenda command ???"))) (defun org-mouse-list-options-menu (alloptions &optional function) (let ((options (save-match-data (split-string (match-string-no-properties 1))))) (print options) - (loop for name in alloptions - collect - (vector name - `(progn - (replace-match - (mapconcat 'identity - (sort (if (member ',name ',options) - (delete ',name ',options) - (cons ',name ',options)) - 'string-lessp) - " ") - nil nil nil 1) - (when (functionp ',function) (funcall ',function))) - :style 'toggle - :selected (and (member name options) t))))) + (cl-loop for name in alloptions + collect + (vector name + `(progn + (replace-match + (mapconcat 'identity + (sort (if (member ',name ',options) + (delete ',name ',options) + (cons ',name ',options)) + 'string-lessp) + " ") + nil nil nil 1) + (when (functionp ',function) (funcall ',function))) + :style 'toggle + :selected (and (member name options) t))))) (defun org-mouse-clip-text (text maxlength) (if (> (length text) maxlength) @@ -498,7 +498,7 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:" `("Main Menu" ["Show Overview" org-mouse-show-overview t] ["Show Headlines" org-mouse-show-headlines t] - ["Show All" show-all t] + ["Show All" outline-show-all t] ["Remove Highlights" org-remove-occur-highlights :visible org-occur-highlights] "--" @@ -556,12 +556,12 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:" (let ((contextdata (assq context contextlist))) (when contextdata (save-excursion - (goto-char (second contextdata)) - (re-search-forward ".*" (third contextdata)))))) + (goto-char (nth 1 contextdata)) + (re-search-forward ".*" (nth 2 contextdata)))))) (defun org-mouse-for-each-item (funct) - ;; Functions called by `org-apply-on-list' need an argument - (let ((wrap-fun (lambda (c) (funcall funct)))) + ;; Functions called by `org-apply-on-list' need an argument. + (let ((wrap-fun (lambda (_) (funcall funct)))) (when (ignore-errors (goto-char (org-in-item-p))) (save-excursion (org-apply-on-list wrap-fun nil))))) @@ -572,14 +572,14 @@ This means, between the beginning of line and the point." (skip-chars-backward " \t*") (bolp))) (defun org-mouse-insert-item (text) - (case (org-mouse-line-position) - (:beginning ; insert before + (cl-case (org-mouse-line-position) + (:beginning ; insert before (beginning-of-line) (looking-at "[ \t]*") (open-line 1) - (org-indent-to-column (- (match-end 0) (match-beginning 0))) + (indent-to-column (- (match-end 0) (match-beginning 0))) (insert "+ ")) - (:middle ; insert after + (:middle ; insert after (end-of-line) (newline t) (indent-relative) @@ -587,7 +587,7 @@ This means, between the beginning of line and the point." (:end ; insert text here (skip-chars-backward " \t") (kill-region (point) (point-at-eol)) - (unless (org-looking-back org-mouse-punctuation) + (unless (looking-back org-mouse-punctuation (line-beginning-position)) (insert (concat org-mouse-punctuation " "))))) (insert text) (beginning-of-line)) @@ -638,14 +638,15 @@ This means, between the beginning of line and the point." (progn (save-excursion (goto-char (region-beginning)) (insert "[[")) (save-excursion (goto-char (region-end)) (insert "]]")))] ["Insert Link Here" (org-mouse-yank-link ',event)])))) - ((save-excursion (beginning-of-line) (looking-at "#\\+STARTUP: \\(.*\\)")) + ((save-excursion (beginning-of-line) (looking-at "[ \t]*#\\+STARTUP: \\(.*\\)")) (popup-menu `(nil ,@(org-mouse-list-options-menu (mapcar 'car org-startup-options) 'org-mode-restart)))) ((or (eolp) (and (looking-at "\\( \\|\t\\)\\(+:[0-9a-zA-Z_:]+\\)?\\( \\|\t\\)+$") - (org-looking-back " \\|\t" (- (point) 2)))) + (looking-back " \\|\t" (- (point) 2) + (line-beginning-position)))) (org-mouse-popup-global-menu)) ((funcall get-context :checkbox) (popup-menu @@ -737,13 +738,13 @@ This means, between the beginning of line and the point." ["- 1 Month" (org-timestamp-change -1 'month)]))) ((funcall get-context :table-special) (let ((mdata (match-data))) - (incf (car mdata) 2) + (cl-incf (car mdata) 2) (store-match-data mdata)) (message "match: %S" (match-string 0)) (popup-menu `(nil ,@(org-mouse-keyword-replace-menu '(" " "!" "^" "_" "$" "#" "*" "'") 0 (lambda (mark) - (case (string-to-char mark) + (cl-case (string-to-char mark) (? "( ) Nothing Special") (?! "(!) Column Names") (?^ "(^) Field Names Above") @@ -914,7 +915,7 @@ This means, between the beginning of line and the point." ((org-footnote-at-reference-p) nil) (t ad-do-it)))))) -(defun org-mouse-move-tree-start (event) +(defun org-mouse-move-tree-start (_event) (interactive "e") (message "Same line: promote/demote, (***):move before, (text): make a child")) @@ -993,7 +994,7 @@ This means, between the beginning of line and the point." (defvar org-mouse-cmd) ;dynamically scoped from `org-with-remote-undo'. (defun org-mouse-do-remotely (command) - ; (org-agenda-check-no-diary) + ;; (org-agenda-check-no-diary) (when (get-text-property (point) 'org-marker) (let* ((anticol (- (point-at-eol) (point))) (marker (get-text-property (point) 'org-marker)) @@ -1031,7 +1032,7 @@ This means, between the beginning of line and the point." (org-agenda-change-all-lines newhead hdmarker 'fixface)))) t)))) -(defun org-mouse-agenda-context-menu (&optional event) +(defun org-mouse-agenda-context-menu (&optional _event) (or (org-mouse-do-remotely 'org-mouse-context-menu) (popup-menu '("Agenda" @@ -1093,17 +1094,17 @@ This means, between the beginning of line and the point." ; (setq org-agenda-mode-hook nil) (defvar org-agenda-mode-map) (add-hook 'org-agenda-mode-hook - #'(lambda () - (setq org-mouse-context-menu-function 'org-mouse-agenda-context-menu) - (org-defkey org-agenda-mode-map [mouse-3] 'org-mouse-show-context-menu) - (org-defkey org-agenda-mode-map [down-mouse-3] 'org-mouse-move-tree-start) - (org-defkey org-agenda-mode-map [C-mouse-4] 'org-agenda-earlier) - (org-defkey org-agenda-mode-map [C-mouse-5] 'org-agenda-later) - (org-defkey org-agenda-mode-map [drag-mouse-3] - #'(lambda (event) (interactive "e") - (case (org-mouse-get-gesture event) - (:left (org-agenda-earlier 1)) - (:right (org-agenda-later 1))))))) + (lambda () + (setq org-mouse-context-menu-function 'org-mouse-agenda-context-menu) + (org-defkey org-agenda-mode-map [mouse-3] 'org-mouse-show-context-menu) + (org-defkey org-agenda-mode-map [down-mouse-3] 'org-mouse-move-tree-start) + (org-defkey org-agenda-mode-map [C-mouse-4] 'org-agenda-earlier) + (org-defkey org-agenda-mode-map [C-mouse-5] 'org-agenda-later) + (org-defkey org-agenda-mode-map [drag-mouse-3] + (lambda (event) (interactive "e") + (cl-case (org-mouse-get-gesture event) + (:left (org-agenda-earlier 1)) + (:right (org-agenda-later 1))))))) (provide 'org-mouse) diff --git a/lisp/org/org-pcomplete.el b/lisp/org/org-pcomplete.el index 034c20e307..61ec5fad4c 100644 --- a/lisp/org/org-pcomplete.el +++ b/lisp/org/org-pcomplete.el @@ -1,4 +1,4 @@ -;;; org-pcomplete.el --- In-buffer completion code +;;; org-pcomplete.el --- In-buffer Completion Code -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. ;; @@ -27,21 +27,17 @@ ;;;; Require other packages -(eval-when-compile - (require 'cl)) - (require 'org-macs) (require 'org-compat) (require 'pcomplete) -(declare-function org-split-string "org" (string &optional separators)) -(declare-function org-make-org-heading-search-string "org" - (&optional string)) +(declare-function org-make-org-heading-search-string "org" (&optional string)) (declare-function org-get-buffer-tags "org" ()) (declare-function org-get-tags "org" ()) (declare-function org-buffer-property-keys "org" - (&optional include-specials include-defaults include-columns)) -(declare-function org-entry-properties "org" (&optional pom which specific)) + (&optional specials defaults columns ignore-malformed)) +(declare-function org-entry-properties "org" (&optional pom which)) +(declare-function org-tag-alist-to-string "org" (alist &optional skip-key)) ;;;; Customization variables @@ -52,12 +48,13 @@ (defvar org-drawer-regexp) (defvar org-property-re) +(defvar org-current-tag-alist) (defun org-thing-at-point () "Examine the thing at point and let the caller know what it is. The return value is a string naming the thing at point." (let ((beg1 (save-excursion - (skip-chars-backward (org-re "[:alnum:]-_@")) + (skip-chars-backward "[:alnum:]-_@") (point))) (beg (save-excursion (skip-chars-backward "a-zA-Z0-9-_:$") @@ -93,8 +90,10 @@ The return value is a string naming the thing at point." (skip-chars-backward "[ \t\n]") ;; org-drawer-regexp matches a whole line but while ;; looking-back, we just ignore trailing whitespaces - (or (org-looking-back (substring org-drawer-regexp 0 -1)) - (org-looking-back org-property-re)))) + (or (looking-back (substring org-drawer-regexp 0 -1) + (line-beginning-position)) + (looking-back org-property-re + (line-beginning-position))))) (cons "prop" nil)) ((and (equal (char-before beg1) ?:) (not (equal (char-after (point-at-bol)) ?*))) @@ -140,7 +139,6 @@ When completing for #+STARTUP, for example, this function returns pcomplete-default-completion-function)))) (defvar org-options-keywords) ; From org.el -(defvar org-element-block-name-alist) ; From org-element.el (defvar org-element-affiliated-keywords) ; From org-element.el (declare-function org-get-export-keywords "org" ()) (defun pcomplete/org-mode/file-option () @@ -153,16 +151,19 @@ When completing for #+STARTUP, for example, this function returns (mapcar (lambda (keyword) (concat keyword ": ")) org-element-affiliated-keywords) (let (block-names) - (dolist (block-info org-element-block-name-alist block-names) - (let ((name (car block-info))) - (push (format "END_%s" name) block-names) - (push (concat "BEGIN_" - name - ;; Since language is compulsory in - ;; source blocks, add a space. - (and (equal name "SRC") " ")) - block-names) - (push (format "ATTR_%s: " name) block-names)))) + (dolist (name + '("CENTER" "COMMENT" "EXAMPLE" "EXPORT" "QUOTE" "SRC" + "VERSE") + block-names) + (push (format "END_%s" name) block-names) + (push (concat "BEGIN_" + name + ;; Since language is compulsory in + ;; export blocks source blocks, add + ;; a space. + (and (member name '("EXPORT" "SRC")) " ")) + block-names) + (push (format "ATTR_%s: " name) block-names))) (mapcar (lambda (keyword) (concat keyword ": ")) (org-get-export-keywords)))) (substring pcomplete-stub 2))) @@ -233,20 +234,10 @@ When completing for #+STARTUP, for example, this function returns (setq opts (delete "showstars" opts))))) opts)))) -(defvar org-tag-alist) (defun pcomplete/org-mode/file-option/tags () "Complete arguments for the #+TAGS file option." (pcomplete-here - (list - (mapconcat (lambda (x) - (cond - ((eq :startgroup (car x)) "{") - ((eq :endgroup (car x)) "}") - ((eq :grouptags (car x)) ":") - ((eq :newline (car x)) "\\n") - ((cdr x) (format "%s(%c)" (car x) (cdr x))) - (t (car x)))) - org-tag-alist " ")))) + (list (org-tag-alist-to-string org-current-tag-alist)))) (defun pcomplete/org-mode/file-option/title () "Complete arguments for the #+TITLE file option." @@ -271,8 +262,8 @@ When completing for #+STARTUP, for example, this function returns "|:" "tags:" "tasks:" "<:" "todo:") ;; OPTION items from registered back-ends. (let (items) - (dolist (backend (org-bound-and-true-p - org-export--registered-backends)) + (dolist (backend (bound-and-true-p + org-export-registered-backends)) (dolist (option (org-export-backend-options backend)) (let ((item (nth 2 option))) (when item (push (concat item ":") items))))) @@ -283,7 +274,7 @@ When completing for #+STARTUP, for example, this function returns (while (pcomplete-here (pcomplete-uniqify-list (mapcar (lambda (item) (format "%s:" (car item))) - (org-bound-and-true-p org-html-infojs-opts-table)))))) + (bound-and-true-p org-html-infojs-opts-table)))))) (defun pcomplete/org-mode/file-option/bind () "Complete arguments for the #+BIND file option, which are variable names." @@ -324,26 +315,24 @@ This needs more work, to handle headings with lots of spaces in them." (save-excursion (goto-char (point-min)) (let (tbl) - (while (re-search-forward org-todo-line-regexp nil t) - (push (org-make-org-heading-search-string - (match-string-no-properties 3)) - tbl)) + (let ((case-fold-search nil)) + (while (re-search-forward org-todo-line-regexp nil t) + (push (org-make-org-heading-search-string + (match-string-no-properties 3)) + tbl))) (pcomplete-uniqify-list tbl))) (substring pcomplete-stub 1)))) -(defvar org-tag-alist) (defun pcomplete/org-mode/tag () "Complete a tag name. Omit tags already set." (while (pcomplete-here - (mapcar (lambda (x) - (concat x ":")) + (mapcar (lambda (x) (concat x ":")) (let ((lst (pcomplete-uniqify-list - (or (remove + (or (remq nil - (mapcar (lambda (x) - (and (stringp (car x)) (car x))) - org-tag-alist)) - (mapcar 'car (org-get-buffer-tags)))))) + (mapcar (lambda (x) (org-string-nw-p (car x))) + org-current-tag-alist)) + (mapcar #'car (org-get-buffer-tags)))))) (dolist (tag (org-get-tags)) (setq lst (delete tag lst))) lst)) @@ -357,31 +346,12 @@ This needs more work, to handle headings with lots of spaces in them." (concat x ": ")) (let ((lst (pcomplete-uniqify-list (copy-sequence - (org-buffer-property-keys nil t t))))) + (org-buffer-property-keys nil t t t))))) (dolist (prop (org-entry-properties)) (setq lst (delete (car prop) lst))) lst)) (substring pcomplete-stub 1))) -(defvar org-drawers) - -(defun pcomplete/org-mode/drawer () - "Complete a drawer name." - (let ((spc (save-excursion - (move-beginning-of-line 1) - (looking-at "^\\([ \t]*\\):") - (match-string 1))) - (cpllist (mapcar (lambda (x) (concat x ": ")) org-drawers))) - (pcomplete-here cpllist - (substring pcomplete-stub 1) - (unless (or (not (delq - nil - (mapcar (lambda(x) - (string-match (substring pcomplete-stub 1) x)) - cpllist))) - (looking-at "[ \t]*\n.*:END:")) - (save-excursion (insert "\n" spc ":END:")))))) - (defun pcomplete/org-mode/block-option/src () "Complete the arguments of a begin_src block. Complete a language in the first field, the header arguments and switches." diff --git a/lisp/org/org-plot.el b/lisp/org/org-plot.el index 5ccfbb1e66..449143a47a 100644 --- a/lisp/org/org-plot.el +++ b/lisp/org/org-plot.el @@ -1,4 +1,4 @@ -;;; org-plot.el --- Support for plotting from Org-mode +;;; org-plot.el --- Support for Plotting from Org -*- lexical-binding: t; -*- ;; Copyright (C) 2008-2017 Free Software Foundation, Inc. ;; @@ -25,14 +25,14 @@ ;; Borrows ideas and a couple of lines of code from org-exp.el. -;; Thanks to the org-mode mailing list for testing and implementation -;; and feature suggestions +;; Thanks to the Org mailing list for testing and implementation and +;; feature suggestions ;;; Code: + +(require 'cl-lib) (require 'org) (require 'org-table) -(eval-when-compile - (require 'cl)) (declare-function gnuplot-delchar-or-maybe-eof "ext:gnuplot" (arg)) (declare-function gnuplot-mode "ext:gnuplot" ()) @@ -49,41 +49,39 @@ (defun org-plot/add-options-to-plist (p options) "Parse an OPTIONS line and set values in the property list P. Returns the resulting property list." - (let (o) - (when options - (let ((op '(("type" . :plot-type) - ("script" . :script) - ("line" . :line) - ("set" . :set) - ("title" . :title) - ("ind" . :ind) - ("deps" . :deps) - ("with" . :with) - ("file" . :file) - ("labels" . :labels) - ("map" . :map) - ("timeind" . :timeind) - ("timefmt" . :timefmt))) - (multiples '("set" "line")) - (regexp ":\\([\"][^\"]+?[\"]\\|[(][^)]+?[)]\\|[^ \t\n\r;,.]*\\)") - (start 0) - o) - (while (setq o (pop op)) - (if (member (car o) multiples) ;; keys with multiple values - (while (string-match - (concat (regexp-quote (car o)) regexp) - options start) - (setq start (match-end 0)) - (setq p (plist-put p (cdr o) - (cons (car (read-from-string - (match-string 1 options))) - (plist-get p (cdr o))))) - p) - (if (string-match (concat (regexp-quote (car o)) regexp) - options) - (setq p (plist-put p (cdr o) - (car (read-from-string - (match-string 1 options))))))))))) + (when options + (let ((op '(("type" . :plot-type) + ("script" . :script) + ("line" . :line) + ("set" . :set) + ("title" . :title) + ("ind" . :ind) + ("deps" . :deps) + ("with" . :with) + ("file" . :file) + ("labels" . :labels) + ("map" . :map) + ("timeind" . :timeind) + ("timefmt" . :timefmt))) + (multiples '("set" "line")) + (regexp ":\\([\"][^\"]+?[\"]\\|[(][^)]+?[)]\\|[^ \t\n\r;,.]*\\)") + (start 0)) + (dolist (o op) + (if (member (car o) multiples) ;; keys with multiple values + (while (string-match + (concat (regexp-quote (car o)) regexp) + options start) + (setq start (match-end 0)) + (setq p (plist-put p (cdr o) + (cons (car (read-from-string + (match-string 1 options))) + (plist-get p (cdr o))))) + p) + (if (string-match (concat (regexp-quote (car o)) regexp) + options) + (setq p (plist-put p (cdr o) + (car (read-from-string + (match-string 1 options)))))))))) p) (defun org-plot/goto-nearest-table () @@ -119,10 +117,9 @@ will be added. Returns the resulting property list." Pass PARAMS through to `orgtbl-to-generic' when exporting TABLE." (with-temp-file data-file - (make-local-variable 'org-plot-timestamp-fmt) - (setq org-plot-timestamp-fmt (or - (plist-get params :timefmt) - "%Y-%m-%d-%H:%M:%S")) + (setq-local org-plot-timestamp-fmt (or + (plist-get params :timefmt) + "%Y-%m-%d-%H:%M:%S")) (insert (orgtbl-to-generic table (org-combine-plists @@ -140,7 +137,7 @@ and dependant variables." (deps (if (plist-member params :deps) (mapcar (lambda (val) (- val 1)) (plist-get params :deps)) (let (collector) - (dotimes (col (length (first table))) + (dotimes (col (length (nth 0 table))) (setf collector (cons col collector))) collector))) (counter 0) @@ -158,7 +155,7 @@ and dependant variables." table))) ;; write table to gnuplot grid datafile format (with-temp-file data-file - (let ((num-rows (length table)) (num-cols (length (first table))) + (let ((num-rows (length table)) (num-cols (length (nth 0 table))) (gnuplot-row (lambda (col row value) (setf col (+ 1 col)) (setf row (+ 1 row)) (format "%f %f %f\n%f %f %f\n" @@ -187,9 +184,7 @@ NUM-COLS controls the number of columns plotted in a 2-d plot. Optional argument PREFACE returns only option parameters in a manner suitable for prepending to a user-specified script." (let* ((type (plist-get params :plot-type)) - (with (if (equal type 'grid) - 'pm3d - (plist-get params :with))) + (with (if (eq type 'grid) 'pm3d (plist-get params :with))) (sets (plist-get params :set)) (lines (plist-get params :line)) (map (plist-get params :map)) @@ -204,68 +199,72 @@ manner suitable for prepending to a user-specified script." (x-labels (plist-get params :xlabels)) (y-labels (plist-get params :ylabels)) (plot-str "'%s' using %s%d%s with %s title '%s'") - (plot-cmd (case type - ('2d "plot") - ('3d "splot") - ('grid "splot"))) + (plot-cmd (pcase type + (`2d "plot") + (`3d "splot") + (`grid "splot"))) (script "reset") - ; ats = add-to-script - (ats (lambda (line) (setf script (format "%s\n%s" script line)))) + ;; ats = add-to-script + (ats (lambda (line) (setf script (concat script "\n" line)))) plot-lines) - (when file ;; output file + (when file ; output file (funcall ats (format "set term %s" (file-name-extension file))) (funcall ats (format "set output '%s'" file))) - (case type ;; type - ('2d ()) - ('3d (if map (funcall ats "set map"))) - ('grid (if map (funcall ats "set pm3d map") - (funcall ats "set pm3d")))) - (when title (funcall ats (format "set title '%s'" title))) ;; title - (when lines (mapc (lambda (el) (funcall ats el)) lines)) ;; line - (when sets ;; set - (mapc (lambda (el) (funcall ats (format "set %s" el))) sets)) - (when x-labels ;; x labels (xtics) + (pcase type ; type + (`2d ()) + (`3d (when map (funcall ats "set map"))) + (`grid (funcall ats (if map "set pm3d map" "set pm3d")))) + (when title (funcall ats (format "set title '%s'" title))) ; title + (mapc ats lines) ; line + (dolist (el sets) (funcall ats (format "set %s" el))) ; set + ;; Unless specified otherwise, values are TAB separated. + (unless (string-match-p "^set datafile separator" script) + (funcall ats "set datafile separator \"\\t\"")) + (when x-labels ; x labels (xtics) (funcall ats (format "set xtics (%s)" (mapconcat (lambda (pair) (format "\"%s\" %d" (cdr pair) (car pair))) x-labels ", ")))) - (when y-labels ;; y labels (ytics) + (when y-labels ; y labels (ytics) (funcall ats (format "set ytics (%s)" (mapconcat (lambda (pair) (format "\"%s\" %d" (cdr pair) (car pair))) y-labels ", ")))) - (when time-ind ;; timestamp index + (when time-ind ; timestamp index (funcall ats "set xdata time") (funcall ats (concat "set timefmt \"" - (or timefmt ;; timefmt passed to gnuplot + (or timefmt ; timefmt passed to gnuplot "%Y-%m-%d-%H:%M:%S") "\""))) (unless preface - (case type ;; plot command - ('2d (dotimes (col num-cols) - (unless (and (equal type '2d) - (or (and ind (equal (+ 1 col) ind)) - (and deps (not (member (+ 1 col) deps))))) + (pcase type ; plot command + (`2d (dotimes (col num-cols) + (unless (and (eq type '2d) + (or (and ind (equal (1+ col) ind)) + (and deps (not (member (1+ col) deps))))) (setf plot-lines (cons (format plot-str data-file (or (and ind (> ind 0) - (not text-ind) - (format "%d:" ind)) "") - (+ 1 col) + (not text-ind) + (format "%d:" ind)) "") + (1+ col) (if text-ind (format ":xticlabel(%d)" ind) "") with - (or (nth col col-labels) (format "%d" (+ 1 col)))) + (or (nth col col-labels) + (format "%d" (1+ col)))) plot-lines))))) - ('3d + (`3d (setq plot-lines (list (format "'%s' matrix with %s title ''" data-file with)))) - ('grid + (`grid (setq plot-lines (list (format "'%s' with %s title ''" data-file with))))) (funcall ats - (concat plot-cmd " " (mapconcat 'identity (reverse plot-lines) ",\\\n ")))) + (concat plot-cmd " " (mapconcat #'identity + (reverse plot-lines) + ",\\\n ")))) script)) ;;----------------------------------------------------------------------------- @@ -279,59 +278,59 @@ line directly before or after the table." (require 'gnuplot) (save-window-excursion (delete-other-windows) - (when (get-buffer "*gnuplot*") ;; reset *gnuplot* if it already running + (when (get-buffer "*gnuplot*") ; reset *gnuplot* if it already running (with-current-buffer "*gnuplot*" - (goto-char (point-max)) - (gnuplot-delchar-or-maybe-eof nil))) + (goto-char (point-max)))) (org-plot/goto-nearest-table) - ;; set default options - (mapc - (lambda (pair) - (unless (plist-member params (car pair)) - (setf params (plist-put params (car pair) (cdr pair))))) - org-plot/gnuplot-default-options) + ;; Set default options. + (dolist (pair org-plot/gnuplot-default-options) + (unless (plist-member params (car pair)) + (setf params (plist-put params (car pair) (cdr pair))))) ;; collect table and table information (let* ((data-file (make-temp-file "org-plot")) (table (org-table-to-lisp)) - (num-cols (length (if (eq (first table) 'hline) (second table) - (first table))))) - (while (equal 'hline (first table)) (setf table (cdr table))) - (when (equal (second table) 'hline) - (setf params (plist-put params :labels (first table))) ;; headers to labels - (setf table (delq 'hline (cdr table)))) ;; clean non-data from table - ;; collect options + (num-cols (length (if (eq (nth 0 table) 'hline) (nth 1 table) + (nth 0 table))))) + (run-with-idle-timer 0.1 nil #'delete-file data-file) + (while (eq 'hline (car table)) (setf table (cdr table))) + (when (eq (cadr table) 'hline) + (setf params + (plist-put params :labels (nth 0 table))) ; headers to labels + (setf table (delq 'hline (cdr table)))) ; clean non-data from table + ;; Collect options. (save-excursion (while (and (equal 0 (forward-line -1)) (looking-at "[[:space:]]*#\\+")) (setf params (org-plot/collect-options params)))) - ;; dump table to datafile (very different for grid) - (case (plist-get params :plot-type) - ('2d (org-plot/gnuplot-to-data table data-file params)) - ('3d (org-plot/gnuplot-to-data table data-file params)) - ('grid (let ((y-labels (org-plot/gnuplot-to-grid-data + ;; Dump table to datafile (very different for grid). + (pcase (plist-get params :plot-type) + (`2d (org-plot/gnuplot-to-data table data-file params)) + (`3d (org-plot/gnuplot-to-data table data-file params)) + (`grid (let ((y-labels (org-plot/gnuplot-to-grid-data table data-file params))) (when y-labels (plist-put params :ylabels y-labels))))) - ;; check for timestamp ind column - (let ((ind (- (plist-get params :ind) 1))) - (when (and (>= ind 0) (equal '2d (plist-get params :plot-type))) + ;; Check for timestamp ind column. + (let ((ind (1- (plist-get params :ind)))) + (when (and (>= ind 0) (eq '2d (plist-get params :plot-type))) (if (= (length (delq 0 (mapcar (lambda (el) - (if (string-match org-ts-regexp3 el) - 0 1)) - (mapcar (lambda (row) (nth ind row)) table)))) 0) + (if (string-match org-ts-regexp3 el) 0 1)) + (mapcar (lambda (row) (nth ind row)) table)))) + 0) (plist-put params :timeind t) - ;; check for text ind column + ;; Check for text ind column. (if (or (string= (plist-get params :with) "hist") (> (length (delq 0 (mapcar (lambda (el) (if (string-match org-table-number-regexp el) 0 1)) - (mapcar (lambda (row) (nth ind row)) table)))) 0)) + (mapcar (lambda (row) (nth ind row)) table)))) + 0)) (plist-put params :textind t))))) - ;; write script + ;; Write script. (with-temp-buffer - (if (plist-get params :script) ;; user script + (if (plist-get params :script) ; user script (progn (insert (org-plot/gnuplot-script data-file num-cols params t)) (insert "\n") @@ -339,14 +338,12 @@ line directly before or after the table." (goto-char (point-min)) (while (re-search-forward "$datafile" nil t) (replace-match data-file nil nil))) - (insert - (org-plot/gnuplot-script data-file num-cols params))) - ;; graph table + (insert (org-plot/gnuplot-script data-file num-cols params))) + ;; Graph table. (gnuplot-mode) (gnuplot-send-buffer-to-gnuplot)) - ;; cleanup - (bury-buffer (get-buffer "*gnuplot*")) - (run-with-idle-timer 0.1 nil (lambda () (delete-file data-file)))))) + ;; Cleanup. + (bury-buffer (get-buffer "*gnuplot*"))))) (provide 'org-plot) diff --git a/lisp/org/org-protocol.el b/lisp/org/org-protocol.el index 4bd83bea48..8254356745 100644 --- a/lisp/org/org-protocol.el +++ b/lisp/org/org-protocol.el @@ -1,4 +1,4 @@ -;;; org-protocol.el --- Intercept calls from emacsclient to trigger custom actions. +;;; org-protocol.el --- Intercept Calls from Emacsclient to Trigger Custom Actions -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2008-2017 Free Software Foundation, Inc. ;; @@ -49,7 +49,7 @@ ;; 4.) Try this from the command line (adjust the URL as needed): ;; ;; $ emacsclient \ -;; org-protocol://store-link://http:%2F%2Flocalhost%2Findex.html/The%20title +;; org-protocol://store-link?url=http:%2F%2Flocalhost%2Findex.html&title=The%20title ;; ;; 5.) Optionally add custom sub-protocols and handlers: ;; @@ -60,7 +60,7 @@ ;; ;; A "sub-protocol" will be found in URLs like this: ;; -;; org-protocol://sub-protocol://data +;; org-protocol://sub-protocol?key=val&key2=val2 ;; ;; If it works, you can now setup other applications for using this feature. ;; @@ -81,12 +81,12 @@ ;; * `org-protocol-open-source' uses the sub-protocol \"open-source\" and maps ;; URLs to local filenames defined in `org-protocol-project-alist'. ;; -;; * `org-protocol-store-link' stores an Org-link (if Org-mode is present) and +;; * `org-protocol-store-link' stores an Org link (if Org is present) and ;; pushes the browsers URL to the `kill-ring' for yanking. This handler is ;; triggered through the sub-protocol \"store-link\". ;; ;; * Call `org-protocol-capture' by using the sub-protocol \"capture\". If -;; Org-mode is loaded, Emacs will pop-up a capture buffer and fill the +;; Org is loaded, Emacs will pop-up a capture buffer and fill the ;; template with the data provided. I.e. the browser's URL is inserted as an ;; Org-link of which the page title will be the description part. If text ;; was select in the browser, that text will be the body of the entry. @@ -94,20 +94,20 @@ ;; You may use the same bookmark URL for all those standard handlers and just ;; adjust the sub-protocol used: ;; -;; location.href='org-protocol://sub-protocol://'+ -;; encodeURIComponent(location.href)+'/'+ -;; encodeURIComponent(document.title)+'/'+ +;; location.href='org-protocol://sub-protocol?url='+ +;; encodeURIComponent(location.href)+'&title='+ +;; encodeURIComponent(document.title)+'&body='+ ;; encodeURIComponent(window.getSelection()) ;; ;; The handler for the sub-protocol \"capture\" detects an optional template ;; char that, if present, triggers the use of a special template. ;; Example: ;; -;; location.href='org-protocol://sub-protocol://x/'+ ... +;; location.href='org-protocol://capture?template=x'+ ... ;; -;; use template ?x. +;; uses template ?x. ;; -;; Note, that using double slashes is optional from org-protocol.el's point of +;; Note that using double slashes is optional from org-protocol.el's point of ;; view because emacsclient squashes the slashes to one. ;; ;; @@ -116,25 +116,12 @@ ;;; Code: (require 'org) -(eval-when-compile - (require 'cl)) (declare-function org-publish-get-project-from-filename "ox-publish" (filename &optional up)) (declare-function server-edit "server" (&optional arg)) -(define-obsolete-function-alias - 'org-protocol-unhex-compound 'org-link-unescape-compound - "2011-02-17") - -(define-obsolete-function-alias - 'org-protocol-unhex-string 'org-link-unescape - "2011-02-17") - -(define-obsolete-function-alias - 'org-protocol-unhex-single-byte-sequence - 'org-link-unescape-single-byte-sequence - "2011-02-17") +(defvar org-capture-link-is-already-stored) (defgroup org-protocol nil "Intercept calls from emacsclient to trigger custom actions. @@ -225,27 +212,36 @@ Each element of this list must be of the form: (module-name :protocol protocol :function func :kill-client nil) -protocol - protocol to detect in a filename without trailing colon and slashes. - See rfc1738 section 2.1 for more on this. - If you define a protocol \"my-protocol\", `org-protocol-check-filename-for-protocol' - will search filenames for \"org-protocol:/my-protocol:/\" - and trigger your action for every match. `org-protocol' is defined in - `org-protocol-the-protocol'. Double and triple slashes are compressed - to one by emacsclient. - -function - function that handles requests with protocol and takes exactly one - argument: the filename with all protocols stripped. If the function - returns nil, emacsclient and -server do nothing. Any non-nil return - value is considered a valid filename and thus passed to the server. - - `org-protocol.el provides some support for handling those filenames, - if you stay with the conventions used for the standard handlers in - `org-protocol-protocol-alist-default'. See `org-protocol-split-data'. +protocol - protocol to detect in a filename without trailing + colon and slashes. See rfc1738 section 2.1 for more + on this. If you define a protocol \"my-protocol\", + `org-protocol-check-filename-for-protocol' will search + filenames for \"org-protocol:/my-protocol\" and + trigger your action for every match. `org-protocol' + is defined in `org-protocol-the-protocol'. Double and + triple slashes are compressed to one by emacsclient. + +function - function that handles requests with protocol and takes + one argument. If a new-style link (key=val&key2=val2) + is given, the argument will be a property list with + the values from the link. If an old-style link is + given (val1/val2), the argument will be the filename + with all protocols stripped. + + If the function returns nil, emacsclient and -server + do nothing. Any non-nil return value is considered a + valid filename and thus passed to the server. + + `org-protocol.el' provides some support for handling + old-style filenames, if you follow the conventions + used for the standard handlers in + `org-protocol-protocol-alist-default'. See + `org-protocol-parse-parameters'. kill-client - If t, kill the client immediately, once the sub-protocol is detected. This is necessary for actions that can be interrupted by - `C-g' to avoid dangling emacsclients. Note, that all other command - line arguments but the this one will be discarded, greedy handlers + `C-g' to avoid dangling emacsclients. Note that all other command + line arguments but the this one will be discarded. Greedy handlers still receive the whole list of arguments though. Here is an example: @@ -269,7 +265,7 @@ string with two characters." (defcustom org-protocol-data-separator "/+\\|\\?" "The default data separator to use. - This should be a single regexp string." +This should be a single regexp string." :group 'org-protocol :version "24.4" :package-version '(Org . "8.0") @@ -278,21 +274,20 @@ string with two characters." ;;; Helper functions: (defun org-protocol-sanitize-uri (uri) - "emacsclient compresses double and triple slashes. -Slashes are sanitized to double slashes here." + "Sanitize slashes to double-slashes in URI. +Emacsclient compresses double and triple slashes." (when (string-match "^\\([a-z]+\\):/" uri) (let* ((splitparts (split-string uri "/+"))) (setq uri (concat (car splitparts) "//" (mapconcat 'identity (cdr splitparts) "/"))))) uri) (defun org-protocol-split-data (data &optional unhexify separator) - "Split what an org-protocol handler function gets as only argument. -DATA is that one argument. DATA is split at each occurrence of -SEPARATOR (regexp). If no SEPARATOR is specified or SEPARATOR is -nil, assume \"/+\". The results of that splitting are returned -as a list. If UNHEXIFY is non-nil, hex-decode each split part. -If UNHEXIFY is a function, use that function to decode each split -part." + "Split the DATA argument for an org-protocol handler function. +If UNHEXIFY is non-nil, hex-decode each split part. If UNHEXIFY +is a function, use that function to decode each split part. The +string is split at each occurrence of SEPARATOR (regexp). If no +SEPARATOR is specified or SEPARATOR is nil, assume \"/+\". The +results of that splitting are returned as a list." (let* ((sep (or separator "/+\\|\\?")) (split-parts (split-string data sep))) (if unhexify @@ -302,23 +297,25 @@ part." split-parts))) (defun org-protocol-flatten-greedy (param-list &optional strip-path replacement) - "Greedy handlers might receive a list like this from emacsclient: - ((\"/dir/org-protocol:/greedy:/~/path1\" (23 . 12)) (\"/dir/param\")) -where \"/dir/\" is the absolute path to emacsclients working directory. This + "Transform PARAM-LIST into a flat list for greedy handlers. + +Greedy handlers might receive a list like this from emacsclient: +\((\"/dir/org-protocol:/greedy:/~/path1\" (23 . 12)) (\"/dir/param\")) +where \"/dir/\" is the absolute path to emacsclient's working directory. This function transforms it into a flat list using `org-protocol-flatten' and transforms the elements of that list as follows: -If strip-path is non-nil, remove the \"/dir/\" prefix from all members of +If STRIP-PATH is non-nil, remove the \"/dir/\" prefix from all members of param-list. -If replacement is string, replace the \"/dir/\" prefix with it. +If REPLACEMENT is string, replace the \"/dir/\" prefix with it. The first parameter, the one that contains the protocols, is always changed. Everything up to the end of the protocols is stripped. Note, that this function will always behave as if `org-protocol-reverse-list-of-files' was set to t and the returned list will -reflect that. I.e. emacsclients first parameter will be the first one in the +reflect that. emacsclient's first parameter will be the first one in the returned list." (let* ((l (org-protocol-flatten (if org-protocol-reverse-list-of-files param-list @@ -345,50 +342,106 @@ returned list." ret) l))) -(defun org-protocol-flatten (l) - "Greedy handlers might receive a list like this from emacsclient: - ((\"/dir/org-protocol:/greedy:/~/path1\" (23 . 12)) (\"/dir/param\")) +(defun org-protocol-flatten (list) + "Transform LIST into a flat list. + +Greedy handlers might receive a list like this from emacsclient: +\((\"/dir/org-protocol:/greedy:/~/path1\" (23 . 12)) (\"/dir/param\")) where \"/dir/\" is the absolute path to emacsclients working directory. This function transforms it into a flat list." - (if (null l) () - (if (listp l) - (append (org-protocol-flatten (car l)) (org-protocol-flatten (cdr l))) - (list l)))) - + (if (null list) () + (if (listp list) + (append (org-protocol-flatten (car list)) (org-protocol-flatten (cdr list))) + (list list)))) + +(defun org-protocol-parse-parameters (info &optional new-style default-order) + "Return a property list of parameters from INFO. +If NEW-STYLE is non-nil, treat INFO as a query string (ex: +url=URL&title=TITLE). If old-style links are used (ex: +org-protocol://store-link/url/title), assign them to attributes +following DEFAULT-ORDER. + +If no DEFAULT-ORDER is specified, return the list of values. + +If INFO is already a property list, return it unchanged." + (if (listp info) + info + (if new-style + (let ((data (org-protocol-convert-query-to-plist info)) + result) + (while data + (setq result + (append + result + (list + (pop data) + (org-link-unescape (pop data)))))) + result) + (let ((data (org-protocol-split-data info t org-protocol-data-separator))) + (if default-order + (org-protocol-assign-parameters data default-order) + data))))) + +(defun org-protocol-assign-parameters (data default-order) + "Return a property list of parameters from DATA. +Key names are taken from DEFAULT-ORDER, which should be a list of +symbols. If DEFAULT-ORDER is shorter than the number of values +specified, the rest of the values are treated as :key value pairs." + (let (result) + (while default-order + (setq result + (append result + (list (pop default-order) + (pop data))))) + (while data + (setq result + (append result + (list (intern (concat ":" (pop data))) + (pop data))))) + result)) ;;; Standard protocol handlers: (defun org-protocol-store-link (fname) - "Process an org-protocol://store-link:// style url. + "Process an org-protocol://store-link style url. Additionally store a browser URL as an org link. Also pushes the link's URL to the `kill-ring'. +Parameters: url, title (optional), body (optional) + +Old-style links such as org-protocol://store-link://URL/TITLE are +also recognized. + The location for a browser's bookmark has to look like this: - javascript:location.href=\\='org-protocol://store-link://\\='+ \\ - encodeURIComponent(location.href) - encodeURIComponent(document.title)+\\='/\\='+ \\ + javascript:location.href = \\ + \\='org-protocol://store-link?url=\\=' + \\ + encodeURIComponent(location.href) + \\='&title=\\=' + \\ + encodeURIComponent(document.title); -Don't use `escape()'! Use `encodeURIComponent()' instead. The title of the page -could contain slashes and the location definitely will. +Don't use `escape()'! Use `encodeURIComponent()' instead. The +title of the page could contain slashes and the location +definitely will. The sub-protocol used to reach this function is set in -`org-protocol-protocol-alist'." - (let* ((splitparts (org-protocol-split-data fname t org-protocol-data-separator)) - (uri (org-protocol-sanitize-uri (car splitparts))) - (title (cadr splitparts)) - orglink) - (if (boundp 'org-stored-links) - (setq org-stored-links (cons (list uri title) org-stored-links))) +`org-protocol-protocol-alist'. + +FNAME should be a property list. If not, an old-style link of the +form URL/TITLE can also be used." + (let* ((splitparts (org-protocol-parse-parameters fname nil '(:url :title))) + (uri (org-protocol-sanitize-uri (plist-get splitparts :url))) + (title (plist-get splitparts :title))) + (when (boundp 'org-stored-links) + (push (list uri title) org-stored-links)) (kill-new uri) (message "`%s' to insert new org-link, `%s' to insert `%s'" - (substitute-command-keys"\\[org-insert-link]") - (substitute-command-keys"\\[yank]") + (substitute-command-keys "`\\[org-insert-link]'") + (substitute-command-keys "`\\[yank]'") uri)) nil) (defun org-protocol-capture (info) - "Process an org-protocol://capture:// style url. + "Process an org-protocol://capture style url with INFO. The sub-protocol used to reach this function is set in `org-protocol-protocol-alist'. @@ -396,16 +449,16 @@ The sub-protocol used to reach this function is set in This function detects an URL, title and optional text, separated by `/'. The location for a browser's bookmark looks like this: - javascript:location.href=\\='org-protocol://capture://\\='+ \\ - encodeURIComponent(location.href)+\\='/\\=' \\ - encodeURIComponent(document.title)+\\='/\\='+ \\ + javascript:location.href = \\='org-protocol://capture?url=\\='+ \\ + encodeURIComponent(location.href) + \\='&title=\\=' \\ + encodeURIComponent(document.title) + \\='&body=\\=' + \\ encodeURIComponent(window.getSelection()) By default, it uses the character `org-protocol-default-template-key', which should be associated with a template in `org-capture-templates'. -But you may prepend the encoded URL with a character and a slash like so: +You may specify the template with a template= query parameter, like this: - javascript:location.href=\\='org-protocol://capture://b/\\='+ ... + javascript:location.href = \\='org-protocol://capture?template=b\\='+ ... Now template ?b will be used." (if (and (boundp 'org-stored-links) @@ -414,7 +467,7 @@ Now template ?b will be used." nil) (defun org-protocol-convert-query-to-plist (query) - "Convert query string that is part of url to property list." + "Convert QUERY key=value pairs in the URL to a property list." (if query (apply 'append (mapcar (lambda (x) (let ((c (split-string x "="))) @@ -422,45 +475,52 @@ Now template ?b will be used." (split-string query "&"))))) (defun org-protocol-do-capture (info) - "Support `org-capture'." - (let* ((parts (org-protocol-split-data info t org-protocol-data-separator)) - (template (or (and (>= 2 (length (car parts))) (pop parts)) + "Perform the actual capture based on INFO." + (let* ((temp-parts (org-protocol-parse-parameters info)) + (parts + (cond + ((and (listp info) (symbolp (car info))) info) + ((= (length (car temp-parts)) 1) ;; First parameter is exactly one character long + (org-protocol-assign-parameters temp-parts '(:template :url :title :body))) + (t + (org-protocol-assign-parameters temp-parts '(:url :title :body))))) + (template (or (plist-get parts :template) org-protocol-default-template-key)) - (url (org-protocol-sanitize-uri (car parts))) - (type (if (string-match "^\\([a-z]+\\):" url) - (match-string 1 url))) - (title (or (cadr parts) "")) - (region (or (caddr parts) "")) - (orglink (org-make-link-string - url (if (string-match "[^[:space:]]" title) title url))) - (query (or (org-protocol-convert-query-to-plist (cadddr parts)) "")) + (url (and (plist-get parts :url) (org-protocol-sanitize-uri (plist-get parts :url)))) + (type (and url (if (string-match "^\\([a-z]+\\):" url) + (match-string 1 url)))) + (title (or (plist-get parts :title) "")) + (region (or (plist-get parts :body) "")) + (orglink (if url + (org-make-link-string + url (if (string-match "[^[:space:]]" title) title url)) + title)) (org-capture-link-is-already-stored t)) ;; avoid call to org-store-link (setq org-stored-links (cons (list url title) org-stored-links)) - (kill-new orglink) (org-store-link-props :type type :link url :description title :annotation orglink :initial region - :query query) + :query parts) (raise-frame) (funcall 'org-capture nil template))) (defun org-protocol-open-source (fname) - "Process an org-protocol://open-source:// style url. + "Process an org-protocol://open-source?url= style URL with FNAME. Change a filename by mapping URLs to local filenames as set in `org-protocol-project-alist'. The location for a browser's bookmark should look like this: - javascript:location.href=\\='org-protocol://open-source://\\='+ \\ + javascript:location.href = \\='org-protocol://open-source?url=\\=' + \\ encodeURIComponent(location.href)" ;; As we enter this function for a match on our protocol, the return value ;; defaults to nil. (let ((result nil) - (f (org-link-unescape fname))) + (f (plist-get (org-protocol-parse-parameters fname nil '(:url)) :url))) (catch 'result (dolist (prolist org-protocol-project-alist) (let* ((base-url (plist-get (cdr prolist) :base-url)) @@ -490,13 +550,12 @@ The location for a browser's bookmark should look like this: (let ((rewrites (plist-get (cdr prolist) :rewrites))) (when rewrites (message "Rewrites found: %S" rewrites) - (mapc - (lambda (rewrite) - "Try to match a rewritten URL and map it to a real file." - ;; Compare redirects without suffix: - (if (string-match (car rewrite) f2) - (throw 'result (concat wdir (cdr rewrite))))) - rewrites)))) + (dolist (rewrite rewrites) + ;; Try to match a rewritten URL and map it to + ;; a real file. Compare redirects without + ;; suffix. + (when (string-match-p (car rewrite) f2) + (throw 'result (concat wdir (cdr rewrite)))))))) ;; -- end of redirects -- (if (file-readable-p the-file) @@ -509,44 +568,63 @@ The location for a browser's bookmark should look like this: ;;; Core functions: -(defun org-protocol-check-filename-for-protocol (fname restoffiles client) - "Detect if `org-protocol-the-protocol' and a known sub-protocol is used in fname. +(defun org-protocol-check-filename-for-protocol (fname restoffiles _client) + "Check if `org-protocol-the-protocol' and a valid protocol are used in FNAME. Sub-protocols are registered in `org-protocol-protocol-alist' and -`org-protocol-protocol-alist-default'. -This is, how the matching is done: +`org-protocol-protocol-alist-default'. This is how the matching is done: - (string-match \"protocol:/+sub-protocol:/+\" ...) + (string-match \"protocol:/+sub-protocol\\\\(://\\\\|\\\\?\\\\)\" ...) protocol and sub-protocol are regexp-quoted. -If a matching protocol is found, the protocol is stripped from fname and the -result is passed to the protocols function as the only parameter. If the -function returns nil, the filename is removed from the list of filenames -passed from emacsclient to the server. -If the function returns a non nil value, that value is passed to the server -as filename." +Old-style links such as \"protocol://sub-protocol://param1/param2\" are +also recognized. + +If a matching protocol is found, the protocol is stripped from +fname and the result is passed to the protocol function as the +first parameter. The second parameter will be non-nil if FNAME +uses key=val&key2=val2-type arguments, or nil if FNAME uses +val/val2-type arguments. If the function returns nil, the +filename is removed from the list of filenames passed from +emacsclient to the server. If the function returns a non-nil +value, that value is passed to the server as filename. + +If the handler function is greedy, RESTOFFILES will also be passed to it. + +CLIENT is ignored." (let ((sub-protocols (append org-protocol-protocol-alist org-protocol-protocol-alist-default))) (catch 'fname - (let ((the-protocol (concat (regexp-quote org-protocol-the-protocol) ":/+"))) + (let ((the-protocol (concat (regexp-quote org-protocol-the-protocol) + ":/+"))) (when (string-match the-protocol fname) (dolist (prolist sub-protocols) - (let ((proto (concat the-protocol - (regexp-quote (plist-get (cdr prolist) :protocol)) ":/+"))) + (let ((proto + (concat the-protocol + (regexp-quote (plist-get (cdr prolist) :protocol)) + "\\(:/+\\|\\?\\)"))) (when (string-match proto fname) (let* ((func (plist-get (cdr prolist) :function)) (greedy (plist-get (cdr prolist) :greedy)) (split (split-string fname proto)) - (result (if greedy restoffiles (cadr split)))) + (result (if greedy restoffiles (cadr split))) + (new-style (string= (match-string 1 fname) "?"))) (when (plist-get (cdr prolist) :kill-client) (message "Greedy org-protocol handler. Killing client.") (server-edit)) (when (fboundp func) (unless greedy - (throw 'fname (funcall func result))) - (funcall func result) + (throw 'fname + (if new-style + (funcall func (org-protocol-parse-parameters + result new-style)) + (warn "Please update your Org Protocol handler \ +to deal with new-style links.") + (funcall func result)))) + ;; Greedy protocol handlers are responsible for + ;; parsing their own filenames. + (funcall func result) (throw 'fname t)))))))) - ;; (message "fname: %s" fname) fname))) (defadvice server-visit-files (before org-protocol-detect-protocol-server activate) @@ -572,16 +650,18 @@ as filename." ;;; Org specific functions: (defun org-protocol-create-for-org () - "Create a org-protocol project for the current file's Org-mode project. + "Create a Org protocol project for the current file's project. The visited file needs to be part of a publishing project in `org-publish-project-alist' for this to work. The function delegates most of the work to `org-protocol-create'." (interactive) - (require 'org-publish) + (require 'ox-publish) (let ((all (or (org-publish-get-project-from-filename buffer-file-name)))) (if all (org-protocol-create (cdr all)) - (message "Not in an org-project. Did mean %s?" - (substitute-command-keys"\\[org-protocol-create]"))))) + (message "%s" + (substitute-command-keys + "Not in an Org project. \ +Did you mean `\\[org-protocol-create]'?"))))) (defun org-protocol-create (&optional project-plist) "Create a new org-protocol project interactively. @@ -600,19 +680,18 @@ the cdr of an element in `org-publish-project-alist', reuse (working-suffix (if (plist-get project-plist :base-extension) (concat "." (plist-get project-plist :base-extension)) ".org")) - (worglet-buffer nil) (insert-default-directory t) (minibuffer-allow-text-properties nil)) (setq base-url (read-string "Base URL of published content: " base-url nil base-url t)) - (if (not (string-match "\\/$" base-url)) - (setq base-url (concat base-url "/"))) + (or (string-suffix-p "/" base-url) + (setq base-url (concat base-url "/"))) (setq working-dir (expand-file-name (read-directory-name "Local working directory: " working-dir working-dir t))) - (if (not (string-match "\\/$" working-dir)) - (setq working-dir (concat working-dir "/"))) + (or (string-suffix-p "/" working-dir) + (setq working-dir (concat working-dir "/"))) (setq strip-suffix (read-string diff --git a/lisp/org/org-rmail.el b/lisp/org/org-rmail.el index 80bfce920c..31c59a13d8 100644 --- a/lisp/org/org-rmail.el +++ b/lisp/org/org-rmail.el @@ -1,4 +1,4 @@ -;;; org-rmail.el --- Support for links to Rmail messages from within Org-mode +;;; org-rmail.el --- Support for Links to Rmail Messages -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -24,9 +24,9 @@ ;; ;;; Commentary: -;; This file implements links to Rmail messages from within Org-mode. -;; Org-mode loads this module by default - if this is not what you want, -;; configure the variable `org-modules'. +;; This file implements links to Rmail messages from within Org mode. +;; Org mode loads this module by default - if this is not what you +;; want, configure the variable `org-modules'. ;;; Code: @@ -36,13 +36,14 @@ (declare-function rmail-show-message "rmail" (&optional n no-summary)) (declare-function rmail-what-message "rmail" (&optional pos)) (declare-function rmail-toggle-header "rmail" (&optional arg)) +(declare-function rmail "rmail" (&optional file-name-arg)) (declare-function rmail-widen "rmail" ()) (defvar rmail-current-message) ; From rmail.el (defvar rmail-header-style) ; From rmail.el +(defvar rmail-file-name) ; From rmail.el ;; Install the link type -(org-add-link-type "rmail" 'org-rmail-open) -(add-hook 'org-store-link-functions 'org-rmail-store-link) +(org-link-set-parameters "rmail" :follow #'org-rmail-open :store #'org-rmail-store-link) ;; Implementation (defun org-rmail-store-link () @@ -63,20 +64,11 @@ (to (mail-fetch-field "to")) (subject (mail-fetch-field "subject")) (date (mail-fetch-field "date")) - (date-ts (and date (format-time-string - (org-time-stamp-format t) - (date-to-time date)))) - (date-ts-ia (and date (format-time-string - (org-time-stamp-format t t) - (date-to-time date)))) desc link) (org-store-link-props - :type "rmail" :from from :to to + :type "rmail" :from from :to to :date date :subject subject :message-id message-id) - (when date - (org-add-link-props :date date :date-timestamp date-ts - :date-timestamp-inactive date-ts-ia)) - (setq message-id (org-remove-angle-brackets message-id)) + (setq message-id (org-unbracket-string "<" ">" message-id)) (setq desc (org-email-link-description)) (setq link (concat "rmail:" folder "#" message-id)) (org-add-link-props :link link :description desc) diff --git a/lisp/org/org-src.el b/lisp/org/org-src.el index 4eb8a531b8..0e04d4b5a8 100644 --- a/lisp/org/org-src.el +++ b/lisp/org/org-src.el @@ -1,4 +1,4 @@ -;;; org-src.el --- Source code examples in Org +;;; org-src.el --- Source code examples in Org -*- lexical-binding: t; -*- ;; ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. ;; @@ -26,43 +26,33 @@ ;; ;;; Commentary: -;; This file contains the code dealing with source code examples in Org-mode. +;; This file contains the code dealing with source code examples in +;; Org mode. ;;; Code: +(require 'cl-lib) (require 'org-macs) (require 'org-compat) (require 'ob-keys) (require 'ob-comint) -(eval-when-compile - (require 'cl)) +(declare-function org-base-buffer "org" (buffer)) (declare-function org-do-remove-indentation "org" (&optional n)) -(declare-function org-at-table.el-p "org" ()) -(declare-function org-in-src-block-p "org" (&optional inside)) -(declare-function org-in-block-p "org" (names)) +(declare-function org-element-at-point "org-element" ()) +(declare-function org-element-class "org-element" (datum &optional parent)) +(declare-function org-element-context "org-element" (&optional element)) +(declare-function org-element-lineage "org-element" + (blob &optional types with-self)) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-type "org-element" (element)) +(declare-function org-footnote-goto-definition "org-footnote" + (label &optional location)) (declare-function org-get-indentation "org" (&optional line)) (declare-function org-switch-to-buffer-other-window "org" (&rest args)) -(declare-function org-pop-to-buffer-same-window - "org-compat" (&optional buffer-or-name norecord label)) -(declare-function org-base-buffer "org" (buffer)) +(declare-function org-trim "org" (s &optional keep-lead)) -(defcustom org-edit-src-region-extra nil - "Additional regexps to identify regions for editing with `org-edit-src-code'. -For examples see the function `org-edit-src-find-region-and-lang'. -The regular expression identifying the begin marker should end with a newline, -and the regexp marking the end line should start with a newline, to make sure -there are kept outside the narrowed region." - :group 'org-edit-structure - :type '(repeat - (list - (regexp :tag "begin regexp") - (regexp :tag "end regexp") - (choice :tag "language" - (string :tag "specify") - (integer :tag "from match group") - (const :tag "from `lang' element") - (const :tag "from `style' element"))))) +(defvar org-inhibit-startup) (defcustom org-edit-src-turn-on-auto-save nil "Non-nil means turn `auto-save-mode' on when editing a source block. @@ -117,28 +107,29 @@ These are the regions where each line starts with a colon." (defcustom org-src-preserve-indentation nil "If non-nil preserve leading whitespace characters on export. +\\ If non-nil leading whitespace characters in source code blocks are preserved on export, and when switching between the org -buffer and the language mode edit buffer. If this variable is nil -then, after editing with \\[org-edit-src-code], the -minimum (across-lines) number of leading whitespace characters -are removed from all lines, and the code block is uniformly -indented according to the value of `org-edit-src-content-indentation'." +buffer and the language mode edit buffer. + +When this variable is nil, after editing with `\\[org-edit-src-code]', +the minimum (across-lines) number of leading whitespace characters +are removed from all lines, and the code block is uniformly indented +according to the value of `org-edit-src-content-indentation'." :group 'org-edit-structure :type 'boolean) (defcustom org-edit-src-content-indentation 2 "Indentation for the content of a source code block. + This should be the number of spaces added to the indentation of the #+begin line in order to compute the indentation of the block content after -editing it with \\[org-edit-src-code]. Has no effect if -`org-src-preserve-indentation' is non-nil." +editing it with `\\[org-edit-src-code]'. + +It has no effect if `org-src-preserve-indentation' is non-nil." :group 'org-edit-structure :type 'integer) -(defvar org-src-strip-leading-and-trailing-blank-lines nil - "If non-nil, blank lines are removed when exiting the code edit buffer.") - (defcustom org-edit-src-persistent-message t "Non-nil means show persistent exit help message while editing src examples. The message is shown in the header-line, which will be created in the @@ -146,6 +137,17 @@ first line of the window showing the editing buffer." :group 'org-edit-structure :type 'boolean) +(defcustom org-src-ask-before-returning-to-edit-buffer t + "Non-nil means ask before switching to an existing edit buffer. +If nil, when `org-edit-src-code' is used on a block that already +has an active edit buffer, it will switch to that edit buffer +immediately; otherwise it will ask whether you want to return to +the existing edit buffer." + :group 'org-edit-structure + :version "24.4" + :package-version '(Org . "8.0") + :type 'boolean) + (defcustom org-src-window-setup 'reorganize-frame "How the source code edit buffer should be displayed. Possible values for this option are: @@ -167,10 +169,10 @@ other-frame Use `switch-to-buffer-other-frame' to display edit buffer. (defvar org-src-mode-hook nil "Hook run after Org switched a source code snippet to its Emacs mode. -This hook will run - -- when editing a source code snippet with `\\[org-src-mode-map]'. -- When formatting a source code snippet for export with htmlize. +\\ +This hook will run: +- when editing a source code snippet with `\\[org-edit-special]' +- when formatting a source code snippet for export with htmlize. You may want to use this hook for example to turn off `outline-minor-mode' or similar things which you want to have when editing a source code file, @@ -180,7 +182,7 @@ but which mess up the display of a snippet in Org exported files.") '(("ocaml" . tuareg) ("elisp" . emacs-lisp) ("ditaa" . artist) ("asymptote" . asy) ("dot" . fundamental) ("sqlite" . sql) ("calc" . fundamental) ("C" . c) ("cpp" . c++) ("C++" . c++) - ("screen" . shell-script)) + ("screen" . shell-script) ("shell" . sh) ("bash" . sh)) "Alist mapping languages to their major mode. The key is the language name, the value is the string that should be inserted as the name of the major mode. For many languages this is @@ -194,451 +196,383 @@ For example, there is no ocaml-mode in Emacs, but the mode to use is (string "Language name") (symbol "Major mode")))) -;;; Editing source examples +(defcustom org-src-block-faces nil + "Alist of faces to be used for source-block. +Each element is a cell of the format -(defvar org-src-mode-map (make-sparse-keymap)) -(define-key org-src-mode-map "\C-c'" 'org-edit-src-exit) -(define-key org-src-mode-map "\C-c\C-k" 'org-edit-src-abort) -(define-key org-src-mode-map "\C-x\C-s" 'org-edit-src-save) + (\"language\" FACE) -(defvar org-edit-src-force-single-line nil) -(defvar org-edit-src-from-org-mode nil) -(defvar org-edit-src-allow-write-back-p t) -(defvar org-edit-src-picture nil) -(defvar org-edit-src-beg-marker nil) -(defvar org-edit-src-end-marker nil) -(defvar org-edit-src-overlay nil) -(defvar org-edit-src-block-indentation nil) -(defvar org-edit-src-saved-temp-window-config nil) +Where FACE is either a defined face or an anonymous face. -(defcustom org-src-ask-before-returning-to-edit-buffer t - "If nil, when org-edit-src code is used on a block that already -has an active edit buffer, it will switch to that edit buffer -immediately; otherwise it will ask whether you want to return to -the existing edit buffer." - :group 'org-edit-structure - :version "24.4" - :package-version '(Org . "8.0") - :type 'boolean) - -(defvar org-src-babel-info nil) +For instance, the following value would color the background of +emacs-lisp source blocks and python source blocks in purple and +green, respectability. -(define-minor-mode org-src-mode - "Minor mode for language major mode buffers generated by org. -This minor mode is turned on in two situations: -- when editing a source code snippet with `\\[org-src-mode-map]'. -- When formatting a source code snippet for export with htmlize. -There is a mode hook, and keybindings for `org-edit-src-exit' and -`org-edit-src-save'") - -(defvar org-edit-src-code-timer nil) -(defvar org-inhibit-startup) + \\='((\"emacs-lisp\" (:background \"#EEE2FF\")) + (\"python\" (:background \"#e5ffb8\")))" + :group 'org-edit-structure + :type '(repeat (list (string :tag "language") + (choice + (face :tag "Face") + (sexp :tag "Anonymous face")))) + :version "26.1" + :package-version '(Org . "9.0")) -(defun org-edit-src-code (&optional context code edit-buffer-name) - "Edit the source CODE block at point. -The code is copied to a separate buffer and the appropriate mode -is turned on. When done, exit with \\[org-edit-src-exit]. This will -remove the original code in the Org buffer, and replace it with the -edited version. An optional argument CONTEXT is used by \\[org-edit-src-save] -when calling this function. See `org-src-window-setup' to configure -the display of windows containing the Org buffer and the code buffer." - (interactive) - (if (not (or (org-in-block-p '("src" "example" "latex" "html")) - (org-at-table.el-p))) - (user-error "Not in a source code or example block") - (unless (eq context 'save) - (setq org-edit-src-saved-temp-window-config (current-window-configuration))) - (let* ((mark (and (org-region-active-p) (mark))) - (case-fold-search t) - (info - ;; If the src region consists in no lines, we insert a blank - ;; line. - (let* ((temp (org-edit-src-find-region-and-lang)) - (beg (nth 0 temp)) - (end (nth 1 temp))) - (if (>= end beg) temp - (goto-char beg) - (insert "\n") - (org-edit-src-find-region-and-lang)))) - (full-info (org-babel-get-src-block-info 'light)) - (org-mode-p (derived-mode-p 'org-mode)) ;; derived-mode-p is reflexive - (beg (make-marker)) - ;; Move marker with inserted text for case when src block is - ;; just one empty line, i.e. beg == end. - (end (copy-marker (make-marker) t)) - (allow-write-back-p (null code)) - block-nindent total-nindent ovl lang lang-f single buffer msg - begline markline markcol line col transmitted-variables) - (setq beg (move-marker beg (nth 0 info)) - end (move-marker end (nth 1 info)) - msg (if allow-write-back-p - "Edit, then exit with C-c ' (C-c and single quote) -- C-c C-k to abort" - "Exit with C-c ' (C-c and single quote) -- C-c C-k to abort") - code (or code (buffer-substring-no-properties beg end)) - lang (or (cdr (assoc (nth 2 info) org-src-lang-modes)) - (nth 2 info)) - lang (if (symbolp lang) (symbol-name lang) lang) - single (nth 3 info) - block-nindent (nth 5 info) - lang-f (intern (concat lang "-mode")) - begline (save-excursion (goto-char beg) (org-current-line)) - transmitted-variables - `((org-edit-src-content-indentation - ,org-edit-src-content-indentation) - (org-edit-src-force-single-line ,single) - (org-edit-src-from-org-mode ,org-mode-p) - (org-edit-src-allow-write-back-p ,allow-write-back-p) - (org-src-preserve-indentation ,org-src-preserve-indentation) - (org-src-babel-info ,(org-babel-get-src-block-info 'light)) - (org-coderef-label-format - ,(or (nth 4 info) org-coderef-label-format)) - (org-edit-src-beg-marker ,beg) - (org-edit-src-end-marker ,end) - (org-edit-src-block-indentation ,block-nindent))) - (if (and mark (>= mark beg) (<= mark (1+ end))) - (save-excursion (goto-char (min mark end)) - (setq markline (org-current-line) - markcol (current-column)))) - (if (equal lang-f 'table.el-mode) - (setq lang-f (lambda () - (text-mode) - (if (org-bound-and-true-p flyspell-mode) - (flyspell-mode -1)) - (table-recognize) - (org-set-local 'org-edit-src-content-indentation 0)))) - (unless (functionp lang-f) - (error "No such language mode: %s" lang-f)) - (save-excursion - (if (> (point) end) (goto-char end)) - (setq line (org-current-line) - col (current-column))) - (if (and (setq buffer (org-edit-src-find-buffer beg end)) - (or (eq context 'save) - (if org-src-ask-before-returning-to-edit-buffer - (y-or-n-p "Return to existing edit buffer ([n] will revert changes)? ") t))) - (org-src-switch-to-buffer buffer 'return) - (when buffer - (with-current-buffer buffer - (if (boundp 'org-edit-src-overlay) - (delete-overlay org-edit-src-overlay))) - (kill-buffer buffer)) - (setq buffer (generate-new-buffer - (or edit-buffer-name - (org-src-construct-edit-buffer-name (buffer-name) lang)))) - (setq ovl (make-overlay beg end)) - (overlay-put ovl 'edit-buffer buffer) - (overlay-put ovl 'help-echo "Click with mouse-1 to switch to buffer editing this segment") - (overlay-put ovl 'face 'secondary-selection) - (overlay-put ovl - 'keymap - (let ((map (make-sparse-keymap))) - (define-key map [mouse-1] 'org-edit-src-continue) - map)) - (overlay-put ovl :read-only "Leave me alone") - (setq transmitted-variables - (append transmitted-variables `((org-edit-src-overlay ,ovl)))) - (org-src-switch-to-buffer buffer 'edit) - (if (eq single 'macro-definition) - (setq code (replace-regexp-in-string "\\\\n" "\n" code t t))) - (insert code) - (remove-text-properties (point-min) (point-max) - '(display nil invisible nil intangible nil)) - (unless (cadr (assq 'org-src-preserve-indentation transmitted-variables)) - (setq total-nindent (or (org-do-remove-indentation) 0))) - (let ((org-inhibit-startup t)) - (condition-case e - (funcall lang-f) - (error - (message "Language mode `%s' fails with: %S" lang-f (nth 1 e))))) - (dolist (pair transmitted-variables) - (org-set-local (car pair) (cadr pair))) - ;; Remove protecting commas from visible part of buffer. - (org-unescape-code-in-region (point-min) (point-max)) - (when markline - (org-goto-line (1+ (- markline begline))) - (org-move-to-column - (if org-src-preserve-indentation markcol - (max 0 (- markcol total-nindent)))) - (push-mark (point) 'no-message t) - (setq deactivate-mark nil)) - (org-goto-line (1+ (- line begline))) - (org-move-to-column - (if org-src-preserve-indentation col (max 0 (- col total-nindent)))) - (org-src-mode) - (set-buffer-modified-p nil) - (setq buffer-file-name nil) - (when org-edit-src-turn-on-auto-save - (setq buffer-auto-save-file-name - (concat (make-temp-name "org-src-") - (format-time-string "-%Y-%d-%m") ".txt"))) - (and org-edit-src-persistent-message - (org-set-local 'header-line-format msg)) - (let ((edit-prep-func (intern (concat "org-babel-edit-prep:" lang)))) - (when (fboundp edit-prep-func) - (funcall edit-prep-func full-info))) - (or org-edit-src-code-timer - (zerop org-edit-src-auto-save-idle-delay) - (setq org-edit-src-code-timer - (run-with-idle-timer - org-edit-src-auto-save-idle-delay t - (lambda () - (cond - ((org-string-match-p "\\`\\*Org Src" (buffer-name)) - (when (buffer-modified-p) (org-edit-src-save))) - ((not (org-some (lambda (b) - (org-string-match-p "\\`\\*Org Src" - (buffer-name b))) - (buffer-list))) - (cancel-timer org-edit-src-code-timer) - (setq org-edit-src-code-timer nil)))))))) - t))) +(defcustom org-src-tab-acts-natively nil + "If non-nil, the effect of TAB in a code block is as if it were +issued in the language major mode buffer." + :type 'boolean + :version "24.1" + :group 'org-babel) -(defun org-edit-src-continue (e) - "Continue editing source blocks." ;; Fixme: be more accurate - (interactive "e") - (mouse-set-point e) - (let ((buf (get-char-property (point) 'edit-buffer))) - (if buf (org-src-switch-to-buffer buf 'continue) - (error "Something is wrong here")))) -(defun org-src-switch-to-buffer (buffer context) - (case org-src-window-setup - ('current-window - (org-pop-to-buffer-same-window buffer)) - ('other-window - (switch-to-buffer-other-window buffer)) - ('other-frame - (case context - ('exit - (let ((frame (selected-frame))) - (switch-to-buffer-other-frame buffer) - (delete-frame frame))) - ('save - (kill-buffer (current-buffer)) - (org-pop-to-buffer-same-window buffer)) - (t - (switch-to-buffer-other-frame buffer)))) - ('reorganize-frame - (if (eq context 'edit) (delete-other-windows)) - (org-switch-to-buffer-other-window buffer) - (if (eq context 'exit) (delete-other-windows))) - ('switch-invisibly - (set-buffer buffer)) - (t - (message "Invalid value %s for org-src-window-setup" - (symbol-name org-src-window-setup)) - (org-pop-to-buffer-same-window buffer)))) - -(defun org-src-construct-edit-buffer-name (org-buffer-name lang) + +;;; Internal functions and variables + +(defvar org-src--allow-write-back t) +(defvar org-src--auto-save-timer nil) +(defvar org-src--babel-info nil) +(defvar org-src--beg-marker nil) +(defvar org-src--block-indentation nil) +(defvar org-src--end-marker nil) +(defvar org-src--from-org-mode nil) +(defvar org-src--overlay nil) +(defvar org-src--preserve-indentation nil) +(defvar org-src--remote nil) +(defvar org-src--saved-temp-window-config nil) +(defvar org-src--source-type nil + "Type of element being edited, as a symbol.") +(defvar org-src--tab-width nil + "Contains `tab-width' value from Org source buffer. +However, if `indent-tabs-mode' is nil in that buffer, its value +is 0.") + +(defun org-src--construct-edit-buffer-name (org-buffer-name lang) "Construct the buffer name for a source editing buffer." (concat "*Org Src " org-buffer-name "[ " lang " ]*")) -(defun org-src-edit-buffer-p (&optional buffer) - "Test whether BUFFER (or the current buffer if BUFFER is nil) -is a source block editing buffer." - (let ((buffer (org-base-buffer (or buffer (current-buffer))))) - (and (buffer-name buffer) - (string-match "\\`*Org Src " (buffer-name buffer)) - (local-variable-p 'org-edit-src-beg-marker buffer) - (local-variable-p 'org-edit-src-end-marker buffer)))) - -(defun org-edit-src-find-buffer (beg end) - "Find a source editing buffer that is already editing the region BEG to END." +(defun org-src--edit-buffer (beg end) + "Return buffer editing area between BEG and END. +Return nil if there is no such buffer." (catch 'exit - (mapc - (lambda (b) - (with-current-buffer b - (if (and (string-match "\\`*Org Src " (buffer-name)) - (local-variable-p 'org-edit-src-beg-marker (current-buffer)) - (local-variable-p 'org-edit-src-end-marker (current-buffer)) - (equal beg org-edit-src-beg-marker) - (equal end org-edit-src-end-marker)) - (throw 'exit (current-buffer))))) - (buffer-list)) - nil)) + (dolist (b (buffer-list)) + (with-current-buffer b + (and (org-src-edit-buffer-p) + (= beg org-src--beg-marker) + (eq (marker-buffer beg) (marker-buffer org-src--beg-marker)) + (= end org-src--end-marker) + (eq (marker-buffer end) (marker-buffer org-src--end-marker)) + (throw 'exit b)))))) + +(defun org-src--source-buffer () + "Return source buffer edited by current buffer." + (unless (org-src-edit-buffer-p) (error "Not in a source buffer")) + (or (marker-buffer org-src--beg-marker) + (error "No source buffer available for current editing session"))) + +(defun org-src--get-lang-mode (lang) + "Return major mode that should be used for LANG. +LANG is a string, and the returned major mode is a symbol." + (intern + (concat + (let ((l (or (cdr (assoc lang org-src-lang-modes)) lang))) + (if (symbolp l) (symbol-name l) l)) + "-mode"))) -(defun org-edit-fixed-width-region () - "Edit the fixed-width ascii drawing at point. -This must be a region where each line starts with a colon followed by -a space character. -An new buffer is created and the fixed-width region is copied into it, -and the buffer is switched into `artist-mode' for editing. When done, -exit with \\[org-edit-src-exit]. The edited text will then replace -the fragment in the Org-mode buffer." - (interactive) - (let ((line (org-current-line)) - (col (current-column)) - (case-fold-search t) - (msg "Edit, then exit with C-c ' (C-c and single quote) -- C-c C-k to abort") - (org-mode-p (derived-mode-p 'org-mode)) - (beg (make-marker)) - (end (make-marker)) - block-nindent ovl beg1 end1 code begline buffer) - (beginning-of-line 1) - (if (looking-at "[ \t]*[^:\n \t]") - nil - (if (looking-at "[ \t]*\\(\n\\|\\'\\)") - (setq beg1 (point) end1 beg1) - (save-excursion - (if (re-search-backward "^[ \t]*[^: \t]" nil 'move) - (setq beg1 (point-at-bol 2)) - (setq beg1 (point)))) - (save-excursion - (if (re-search-forward "^[ \t]*[^: \t]" nil 'move) - (setq end1 (1- (match-beginning 0))) - (setq end1 (point)))) - (org-goto-line line)) - (setq beg (move-marker beg beg1) - end (move-marker end end1) - code (buffer-substring-no-properties beg end) - begline (save-excursion (goto-char beg) (org-current-line))) - (if (and (setq buffer (org-edit-src-find-buffer beg end)) - (y-or-n-p "Return to existing edit buffer ([n] will revert changes)? ")) - (org-pop-to-buffer-same-window buffer) - (when buffer - (with-current-buffer buffer - (if (boundp 'org-edit-src-overlay) - (delete-overlay org-edit-src-overlay))) - (kill-buffer buffer)) - (setq buffer (generate-new-buffer - (org-src-construct-edit-buffer-name - (buffer-name) "Fixed Width"))) - (setq ovl (make-overlay beg end)) - (overlay-put ovl 'face 'secondary-selection) - (overlay-put ovl 'edit-buffer buffer) - (overlay-put ovl 'help-echo "Click with mouse-1 to switch to buffer editing this segment") - (overlay-put ovl 'face 'secondary-selection) - (overlay-put ovl - 'keymap - (let ((map (make-sparse-keymap))) - (define-key map [mouse-1] 'org-edit-src-continue) - map)) - (overlay-put ovl :read-only "Leave me alone") - (org-pop-to-buffer-same-window buffer) - (insert code) +(defun org-src--coordinates (pos beg end) + "Return coordinates of POS relatively to BEG and END. +POS, BEG and END are buffer positions. Return value is either +a cons cell (LINE . COLUMN) or symbol `end'. See also +`org-src--goto-coordinates'." + (if (>= pos end) 'end + (org-with-wide-buffer + (goto-char (max beg pos)) + (cons (count-lines beg (line-beginning-position)) + ;; Column is relative to the end of line to avoid problems of + ;; comma escaping or colons appended in front of the line. + (- (current-column) + (progn (end-of-line) (current-column))))))) + +(defun org-src--goto-coordinates (coord beg end) + "Move to coordinates COORD relatively to BEG and END. +COORD are coordinates, as returned by `org-src--coordinates', +which see. BEG and END are buffer positions." + (goto-char + (if (eq coord 'end) (max (1- end) beg) + ;; If BEG happens to be located outside of the narrowed part of + ;; the buffer, widen it first. + (org-with-wide-buffer + (goto-char beg) + (forward-line (car coord)) + (end-of-line) + (org-move-to-column (max (+ (current-column) (cdr coord)) 0)) + (point))))) + +(defun org-src--contents-area (datum) + "Return contents boundaries of DATUM. +DATUM is an element or object. Return a list (BEG END CONTENTS) +where BEG and END are buffer positions and CONTENTS is a string." + (let ((type (org-element-type datum))) + (org-with-wide-buffer + (cond + ((eq type 'footnote-definition) + (let* ((beg (progn + (goto-char (org-element-property :post-affiliated datum)) + (search-forward "]"))) + (end (or (org-element-property :contents-end datum) beg))) + (list beg end (buffer-substring-no-properties beg end)))) + ((eq type 'inline-src-block) + (let ((beg (progn (goto-char (org-element-property :begin datum)) + (search-forward "{" (line-end-position) t))) + (end (progn (goto-char (org-element-property :end datum)) + (search-backward "}" (line-beginning-position) t)))) + (list beg end (buffer-substring-no-properties beg end)))) + ((org-element-property :contents-begin datum) + (let ((beg (org-element-property :contents-begin datum)) + (end (org-element-property :contents-end datum))) + (list beg end (buffer-substring-no-properties beg end)))) + ((memq type '(example-block export-block src-block)) + (list (progn (goto-char (org-element-property :post-affiliated datum)) + (line-beginning-position 2)) + (progn (goto-char (org-element-property :end datum)) + (skip-chars-backward " \r\t\n") + (line-beginning-position 1)) + (org-element-property :value datum))) + ((memq type '(fixed-width table)) + (let ((beg (org-element-property :post-affiliated datum)) + (end (progn (goto-char (org-element-property :end datum)) + (skip-chars-backward " \r\t\n") + (line-beginning-position 2)))) + (list beg + end + (if (eq type 'fixed-width) (org-element-property :value datum) + (buffer-substring-no-properties beg end))))) + (t (error "Unsupported element or object: %s" type)))))) + +(defun org-src--make-source-overlay (beg end edit-buffer) + "Create overlay between BEG and END positions and return it. +EDIT-BUFFER is the buffer currently editing area between BEG and +END." + (let ((overlay (make-overlay beg end))) + (overlay-put overlay 'face 'secondary-selection) + (overlay-put overlay 'edit-buffer edit-buffer) + (overlay-put overlay 'help-echo + "Click with mouse-1 to switch to buffer editing this segment") + (overlay-put overlay 'face 'secondary-selection) + (overlay-put overlay 'keymap + (let ((map (make-sparse-keymap))) + (define-key map [mouse-1] 'org-edit-src-continue) + map)) + (let ((read-only + (list + (lambda (&rest _) + (user-error + "Cannot modify an area being edited in a dedicated buffer"))))) + (overlay-put overlay 'modification-hooks read-only) + (overlay-put overlay 'insert-in-front-hooks read-only) + (overlay-put overlay 'insert-behind-hooks read-only)) + overlay)) + +(defun org-src--remove-overlay () + "Remove overlay from current source buffer." + (when (overlayp org-src--overlay) (delete-overlay org-src--overlay))) + +(defun org-src--on-datum-p (datum) + "Non-nil when point is on DATUM. +DATUM is an element or an object. Consider blank lines or white +spaces after it as being outside." + (and (>= (point) (org-element-property :begin datum)) + (<= (point) + (org-with-wide-buffer + (goto-char (org-element-property :end datum)) + (skip-chars-backward " \r\t\n") + (if (eq (org-element-class datum) 'element) + (line-end-position) + (point)))))) + +(defun org-src--contents-for-write-back () + "Return buffer contents in a format appropriate for write back. +Assume point is in the corresponding edit buffer." + (let ((indentation-offset + (if org-src--preserve-indentation 0 + (+ (or org-src--block-indentation 0) + (if (memq org-src--source-type '(example-block src-block)) + org-edit-src-content-indentation + 0)))) + (use-tabs? (and (> org-src--tab-width 0) t)) + (source-tab-width org-src--tab-width) + (contents (org-with-wide-buffer (buffer-string))) + (write-back org-src--allow-write-back)) + (with-temp-buffer + ;; Reproduce indentation parameters from source buffer. + (setq-local indent-tabs-mode use-tabs?) + (when (> source-tab-width 0) (setq-local tab-width source-tab-width)) + ;; Apply WRITE-BACK function on edit buffer contents. + (insert (org-no-properties contents)) + (goto-char (point-min)) + (when (functionp write-back) (save-excursion (funcall write-back))) + ;; Add INDENTATION-OFFSET to every non-empty line in buffer, + ;; unless indentation is meant to be preserved. + (when (> indentation-offset 0) + (while (not (eobp)) + (skip-chars-forward " \t") + (unless (eolp) ;ignore blank lines + (let ((i (current-column))) + (delete-region (line-beginning-position) (point)) + (indent-to (+ i indentation-offset)))) + (forward-line))) + (buffer-string)))) + +(defun org-src--edit-element + (datum name &optional major write-back contents remote) + "Edit DATUM contents in a dedicated buffer NAME. + +MAJOR is the major mode used in the edit buffer. A nil value is +equivalent to `fundamental-mode'. + +When WRITE-BACK is non-nil, assume contents will replace original +region. Moreover, if it is a function, apply it in the edit +buffer, from point min, before returning the contents. + +When CONTENTS is non-nil, display them in the edit buffer. +Otherwise, show DATUM contents as specified by +`org-src--contents-area'. + +When REMOTE is non-nil, do not try to preserve point or mark when +moving from the edit area to the source. + +Leave point in edit buffer." + (setq org-src--saved-temp-window-config (current-window-configuration)) + (let* ((area (org-src--contents-area datum)) + (beg (copy-marker (nth 0 area))) + (end (copy-marker (nth 1 area) t)) + (old-edit-buffer (org-src--edit-buffer beg end)) + (contents (or contents (nth 2 area)))) + (if (and old-edit-buffer + (or (not org-src-ask-before-returning-to-edit-buffer) + (y-or-n-p "Return to existing edit buffer ([n] will revert changes)? "))) + ;; Move to existing buffer. + (org-src-switch-to-buffer old-edit-buffer 'return) + ;; Discard old edit buffer. + (when old-edit-buffer + (with-current-buffer old-edit-buffer (org-src--remove-overlay)) + (kill-buffer old-edit-buffer)) + (let* ((org-mode-p (derived-mode-p 'org-mode)) + (source-tab-width (if indent-tabs-mode tab-width 0)) + (type (org-element-type datum)) + (ind (org-with-wide-buffer + (goto-char (org-element-property :begin datum)) + (org-get-indentation))) + (preserve-ind + (and (memq type '(example-block src-block)) + (or (org-element-property :preserve-indent datum) + org-src-preserve-indentation))) + ;; Store relative positions of mark (if any) and point + ;; within the edited area. + (point-coordinates (and (not remote) + (org-src--coordinates (point) beg end))) + (mark-coordinates (and (not remote) + (org-region-active-p) + (let ((m (mark))) + (and (>= m beg) (>= end m) + (org-src--coordinates m beg end))))) + ;; Generate a new edit buffer. + (buffer (generate-new-buffer name)) + ;; Add an overlay on top of source. + (overlay (org-src--make-source-overlay beg end buffer))) + ;; Switch to edit buffer. + (org-src-switch-to-buffer buffer 'edit) + ;; Insert contents. + (insert contents) (remove-text-properties (point-min) (point-max) '(display nil invisible nil intangible nil)) - (setq block-nindent (or (org-do-remove-indentation) 0)) - (cond - ((eq org-edit-fixed-width-region-mode 'artist-mode) - (fundamental-mode) - (artist-mode 1)) - (t (funcall org-edit-fixed-width-region-mode))) - (set (make-local-variable 'org-edit-src-force-single-line) nil) - (set (make-local-variable 'org-edit-src-from-org-mode) org-mode-p) - (set (make-local-variable 'org-edit-src-picture) t) - (goto-char (point-min)) - (while (re-search-forward "^[ \t]*: ?" nil t) - (replace-match "")) - (org-goto-line (1+ (- line begline))) - (org-move-to-column (max 0 (- col block-nindent 2))) - (org-set-local 'org-edit-src-beg-marker beg) - (org-set-local 'org-edit-src-end-marker end) - (org-set-local 'org-edit-src-overlay ovl) - (org-set-local 'org-edit-src-block-indentation block-nindent) - (org-set-local 'org-edit-src-content-indentation 0) - (org-set-local 'org-src-preserve-indentation nil) - (org-src-mode) + (unless preserve-ind (org-do-remove-indentation)) (set-buffer-modified-p nil) - (and org-edit-src-persistent-message - (org-set-local 'header-line-format msg))) - (message "%s" msg) - t))) + (setq buffer-file-name nil) + ;; Start major mode. + (if (not major) (fundamental-mode) + (let ((org-inhibit-startup t)) + (condition-case e (funcall major) + (error (message "Language mode `%s' fails with: %S" + major (nth 1 e)))))) + ;; Transmit buffer-local variables for exit function. It must + ;; be done after initializing major mode, as this operation + ;; may reset them otherwise. + (setq-local org-src--tab-width source-tab-width) + (setq-local org-src--from-org-mode org-mode-p) + (setq-local org-src--beg-marker beg) + (setq-local org-src--end-marker end) + (setq-local org-src--remote remote) + (setq-local org-src--source-type type) + (setq-local org-src--block-indentation ind) + (setq-local org-src--preserve-indentation preserve-ind) + (setq-local org-src--overlay overlay) + (setq-local org-src--allow-write-back write-back) + ;; Start minor mode. + (org-src-mode) + ;; Move mark and point in edit buffer to the corresponding + ;; location. + (if remote + (progn + ;; Put point at first non read-only character after + ;; leading blank. + (goto-char + (or (text-property-any (point-min) (point-max) 'read-only nil) + (point-max))) + (skip-chars-forward " \r\t\n")) + ;; Set mark and point. + (when mark-coordinates + (org-src--goto-coordinates mark-coordinates (point-min) (point-max)) + (push-mark (point) 'no-message t) + (setq deactivate-mark nil)) + (org-src--goto-coordinates + point-coordinates (point-min) (point-max))))))) + + + +;;; Fontification of source blocks -(defun org-edit-src-find-region-and-lang () - "Find the region and language for a local edit. -Return a list with beginning and end of the region, a string representing -the language, a switch telling if the content should be in a single line." - (let ((re-list - (append - org-edit-src-region-extra - '( - ("[^<]*>[ \t]*\n?" "\n?[ \t]*" lang) - ("[^<]*>[ \t]*\n?" "\n?[ \t]*" style) - ("[ \t]*\n?" "\n?[ \t]*" "fundamental") - ("[ \t]*\n?" "\n?[ \t]*" "emacs-lisp") - ("[ \t]*\n?" "\n?[ \t]*" "perl") - ("[ \t]*\n?" "\n?[ \t]*" "python") - ("[ \t]*\n?" "\n?[ \t]*" "ruby") - ("^[ \t]*#\\+begin_src\\( \\([^ \t\n]+\\)\\)?.*\n" "\n[ \t]*#\\+end_src" 2) - ("^[ \t]*#\\+begin_example.*\n" "\n[ \t]*#\\+end_example" "fundamental") - ("^[ \t]*#\\+html:" "\n" "html" single-line) - ("^[ \t]*#\\+begin_html.*\n" "\n[ \t]*#\\+end_html" "html") - ("^[ \t]*#\\+latex:" "\n" "latex" single-line) - ("^[ \t]*#\\+begin_latex.*\n" "\n[ \t]*#\\+end_latex" "latex") - ("^[ \t]*#\\+ascii:" "\n" "fundamental" single-line) - ("^[ \t]*#\\+begin_ascii.*\n" "\n[ \t]*#\\+end_ascii" "fundamental") - ("^[ \t]*#\\+macro:[ \t]+\\S-+\\( \\|$\\)" - "\n" "fundamental" macro-definition) - ))) - (pos (point)) - re1 re2 single beg end lang lfmt match-re1 ind entry) - (catch 'exit - (while (setq entry (pop re-list)) - (setq re1 (car entry) re2 (nth 1 entry) lang (nth 2 entry) - single (nth 3 entry)) - (save-excursion - (if (or (looking-at re1) - (re-search-backward re1 nil t)) - (progn - (setq match-re1 (match-string 0)) - (setq beg (match-end 0) - lang (org-edit-src-get-lang lang) - lfmt (org-edit-src-get-label-format match-re1) - ind (org-edit-src-get-indentation (match-beginning 0))) - (if (and (re-search-forward re2 nil t) - (>= (match-end 0) pos)) - (throw 'exit (list beg (match-beginning 0) - lang single lfmt ind)))) - (if (or (looking-at re2) - (re-search-forward re2 nil t)) - (progn - (setq end (match-beginning 0)) - (if (and (re-search-backward re1 nil t) - (<= (match-beginning 0) pos)) - (progn - (setq lfmt (org-edit-src-get-label-format - (match-string 0)) - ind (org-edit-src-get-indentation - (match-beginning 0))) - (throw 'exit - (list (match-end 0) end - (org-edit-src-get-lang lang) - single lfmt ind))))))))) - (when (org-at-table.el-p) - (re-search-backward "^[\t]*[^ \t|\\+]" nil t) - (setq beg (1+ (point-at-eol))) - (goto-char beg) - (or (re-search-forward "^[\t]*[^ \t|\\+]" nil t) - (progn (goto-char (point-max)) (newline))) - (setq end (1- (point-at-bol))) - (throw 'exit (list beg end 'table.el nil nil 0)))))) - -(defun org-edit-src-get-lang (lang) - "Extract the src language." - (let ((m (match-string 0))) - (cond - ((stringp lang) lang) - ((integerp lang) (match-string lang)) - ((and (eq lang 'lang) - (string-match "\\ cnt 0)) - (goto-char (point-max)) (insert "\\n"))) - (goto-char (point-min)) - (if (looking-at "\\s-*") (replace-match " "))) - (when (and (org-bound-and-true-p org-edit-src-from-org-mode) - (not fixed-width-p)) - (org-escape-code-in-region (point-min) (point-max)) - (setq delta (+ delta - (save-excursion - (org-goto-line line) - (if (looking-at "[ \t]*\\(,,\\)?\\(\\*\\|#+\\)") 1 - 0))))) - (when (org-bound-and-true-p org-edit-src-picture) - (setq preserve-indentation nil) - (untabify (point-min) (point-max)) - (goto-char (point-min)) - (while (re-search-forward "^" nil t) - (replace-match ": "))) - (unless (or single preserve-indentation (= total-nindent 0)) - (setq indent (make-string total-nindent ?\ )) - (goto-char (point-min)) - (while (re-search-forward "\\(^\\).+" nil t) - (replace-match indent nil nil nil 1))) - (if (org-bound-and-true-p org-edit-src-picture) - (setq total-nindent (+ total-nindent 2))) - (setq code (buffer-string)) - (when (eq context 'save) - (erase-buffer) - (insert bufstr)) - (set-buffer-modified-p nil)) - (org-src-switch-to-buffer (marker-buffer beg) (or context 'exit)) - (if (eq context 'save) (save-buffer) - (with-current-buffer buffer - (set-buffer-modified-p nil)) - (kill-buffer buffer)) - (goto-char beg) - (when allow-write-back-p - (undo-boundary) - (delete-region beg (max beg end)) - (unless (string-match "\\`[ \t]*\\'" code) - (insert code)) - ;; Make sure the overlay stays in place - (when (eq context 'save) (move-overlay ovl beg (point))) - (goto-char beg) - (if single (just-one-space))) - (if (memq t (mapcar (lambda (overlay) - (eq (overlay-get overlay 'invisible) - 'org-hide-block)) - (overlays-at (point)))) - ;; Block is hidden; put point at start of block - (beginning-of-line 0) - ;; Block is visible, put point where it was in the code buffer - (when allow-write-back-p - (org-goto-line (1- (+ (org-current-line) line))) - (org-move-to-column (if preserve-indentation col (+ col total-nindent delta))))) - (unless (eq context 'save) - (move-marker beg nil) - (move-marker end nil))) - (unless (eq context 'save) - (when org-edit-src-saved-temp-window-config - (set-window-configuration org-edit-src-saved-temp-window-config) - (setq org-edit-src-saved-temp-window-config nil)))) - -(defun org-edit-src-abort () - "Abort editing of the src code and return to the Org buffer." - (interactive) - (let (org-edit-src-allow-write-back-p) - (org-edit-src-exit 'exit))) - -(defmacro org-src-in-org-buffer (&rest body) - `(let ((p (point)) (m (mark)) (ul buffer-undo-list) msg) - (save-window-excursion - (org-edit-src-exit 'save) - ,@body - (setq msg (current-message)) - (if (eq org-src-window-setup 'other-frame) - (let ((org-src-window-setup 'current-window)) - (org-edit-src-code 'save)) - (org-edit-src-code 'save))) - (setq buffer-undo-list ul) - (push-mark m 'nomessage) - (goto-char (min p (point-max))) - (message (or msg "")))) -(def-edebug-spec org-src-in-org-buffer (body)) -(defun org-edit-src-save () - "Save parent buffer with current state source-code buffer." - (interactive) - (if (string-match "Fixed Width" (buffer-name)) - (user-error "%s" "Use C-c ' to save and exit, C-c C-k to abort editing") - (org-src-in-org-buffer (save-buffer)))) + +;;; Org src minor mode -(declare-function org-babel-tangle "ob-tangle" (&optional arg target-file lang)) +(defvar org-src-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "\C-c'" 'org-edit-src-exit) + (define-key map "\C-c\C-k" 'org-edit-src-abort) + (define-key map "\C-x\C-s" 'org-edit-src-save) + map)) -(defun org-src-tangle (arg) - "Tangle the parent buffer." - (interactive) - (org-src-in-org-buffer (org-babel-tangle arg))) +(define-minor-mode org-src-mode + "Minor mode for language major mode buffers generated by Org. +\\ +This minor mode is turned on in two situations: + - when editing a source code snippet with `\\[org-edit-special]' + - when formatting a source code snippet for export with htmlize. + +\\{org-src-mode-map} + +See also `org-src-mode-hook'." + nil " OrgSrc" nil + (when org-edit-src-persistent-message + (setq-local + header-line-format + (substitute-command-keys + (if org-src--allow-write-back + "Edit, then exit with `\\[org-edit-src-exit]' or abort with \ +`\\[org-edit-src-abort]'" + "Exit with `\\[org-edit-src-exit]' or abort with \ +`\\[org-edit-src-abort]'")))) + ;; Possibly activate various auto-save features (for the edit buffer + ;; or the source buffer). + (when org-edit-src-turn-on-auto-save + (setq buffer-auto-save-file-name + (concat (make-temp-name "org-src-") + (format-time-string "-%Y-%d-%m") + ".txt"))) + (unless (or org-src--auto-save-timer (zerop org-edit-src-auto-save-idle-delay)) + (setq org-src--auto-save-timer + (run-with-idle-timer + org-edit-src-auto-save-idle-delay t + (lambda () + (save-excursion + (let (edit-flag) + (dolist (b (buffer-list)) + (with-current-buffer b + (when (org-src-edit-buffer-p) + (unless edit-flag (setq edit-flag t)) + (when (buffer-modified-p) (org-edit-src-save))))) + (unless edit-flag + (cancel-timer org-src--auto-save-timer) + (setq org-src--auto-save-timer nil))))))))) (defun org-src-mode-configure-edit-buffer () - (when (org-bound-and-true-p org-edit-src-from-org-mode) - (org-add-hook 'kill-buffer-hook - #'(lambda () (delete-overlay org-edit-src-overlay)) nil 'local) - (if (org-bound-and-true-p org-edit-src-allow-write-back-p) + (when (bound-and-true-p org-src--from-org-mode) + (add-hook 'kill-buffer-hook #'org-src--remove-overlay nil 'local) + (if (bound-and-true-p org-src--allow-write-back) (progn (setq buffer-offer-save t) (setq buffer-file-name - (concat (buffer-file-name (marker-buffer org-edit-src-beg-marker)) + (concat (buffer-file-name (marker-buffer org-src--beg-marker)) "[" (buffer-name) "]")) - (if (featurep 'xemacs) - (progn - (make-variable-buffer-local 'write-contents-hooks) ; needed only for 21.4 - (setq write-contents-hooks '(org-edit-src-save))) - (setq write-contents-functions '(org-edit-src-save)))) + (setq-local write-contents-functions '(org-edit-src-save))) (setq buffer-read-only t)))) -(org-add-hook 'org-src-mode-hook 'org-src-mode-configure-edit-buffer) +(add-hook 'org-src-mode-hook #'org-src-mode-configure-edit-buffer) + + +;;; Babel related functions (defun org-src-associate-babel-session (info) "Associate edit buffer with comint session." (interactive) - (let ((session (cdr (assoc :session (nth 2 info))))) + (let ((session (cdr (assq :session (nth 2 info))))) (and session (not (string= session "none")) (org-babel-comint-buffer-livep session) (let ((f (intern (format "org-babel-%s-associate-session" @@ -843,18 +690,22 @@ with \",*\", \",#+\", \",,*\" and \",,#+\"." (and (fboundp f) (funcall f session)))))) (defun org-src-babel-configure-edit-buffer () - (when org-src-babel-info - (org-src-associate-babel-session org-src-babel-info))) + (when org-src--babel-info + (org-src-associate-babel-session org-src--babel-info))) + +(add-hook 'org-src-mode-hook #'org-src-babel-configure-edit-buffer) + + +;;; Public API -(org-add-hook 'org-src-mode-hook 'org-src-babel-configure-edit-buffer) (defmacro org-src-do-at-code-block (&rest body) - "Execute a command from an edit buffer in the Org-mode buffer." - `(let ((beg-marker org-edit-src-beg-marker)) - (if beg-marker - (with-current-buffer (marker-buffer beg-marker) - (goto-char (marker-position beg-marker)) - ,@body)))) -(def-edebug-spec org-src-do-at-code-block (body)) + "Execute BODY from an edit buffer in the Org mode buffer." + (declare (debug (body))) + `(let ((beg-marker org-src--beg-marker)) + (when beg-marker + (with-current-buffer (marker-buffer beg-marker) + (goto-char beg-marker) + ,@body)))) (defun org-src-do-key-sequence-at-code-block (&optional key) "Execute key sequence at code block in the source Org buffer. @@ -878,85 +729,375 @@ Org-babel commands." (if (equal key (kbd "C-g")) (keyboard-quit) (org-edit-src-save) (org-src-do-at-code-block - (call-interactively - (lookup-key org-babel-map key))))) + (call-interactively (lookup-key org-babel-map key))))) -(defcustom org-src-tab-acts-natively nil - "If non-nil, the effect of TAB in a code block is as if it were -issued in the language major mode buffer." - :type 'boolean - :version "24.1" - :group 'org-babel) +(defun org-src-edit-buffer-p (&optional buffer) + "Non-nil when current buffer is a source editing buffer. +If BUFFER is non-nil, test it instead." + (let ((buffer (org-base-buffer (or buffer (current-buffer))))) + (and (buffer-live-p buffer) + (local-variable-p 'org-src--beg-marker buffer) + (local-variable-p 'org-src--end-marker buffer)))) + +(defun org-src-switch-to-buffer (buffer context) + (pcase org-src-window-setup + (`current-window (pop-to-buffer-same-window buffer)) + (`other-window + (switch-to-buffer-other-window buffer)) + (`other-frame + (pcase context + (`exit + (let ((frame (selected-frame))) + (switch-to-buffer-other-frame buffer) + (delete-frame frame))) + (`save + (kill-buffer (current-buffer)) + (pop-to-buffer-same-window buffer)) + (_ (switch-to-buffer-other-frame buffer)))) + (`reorganize-frame + (when (eq context 'edit) (delete-other-windows)) + (org-switch-to-buffer-other-window buffer) + (when (eq context 'exit) (delete-other-windows))) + (`switch-invisibly (set-buffer buffer)) + (_ + (message "Invalid value %s for `org-src-window-setup'" + org-src-window-setup) + (pop-to-buffer-same-window buffer)))) + +(defun org-src-coderef-format (&optional element) + "Return format string for block at point. + +When optional argument ELEMENT is provided, use that block. +Otherwise, assume point is either at a source block, at an +example block. + +If point is in an edit buffer, retrieve format string associated +to the remote source block." + (cond + ((and element (org-element-property :label-fmt element))) + ((org-src-edit-buffer-p) (org-src-do-at-code-block (org-src-coderef-format))) + ((org-element-property :label-fmt (org-element-at-point))) + (t org-coderef-label-format))) + +(defun org-src-coderef-regexp (fmt &optional label) + "Return regexp matching a coderef format string FMT. + +When optional argument LABEL is non-nil, match coderef for that +label only. + +Match group 1 contains the full coderef string with surrounding +white spaces. Match group 2 contains the same string without any +surrounding space. Match group 3 contains the label. + +A coderef format regexp can only match at the end of a line." + (format "\\([ \t]*\\(%s\\)[ \t]*\\)$" + (replace-regexp-in-string + "%s" + (if label (regexp-quote label) "\\([-a-zA-Z0-9_][-a-zA-Z0-9_ ]*\\)") + (regexp-quote fmt) + nil t))) + +(defun org-edit-footnote-reference () + "Edit definition of footnote reference at point." + (interactive) + (let* ((context (org-element-context)) + (label (org-element-property :label context))) + (unless (and (eq (org-element-type context) 'footnote-reference) + (org-src--on-datum-p context)) + (user-error "Not on a footnote reference")) + (unless label (user-error "Cannot edit remotely anonymous footnotes")) + (let* ((definition (org-with-wide-buffer + (org-footnote-goto-definition label) + (backward-char) + (org-element-context))) + (inline? (eq 'footnote-reference (org-element-type definition))) + (contents + (org-with-wide-buffer + (buffer-substring-no-properties + (or (org-element-property :post-affiliated definition) + (org-element-property :begin definition)) + (cond + (inline? (1+ (org-element-property :contents-end definition))) + ((org-element-property :contents-end definition)) + (t (goto-char (org-element-property :post-affiliated definition)) + (line-end-position))))))) + (add-text-properties + 0 + (progn (string-match (if inline? "\\`\\[fn:.*?:" "\\`.*?\\]") contents) + (match-end 0)) + '(read-only "Cannot edit footnote label" front-sticky t rear-nonsticky t) + contents) + (when inline? + (let ((l (length contents))) + (add-text-properties + (1- l) l + '(read-only "Cannot edit past footnote reference" + front-sticky nil rear-nonsticky nil) + contents))) + (org-src--edit-element + definition + (format "*Edit footnote [%s]*" label) + #'org-mode + (lambda () + (if (not inline?) (delete-region (point) (search-forward "]")) + (delete-region (point) (search-forward ":" nil t 2)) + (delete-region (1- (point-max)) (point-max)) + (when (re-search-forward "\n[ \t]*\n" nil t) + (user-error "Inline definitions cannot contain blank lines")) + ;; If footnote reference belongs to a table, make sure to + ;; remove any newline characters in order to preserve + ;; table's structure. + (when (org-element-lineage definition '(table-cell)) + (while (search-forward "\n" nil t) (replace-match ""))))) + contents + 'remote)) + ;; Report success. + t)) + +(defun org-edit-table.el () + "Edit \"table.el\" table at point. +\\ +A new buffer is created and the table is copied into it. Then +the table is recognized with `table-recognize'. When done +editing, exit with `\\[org-edit-src-exit]'. The edited text will \ +then replace +the area in the Org mode buffer. + +Throw an error when not at such a table." + (interactive) + (let ((element (org-element-at-point))) + (unless (and (eq (org-element-type element) 'table) + (eq (org-element-property :type element) 'table.el) + (org-src--on-datum-p element)) + (user-error "Not in a table.el table")) + (org-src--edit-element + element + (org-src--construct-edit-buffer-name (buffer-name) "Table") + #'text-mode t) + (when (bound-and-true-p flyspell-mode) (flyspell-mode -1)) + (table-recognize) + t)) + +(defun org-edit-export-block () + "Edit export block at point. +\\ +A new buffer is created and the block is copied into it, and the +buffer is switched into an appropriate major mode. See also +`org-src-lang-modes'. + +When done, exit with `\\[org-edit-src-exit]'. The edited text \ +will then replace +the area in the Org mode buffer. + +Throw an error when not at an export block." + (interactive) + (let ((element (org-element-at-point))) + (unless (and (eq (org-element-type element) 'export-block) + (org-src--on-datum-p element)) + (user-error "Not in an export block")) + (let* ((type (downcase (org-element-property :type element))) + (mode (org-src--get-lang-mode type))) + (unless (functionp mode) (error "No such language mode: %s" mode)) + (org-src--edit-element + element + (org-src--construct-edit-buffer-name (buffer-name) type) + mode + (lambda () (org-escape-code-in-region (point-min) (point-max))))) + t)) + +(defun org-edit-src-code (&optional code edit-buffer-name) + "Edit the source or example block at point. +\\ +The code is copied to a separate buffer and the appropriate mode +is turned on. When done, exit with `\\[org-edit-src-exit]'. This \ +will remove the +original code in the Org buffer, and replace it with the edited +version. See `org-src-window-setup' to configure the display of +windows containing the Org buffer and the code buffer. -(defun org-src-native-tab-command-maybe () - "Perform language-specific TAB action. -Alter code block according to what TAB does in the language major mode." - (and org-src-tab-acts-natively - (org-in-src-block-p) - (not (equal this-command 'org-shifttab)) - (let ((org-src-strip-leading-and-trailing-blank-lines nil)) - (org-babel-do-key-sequence-in-edit-buffer (kbd "TAB"))))) +When optional argument CODE is a string, edit it in a dedicated +buffer instead. -(add-hook 'org-tab-first-hook 'org-src-native-tab-command-maybe) +When optional argument EDIT-BUFFER-NAME is non-nil, use it as the +name of the sub-editing buffer." + (interactive) + (let* ((element (org-element-at-point)) + (type (org-element-type element))) + (unless (and (memq type '(example-block src-block)) + (org-src--on-datum-p element)) + (user-error "Not in a source or example block")) + (let* ((lang + (if (eq type 'src-block) (org-element-property :language element) + "example")) + (lang-f (and (eq type 'src-block) (org-src--get-lang-mode lang))) + (babel-info (and (eq type 'src-block) + (org-babel-get-src-block-info 'light))) + deactivate-mark) + (when (and (eq type 'src-block) (not (functionp lang-f))) + (error "No such language mode: %s" lang-f)) + (org-src--edit-element + element + (or edit-buffer-name + (org-src--construct-edit-buffer-name (buffer-name) lang)) + lang-f + (and (null code) + (lambda () (org-escape-code-in-region (point-min) (point-max)))) + (and code (org-unescape-code-in-string code))) + ;; Finalize buffer. + (setq-local org-coderef-label-format + (or (org-element-property :label-fmt element) + org-coderef-label-format)) + (when (eq type 'src-block) + (setq-local org-src--babel-info babel-info) + (let ((edit-prep-func (intern (concat "org-babel-edit-prep:" lang)))) + (when (fboundp edit-prep-func) + (funcall edit-prep-func babel-info)))) + t))) -(defun org-src-font-lock-fontify-block (lang start end) - "Fontify code block. -This function is called by emacs automatic fontification, as long -as `org-src-fontify-natively' is non-nil. For manual -fontification of code blocks see `org-src-fontify-block' and -`org-src-fontify-buffer'" - (let ((lang-mode (org-src-get-lang-mode lang))) - (if (fboundp lang-mode) - (let ((string (buffer-substring-no-properties start end)) - (modified (buffer-modified-p)) - (org-buffer (current-buffer)) pos next) - (remove-text-properties start end '(face nil)) - (with-current-buffer - (get-buffer-create - (concat " org-src-fontification:" (symbol-name lang-mode))) - ;; Make sure that modification hooks are not inhibited in - ;; the org-src-fontification buffer in case we're called - ;; from `jit-lock-function' (Bug#25132). - (let ((inhibit-modification-hooks nil)) - (delete-region (point-min) (point-max)) - (insert string " ")) ;; so there's a final property change - (unless (eq major-mode lang-mode) (funcall lang-mode)) - (org-font-lock-ensure) - (setq pos (point-min)) - (while (setq next (next-single-property-change pos 'face)) - (put-text-property - (+ start (1- pos)) (1- (+ start next)) 'face - (get-text-property pos 'face) org-buffer) - (setq pos next))) - (add-text-properties - start end - '(font-lock-fontified t fontified t font-lock-multiline t)) - (set-buffer-modified-p modified))))) +(defun org-edit-inline-src-code () + "Edit inline source code at point." + (interactive) + (let ((context (org-element-context))) + (unless (and (eq (org-element-type context) 'inline-src-block) + (org-src--on-datum-p context)) + (user-error "Not on inline source code")) + (let* ((lang (org-element-property :language context)) + (lang-f (org-src--get-lang-mode lang)) + (babel-info (org-babel-get-src-block-info 'light)) + deactivate-mark) + (unless (functionp lang-f) (error "No such language mode: %s" lang-f)) + (org-src--edit-element + context + (org-src--construct-edit-buffer-name (buffer-name) lang) + lang-f + (lambda () + ;; Inline src blocks are limited to one line. + (while (re-search-forward "\n[ \t]*" nil t) (replace-match " ")) + ;; Trim contents. + (goto-char (point-min)) + (skip-chars-forward " \t") + (delete-region (point-min) (point)) + (goto-char (point-max)) + (skip-chars-backward " \t") + (delete-region (point) (point-max)))) + ;; Finalize buffer. + (setq-local org-src--babel-info babel-info) + (setq-local org-src--preserve-indentation t) + (let ((edit-prep-func (intern (concat "org-babel-edit-prep:" lang)))) + (when (fboundp edit-prep-func) (funcall edit-prep-func babel-info))) + ;; Return success. + t))) -(defvar org-src-fontify-natively) +(defun org-edit-fixed-width-region () + "Edit the fixed-width ASCII drawing at point. +\\ +This must be a region where each line starts with a colon +followed by a space or a newline character. + +A new buffer is created and the fixed-width region is copied into +it, and the buffer is switched into the major mode defined in +`org-edit-fixed-width-region-mode', which see. + +When done, exit with `\\[org-edit-src-exit]'. The edited text \ +will then replace +the area in the Org mode buffer." + (interactive) + (let ((element (org-element-at-point))) + (unless (and (eq (org-element-type element) 'fixed-width) + (org-src--on-datum-p element)) + (user-error "Not in a fixed-width area")) + (org-src--edit-element + element + (org-src--construct-edit-buffer-name (buffer-name) "Fixed Width") + org-edit-fixed-width-region-mode + (lambda () (while (not (eobp)) (insert ": ") (forward-line)))) + ;; Return success. + t)) -(defun org-src-fontify-block () - "Fontify code block at point." +(defun org-edit-src-abort () + "Abort editing of the src code and return to the Org buffer." (interactive) - (save-excursion - (let ((org-src-fontify-natively t) - (info (org-edit-src-find-region-and-lang))) - (font-lock-fontify-region (nth 0 info) (nth 1 info))))) + (let (org-src--allow-write-back) (org-edit-src-exit))) -(defun org-src-fontify-buffer () - "Fontify all code blocks in the current buffer." +(defun org-edit-src-continue (e) + "Unconditionally return to buffer editing area under point. +Throw an error if there is no such buffer." + (interactive "e") + (mouse-set-point e) + (let ((buf (get-char-property (point) 'edit-buffer))) + (if buf (org-src-switch-to-buffer buf 'continue) + (user-error "No sub-editing buffer for area at point")))) + +(defun org-edit-src-save () + "Save parent buffer with current state source-code buffer." (interactive) - (org-babel-map-src-blocks nil - (org-src-fontify-block))) + (unless (org-src-edit-buffer-p) (user-error "Not in a sub-editing buffer")) + (set-buffer-modified-p nil) + (let ((edited-code (org-src--contents-for-write-back)) + (beg org-src--beg-marker) + (end org-src--end-marker) + (overlay org-src--overlay)) + (with-current-buffer (org-src--source-buffer) + (undo-boundary) + (goto-char beg) + ;; Temporarily disable read-only features of OVERLAY in order to + ;; insert new contents. + (delete-overlay overlay) + (delete-region beg end) + (let ((expecting-bol (bolp))) + (insert edited-code) + (when (and expecting-bol (not (bolp))) (insert "\n"))) + (save-buffer) + (move-overlay overlay beg (point)))) + ;; `write-contents-functions' requires the function to return + ;; a non-nil value so that other functions are not called. + t) + +(defun org-edit-src-exit () + "Kill current sub-editing buffer and return to source buffer." + (interactive) + (unless (org-src-edit-buffer-p) (error "Not in a sub-editing buffer")) + (let* ((beg org-src--beg-marker) + (end org-src--end-marker) + (write-back org-src--allow-write-back) + (remote org-src--remote) + (coordinates (and (not remote) + (org-src--coordinates (point) 1 (point-max)))) + (code (and write-back (org-src--contents-for-write-back)))) + (set-buffer-modified-p nil) + ;; Switch to source buffer. Kill sub-editing buffer. + (let ((edit-buffer (current-buffer)) + (source-buffer (marker-buffer beg))) + (unless source-buffer (error "Source buffer disappeared. Aborting")) + (org-src-switch-to-buffer source-buffer 'exit) + (kill-buffer edit-buffer)) + ;; Insert modified code. Ensure it ends with a newline character. + (org-with-wide-buffer + (when (and write-back (not (equal (buffer-substring beg end) code))) + (undo-boundary) + (goto-char beg) + (delete-region beg end) + (let ((expecting-bol (bolp))) + (insert code) + (when (and expecting-bol (not (bolp))) (insert "\n"))))) + ;; If we are to return to source buffer, put point at an + ;; appropriate location. In particular, if block is hidden, move + ;; to the beginning of the block opening line. + (unless remote + (goto-char beg) + (cond + ;; Block is hidden; move at start of block. + ((cl-some (lambda (o) (eq (overlay-get o 'invisible) 'org-hide-block)) + (overlays-at (point))) + (beginning-of-line 0)) + (write-back (org-src--goto-coordinates coordinates beg end)))) + ;; Clean up left-over markers and restore window configuration. + (set-marker beg nil) + (set-marker end nil) + (when org-src--saved-temp-window-config + (set-window-configuration org-src--saved-temp-window-config) + (setq org-src--saved-temp-window-config nil)))) -(defun org-src-get-lang-mode (lang) - "Return major mode that should be used for LANG. -LANG is a string, and the returned major mode is a symbol." - (intern - (concat - (let ((l (or (cdr (assoc lang org-src-lang-modes)) lang))) - (if (symbolp l) (symbol-name l) l)) - "-mode"))) (provide 'org-src) diff --git a/lisp/org/org-table.el b/lisp/org/org-table.el index 0c813d03a1..40a715aebd 100644 --- a/lisp/org/org-table.el +++ b/lisp/org/org-table.el @@ -1,4 +1,4 @@ -;;; org-table.el --- The table editor for Org-mode +;;; org-table.el --- The Table Editor for Org -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -24,27 +24,53 @@ ;; ;;; Commentary: -;; This file contains the table editor and spreadsheet for Org-mode. +;; This file contains the table editor and spreadsheet for Org mode. ;; Watch out: Here we are talking about two different kind of tables. -;; Most of the code is for the tables created with the Org-mode table editor. +;; Most of the code is for the tables created with the Org mode table editor. ;; Sometimes, we talk about tables created and edited with the table.el ;; Emacs package. We call the former org-type tables, and the latter ;; table.el-type tables. ;;; Code: -(eval-when-compile - (require 'cl)) +(require 'cl-lib) (require 'org) -(declare-function org-export-string-as "ox" - (string backend &optional body-only ext-plist)) -(declare-function aa2u "ext:ascii-art-to-unicode" ()) +(declare-function org-element-at-point "org-element" ()) +(declare-function org-element-contents "org-element" (element)) +(declare-function org-element-extract-element "org-element" (element)) +(declare-function org-element-interpret-data "org-element" (data)) +(declare-function org-element-lineage "org-element" + (blob &optional types with-self)) +(declare-function org-element-map "org-element" + (data types fun + &optional info first-match no-recursion with-affiliated)) +(declare-function org-element-parse-buffer "org-element" + (&optional granularity visible-only)) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-type "org-element" (element)) + +(declare-function org-export-create-backend "ox" (&rest rest) t) +(declare-function org-export-data-with-backend "ox" (data backend info)) +(declare-function org-export-filter-apply-functions "ox" + (filters value info)) +(declare-function org-export-first-sibling-p "ox" (blob info)) +(declare-function org-export-get-backend "ox" (name)) +(declare-function org-export-get-environment "ox" + (&optional backend subtreep ext-plist)) +(declare-function org-export-install-filters "ox" (info)) +(declare-function org-export-table-has-special-column-p "ox" (table)) +(declare-function org-export-table-row-is-special-p "ox" (table-row info)) + +(declare-function calc-eval "calc" (str &optional separator &rest args)) + (defvar orgtbl-mode) ; defined below (defvar orgtbl-mode-menu) ; defined when orgtbl mode get initialized (defvar constants-unit-system) +(defvar org-export-filters-alist) (defvar org-table-follow-field-mode) +(defvar sort-fold-case) (defvar orgtbl-after-send-table-hook nil "Hook for functions attaching to `C-c C-c', if the table is sent. @@ -52,7 +78,7 @@ This can be used to add additional functionality after the table is sent to the receiver position, otherwise, if table is not sent, the functions are not run.") -(defvar org-table-TBLFM-begin-regexp "|\n[ \t]*#\\+TBLFM: ") +(defvar org-table-TBLFM-begin-regexp "^[ \t]*|.*\n[ \t]*#\\+TBLFM: ") (defcustom orgtbl-optimized (eq org-enable-table-editor 'optimized) "Non-nil means use the optimized table editor version for `orgtbl-mode'. @@ -63,7 +89,7 @@ for empty fields). Outside tables, the correct binding of the keys is restored. The default for this option is t if the optimized version is also used in -Org-mode. See the variable `org-enable-table-editor' for details. Changing +Org mode. See the variable `org-enable-table-editor' for details. Changing this variable requires a restart of Emacs to become effective." :group 'org-table :type 'boolean) @@ -118,7 +144,7 @@ table, obtained by prompting the user." (string :tag "Format")))) (defgroup org-table-settings nil - "Settings for tables in Org-mode." + "Settings for tables in Org mode." :tag "Org Table Settings" :group 'org-table) @@ -167,13 +193,13 @@ alignment to the right border applies." :type 'number) (defgroup org-table-editing nil - "Behavior of tables during editing in Org-mode." + "Behavior of tables during editing in Org mode." :tag "Org Table Editing" :group 'org-table) (defcustom org-table-automatic-realign t "Non-nil means automatically re-align table when pressing TAB or RETURN. -When nil, aligning is only done with \\[org-table-align], or after column +When nil, aligning is only done with `\\[org-table-align]', or after column removal/insertion." :group 'org-table-editing :type 'boolean) @@ -219,12 +245,12 @@ this line." :type 'boolean) (defgroup org-table-calculation nil - "Options concerning tables in Org-mode." + "Options concerning tables in Org mode." :tag "Org Table Calculation" :group 'org-table) (defcustom org-table-use-standard-references 'from - "Should org-mode work with table references like B3 instead of @3$2? + "Non-nil means using table references like B3 instead of @3$2. Possible values are: nil never use them from accept as input, do not present for editing @@ -236,9 +262,15 @@ t accept as input and present for editing" (const :tag "Convert user input, don't offer during editing" from))) (defcustom org-table-copy-increment t - "Non-nil means increment when copying current field with \\[org-table-copy-down]." + "Non-nil means increment when copying current field with \ +`\\[org-table-copy-down]'." :group 'org-table-calculation - :type 'boolean) + :version "26.1" + :package-version '(Org . "8.3") + :type '(choice + (const :tag "Use the difference between the current and the above fields" t) + (integer :tag "Use a number" 1) + (const :tag "Don't increment the value when copying a field" nil))) (defcustom org-calc-default-modes '(calc-internal-prec 12 @@ -251,16 +283,16 @@ t accept as input and present for editing" ) "List with Calc mode settings for use in `calc-eval' for table formulas. The list must contain alternating symbols (Calc modes variables and values). -Don't remove any of the default settings, just change the values. Org-mode +Don't remove any of the default settings, just change the values. Org mode relies on the variables to be present in the list." :group 'org-table-calculation :type 'plist) (defcustom org-table-duration-custom-format 'hours "Format for the output of calc computations like $1+$2;t. -The default value is 'hours, and will output the results as a -number of hours. Other allowed values are 'seconds, 'minutes and -'days, and the output will be a fraction of seconds, minutes or +The default value is `hours', and will output the results as a +number of hours. Other allowed values are `seconds', `minutes' and +`days', and the output will be a fraction of seconds, minutes or days." :group 'org-table-calculation :version "24.1" @@ -285,7 +317,7 @@ which should be evaluated as described in the manual and in the documentation string of the command `org-table-eval-formula'. This feature requires the Emacs calc package. When this variable is nil, formula calculation is only available through -the command \\[org-table-eval-formula]." +the command `\\[org-table-eval-formula]'." :group 'org-table-calculation :type 'boolean) @@ -317,15 +349,12 @@ Constants can also be defined on a per-file basis using a line like (defcustom org-table-allow-automatic-line-recalculation t "Non-nil means lines marked with |#| or |*| will be recomputed automatically. -Automatically means when TAB or RET or C-c C-c are pressed in the line." +\\\ +Automatically means when `TAB' or `RET' or `\\[org-ctrl-c-ctrl-c]' \ +are pressed in the line." :group 'org-table-calculation :type 'boolean) -(defcustom org-table-error-on-row-ref-crossing-hline t - "OBSOLETE VARIABLE, please see `org-table-relative-ref-may-cross-hline'." - :group 'org-table - :type 'boolean) - (defcustom org-table-relative-ref-may-cross-hline t "Non-nil means relative formula references may cross hlines. Here are the allowed values: @@ -345,8 +374,20 @@ portability of tables." (const :tag "Stick to hline" nil) (const :tag "Error on attempt to cross" error))) +(defcustom org-table-formula-create-columns nil + "Non-nil means that evaluation of a field formula can add new +columns if an out-of-bounds field is being set." + :group 'org-table-calculation + :version "26.1" + :package-version '(Org . "8.3") + :type '(choice + (const :tag "Setting an out-of-bounds field generates an error (default)" nil) + (const :tag "Setting an out-of-bounds field silently adds columns as needed" t) + (const :tag "Setting an out-of-bounds field adds columns as needed, but issues a warning message" warn) + (const :tag "When setting an out-of-bounds field, the user is prompted" prompt))) + (defgroup org-table-import-export nil - "Options concerning table import and export in Org-mode." + "Options concerning table import and export in Org mode." :tag "Org Table Import Export" :group 'org-table) @@ -359,38 +400,73 @@ available parameters." :group 'org-table-import-export :type 'string) +(defcustom org-table-convert-region-max-lines 999 + "Max lines that `org-table-convert-region' will attempt to process. + +The function can be slow on larger regions; this safety feature +prevents it from hanging emacs." + :group 'org-table-import-export + :type 'integer + :version "26.1" + :package-version '(Org . "8.3")) + (defconst org-table-auto-recalculate-regexp "^[ \t]*| *# *\\(|\\|$\\)" - "Detects a table line marked for automatic recalculation.") + "Regexp matching a line marked for automatic recalculation.") + (defconst org-table-recalculate-regexp "^[ \t]*| *[#*] *\\(|\\|$\\)" - "Detects a table line marked for automatic recalculation.") + "Regexp matching a line marked for recalculation.") + (defconst org-table-calculate-mark-regexp "^[ \t]*| *[!$^_#*] *\\(|\\|$\\)" - "Detects a table line marked for automatic recalculation.") + "Regexp matching a line marked for calculation.") + (defconst org-table-border-regexp "^[ \t]*[^| \t]" - "Searching from within a table (any type) this finds the first line outside the table.") + "Regexp matching any line outside an Org table.") + (defvar org-table-last-highlighted-reference nil) + (defvar org-table-formula-history nil) (defvar org-table-column-names nil - "Alist with column names, derived from the `!' line.") + "Alist with column names, derived from the `!' line. +This variable is initialized with `org-table-analyze'.") + (defvar org-table-column-name-regexp nil - "Regular expression matching the current column names.") + "Regular expression matching the current column names. +This variable is initialized with `org-table-analyze'.") + (defvar org-table-local-parameters nil - "Alist with parameter names, derived from the `$' line.") + "Alist with parameter names, derived from the `$' line. +This variable is initialized with `org-table-analyze'.") + (defvar org-table-named-field-locations nil - "Alist with locations of named fields.") + "Alist with locations of named fields. +Associations follow the pattern (NAME LINE COLUMN) where + NAME is the name of the field as a string, + LINE is the number of lines from the beginning of the table, + COLUMN is the column of the field, as an integer. +This variable is initialized with `org-table-analyze'.") (defvar org-table-current-line-types nil - "Table row types, non-nil only for the duration of a command.") -(defvar org-table-current-begin-line nil - "Table begin line, non-nil only for the duration of a command.") + "Table row types in current table. +This variable is initialized with `org-table-analyze'.") + (defvar org-table-current-begin-pos nil - "Table begin position, non-nil only for the duration of a command.") + "Current table begin position, as a marker. +This variable is initialized with `org-table-analyze'.") + (defvar org-table-current-ncol nil - "Number of columns in table, non-nil only for the duration of a command.") + "Number of columns in current table. +This variable is initialized with `org-table-analyze'.") + (defvar org-table-dlines nil - "Vector of data line line numbers in the current table.") + "Vector of data line line numbers in the current table. +Line numbers are counted from the beginning of the table. This +variable is initialized with `org-table-analyze'.") + (defvar org-table-hlines nil - "Vector of hline line numbers in the current table.") + "Vector of hline line numbers in the current table. +Line numbers are counted from the beginning of the table. This +variable is initialized with `org-table-analyze'.") (defconst org-table-range-regexp "@\\([-+]?I*[-+]?[0-9]*\\)?\\(\\$[-+]?[0-9]+\\)?\\(\\.\\.@?\\([-+]?I*[-+]?[0-9]*\\)?\\(\\$[-+]?[0-9]+\\)?\\)?" @@ -404,85 +480,33 @@ available parameters." "\\(" "@?[-0-9I$&]+" "\\|" "[a-zA-Z]\\{1,2\\}\\([0-9]+\\|&\\)" "\\|" "\\$[a-zA-Z0-9]+" "\\)") "Match a range for reference display.") -(defun org-table-colgroup-line-p (line) - "Is this a table line colgroup information?" - (save-match-data - (and (string-match "[<>]\\|&[lg]t;" line) - (string-match "\\`[ \t]*|[ \t]*/[ \t]*\\(|[ \t<>0-9|lgt&;]+\\)\\'" - line) - (not (delq - nil - (mapcar - (lambda (s) - (not (member s '("" "<" ">" "<>" "<" ">" "<>")))) - (org-split-string (match-string 1 line) "[ \t]*|[ \t]*"))))))) - -(defun org-table-cookie-line-p (line) - "Is this a table line with only alignment/width cookies?" - (save-match-data - (and (string-match "[<>]\\|&[lg]t;" line) - (or (string-match - "\\`[ \t]*|[ \t]*/[ \t]*\\(|[ \t<>0-9|lrcgt&;]+\\)\\'" line) - (string-match "\\(\\`[ \t<>lrc0-9|gt&;]+\\'\\)" line)) - (not (delq nil (mapcar - (lambda (s) - (not (or (equal s "") - (string-match - "\\`<\\([lrc]?[0-9]+\\|[lrc]\\)>\\'" s) - (string-match - "\\`<\\([lrc]?[0-9]+\\|[lrc]\\)>\\'" - s)))) - (org-split-string (match-string 1 line) - "[ \t]*|[ \t]*"))))))) - -(defvar org-table-clean-did-remove-column nil) ; dynamically scoped -(defun org-table-clean-before-export (lines &optional maybe-quoted) - "Check if the table has a marking column. -If yes remove the column and the special lines." - (let ((special (if maybe-quoted - "^[ \t]*| *\\\\?[#!$*_^/ ] *|" - "^[ \t]*| *[#!$*_^/ ] *|")) - (ignore (if maybe-quoted - "^[ \t]*| *\\\\?[!$_^/] *|" - "^[ \t]*| *[!$_^/] *|"))) - (setq org-table-clean-did-remove-column - (not (memq nil - (mapcar - (lambda (line) - (or (string-match org-table-hline-regexp line) - (string-match special line))) - lines)))) - (delq nil - (mapcar - (lambda (line) - (cond - ((or (org-table-colgroup-line-p line) ;; colgroup info - (org-table-cookie-line-p line) ;; formatting cookies - (and org-table-clean-did-remove-column - (string-match ignore line))) ;; non-exportable data - nil) - ((and org-table-clean-did-remove-column - (or (string-match "^\\([ \t]*\\)|-+\\+" line) - (string-match "^\\([ \t]*\\)|[^|]*|" line))) - ;; remove the first column - (replace-match "\\1|" t nil line)) - (t line))) - lines)))) - (defconst org-table-translate-regexp (concat "\\(" "@[-0-9I$]+" "\\|" "[a-zA-Z]\\{1,2\\}\\([0-9]+\\|&\\)" "\\)") "Match a reference that needs translation, for reference display.") +(defmacro org-table-save-field (&rest body) + "Save current field; execute BODY; restore field. +Field is restored even in case of abnormal exit." + (declare (debug (body))) + (org-with-gensyms (line column) + `(let ((,line (copy-marker (line-beginning-position))) + (,column (org-table-current-column))) + (unwind-protect + (progn ,@body) + (goto-char ,line) + (org-table-goto-column ,column) + (set-marker ,line nil))))) + ;;;###autoload (defun org-table-create-with-table.el () "Use the table.el package to insert a new table. -If there is already a table at point, convert between Org-mode tables +If there is already a table at point, convert between Org tables and table.el tables." (interactive) (require 'table) (cond ((org-at-table.el-p) - (if (y-or-n-p "Convert table to Org-mode table? ") + (if (y-or-n-p "Convert table to Org table? ") (org-table-convert))) ((org-at-table-p) (when (y-or-n-p "Convert table to table.el table? ") @@ -526,7 +550,7 @@ SIZE is a string Columns x Rows like for example \"3x2\"." (beginning-of-line 1) (newline)) ;; (mapcar (lambda (x) (insert line)) (make-list rows t)) - (dotimes (i rows) (insert line)) + (dotimes (_ rows) (insert line)) (goto-char pos) (if (> rows 1) ;; Insert a hline after the first row. @@ -539,15 +563,18 @@ SIZE is a string Columns x Rows like for example \"3x2\"." ;;;###autoload (defun org-table-convert-region (beg0 end0 &optional separator) "Convert region to a table. + The region goes from BEG0 to END0, but these borders will be moved slightly, to make sure a beginning of line in the first line is included. SEPARATOR specifies the field separator in the lines. It can have the following values: -(4) Use the comma as a field separator -(16) Use a TAB as field separator -integer When a number, use that many spaces as field separator +(4) Use the comma as a field separator +(16) Use a TAB as field separator +(64) Prompt for a regular expression as field separator +integer When a number, use that many spaces, or a TAB, as field separator +regexp When a regular expression, use it to match the separator nil When nil, the command tries to be smart and figure out the separator in the following way: - when each line contains a TAB, assume TAB-separated material @@ -557,45 +584,52 @@ nil When nil, the command tries to be smart and figure out the (let* ((beg (min beg0 end0)) (end (max beg0 end0)) re) - (goto-char beg) - (beginning-of-line 1) - (setq beg (point-marker)) - (goto-char end) - (if (bolp) (backward-char 1) (end-of-line 1)) - (setq end (point-marker)) - ;; Get the right field separator - (unless separator + (if (> (count-lines beg end) org-table-convert-region-max-lines) + (user-error "Region is longer than `org-table-convert-region-max-lines' (%s) lines; not converting" + org-table-convert-region-max-lines) + (if (equal separator '(64)) + (setq separator (read-regexp "Regexp for field separator"))) (goto-char beg) - (setq separator + (beginning-of-line 1) + (setq beg (point-marker)) + (goto-char end) + (if (bolp) (backward-char 1) (end-of-line 1)) + (setq end (point-marker)) + ;; Get the right field separator + (unless separator + (goto-char beg) + (setq separator + (cond + ((not (re-search-forward "^[^\n\t]+$" end t)) '(16)) + ((not (re-search-forward "^[^\n,]+$" end t)) '(4)) + (t 1)))) + (goto-char beg) + (if (equal separator '(4)) + (while (< (point) end) + ;; parse the csv stuff (cond - ((not (re-search-forward "^[^\n\t]+$" end t)) '(16)) - ((not (re-search-forward "^[^\n,]+$" end t)) '(4)) - (t 1)))) - (goto-char beg) - (if (equal separator '(4)) - (while (< (point) end) - ;; parse the csv stuff - (cond - ((looking-at "^") (insert "| ")) - ((looking-at "[ \t]*$") (replace-match " |") (beginning-of-line 2)) - ((looking-at "[ \t]*\"\\([^\"\n]*\\)\"") - (replace-match "\\1") - (if (looking-at "\"") (insert "\""))) - ((looking-at "[^,\n]+") (goto-char (match-end 0))) - ((looking-at "[ \t]*,") (replace-match " | ")) - (t (beginning-of-line 2)))) - (setq re (cond - ((equal separator '(4)) "^\\|\"?[ \t]*,[ \t]*\"?") - ((equal separator '(16)) "^\\|\t") - ((integerp separator) - (if (< separator 1) - (user-error "Number of spaces in separator must be >= 1") - (format "^ *\\| *\t *\\| \\{%d,\\}" separator))) - (t (error "This should not happen")))) - (while (re-search-forward re end t) - (replace-match "| " t t))) - (goto-char beg) - (org-table-align))) + ((looking-at "^") (insert "| ")) + ((looking-at "[ \t]*$") (replace-match " |") (beginning-of-line 2)) + ((looking-at "[ \t]*\"\\([^\"\n]*\\)\"") + (replace-match "\\1") + (if (looking-at "\"") (insert "\""))) + ((looking-at "[^,\n]+") (goto-char (match-end 0))) + ((looking-at "[ \t]*,") (replace-match " | ")) + (t (beginning-of-line 2)))) + (setq re (cond + ((equal separator '(4)) "^\\|\"?[ \t]*,[ \t]*\"?") + ((equal separator '(16)) "^\\|\t") + ((integerp separator) + (if (< separator 1) + (user-error "Number of spaces in separator must be >= 1") + (format "^ *\\| *\t *\\| \\{%d,\\}" separator))) + ((stringp separator) + (format "^ *\\|%s" separator)) + (t (error "This should not happen")))) + (while (re-search-forward re end t) + (replace-match "| " t t))) + (goto-char beg) + (org-table-align)))) ;;;###autoload (defun org-table-import (file arg) @@ -611,8 +645,6 @@ are found, lines will be split on whitespace into fields." (org-table-convert-region beg (+ (point) (- (point-max) pm)) arg))) -(defvar org-table-last-alignment) -(defvar org-table-last-column-widths) ;;;###autoload (defun org-table-export (&optional file format) "Export table to a file, with configurable format. @@ -630,77 +662,61 @@ extension of the given file name, and finally on the variable `org-table-export-default-format'." (interactive) (unless (org-at-table-p) (user-error "No table at point")) - (org-table-align) ;; make sure we have everything we need - (let* ((beg (org-table-begin)) - (end (org-table-end)) - (txt (buffer-substring-no-properties beg end)) - (file (or file (org-entry-get beg "TABLE_EXPORT_FILE" t))) - (formats '("orgtbl-to-tsv" "orgtbl-to-csv" - "orgtbl-to-latex" "orgtbl-to-html" - "orgtbl-to-generic" "orgtbl-to-texinfo" - "orgtbl-to-orgtbl")) - (format (or format - (org-entry-get beg "TABLE_EXPORT_FORMAT" t))) - buf deffmt-readable fileext) + (org-table-align) ; Make sure we have everything we need. + (let ((file (or file (org-entry-get (point) "TABLE_EXPORT_FILE" t)))) (unless file (setq file (read-file-name "Export table to: ")) (unless (or (not (file-exists-p file)) (y-or-n-p (format "Overwrite file %s? " file))) (user-error "File not written"))) - (if (file-directory-p file) - (user-error "This is a directory path, not a file")) - (if (and (buffer-file-name) - (equal (file-truename file) - (file-truename (buffer-file-name)))) - (user-error "Please specify a file name that is different from current")) - (setq fileext (concat (file-name-extension file) "$")) - (unless format - (setq deffmt-readable - (or (car (delq nil (mapcar (lambda(f) (if (string-match fileext f) f)) formats))) - org-table-export-default-format)) - (while (string-match "\t" deffmt-readable) - (setq deffmt-readable (replace-match "\\t" t t deffmt-readable))) - (while (string-match "\n" deffmt-readable) - (setq deffmt-readable (replace-match "\\n" t t deffmt-readable))) - (setq format (org-completing-read "Format: " formats nil nil deffmt-readable))) - (if (string-match "\\([^ \t\r\n]+\\)\\( +.*\\)?" format) - (let* ((transform (intern (match-string 1 format))) - (params (if (match-end 2) - (read (concat "(" (match-string 2 format) ")")))) - (skip (plist-get params :skip)) - (skipcols (plist-get params :skipcols)) - (lines (nthcdr (or skip 0) (org-split-string txt "[ \t]*\n[ \t]*"))) - (lines (org-table-clean-before-export lines)) - (i0 (if org-table-clean-did-remove-column 2 1)) - (table (mapcar - (lambda (x) - (if (string-match org-table-hline-regexp x) - 'hline - (org-remove-by-index - (org-split-string (org-trim x) "\\s-*|\\s-*") - skipcols i0))) - lines)) - (fun (if (= i0 2) 'cdr 'identity)) - (org-table-last-alignment - (org-remove-by-index (funcall fun org-table-last-alignment) - skipcols i0)) - (org-table-last-column-widths - (org-remove-by-index (funcall fun org-table-last-column-widths) - skipcols i0))) - - (unless (fboundp transform) - (user-error "No such transformation function %s" transform)) - (setq txt (funcall transform table params)) - - (with-current-buffer (find-file-noselect file) - (setq buf (current-buffer)) - (erase-buffer) - (fundamental-mode) - (insert txt "\n") - (save-buffer)) - (kill-buffer buf) - (message "Export done.")) - (user-error "TABLE_EXPORT_FORMAT invalid")))) + (when (file-directory-p file) + (user-error "This is a directory path, not a file")) + (when (and (buffer-file-name (buffer-base-buffer)) + (file-equal-p + (file-truename file) + (file-truename (buffer-file-name (buffer-base-buffer))))) + (user-error "Please specify a file name that is different from current")) + (let ((fileext (concat (file-name-extension file) "$")) + (format (or format (org-entry-get (point) "TABLE_EXPORT_FORMAT" t)))) + (unless format + (let* ((formats '("orgtbl-to-tsv" "orgtbl-to-csv" "orgtbl-to-latex" + "orgtbl-to-html" "orgtbl-to-generic" + "orgtbl-to-texinfo" "orgtbl-to-orgtbl" + "orgtbl-to-unicode")) + (deffmt-readable + (replace-regexp-in-string + "\t" "\\t" + (replace-regexp-in-string + "\n" "\\n" + (or (car (delq nil + (mapcar + (lambda (f) + (and (string-match-p fileext f) f)) + formats))) + org-table-export-default-format) + t t) t t))) + (setq format + (org-completing-read + "Format: " formats nil nil deffmt-readable)))) + (if (string-match "\\([^ \t\r\n]+\\)\\( +.*\\)?" format) + (let ((transform (intern (match-string 1 format))) + (params (and (match-end 2) + (read (concat "(" (match-string 2 format) ")")))) + (table (org-table-to-lisp + (buffer-substring-no-properties + (org-table-begin) (org-table-end))))) + (unless (fboundp transform) + (user-error "No such transformation function %s" transform)) + (let (buf) + (with-current-buffer (find-file-noselect file) + (setq buf (current-buffer)) + (erase-buffer) + (fundamental-mode) + (insert (funcall transform table params) "\n") + (save-buffer)) + (kill-buffer buf)) + (message "Export done.")) + (user-error "TABLE_EXPORT_FORMAT invalid"))))) (defvar org-table-aligned-begin-marker (make-marker) "Marker at the beginning of the table last aligned. @@ -714,13 +730,11 @@ This is being used to correctly align a single field after TAB or RET.") (defvar org-table-last-column-widths nil "List of max width of fields in each column. This is being used to correctly align a single field after TAB or RET.") -(defvar org-table-formula-debug nil +(defvar-local org-table-formula-debug nil "Non-nil means debug table formulas. When nil, simply write \"#ERROR\" in corrupted fields.") -(make-variable-buffer-local 'org-table-formula-debug) -(defvar org-table-overlay-coordinates nil +(defvar-local org-table-overlay-coordinates nil "Overlay coordinates after each align of a table.") -(make-variable-buffer-local 'org-table-overlay-coordinates) (defvar org-last-recalc-line nil) (defvar org-table-do-narrow t) ; for dynamic scoping @@ -731,216 +745,198 @@ When nil, simply write \"#ERROR\" in corrupted fields.") (defun org-table-align () "Align the table at point by aligning all vertical bars." (interactive) - (let* ( - ;; Limits of table - (beg (org-table-begin)) - (end (org-table-end)) - ;; Current cursor position - (linepos (org-current-line)) - (colpos (org-table-current-column)) - (winstart (window-start)) - (winstartline (org-current-line (min winstart (1- (point-max))))) - lines (new "") lengths l typenums ty fields maxfields i - column - (indent "") cnt frac - rfmt hfmt - (spaces '(1 . 1)) - (sp1 (car spaces)) - (sp2 (cdr spaces)) - (rfmt1 (concat - (make-string sp2 ?\ ) "%%%s%ds" (make-string sp1 ?\ ) "|")) - (hfmt1 (concat - (make-string sp2 ?-) "%s" (make-string sp1 ?-) "+")) - emptystrings links dates emph raise narrow - falign falign1 fmax f1 len c e space) - (untabify beg end) - (remove-text-properties beg end '(org-cwidth t org-dwidth t display t)) - ;; Check if we have links or dates - (goto-char beg) - (setq links (re-search-forward org-bracket-link-regexp end t)) - (goto-char beg) - (setq emph (and org-hide-emphasis-markers - (re-search-forward org-emph-re end t))) - (goto-char beg) - (setq raise (and org-use-sub-superscripts - (re-search-forward org-match-substring-regexp end t))) - (goto-char beg) - (setq dates (and org-display-custom-times - (re-search-forward org-ts-regexp-both end t))) - ;; Make sure the link properties are right - (when links (goto-char beg) (while (org-activate-bracket-links end))) - ;; Make sure the date properties are right - (when dates (goto-char beg) (while (org-activate-dates end))) - (when emph (goto-char beg) (while (org-do-emphasis-faces end))) - (when raise (goto-char beg) (while (org-raise-scripts end))) - - ;; Check if we are narrowing any columns - (goto-char beg) - (setq narrow (and org-table-do-narrow - org-format-transports-properties-p - (re-search-forward "<[lrc]?[0-9]+>" end t))) - (goto-char beg) - (setq falign (re-search-forward "<[lrc][0-9]*>" end t)) - (goto-char beg) - ;; Get the rows - (setq lines (org-split-string - (buffer-substring beg end) "\n")) - ;; Store the indentation of the first line - (if (string-match "^ *" (car lines)) - (setq indent (make-string (- (match-end 0) (match-beginning 0)) ?\ ))) - ;; Mark the hlines by setting the corresponding element to nil - ;; At the same time, we remove trailing space. - (setq lines (mapcar (lambda (l) - (if (string-match "^ *|-" l) - nil - (if (string-match "[ \t]+$" l) - (substring l 0 (match-beginning 0)) - l))) - lines)) - ;; Get the data fields by splitting the lines. - (setq fields (mapcar - (lambda (l) - (org-split-string l " *| *")) - (delq nil (copy-sequence lines)))) - ;; How many fields in the longest line? - (condition-case nil - (setq maxfields (apply 'max (mapcar 'length fields))) - (error - (kill-region beg end) - (org-table-create org-table-default-size) - (user-error "Empty table - created default table"))) - ;; A list of empty strings to fill any short rows on output - (setq emptystrings (make-list maxfields "")) - ;; Check for special formatting. - (setq i -1) - (while (< (setq i (1+ i)) maxfields) ;; Loop over all columns - (setq column (mapcar (lambda (x) (or (nth i x) "")) fields)) - ;; Check if there is an explicit width specified - (setq fmax nil) - (when (or narrow falign) - (setq c column fmax nil falign1 nil) - (while c - (setq e (pop c)) - (when (and (stringp e) (string-match "^<\\([lrc]\\)?\\([0-9]+\\)?>$" e)) - (if (match-end 1) (setq falign1 (match-string 1 e))) - (if (and org-table-do-narrow (match-end 2)) - (setq fmax (string-to-number (match-string 2 e)) c nil)))) - ;; Find fields that are wider than fmax, and shorten them - (when fmax - (loop for xx in column do - (when (and (stringp xx) - (> (org-string-width xx) fmax)) - (org-add-props xx nil - 'help-echo - (concat "Clipped table field, use C-c ` to edit. Full value is:\n" (org-no-properties (copy-sequence xx)))) - (setq f1 (min fmax (or (string-match org-bracket-link-regexp xx) fmax))) - (unless (> f1 1) - (user-error "Cannot narrow field starting with wide link \"%s\"" - (match-string 0 xx))) - (add-text-properties f1 (length xx) (list 'org-cwidth t) xx) - (add-text-properties (- f1 2) f1 - (list 'display org-narrow-column-arrow) - xx))))) - ;; Get the maximum width for each column - (push (apply 'max (or fmax 1) 1 (mapcar 'org-string-width column)) - lengths) - ;; Get the fraction of numbers, to decide about alignment of the column - (if falign1 - (push (equal (downcase falign1) "r") typenums) - (setq cnt 0 frac 0.0) - (loop for x in column do - (if (equal x "") - nil - (setq frac ( / (+ (* frac cnt) - (if (string-match org-table-number-regexp x) 1 0)) - (setq cnt (1+ cnt)))))) - (push (>= frac org-table-number-fraction) typenums))) - (setq lengths (nreverse lengths) typenums (nreverse typenums)) - - ;; Store the alignment of this table, for later editing of single fields - (setq org-table-last-alignment typenums - org-table-last-column-widths lengths) - - ;; With invisible characters, `format' does not get the field width right - ;; So we need to make these fields wide by hand. - (when (or links emph raise) - (loop for i from 0 upto (1- maxfields) do - (setq len (nth i lengths)) - (loop for j from 0 upto (1- (length fields)) do - (setq c (nthcdr i (car (nthcdr j fields)))) - (if (and (stringp (car c)) - (or (text-property-any 0 (length (car c)) - 'invisible 'org-link (car c)) - (text-property-any 0 (length (car c)) - 'org-dwidth t (car c))) - (< (org-string-width (car c)) len)) - (progn - (setq space (make-string (- len (org-string-width (car c))) ?\ )) - (setcar c (if (nth i typenums) - (concat space (car c)) - (concat (car c) space)))))))) - - ;; Compute the formats needed for output of the table - (setq rfmt (concat indent "|") hfmt (concat indent "|")) - (while (setq l (pop lengths)) - (setq ty (if (pop typenums) "" "-")) ; number types flushright - (setq rfmt (concat rfmt (format rfmt1 ty l)) - hfmt (concat hfmt (format hfmt1 (make-string l ?-))))) - (setq rfmt (concat rfmt "\n") - hfmt (concat (substring hfmt 0 -1) "|\n")) - - (setq new (mapconcat - (lambda (l) - (if l (apply 'format rfmt - (append (pop fields) emptystrings)) - hfmt)) - lines "")) - (move-marker org-table-aligned-begin-marker (point)) - (insert new) - ;; Replace the old one - (delete-region (point) end) - (move-marker end nil) - (move-marker org-table-aligned-end-marker (point)) - (when (and orgtbl-mode (not (derived-mode-p 'org-mode))) - (goto-char org-table-aligned-begin-marker) - (while (org-hide-wide-columns org-table-aligned-end-marker))) - ;; Try to move to the old location - (org-goto-line winstartline) - (setq winstart (point-at-bol)) - (org-goto-line linepos) - (when (eq (window-buffer (selected-window)) (current-buffer)) - (set-window-start (selected-window) winstart 'noforce)) - (org-table-goto-column colpos) - (and org-table-overlay-coordinates (org-table-overlay-coordinates)) - (setq org-table-may-need-update nil) - )) + (let* ((beg (org-table-begin)) + (end (copy-marker (org-table-end)))) + (org-table-save-field + ;; Make sure invisible characters in the table are at the right + ;; place since column widths take them into account. + (font-lock-fontify-region beg end) + (move-marker org-table-aligned-begin-marker beg) + (move-marker org-table-aligned-end-marker end) + (goto-char beg) + (let* ((indent (progn (looking-at "[ \t]*") (match-string 0))) + ;; Table's rows. Separators are replaced by nil. Trailing + ;; spaces are also removed. + (lines (mapcar (lambda (l) + (and (not (string-match-p "\\`[ \t]*|-" l)) + (let ((l (org-trim l))) + (remove-text-properties + 0 (length l) '(display t org-cwidth t) l) + l))) + (org-split-string (buffer-substring beg end) "\n"))) + ;; Get the data fields by splitting the lines. + (fields (mapcar (lambda (l) (org-split-string l " *| *")) + (remq nil lines))) + ;; Compute number of fields in the longest line. If the + ;; table contains no field, create a default table. + (maxfields (if fields (apply #'max (mapcar #'length fields)) + (kill-region beg end) + (org-table-create org-table-default-size) + (user-error "Empty table - created default table"))) + ;; A list of empty strings to fill any short rows on output. + (emptycells (make-list maxfields "")) + lengths typenums) + ;; Check for special formatting. + (dotimes (i maxfields) + (let ((column (mapcar (lambda (x) (or (nth i x) "")) fields)) + fmax falign) + ;; Look for an explicit width or alignment. + (when (save-excursion + (or (re-search-forward "| *<[lrc][0-9]*> *\\(|\\|$\\)" end t) + (and org-table-do-narrow + (re-search-forward + "| *<[lrc]?[0-9]+> *\\(|\\|$\\)" end t)))) + (catch :exit + (dolist (cell column) + (when (string-match "\\`<\\([lrc]\\)?\\([0-9]+\\)?>\\'" cell) + (when (match-end 1) (setq falign (match-string 1 cell))) + (when (and org-table-do-narrow (match-end 2)) + (setq fmax (string-to-number (match-string 2 cell)))) + (when (or falign fmax) (throw :exit nil))))) + ;; Find fields that are wider than FMAX, and shorten them. + (when fmax + (dolist (x column) + (when (> (org-string-width x) fmax) + (org-add-props x nil + 'help-echo + (concat + "Clipped table field, use `\\[org-table-edit-field]' to \ +edit. Full value is:\n" + (substring-no-properties x))) + (let ((l (length x)) + (f1 (min fmax + (or (string-match org-bracket-link-regexp x) + fmax))) + (f2 1)) + (unless (> f1 1) + (user-error + "Cannot narrow field starting with wide link \"%s\"" + (match-string 0 x))) + (if (= (org-string-width x) l) (setq f2 f1) + (setq f2 1) + (while (< (org-string-width (substring x 0 f2)) f1) + (cl-incf f2))) + (add-text-properties f2 l (list 'org-cwidth t) x) + (add-text-properties + (if (>= (string-width (substring x (1- f2) f2)) 2) (1- f2) + (- f2 2)) + f2 + (list 'display org-narrow-column-arrow) + x)))))) + ;; Get the maximum width for each column + (push (apply #'max (or fmax 1) 1 (mapcar #'org-string-width column)) + lengths) + ;; Get the fraction of numbers among non-empty cells to + ;; decide about alignment of the column. + (if falign (push (equal (downcase falign) "r") typenums) + (let ((cnt 0) + (frac 0.0)) + (dolist (x column) + (unless (equal x "") + (setq frac + (/ (+ (* frac cnt) + (if (string-match-p org-table-number-regexp x) + 1 + 0)) + (cl-incf cnt))))) + (push (>= frac org-table-number-fraction) typenums))))) + (setq lengths (nreverse lengths)) + (setq typenums (nreverse typenums)) + ;; Store alignment of this table, for later editing of single + ;; fields. + (setq org-table-last-alignment typenums) + (setq org-table-last-column-widths lengths) + ;; With invisible characters, `format' does not get the field + ;; width right So we need to make these fields wide by hand. + ;; Invisible characters may be introduced by fontified links, + ;; emphasis, macros or sub/superscripts. + (when (or (text-property-any beg end 'invisible 'org-link) + (text-property-any beg end 'invisible t)) + (dotimes (i maxfields) + (let ((len (nth i lengths))) + (dotimes (j (length fields)) + (let* ((c (nthcdr i (nth j fields))) + (cell (car c))) + (when (and + (stringp cell) + (let ((l (length cell))) + (or (text-property-any 0 l 'invisible 'org-link cell) + (text-property-any beg end 'invisible t))) + (< (org-string-width cell) len)) + (let ((s (make-string (- len (org-string-width cell)) ?\s))) + (setcar c (if (nth i typenums) (concat s cell) + (concat cell s)))))))))) + + ;; Compute the formats needed for output of the table. + (let ((hfmt (concat indent "|")) + (rfmt (concat indent "|")) + (rfmt1 " %%%s%ds |") + (hfmt1 "-%s-+")) + (dolist (l lengths (setq hfmt (concat (substring hfmt 0 -1) "|"))) + (let ((ty (if (pop typenums) "" "-"))) ; Flush numbers right. + (setq rfmt (concat rfmt (format rfmt1 ty l))) + (setq hfmt (concat hfmt (format hfmt1 (make-string l ?-)))))) + ;; Replace modified lines only. Check not only contents, but + ;; also columns' width. + (dolist (l lines) + (let ((line + (if l (apply #'format rfmt (append (pop fields) emptycells)) + hfmt)) + (previous (buffer-substring (point) (line-end-position)))) + (if (and (equal previous line) + (let ((a 0) + (b 0)) + (while (and (progn + (setq a (next-single-property-change + a 'org-cwidth previous)) + (setq b (next-single-property-change + b 'org-cwidth line))) + (eq a b))) + (eq a b))) + (forward-line) + (insert line "\n") + (delete-region (point) (line-beginning-position 2)))))) + (when (and orgtbl-mode (not (derived-mode-p 'org-mode))) + (goto-char org-table-aligned-begin-marker) + (while (org-hide-wide-columns org-table-aligned-end-marker))) + (set-marker end nil) + (when org-table-overlay-coordinates (org-table-overlay-coordinates)) + (setq org-table-may-need-update nil))))) ;;;###autoload (defun org-table-begin (&optional table-type) "Find the beginning of the table and return its position. -With argument TABLE-TYPE, go to the beginning of a table.el-type table." - (save-excursion - (if (not (re-search-backward - (if table-type org-table-any-border-regexp - org-table-border-regexp) - nil t)) - (progn (goto-char (point-min)) (point)) - (goto-char (match-beginning 0)) - (beginning-of-line 2) - (point)))) +With a non-nil optional argument TABLE-TYPE, return the beginning +of a table.el-type table. This function assumes point is on +a table." + (cond (table-type + (org-element-property :post-affiliated (org-element-at-point))) + ((save-excursion + (and (re-search-backward org-table-border-regexp nil t) + (line-beginning-position 2)))) + (t (point-min)))) ;;;###autoload (defun org-table-end (&optional table-type) "Find the end of the table and return its position. -With argument TABLE-TYPE, go to the end of a table.el-type table." +With a non-nil optional argument TABLE-TYPE, return the end of +a table.el-type table. This function assumes point is on +a table." (save-excursion - (if (not (re-search-forward - (if table-type org-table-any-border-regexp - org-table-border-regexp) - nil t)) - (goto-char (point-max)) - (goto-char (match-beginning 0))) - (point-marker))) + (cond (table-type + (goto-char (org-element-property :end (org-element-at-point))) + (skip-chars-backward " \t\n") + (line-beginning-position 2)) + ((re-search-forward org-table-border-regexp nil t) + (match-beginning 0)) + ;; When the line right after the table is the last line in + ;; the buffer with trailing spaces but no final newline + ;; character, be sure to catch the correct ending at its + ;; beginning. In any other case, ending is expected to be + ;; at point max. + (t (goto-char (point-max)) + (skip-chars-backward " \t") + (if (bolp) (point) (line-end-position)))))) ;;;###autoload (defun org-table-justify-field-maybe (&optional new) @@ -950,38 +946,40 @@ Optional argument NEW may specify text to replace the current field content." ((and (not new) org-table-may-need-update)) ; Realignment will happen anyway ((org-at-table-hline-p)) ((and (not new) - (or (not (equal (marker-buffer org-table-aligned-begin-marker) - (current-buffer))) + (or (not (eq (marker-buffer org-table-aligned-begin-marker) + (current-buffer))) (< (point) org-table-aligned-begin-marker) (>= (point) org-table-aligned-end-marker))) - ;; This is not the same table, force a full re-align + ;; This is not the same table, force a full re-align. (setq org-table-may-need-update t)) - (t ;; realign the current field, based on previous full realign - (let* ((pos (point)) s - (col (org-table-current-column)) - (num (if (> col 0) (nth (1- col) org-table-last-alignment))) - l f n o e) + (t + ;; Realign the current field, based on previous full realign. + (let ((pos (point)) + (col (org-table-current-column))) (when (> col 0) - (skip-chars-backward "^|\n") - (if (looking-at " *\\([^|\n]*?\\) *\\(|\\|$\\)") - (progn - (setq s (match-string 1) - o (match-string 0) - l (max 1 (- (match-end 0) (match-beginning 0) 3)) - e (not (= (match-beginning 2) (match-end 2)))) - (setq f (format (if num " %%%ds %s" " %%-%ds %s") - l (if e "|" (setq org-table-may-need-update t) "")) - n (format f s)) - (if new - (if (<= (length new) l) ;; FIXME: length -> str-width? - (setq n (format f new)) - (setq n (concat new "|") org-table-may-need-update t))) - (if (equal (string-to-char n) ?-) (setq n (concat " " n))) - (or (equal n o) - (let (org-table-may-need-update) - (replace-match n t t)))) - (setq org-table-may-need-update t)) - (goto-char pos)))))) + (skip-chars-backward "^|") + (if (not (looking-at " *\\([^|\n]*?\\) *\\(|\\|$\\)")) + (setq org-table-may-need-update t) + (let* ((numbers? (nth (1- col) org-table-last-alignment)) + (cell (match-string 0)) + (field (match-string 1)) + (len (max 1 (- (org-string-width cell) 3))) + (properly-closed? (/= (match-beginning 2) (match-end 2))) + (fmt (format (if numbers? " %%%ds %s" " %%-%ds %s") + len + (if properly-closed? "|" + (setq org-table-may-need-update t) + ""))) + (new-cell + (cond ((not new) (format fmt field)) + ((<= (org-string-width new) len) (format fmt new)) + (t + (setq org-table-may-need-update t) + (format " %s |" new))))) + (unless (equal new-cell cell) + (let (org-table-may-need-update) + (replace-match new-cell t t))) + (goto-char pos)))))))) ;;;###autoload (defun org-table-next-field () @@ -1036,9 +1034,10 @@ Before doing so, re-align the table if necessary." (goto-char (match-end 0)))) (defun org-table-beginning-of-field (&optional n) - "Move to the end of the current table field. -If already at or after the end, move to the end of the next table field. -With numeric argument N, move N-1 fields forward first." + "Move to the beginning of the current table field. +If already at or before the beginning, move to the beginning of the +previous field. +With numeric argument N, move N-1 fields backward first." (interactive "p") (let ((pos (point))) (while (> n 1) @@ -1051,10 +1050,9 @@ With numeric argument N, move N-1 fields forward first." (if (>= (point) pos) (org-table-beginning-of-field 2)))) (defun org-table-end-of-field (&optional n) - "Move to the beginning of the current table field. -If already at or before the beginning, move to the beginning of the -previous field. -With numeric argument N, move N-1 fields backward first." + "Move to the end of the current table field. +If already at or after the end, move to the end of the next table field. +With numeric argument N, move N-1 fields forward first." (interactive "p") (let ((pos (point))) (while (> n 1) @@ -1093,30 +1091,36 @@ Before doing so, re-align the table if necessary." ;;;###autoload (defun org-table-copy-down (n) - "Copy a field down in the current column. -If the field at the cursor is empty, copy into it the content of -the nearest non-empty field above. With argument N, use the Nth -non-empty field. If the current field is not empty, it is copied -down to the next row, and the cursor is moved with it. -Therefore, repeating this command causes the column to be filled -row-by-row. + "Copy the value of the current field one row below. + +If the field at the cursor is empty, copy the content of the +nearest non-empty field above. With argument N, use the Nth +non-empty field. + +If the current field is not empty, it is copied down to the next +row, and the cursor is moved with it. Therefore, repeating this +command causes the column to be filled row-by-row. + If the variable `org-table-copy-increment' is non-nil and the field is an integer or a timestamp, it will be incremented while -copying. In the case of a timestamp, increment by one day." +copying. By default, increment by the difference between the +value in the current field and the one in the field above. To +increment using a fixed integer, set `org-table-copy-increment' +to a number. In the case of a timestamp, increment by days." (interactive "p") (let* ((colpos (org-table-current-column)) (col (current-column)) (field (save-excursion (org-table-get-field))) + (field-up (or (save-excursion + (org-table-get (1- (org-table-current-line)) + (org-table-current-column))) "")) (non-empty (string-match "[^ \t]" field)) + (non-empty-up (string-match "[^ \t]" field-up)) (beg (org-table-begin)) (orig-n n) - txt) + txt txt-up inc) (org-table-check-inside-data-field) - (if non-empty - (progn - (setq txt (org-trim field)) - (org-table-next-row) - (org-table-blank-field)) + (if (not non-empty) (save-excursion (setq txt (catch 'exit @@ -1127,35 +1131,60 @@ copying. In the case of a timestamp, increment by one day." (if (and (looking-at "|[ \t]*\\([^| \t][^|]*?\\)[ \t]*|") (<= (setq n (1- n)) 0)) - (throw 'exit (match-string 1)))))))) - (if txt - (progn - (if (and org-table-copy-increment - (not (equal orig-n 0)) - (string-match "^[0-9]+$" txt) - (< (string-to-number txt) 100000000)) - (setq txt (format "%d" (+ (string-to-number txt) 1)))) - (insert txt) - (org-move-to-column col) - (if (and org-table-copy-increment (org-at-timestamp-p t)) - (org-timestamp-up-day) - (org-table-maybe-recalculate-line)) - (org-table-align) - (org-move-to-column col)) - (user-error "No non-empty field found")))) + (throw 'exit (match-string 1)))))) + (setq field-up + (catch 'exit + (while (progn (beginning-of-line 1) + (re-search-backward org-table-dataline-regexp + beg t)) + (org-table-goto-column colpos t) + (if (and (looking-at + "|[ \t]*\\([^| \t][^|]*?\\)[ \t]*|") + (<= (setq n (1- n)) 0)) + (throw 'exit (match-string 1)))))) + (setq non-empty-up (and field-up (string-match "[^ \t]" field-up)))) + ;; Above field was not empty, go down to the next row + (setq txt (org-trim field)) + (org-table-next-row) + (org-table-blank-field)) + (if non-empty-up (setq txt-up (org-trim field-up))) + (setq inc (cond + ((numberp org-table-copy-increment) org-table-copy-increment) + (txt-up (cond ((and (string-match org-ts-regexp3 txt-up) + (string-match org-ts-regexp3 txt)) + (- (org-time-string-to-absolute txt) + (org-time-string-to-absolute txt-up))) + ((string-match org-ts-regexp3 txt) 1) + ((string-match "\\([-+]\\)?[0-9]+\\(?:\.[0-9]+\\)?" txt-up) + (- (string-to-number txt) + (string-to-number (match-string 0 txt-up)))) + (t 1))) + (t 1))) + (if (not txt) + (user-error "No non-empty field found") + (if (and org-table-copy-increment + (not (equal orig-n 0)) + (string-match-p "^[-+^/*0-9eE.]+$" txt) + (< (string-to-number txt) 100000000)) + (setq txt (calc-eval (concat txt "+" (number-to-string inc))))) + (insert txt) + (org-move-to-column col) + (if (and org-table-copy-increment (org-at-timestamp-p t)) + (org-timestamp-up-day inc) + (org-table-maybe-recalculate-line)) + (org-table-align) + (org-move-to-column col)))) (defun org-table-check-inside-data-field (&optional noerror) "Is point inside a table data field? I.e. not on a hline or before the first or after the last column? This actually throws an error, so it aborts the current command." - (if (or (not (org-at-table-p)) - (= (org-table-current-column) 0) - (org-at-table-hline-p) - (looking-at "[ \t]*$")) - (if noerror - nil - (user-error "Not in table data field")) - t)) + (cond ((and (org-at-table-p) + (not (save-excursion (skip-chars-backward " \t") (bolp))) + (not (org-at-table-hline-p)) + (not (looking-at "[ \t]*$")))) + (noerror nil) + (t (user-error "Not in table data field")))) (defvar org-table-clip nil "Clipboard for table regions.") @@ -1166,7 +1195,7 @@ If LINE is larger than the number of data lines in the table, the function returns nil. However, if COLUMN is too large, we will simply return an empty string. If LINE is nil, use the current line. -If column is nil, use the current column." +If COLUMN is nil, use the current column." (setq column (or column (org-table-current-column))) (save-excursion (and (or (not line) (org-table-goto-line line)) @@ -1206,7 +1235,7 @@ Return t when the line exists, nil if it does not exist." "Blank the current table field or active region." (interactive) (org-table-check-inside-data-field) - (if (and (org-called-interactively-p 'any) (org-region-active-p)) + (if (and (called-interactively-p 'any) (org-region-active-p)) (let (org-table-clip) (org-table-cut-region (region-beginning) (region-end))) (skip-chars-backward "^|") @@ -1221,52 +1250,53 @@ Return t when the line exists, nil if it does not exist." (defun org-table-get-field (&optional n replace) "Return the value of the field in column N of current row. -N defaults to current field. -If REPLACE is a string, replace field with this value. The return value -is always the old value." - (and n (org-table-goto-column n)) +N defaults to current column. If REPLACE is a string, replace +field with this value. The return value is always the old +value." + (when n (org-table-goto-column n)) (skip-chars-backward "^|\n") - (backward-char 1) - (if (looking-at "|[^|\r\n]*") - (let* ((pos (match-beginning 0)) - (val (buffer-substring (1+ pos) (match-end 0)))) - (if replace - (replace-match (concat "|" (if (equal replace "") " " replace)) - t t)) - (goto-char (min (point-at-eol) (+ 2 pos))) - val) - (forward-char 1) "")) + (if (or (bolp) (looking-at-p "[ \t]*$")) + ;; Before first column or after last one. + "" + (looking-at "[^|\r\n]*") + (let* ((pos (match-beginning 0)) + (val (buffer-substring pos (match-end 0)))) + (when replace + (replace-match (if (equal replace "") " " replace) t t)) + (goto-char (min (line-end-position) (1+ pos))) + val))) ;;;###autoload -(defun org-table-field-info (arg) +(defun org-table-field-info (_arg) "Show info about the current field, and highlight any reference at point." (interactive "P") (unless (org-at-table-p) (user-error "Not at a table")) - (org-table-get-specials) + (org-table-analyze) (save-excursion (let* ((pos (point)) (col (org-table-current-column)) (cname (car (rassoc (int-to-string col) org-table-column-names))) - (name (car (rassoc (list (org-current-line) col) + (name (car (rassoc (list (count-lines org-table-current-begin-pos + (line-beginning-position)) + col) org-table-named-field-locations))) (eql (org-table-expand-lhs-ranges (mapcar (lambda (e) - (cons (org-table-formula-handle-first/last-rc - (car e)) (cdr e))) + (cons (org-table-formula-handle-first/last-rc (car e)) + (cdr e))) (org-table-get-stored-formulas)))) (dline (org-table-current-dline)) (ref (format "@%d$%d" dline col)) (ref1 (org-table-convert-refs-to-an ref)) + ;; Prioritize field formulas over column formulas. (fequation (or (assoc name eql) (assoc ref eql))) - (cequation (assoc (int-to-string col) eql)) + (cequation (assoc (format "$%d" col) eql)) (eqn (or fequation cequation))) - (if (and eqn (get-text-property 0 :orig-eqn (car eqn))) - (setq eqn (get-text-property 0 :orig-eqn (car eqn)))) + (let ((p (and eqn (get-text-property 0 :orig-eqn (car eqn))))) + (when p (setq eqn p))) (goto-char pos) - (condition-case nil - (org-table-show-reference 'local) - (error nil)) + (ignore-errors (org-table-show-reference 'local)) (message "line @%d, col $%s%s, ref @%d$%d or %s%s%s" dline col (if cname (concat " or $" cname) "") @@ -1277,39 +1307,42 @@ is always the old value." (concat ", formula: " (org-table-formula-to-user (concat - (if (string-match "^[$@]"(car eqn)) "" "$") + (if (or (string-prefix-p "$" (car eqn)) + (string-prefix-p "@" (car eqn))) + "" + "$") (car eqn) "=" (cdr eqn)))) ""))))) (defun org-table-current-column () "Find out which column we are in." (interactive) - (if (org-called-interactively-p 'any) (org-table-check-inside-data-field)) + (when (called-interactively-p 'any) (org-table-check-inside-data-field)) (save-excursion - (let ((cnt 0) (pos (point))) - (beginning-of-line 1) - (while (search-forward "|" pos t) - (setq cnt (1+ cnt))) - (when (org-called-interactively-p 'interactive) - (message "In table column %d" cnt)) - cnt))) + (let ((column 0) (pos (point))) + (beginning-of-line) + (while (search-forward "|" pos t) (cl-incf column)) + (when (called-interactively-p 'interactive) + (message "In table column %d" column)) + column))) ;;;###autoload (defun org-table-current-dline () "Find out what table data line we are in. Only data lines count for this." (interactive) - (when (org-called-interactively-p 'any) + (when (called-interactively-p 'any) (org-table-check-inside-data-field)) (save-excursion - (let ((cnt 0) (pos (point))) + (let ((c 0) + (pos (point))) (goto-char (org-table-begin)) (while (<= (point) pos) - (if (looking-at org-table-dataline-regexp) (setq cnt (1+ cnt))) - (beginning-of-line 2)) - (when (org-called-interactively-p 'any) - (message "This is table line %d" cnt)) - cnt))) + (when (looking-at org-table-dataline-regexp) (cl-incf c)) + (forward-line)) + (when (called-interactively-p 'any) + (message "This is table line %d" c)) + c))) ;;;###autoload (defun org-table-goto-column (n &optional on-delim force) @@ -1338,25 +1371,19 @@ However, when FORCE is non-nil, create new columns if necessary." (defun org-table-insert-column () "Insert a new column into the table." (interactive) - (if (not (org-at-table-p)) - (user-error "Not at a table")) + (unless (org-at-table-p) (user-error "Not at a table")) (org-table-find-dataline) (let* ((col (max 1 (org-table-current-column))) (beg (org-table-begin)) - (end (org-table-end)) - ;; Current cursor position - (linepos (org-current-line)) - (colpos col)) - (goto-char beg) - (while (< (point) end) - (if (org-at-table-hline-p) - nil - (org-table-goto-column col t) - (insert "| ")) - (beginning-of-line 2)) - (move-marker end nil) - (org-goto-line linepos) - (org-table-goto-column colpos) + (end (copy-marker (org-table-end)))) + (org-table-save-field + (goto-char beg) + (while (< (point) end) + (unless (org-at-table-hline-p) + (org-table-goto-column col t) + (insert "| ")) + (forward-line))) + (set-marker end nil) (org-table-align) (when (or (not org-table-fix-formulas-confirm) (funcall org-table-fix-formulas-confirm "Fix formulas? ")) @@ -1384,58 +1411,55 @@ However, when FORCE is non-nil, create new columns if necessary." (defun org-table-line-to-dline (line &optional above) "Turn a buffer line number into a data line number. + If there is no data line in this line, return nil. -If there is no matching dline (most likely te reference was a hline), the -first dline below it is used. When ABOVE is non-nil, the one above is used." - (catch 'exit - (let ((ll (length org-table-dlines)) - i) - (if above - (progn - (setq i (1- ll)) - (while (> i 0) - (if (<= (aref org-table-dlines i) line) - (throw 'exit i)) - (setq i (1- i)))) - (setq i 1) - (while (< i ll) - (if (>= (aref org-table-dlines i) line) - (throw 'exit i)) - (setq i (1+ i))))) - nil)) + +If there is no matching dline (most likely the reference was +a hline), the first dline below it is used. When ABOVE is +non-nil, the one above is used." + (let ((min 1) + (max (1- (length org-table-dlines)))) + (cond ((or (> (aref org-table-dlines min) line) + (< (aref org-table-dlines max) line)) + nil) + ((= (aref org-table-dlines max) line) max) + (t (catch 'exit + (while (> (- max min) 1) + (let* ((mean (/ (+ max min) 2)) + (v (aref org-table-dlines mean))) + (cond ((= v line) (throw 'exit mean)) + ((> v line) (setq max mean)) + (t (setq min mean))))) + (if above min max)))))) ;;;###autoload (defun org-table-delete-column () "Delete a column from the table." (interactive) - (if (not (org-at-table-p)) - (user-error "Not at a table")) + (unless (org-at-table-p) (user-error "Not at a table")) (org-table-find-dataline) (org-table-check-inside-data-field) - (let* ((col (org-table-current-column)) - (beg (org-table-begin)) - (end (org-table-end)) - ;; Current cursor position - (linepos (org-current-line)) - (colpos col)) - (goto-char beg) - (while (< (point) end) - (if (org-at-table-hline-p) - nil - (org-table-goto-column col t) - (and (looking-at "|[^|\n]+|") - (replace-match "|"))) - (beginning-of-line 2)) - (move-marker end nil) - (org-goto-line linepos) - (org-table-goto-column colpos) + (let ((col (org-table-current-column)) + (beg (org-table-begin)) + (end (copy-marker (org-table-end)))) + (org-table-save-field + (goto-char beg) + (while (< (point) end) + (if (org-at-table-hline-p) + nil + (org-table-goto-column col t) + (and (looking-at "|[^|\n]+|") + (replace-match "|"))) + (forward-line))) + (set-marker end nil) + (org-table-goto-column (max 1 (1- col))) (org-table-align) (when (or (not org-table-fix-formulas-confirm) (funcall org-table-fix-formulas-confirm "Fix formulas? ")) - (org-table-fix-formulas "$" (list (cons (number-to-string col) "INVALID")) - col -1 col) - (org-table-fix-formulas "$LR" (list (cons (number-to-string col) "INVALID")) - col -1 col)))) + (org-table-fix-formulas + "$" (list (cons (number-to-string col) "INVALID")) col -1 col) + (org-table-fix-formulas + "$LR" (list (cons (number-to-string col) "INVALID")) col -1 col)))) ;;;###autoload (defun org-table-move-column-right () @@ -1452,31 +1476,29 @@ first dline below it is used. When ABOVE is non-nil, the one above is used." (defun org-table-move-column (&optional left) "Move the current column to the right. With arg LEFT, move to the left." (interactive "P") - (if (not (org-at-table-p)) - (user-error "Not at a table")) + (unless (org-at-table-p) (user-error "Not at a table")) (org-table-find-dataline) (org-table-check-inside-data-field) (let* ((col (org-table-current-column)) (col1 (if left (1- col) col)) + (colpos (if left (1- col) (1+ col))) (beg (org-table-begin)) - (end (org-table-end)) - ;; Current cursor position - (linepos (org-current-line)) - (colpos (if left (1- col) (1+ col)))) - (if (and left (= col 1)) - (user-error "Cannot move column further left")) - (if (and (not left) (looking-at "[^|\n]*|[^|\n]*$")) - (user-error "Cannot move column further right")) - (goto-char beg) - (while (< (point) end) - (if (org-at-table-hline-p) - nil - (org-table-goto-column col1 t) - (and (looking-at "|\\([^|\n]+\\)|\\([^|\n]+\\)|") - (replace-match "|\\2|\\1|"))) - (beginning-of-line 2)) - (move-marker end nil) - (org-goto-line linepos) + (end (copy-marker (org-table-end)))) + (when (and left (= col 1)) + (user-error "Cannot move column further left")) + (when (and (not left) (looking-at "[^|\n]*|[^|\n]*$")) + (user-error "Cannot move column further right")) + (org-table-save-field + (goto-char beg) + (while (< (point) end) + (unless (org-at-table-hline-p) + (org-table-goto-column col1 t) + (when (looking-at "|\\([^|\n]+\\)|\\([^|\n]+\\)|") + (transpose-regions + (match-beginning 1) (match-end 1) + (match-beginning 2) (match-end 2)))) + (forward-line))) + (set-marker end nil) (org-table-goto-column colpos) (org-table-align) (when (or (not org-table-fix-formulas-confirm) @@ -1538,19 +1560,21 @@ first dline below it is used. When ABOVE is non-nil, the one above is used." "Insert a new row above the current line into the table. With prefix ARG, insert below the current line." (interactive "P") - (if (not (org-at-table-p)) - (user-error "Not at a table")) - (let* ((line (buffer-substring (point-at-bol) (point-at-eol))) + (unless (org-at-table-p) (user-error "Not at a table")) + (let* ((line (buffer-substring (line-beginning-position) (line-end-position))) (new (org-table-clean-line line))) ;; Fix the first field if necessary (if (string-match "^[ \t]*| *[#$] *|" line) (setq new (replace-match (match-string 0 line) t t new))) (beginning-of-line (if arg 2 1)) + ;; Buffer may not end of a newline character, so ensure + ;; (beginning-of-line 2) moves point to a new line. + (unless (bolp) (insert "\n")) (let (org-table-may-need-update) (insert-before-markers new "\n")) (beginning-of-line 0) - (re-search-forward "| ?" (point-at-eol) t) - (and (or org-table-may-need-update org-table-overlay-coordinates) - (org-table-align)) + (re-search-forward "| ?" (line-end-position) t) + (when (or org-table-may-need-update org-table-overlay-coordinates) + (org-table-align)) (when (or (not org-table-fix-formulas-confirm) (funcall org-table-fix-formulas-confirm "Fix formulas? ")) (org-table-fix-formulas "@" nil (1- (org-table-current-dline)) 1)))) @@ -1563,7 +1587,7 @@ With prefix ABOVE, insert above the current line." (if (not (org-at-table-p)) (user-error "Not at a table")) (when (eobp) (insert "\n") (backward-char 1)) - (if (not (string-match "|[ \t]*$" (org-current-line-string))) + (if (not (string-match-p "|[ \t]*$" (org-current-line-string))) (org-table-align)) (let ((line (org-table-clean-line (buffer-substring (point-at-bol) (point-at-eol)))) @@ -1623,7 +1647,8 @@ In particular, this does handle wide and invisible characters." dline -1 dline)))) ;;;###autoload -(defun org-table-sort-lines (with-case &optional sorting-type) +(defun org-table-sort-lines + (&optional with-case sorting-type getkey-func compare-func interactive?) "Sort table lines according to the column at point. The position of point indicates the column to be used for @@ -1636,76 +1661,112 @@ should be in the last line to be included into the sorting. The command then prompts for the sorting type which can be alphabetically, numerically, or by time (as given in a time stamp -in the field). Sorting in reverse order is also possible. +in the field, or as a HH:MM value). Sorting in reverse order is +also possible. With prefix argument WITH-CASE, alphabetic sorting will be case-sensitive. If SORTING-TYPE is specified when this function is called from a Lisp program, no prompting will take place. SORTING-TYPE must be a character, -any of (?a ?A ?n ?N ?t ?T) where the capital letter indicate that sorting -should be done in reverse order." - (interactive "P") - (let* ((thisline (org-current-line)) - (thiscol (org-table-current-column)) - (otc org-table-overlay-coordinates) - beg end bcol ecol tend tbeg column lns pos) - (when (equal thiscol 0) - (if (org-called-interactively-p 'any) - (setq thiscol - (string-to-number - (read-string "Use column N for sorting: "))) - (setq thiscol 1)) - (org-table-goto-column thiscol)) - (org-table-check-inside-data-field) - (if (org-region-active-p) - (progn - (setq beg (region-beginning) end (region-end)) - (goto-char beg) - (setq column (org-table-current-column) - beg (point-at-bol)) - (goto-char end) - (setq end (point-at-bol 2))) - (setq column (org-table-current-column) - pos (point) - tbeg (org-table-begin) - tend (org-table-end)) - (if (re-search-backward org-table-hline-regexp tbeg t) - (setq beg (point-at-bol 2)) - (goto-char tbeg) - (setq beg (point-at-bol 1))) - (goto-char pos) - (if (re-search-forward org-table-hline-regexp tend t) - (setq end (point-at-bol 1)) - (goto-char tend) - (setq end (point-at-bol)))) - (setq beg (move-marker (make-marker) beg) - end (move-marker (make-marker) end)) - (untabify beg end) - (goto-char beg) - (org-table-goto-column column) - (skip-chars-backward "^|") - (setq bcol (current-column)) - (org-table-goto-column (1+ column)) - (skip-chars-backward "^|") - (setq ecol (1- (current-column))) - (org-table-goto-column column) - (setq lns (mapcar (lambda(x) (cons - (org-sort-remove-invisible - (nth (1- column) - (org-split-string x "[ \t]*|[ \t]*"))) - x)) - (org-split-string (buffer-substring beg end) "\n"))) - (setq lns (org-do-sort lns "Table" with-case sorting-type)) - (when org-table-overlay-coordinates - (org-table-toggle-coordinate-overlays)) - (delete-region beg end) - (move-marker beg nil) - (move-marker end nil) - (insert (mapconcat 'cdr lns "\n") "\n") - (org-goto-line thisline) - (org-table-goto-column thiscol) - (when otc (org-table-toggle-coordinate-overlays)) - (message "%d lines sorted, based on column %d" (length lns) column))) +any of (?a ?A ?n ?N ?t ?T ?f ?F) where the capital letters indicate that +sorting should be done in reverse order. + +If the SORTING-TYPE is ?f or ?F, then GETKEY-FUNC specifies +a function to be called to extract the key. It must return a value +that is compatible with COMPARE-FUNC, the function used to compare +entries. + +A non-nil value for INTERACTIVE? is used to signal that this +function is being called interactively." + (interactive (list current-prefix-arg nil nil nil t)) + (when (org-region-active-p) (goto-char (region-beginning))) + ;; Point must be either within a field or before a data line. + (save-excursion + (skip-chars-backward " \t") + (when (bolp) (search-forward "|" (line-end-position) t)) + (org-table-check-inside-data-field)) + ;; Set appropriate case sensitivity and column used for sorting. + (let ((column (let ((c (org-table-current-column))) + (cond ((> c 0) c) + (interactive? + (read-number "Use column N for sorting: ")) + (t 1)))) + (sorting-type + (or sorting-type + (read-char-exclusive "Sort Table: [a]lphabetic, [n]umeric, \ +\[t]ime, [f]unc. A/N/T/F means reversed: ")))) + (save-restriction + ;; Narrow buffer to appropriate sorting area. + (if (org-region-active-p) + (progn (goto-char (region-beginning)) + (narrow-to-region + (point) + (save-excursion (goto-char (region-end)) + (line-beginning-position 2)))) + (let ((start (org-table-begin)) + (end (org-table-end))) + (narrow-to-region + (save-excursion + (if (re-search-backward org-table-hline-regexp start t) + (line-beginning-position 2) + start)) + (if (save-excursion (re-search-forward org-table-hline-regexp end t)) + (match-beginning 0) + end)))) + ;; Determine arguments for `sort-subr'. Also record original + ;; position. `org-table-save-field' cannot help here since + ;; sorting is too much destructive. + (let* ((sort-fold-case (not with-case)) + (coordinates + (cons (count-lines (point-min) (line-beginning-position)) + (current-column))) + (extract-key-from-field + ;; Function to be called on the contents of the field + ;; used for sorting in the current row. + (cl-case sorting-type + ((?n ?N) #'string-to-number) + ((?a ?A) #'org-sort-remove-invisible) + ((?t ?T) + (lambda (f) + (cond ((string-match org-ts-regexp-both f) + (float-time + (org-time-string-to-time (match-string 0 f)))) + ((string-match "[0-9]\\{1,2\\}:[0-9]\\{2\\}" f) + (org-hh:mm-string-to-minutes f)) + (t 0)))) + ((?f ?F) + (or getkey-func + (and interactive? + (org-read-function "Function for extracting keys: ")) + (error "Missing key extractor to sort rows"))) + (t (user-error "Invalid sorting type `%c'" sorting-type)))) + (predicate + (cl-case sorting-type + ((?n ?N ?t ?T) #'<) + ((?a ?A) #'string<) + ((?f ?F) + (or compare-func + (and interactive? + (org-read-function + (concat "Fuction for comparing keys " + "(empty for default `sort-subr' predicate): ") + 'allow-empty))))))) + (goto-char (point-min)) + (sort-subr (memq sorting-type '(?A ?N ?T ?F)) + (lambda () + (forward-line) + (while (and (not (eobp)) + (not (looking-at org-table-dataline-regexp))) + (forward-line))) + #'end-of-line + (lambda () + (funcall extract-key-from-field + (org-trim (org-table-get-field column)))) + nil + predicate) + ;; Move back to initial field. + (forward-line (car coordinates)) + (move-to-column (cdr coordinates)))))) ;;;###autoload (defun org-table-cut-region (beg end) @@ -1725,34 +1786,31 @@ with `org-table-paste-rectangle'." (if (org-region-active-p) (region-beginning) (point)) (if (org-region-active-p) (region-end) (point)) current-prefix-arg)) - (let* (l01 c01 l02 c02 l1 c1 l2 c2 ic1 ic2 - region cols - (rpl (if cut " " nil))) - (goto-char beg) - (org-table-check-inside-data-field) - (setq l01 (org-current-line) - c01 (org-table-current-column)) - (goto-char end) + (goto-char (min beg end)) + (org-table-check-inside-data-field) + (let ((beg (line-beginning-position)) + (c01 (org-table-current-column)) + region) + (goto-char (max beg end)) (org-table-check-inside-data-field) - (setq l02 (org-current-line) - c02 (org-table-current-column)) - (setq l1 (min l01 l02) l2 (max l01 l02) - c1 (min c01 c02) c2 (max c01 c02)) - (catch 'exit - (while t - (catch 'nextline - (if (> l1 l2) (throw 'exit t)) - (org-goto-line l1) - (if (org-at-table-hline-p) (throw 'nextline (setq l1 (1+ l1)))) - (setq cols nil ic1 c1 ic2 c2) - (while (< ic1 (1+ ic2)) - (push (org-table-get-field ic1 rpl) cols) - (setq ic1 (1+ ic1))) - (push (nreverse cols) region) - (setq l1 (1+ l1))))) - (setq org-table-clip (nreverse region)) - (if cut (org-table-align)) - org-table-clip)) + (let* ((end (copy-marker (line-end-position))) + (c02 (org-table-current-column)) + (column-start (min c01 c02)) + (column-end (max c01 c02)) + (column-number (1+ (- column-end column-start))) + (rpl (and cut " "))) + (goto-char beg) + (while (< (point) end) + (unless (org-at-table-hline-p) + ;; Collect every cell between COLUMN-START and COLUMN-END. + (let (cols) + (dotimes (c column-number) + (push (org-table-get-field (+ c column-start) rpl) cols)) + (push (nreverse cols) region))) + (forward-line)) + (set-marker end nil)) + (when cut (org-table-align)) + (setq org-table-clip (nreverse region)))) ;;;###autoload (defun org-table-paste-rectangle () @@ -1762,45 +1820,43 @@ will be overwritten. If the rectangle does not fit into the present table, the table is enlarged as needed. The process ignores horizontal separator lines." (interactive) - (unless (and org-table-clip (listp org-table-clip)) + (unless (consp org-table-clip) (user-error "First cut/copy a region to paste!")) (org-table-check-inside-data-field) - (let* ((clip org-table-clip) - (line (org-current-line)) - (col (org-table-current-column)) + (let* ((column (org-table-current-column)) (org-enable-table-editor t) - (org-table-automatic-realign nil) - c cols field) - (while (setq cols (pop clip)) - (while (org-at-table-hline-p) (beginning-of-line 2)) - (if (not (org-at-table-p)) - (progn (end-of-line 0) (org-table-next-field))) - (setq c col) - (while (setq field (pop cols)) - (org-table-goto-column c nil 'force) - (org-table-get-field nil field) - (setq c (1+ c))) - (beginning-of-line 2)) - (org-goto-line line) - (org-table-goto-column col) + (org-table-automatic-realign nil)) + (org-table-save-field + (dolist (row org-table-clip) + (while (org-at-table-hline-p) (forward-line)) + ;; If we left the table, create a new row. + (when (and (bolp) (not (looking-at "[ \t]*|"))) + (end-of-line 0) + (org-table-next-field)) + (let ((c column)) + (dolist (field row) + (org-table-goto-column c nil 'force) + (org-table-get-field nil field) + (cl-incf c))) + (forward-line))) (org-table-align))) ;;;###autoload (defun org-table-convert () "Convert from `org-mode' table to table.el and back. -Obviously, this only works within limits. When an Org-mode table is -converted to table.el, all horizontal separator lines get lost, because -table.el uses these as cell boundaries and has no notion of horizontal lines. -A table.el table can be converted to an Org-mode table only if it does not -do row or column spanning. Multiline cells will become multiple cells. -Beware, Org-mode does not test if the table can be successfully converted - it -blindly applies a recipe that works for simple tables." +Obviously, this only works within limits. When an Org table is converted +to table.el, all horizontal separator lines get lost, because table.el uses +these as cell boundaries and has no notion of horizontal lines. A table.el +table can be converted to an Org table only if it does not do row or column +spanning. Multiline cells will become multiple cells. Beware, Org mode +does not test if the table can be successfully converted - it blindly +applies a recipe that works for simple tables." (interactive) (require 'table) (if (org-at-table.el-p) - ;; convert to Org-mode table - (let ((beg (move-marker (make-marker) (org-table-begin t))) - (end (move-marker (make-marker) (org-table-end t)))) + ;; convert to Org table + (let ((beg (copy-marker (org-table-begin t))) + (end (copy-marker (org-table-end t)))) (table-unrecognize-region beg end) (goto-char beg) (while (re-search-forward "^\\([ \t]*\\)\\+-.*\n" end t) @@ -1808,8 +1864,8 @@ blindly applies a recipe that works for simple tables." (goto-char beg)) (if (org-at-table-p) ;; convert to table.el table - (let ((beg (move-marker (make-marker) (org-table-begin))) - (end (move-marker (make-marker) (org-table-end)))) + (let ((beg (copy-marker (org-table-begin))) + (end (copy-marker (org-table-end)))) ;; first, get rid of all horizontal lines (goto-char beg) (while (re-search-forward "^\\([ \t]*\\)|-.*\n" end t) @@ -1832,7 +1888,7 @@ blindly applies a recipe that works for simple tables." (goto-char beg))))) (defun org-table-transpose-table-at-point () - "Transpose orgmode table at point and eliminate hlines. + "Transpose Org table at point and eliminate hlines. So a table like | 1 | 2 | 4 | 5 | @@ -1847,22 +1903,31 @@ will be transposed as | 4 | c | g | | 5 | d | h | -Note that horizontal lines disappeared." +Note that horizontal lines disappear." (interactive) (let* ((table (delete 'hline (org-table-to-lisp))) - (contents (mapcar (lambda (p) + (dline_old (org-table-current-line)) + (col_old (org-table-current-column)) + (contents (mapcar (lambda (_) (let ((tp table)) (mapcar - (lambda (rown) + (lambda (_) (prog1 (pop (car tp)) (setq tp (cdr tp)))) table))) (car table)))) - (delete-region (org-table-begin) (org-table-end)) - (insert (mapconcat (lambda(x) (concat "| " (mapconcat 'identity x " | " ) " |\n" )) - contents "")) - (org-table-align))) + (goto-char (org-table-begin)) + (re-search-forward "|") + (backward-char) + (delete-region (point) (org-table-end)) + (insert (mapconcat + (lambda(x) + (concat "| " (mapconcat 'identity x " | " ) " |\n" )) + contents "")) + (org-table-goto-line col_old) + (org-table-goto-column dline_old)) + (org-table-align)) ;;;###autoload (defun org-table-wrap-region (arg) @@ -1873,7 +1938,8 @@ lines, in order to keep the table compact. If there is an active region, and both point and mark are in the same column, the text in the column is wrapped to minimum width for the given number of lines. Generally, this makes the table more compact. A prefix ARG may be -used to change the number of desired lines. For example, `C-2 \\[org-table-wrap]' +used to change the number of desired lines. For example, \ +`C-2 \\[org-table-wrap-region]' formats the selected text to two lines. If the region was longer than two lines, the remaining lines remain empty. A negative prefix argument reduces the current number of lines by that amount. The wrapped text is pasted back @@ -1890,57 +1956,53 @@ blank, and the content is appended to the field above." (interactive "P") (org-table-check-inside-data-field) (if (org-region-active-p) - ;; There is a region: fill as a paragraph - (let* ((beg (region-beginning)) - (cline (save-excursion (goto-char beg) (org-current-line))) - (ccol (save-excursion (goto-char beg) (org-table-current-column))) - nlines) + ;; There is a region: fill as a paragraph. + (let ((start (region-beginning))) (org-table-cut-region (region-beginning) (region-end)) - (if (> (length (car org-table-clip)) 1) - (user-error "Region must be limited to single column")) - (setq nlines (if arg - (if (< arg 1) - (+ (length org-table-clip) arg) - arg) - (length org-table-clip))) - (setq org-table-clip - (mapcar 'list (org-wrap (mapconcat 'car org-table-clip " ") - nil nlines))) - (org-goto-line cline) - (org-table-goto-column ccol) + (when (> (length (car org-table-clip)) 1) + (user-error "Region must be limited to single column")) + (let ((nlines (cond ((not arg) (length org-table-clip)) + ((< arg 1) (+ (length org-table-clip) arg)) + (t arg)))) + (setq org-table-clip + (mapcar #'list + (org-wrap (mapconcat #'car org-table-clip " ") + nil + nlines)))) + (goto-char start) (org-table-paste-rectangle)) - ;; No region, split the current field at point + ;; No region, split the current field at point. (unless (org-get-alist-option org-M-RET-may-split-line 'table) (skip-chars-forward "^\r\n|")) - (if arg - ;; combine with field above - (let ((s (org-table-blank-field)) - (col (org-table-current-column))) - (beginning-of-line 0) - (while (org-at-table-hline-p) (beginning-of-line 0)) - (org-table-goto-column col) - (skip-chars-forward "^|") - (skip-chars-backward " ") - (insert " " (org-trim s)) - (org-table-align)) - ;; split field - (if (looking-at "\\([^|]+\\)+|") - (let ((s (match-string 1))) - (replace-match " |") - (goto-char (match-beginning 0)) - (org-table-next-row) - (insert (org-trim s) " ") - (org-table-align)) - (org-table-next-row))))) + (cond + (arg ; Combine with field above. + (let ((s (org-table-blank-field)) + (col (org-table-current-column))) + (forward-line -1) + (while (org-at-table-hline-p) (forward-line -1)) + (org-table-goto-column col) + (skip-chars-forward "^|") + (skip-chars-backward " ") + (insert " " (org-trim s)) + (org-table-align))) + ((looking-at "\\([^|]+\\)+|") ; Split field. + (let ((s (match-string 1))) + (replace-match " |") + (goto-char (match-beginning 0)) + (org-table-next-row) + (insert (org-trim s) " ") + (org-table-align))) + (t (org-table-next-row))))) (defvar org-field-marker nil) ;;;###autoload (defun org-table-edit-field (arg) "Edit table field in a different window. -This is mainly useful for fields that contain hidden parts. -When called with a \\[universal-argument] prefix, just make the full field visible so that -it can be edited in place." +This is mainly useful for fields that contain hidden parts. When called +with a `\\[universal-argument]' prefix, just make the full field \ +visible so that it can be +edited in place." (interactive "P") (cond ((equal arg '(16)) @@ -1980,9 +2042,9 @@ it can be edited in place." '(invisible t org-cwidth t display t intangible t)) (goto-char p) - (org-set-local 'org-finish-function 'org-table-finish-edit-field) - (org-set-local 'org-window-configuration cw) - (org-set-local 'org-field-marker pos) + (setq-local org-finish-function 'org-table-finish-edit-field) + (setq-local org-window-configuration cw) + (setq-local org-field-marker pos) (message "Edit and finish with C-c C-c"))))) (defun org-table-finish-edit-field () @@ -2015,8 +2077,8 @@ current field. The mode exits automatically when the cursor leaves the table (but see `org-table-exit-follow-field-mode-when-leaving-table')." nil " TblFollow" nil (if org-table-follow-field-mode - (org-add-hook 'post-command-hook 'org-table-follow-fields-with-editor - 'append 'local) + (add-hook 'post-command-hook 'org-table-follow-fields-with-editor + 'append 'local) (remove-hook 'post-command-hook 'org-table-follow-fields-with-editor 'local) (let* ((buf (get-buffer "*Org Table Edit Field*")) (win (and buf (get-buffer-window buf)))) @@ -2091,11 +2153,10 @@ If NLAST is a number, only the NLAST fields will actually be summed." s diff) (format "%.0f:%02.0f:%02.0f" h m s)))) (kill-new sres) - (if (org-called-interactively-p 'interactive) - (message "%s" - (substitute-command-keys - (format "Sum of %d items: %-20s (\\[yank] will insert result into buffer)" - (length numbers) sres)))) + (when (called-interactively-p 'interactive) + (message "%s" (substitute-command-keys + (format "Sum of %d items: %-20s \ +\(\\[yank] will insert result into buffer)" (length numbers) sres)))) sres)))) (defun org-table-get-number-for-summing (s) @@ -2120,57 +2181,58 @@ If NLAST is a number, only the NLAST fields will actually be summed." (defun org-table-current-field-formula (&optional key noerror) "Return the formula active for the current field. -Assumes that specials are in place. -If KEY is given, return the key to this formula. -Otherwise return the formula preceded with \"=\" or \":=\"." - (let* ((name (car (rassoc (list (org-current-line) - (org-table-current-column)) - org-table-named-field-locations))) - (col (org-table-current-column)) - (scol (int-to-string col)) - (ref (format "@%d$%d" (org-table-current-dline) col)) - (stored-list (org-table-get-stored-formulas noerror)) - (ass (or (assoc name stored-list) - (assoc ref stored-list) - (assoc scol stored-list)))) - (if key - (car ass) - (if ass (concat (if (string-match "^[0-9]+$" (car ass)) "=" ":=") - (cdr ass)))))) + +Assumes that table is already analyzed. If KEY is given, return +the key to this formula. Otherwise return the formula preceded +with \"=\" or \":=\"." + (let* ((line (count-lines org-table-current-begin-pos + (line-beginning-position))) + (row (org-table-line-to-dline line))) + (cond + (row + (let* ((col (org-table-current-column)) + (name (car (rassoc (list line col) + org-table-named-field-locations))) + (scol (format "$%d" col)) + (ref (format "@%d$%d" (org-table-current-dline) col)) + (stored-list (org-table-get-stored-formulas noerror)) + (ass (or (assoc name stored-list) + (assoc ref stored-list) + (assoc scol stored-list)))) + (cond (key (car ass)) + (ass (concat (if (string-match-p "^[0-9]+$" (car ass)) "=" ":=") + (cdr ass)))))) + (noerror nil) + (t (error "No formula active for the current field"))))) (defun org-table-get-formula (&optional equation named) "Read a formula from the minibuffer, offer stored formula as default. When NAMED is non-nil, look for a named equation." (let* ((stored-list (org-table-get-stored-formulas)) - (name (car (rassoc (list (org-current-line) + (name (car (rassoc (list (count-lines org-table-current-begin-pos + (line-beginning-position)) (org-table-current-column)) org-table-named-field-locations))) - (ref (format "@%d$%d" (org-table-current-dline) + (ref (format "@%d$%d" + (org-table-current-dline) (org-table-current-column))) - (refass (assoc ref stored-list)) - (nameass (assoc name stored-list)) - (scol (if named - (if (and name (not (string-match "^LR[0-9]+$" name))) - name - ref) - (int-to-string (org-table-current-column)))) - (dummy (and (or nameass refass) (not named) - (not (y-or-n-p "Replace existing field formula with column formula? " )) - (message "Formula not replaced"))) + (scol (cond + ((not named) (format "$%d" (org-table-current-column))) + ((and name (not (string-match "\\`LR[0-9]+\\'" name))) name) + (t ref))) (name (or name ref)) (org-table-may-need-update nil) (stored (cdr (assoc scol stored-list))) (eq (cond - ((and stored equation (string-match "^ *=? *$" equation)) + ((and stored equation (string-match-p "^ *=? *$" equation)) stored) ((stringp equation) equation) (t (org-table-formula-from-user (read-string (org-table-formula-to-user - (format "%s formula %s%s=" + (format "%s formula %s=" (if named "Field" "Column") - (if (member (string-to-char scol) '(?$ ?@)) "" "$") scol)) (if stored (org-table-formula-to-user stored) "") 'org-table-formula-history @@ -2194,25 +2256,27 @@ When NAMED is non-nil, look for a named equation." (org-table-store-formulas stored-list)) eq)) -(defun org-table-store-formulas (alist) - "Store the list of formulas below the current table." - (setq alist (sort alist 'org-table-formula-less-p)) - (let ((case-fold-search t)) - (save-excursion - (goto-char (org-table-end)) - (if (looking-at "\\([ \t]*\n\\)*[ \t]*\\(#\\+tblfm:\\)\\(.*\n?\\)") +(defun org-table-store-formulas (alist &optional location) + "Store the list of formulas below the current table. +If optional argument LOCATION is a buffer position, insert it at +LOCATION instead." + (save-excursion + (if location + (progn (goto-char location) (beginning-of-line)) + (goto-char (org-table-end))) + (let ((case-fold-search t)) + (if (looking-at "\\([ \t]*\n\\)*[ \t]*\\(#\\+TBLFM:\\)\\(.*\n?\\)") (progn - ;; don't overwrite TBLFM, we might use text properties to store stuff + ;; Don't overwrite TBLFM, we might use text properties to + ;; store stuff. (goto-char (match-beginning 3)) (delete-region (match-beginning 3) (match-end 0))) (org-indent-line) (insert (or (match-string 2) "#+TBLFM:"))) (insert " " - (mapconcat (lambda (x) - (concat - (if (equal (string-to-char (car x)) ?@) "" "$") - (car x) "=" (cdr x))) - alist "::") + (mapconcat (lambda (x) (concat (car x) "=" (cdr x))) + (sort alist #'org-table-formula-less-p) + "::") "\n")))) (defsubst org-table-formula-make-cmp-string (a) @@ -2241,33 +2305,47 @@ When NAMED is non-nil, look for a named equation." (and as bs (string< as bs)))) ;;;###autoload -(defun org-table-get-stored-formulas (&optional noerror) - "Return an alist with the stored formulas directly after current table." - (interactive) ;; FIXME interactive? - (let ((case-fold-search t) scol eq eq-alist strings string seen) - (save-excursion - (goto-char (org-table-end)) - (when (looking-at "\\([ \t]*\n\\)*[ \t]*#\\+tblfm: *\\(.*\\)") - (setq strings (org-split-string (org-match-string-no-properties 2) - " *:: *")) - (while (setq string (pop strings)) - (when (string-match "\\`\\(@[-+I<>0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\|[<>]+\\)\\) *= *\\(.*[^ \t]\\)" string) - (setq scol (if (match-end 2) - (match-string 2 string) - (match-string 1 string)) - scol (if (member (string-to-char scol) '(?< ?>)) - (concat "$" scol) scol) - eq (match-string 3 string) - eq-alist (cons (cons scol eq) eq-alist)) - (if (member scol seen) - (if noerror - (progn - (message "Double definition `$%s=' in TBLFM line, please fix by hand" scol) - (ding) - (sit-for 2)) - (user-error "Double definition `$%s=' in TBLFM line, please fix by hand" scol)) - (push scol seen)))))) - (nreverse eq-alist))) +(defun org-table-get-stored-formulas (&optional noerror location) + "Return an alist with the stored formulas directly after current table. +By default, only return active formulas, i.e., formulas located +on the first line after the table. However, if optional argument +LOCATION is a buffer position, consider the formulas there." + (save-excursion + (if location + (progn (goto-char location) (beginning-of-line)) + (goto-char (org-table-end))) + (let ((case-fold-search t)) + (when (looking-at "\\([ \t]*\n\\)*[ \t]*#\\+TBLFM: *\\(.*\\)") + (let ((strings (org-split-string (match-string-no-properties 2) + " *:: *")) + eq-alist seen) + (dolist (string strings (nreverse eq-alist)) + (when (string-match "\\`\\(@[-+I<>0-9.$@]+\\|\\$\\([_a-zA-Z0-9]+\\|\ +[<>]+\\)\\) *= *\\(.*[^ \t]\\)" + string) + (let ((lhs + (let ((m (match-string 1 string))) + (cond + ((not (match-end 2)) m) + ;; Is it a column reference? + ((string-match-p "\\`$\\([0-9]+\\|[<>]+\\)\\'" m) m) + ;; Since named columns are not possible in + ;; LHS, assume this is a named field. + (t (match-string 2 string))))) + (rhs (match-string 3 string))) + (push (cons lhs rhs) eq-alist) + (cond + ((not (member lhs seen)) (push lhs seen)) + (noerror + (message + "Double definition `%s=' in TBLFM line, please fix by hand" + lhs) + (ding) + (sit-for 2)) + (t + (user-error + "Double definition `%s=' in TBLFM line, please fix by hand" + lhs))))))))))) (defun org-table-fix-formulas (key replace &optional limit delta remove) "Modify the equations after the table structure has been edited. @@ -2305,83 +2383,6 @@ For all numbers larger than LIMIT, shift them by DELTA." (message msg)))))) (forward-line)))) -(defun org-table-get-specials () - "Get the column names and local parameters for this table." - (save-excursion - (let ((beg (org-table-begin)) (end (org-table-end)) - names name fields fields1 field cnt - c v l line col types dlines hlines last-dline) - (setq org-table-column-names nil - org-table-local-parameters nil - org-table-named-field-locations nil - org-table-current-begin-line nil - org-table-current-begin-pos nil - org-table-current-line-types nil - org-table-current-ncol 0) - (goto-char beg) - (when (re-search-forward "^[ \t]*| *! *\\(|.*\\)" end t) - (setq names (org-split-string (match-string 1) " *| *") - cnt 1) - (while (setq name (pop names)) - (setq cnt (1+ cnt)) - (if (string-match "^[a-zA-Z][_a-zA-Z0-9]*$" name) - (push (cons name (int-to-string cnt)) org-table-column-names)))) - (setq org-table-column-names (nreverse org-table-column-names)) - (setq org-table-column-name-regexp - (concat "\\$\\(" (mapconcat 'car org-table-column-names "\\|") "\\)\\>")) - (goto-char beg) - (while (re-search-forward "^[ \t]*| *\\$ *\\(|.*\\)" end t) - (setq fields (org-split-string (match-string 1) " *| *")) - (while (setq field (pop fields)) - (if (string-match "^\\([a-zA-Z][_a-zA-Z0-9]*\\|%\\) *= *\\(.*\\)" field) - (push (cons (match-string 1 field) (match-string 2 field)) - org-table-local-parameters)))) - (goto-char beg) - (while (re-search-forward "^[ \t]*| *\\([_^]\\) *\\(|.*\\)" end t) - (setq c (match-string 1) - fields (org-split-string (match-string 2) " *| *")) - (save-excursion - (beginning-of-line (if (equal c "_") 2 0)) - (setq line (org-current-line) col 1) - (and (looking-at "^[ \t]*|[^|]*\\(|.*\\)") - (setq fields1 (org-split-string (match-string 1) " *| *")))) - (while (and fields1 (setq field (pop fields))) - (setq v (pop fields1) col (1+ col)) - (when (and (stringp field) (stringp v) - (string-match "^[a-zA-Z][_a-zA-Z0-9]*$" field)) - (push (cons field v) org-table-local-parameters) - (push (list field line col) org-table-named-field-locations)))) - ;; Analyze the line types. - (goto-char beg) - (setq org-table-current-begin-line (org-current-line) - org-table-current-begin-pos (point) - l org-table-current-begin-line) - (while (looking-at "[ \t]*|\\(-\\)?") - (push (if (match-end 1) 'hline 'dline) types) - (if (match-end 1) (push l hlines) (push l dlines)) - (beginning-of-line 2) - (setq l (1+ l))) - (push 'hline types) ;; add an imaginary extra hline to the end - (setq org-table-current-line-types (apply 'vector (nreverse types)) - last-dline (car dlines) - org-table-dlines (apply 'vector (cons nil (nreverse dlines))) - org-table-hlines (apply 'vector (cons nil (nreverse hlines)))) - (org-goto-line last-dline) - (let* ((l last-dline) - (fields (org-split-string - (buffer-substring (point-at-bol) (point-at-eol)) - "[ \t]*|[ \t]*")) - (nfields (length fields)) - al al2) - (setq org-table-current-ncol nfields) - (loop for i from 1 to nfields do - (push (list (format "LR%d" i) l i) al) - (push (cons (format "LR%d" i) (nth (1- i) fields)) al2)) - (setq org-table-named-field-locations - (append org-table-named-field-locations al)) - (setq org-table-local-parameters - (append org-table-local-parameters al2)))))) - ;;;###autoload (defun org-table-maybe-eval-formula () "Check if the current field starts with \"=\" or \":=\". @@ -2394,11 +2395,8 @@ If yes, store the formula and apply it." (when (string-match "^:?=\\(.*[^=]\\)$" field) (setq named (equal (string-to-char field) ?:) eq (match-string 1 field)) - (if (or (fboundp 'calc-eval) - (equal (substring eq 0 (min 2 (length eq))) "'(")) - (org-table-eval-formula (if named '(4) nil) - (org-table-formula-from-user eq)) - (user-error "Calc does not seem to be installed, and is needed to evaluate the formula")))))) + (org-table-eval-formula (and named '(4)) + (org-table-formula-from-user eq)))))) (defvar org-recalc-commands nil "List of commands triggering the recalculation of a line. @@ -2424,56 +2422,199 @@ After each change, a message will be displayed indicating the meaning of the new mark." (interactive) (unless (org-at-table-p) (user-error "Not at a table")) - (let* ((marks (append (mapcar 'car org-recalc-marks) '(" "))) - (beg (org-table-begin)) - (end (org-table-end)) - (l (org-current-line)) - (l1 (if (org-region-active-p) (org-current-line (region-beginning)))) - (l2 (if (org-region-active-p) (org-current-line (region-end)))) - (have-col - (save-excursion - (goto-char beg) - (not (re-search-forward "^[ \t]*|[^-|][^|]*[^#!$*_^| \t][^|]*|" end t)))) + (let* ((region (org-region-active-p)) + (l1 (and region + (save-excursion (goto-char (region-beginning)) + (copy-marker (line-beginning-position))))) + (l2 (and region + (save-excursion (goto-char (region-end)) + (copy-marker (line-beginning-position))))) + (l (copy-marker (line-beginning-position))) (col (org-table-current-column)) - (forcenew (car (assoc newchar org-recalc-marks))) - epos new) - (when l1 - (message "Change region to what mark? Type # * ! $ or SPC: ") - (setq newchar (char-to-string (read-char-exclusive)) - forcenew (car (assoc newchar org-recalc-marks)))) - (if (and newchar (not forcenew)) - (user-error "Invalid NEWCHAR `%s' in `org-table-rotate-recalc-marks'" - newchar)) - (if l1 (org-goto-line l1)) + (newchar (if region + (char-to-string + (read-char-exclusive + "Change region to what mark? Type # * ! $ or SPC: ")) + newchar)) + (no-special-column + (save-excursion + (goto-char (org-table-begin)) + (re-search-forward + "^[ \t]*|[^-|][^|]*[^#!$*_^| \t][^|]*|" (org-table-end) t)))) + (when (and newchar (not (assoc newchar org-recalc-marks))) + (user-error "Invalid character `%s' in `org-table-rotate-recalc-marks'" + newchar)) + (when l1 (goto-char l1)) (save-excursion - (beginning-of-line 1) + (beginning-of-line) (unless (looking-at org-table-dataline-regexp) (user-error "Not at a table data line"))) - (unless have-col + (when no-special-column (org-table-goto-column 1) - (org-table-insert-column) - (org-table-goto-column (1+ col))) - (setq epos (point-at-eol)) + (org-table-insert-column)) + (let ((previous-line-end (line-end-position)) + (newchar + (save-excursion + (beginning-of-line) + (cond ((not (looking-at "^[ \t]*| *\\([#!$*^_ ]\\) *|")) "#") + (newchar) + (t (cadr (member (match-string 1) + (append (mapcar #'car org-recalc-marks) + '(" "))))))))) + ;; Rotate mark in first row. + (org-table-get-field 1 (format " %s " newchar)) + ;; Rotate marks in additional rows if a region is active. + (when region + (save-excursion + (forward-line) + (while (<= (point) l2) + (when (looking-at org-table-dataline-regexp) + (org-table-get-field 1 (format " %s " newchar))) + (forward-line)))) + ;; Only align if rotation actually changed lines' length. + (when (/= previous-line-end (line-end-position)) (org-table-align))) + (goto-char l) + (org-table-goto-column (if no-special-column (1+ col) col)) + (when l1 (set-marker l1 nil)) + (when l2 (set-marker l2 nil)) + (set-marker l nil) + (when (called-interactively-p 'interactive) + (message "%s" (cdr (assoc newchar org-recalc-marks)))))) + +;;;###autoload +(defun org-table-analyze () + "Analyze table at point and store results. + +This function sets up the following dynamically scoped variables: + + `org-table-column-name-regexp', + `org-table-column-names', + `org-table-current-begin-pos', + `org-table-current-line-types', + `org-table-current-ncol', + `org-table-dlines', + `org-table-hlines', + `org-table-local-parameters', + `org-table-named-field-locations'." + (let ((beg (org-table-begin)) + (end (org-table-end))) (save-excursion - (beginning-of-line 1) - (org-table-get-field - 1 (if (looking-at "^[ \t]*| *\\([#!$*^_ ]\\) *|") - (concat " " - (setq new (or forcenew - (cadr (member (match-string 1) marks)))) - " ") - " # "))) - (if (and l1 l2) - (progn - (org-goto-line l1) - (while (progn (beginning-of-line 2) (not (= (org-current-line) l2))) - (and (looking-at org-table-dataline-regexp) - (org-table-get-field 1 (concat " " new " ")))) - (org-goto-line l1))) - (if (not (= epos (point-at-eol))) (org-table-align)) - (org-goto-line l) - (and (org-called-interactively-p 'interactive) - (message "%s" (cdr (assoc new org-recalc-marks)))))) + (goto-char beg) + ;; Extract column names. + (setq org-table-column-names nil) + (when (save-excursion + (re-search-forward "^[ \t]*| *! *\\(|.*\\)" end t)) + (let ((c 1)) + (dolist (name (org-split-string (match-string 1) " *| *")) + (cl-incf c) + (when (string-match "\\`[a-zA-Z][_a-zA-Z0-9]*\\'" name) + (push (cons name (int-to-string c)) org-table-column-names))))) + (setq org-table-column-names (nreverse org-table-column-names)) + (setq org-table-column-name-regexp + (format "\\$\\(%s\\)\\>" + (regexp-opt (mapcar #'car org-table-column-names) t))) + ;; Extract local parameters. + (setq org-table-local-parameters nil) + (save-excursion + (while (re-search-forward "^[ \t]*| *\\$ *\\(|.*\\)" end t) + (dolist (field (org-split-string (match-string 1) " *| *")) + (when (string-match + "\\`\\([a-zA-Z][_a-zA-Z0-9]*\\|%\\) *= *\\(.*\\)" field) + (push (cons (match-string 1 field) (match-string 2 field)) + org-table-local-parameters))))) + ;; Update named fields locations. We minimize `count-lines' + ;; processing by storing last known number of lines in LAST. + (setq org-table-named-field-locations nil) + (save-excursion + (let ((last (cons (point) 0))) + (while (re-search-forward "^[ \t]*| *\\([_^]\\) *\\(|.*\\)" end t) + (let ((c (match-string 1)) + (fields (org-split-string (match-string 2) " *| *"))) + (save-excursion + (forward-line (if (equal c "_") 1 -1)) + (let ((fields1 + (and (looking-at "^[ \t]*|[^|]*\\(|.*\\)") + (org-split-string (match-string 1) " *| *"))) + (line (cl-incf (cdr last) (count-lines (car last) (point)))) + (col 1)) + (setcar last (point)) ; Update last known position. + (while (and fields fields1) + (let ((field (pop fields)) + (v (pop fields1))) + (cl-incf col) + (when (and (stringp field) + (stringp v) + (string-match "\\`[a-zA-Z][_a-zA-Z0-9]*\\'" + field)) + (push (cons field v) org-table-local-parameters) + (push (list field line col) + org-table-named-field-locations)))))))))) + ;; Re-use existing markers when possible. + (if (markerp org-table-current-begin-pos) + (move-marker org-table-current-begin-pos (point)) + (setq org-table-current-begin-pos (point-marker))) + ;; Analyze the line types. + (let ((l 0) hlines dlines types) + (while (looking-at "[ \t]*|\\(-\\)?") + (push (if (match-end 1) 'hline 'dline) types) + (if (match-end 1) (push l hlines) (push l dlines)) + (forward-line) + (cl-incf l)) + (push 'hline types) ; Add an imaginary extra hline to the end. + (setq org-table-current-line-types (apply #'vector (nreverse types))) + (setq org-table-dlines (apply #'vector (cons nil (nreverse dlines)))) + (setq org-table-hlines (apply #'vector (cons nil (nreverse hlines))))) + ;; Get the number of columns from the first data line in table. + (goto-char beg) + (forward-line (aref org-table-dlines 1)) + (let* ((fields + (org-split-string + (buffer-substring (line-beginning-position) (line-end-position)) + "[ \t]*|[ \t]*")) + (nfields (length fields)) + al al2) + (setq org-table-current-ncol nfields) + (let ((last-dline + (aref org-table-dlines (1- (length org-table-dlines))))) + (dotimes (i nfields) + (let ((column (1+ i))) + (push (list (format "LR%d" column) last-dline column) al) + (push (cons (format "LR%d" column) (nth i fields)) al2)))) + (setq org-table-named-field-locations + (append org-table-named-field-locations al)) + (setq org-table-local-parameters + (append org-table-local-parameters al2)))))) + +(defun org-table-goto-field (ref &optional create-column-p) + "Move point to a specific field in the current table. + +REF is either the name of a field its absolute reference, as +a string. No column is created unless CREATE-COLUMN-P is +non-nil. If it is a function, it is called with the column +number as its argument as is used as a predicate to know if the +column can be created. + +This function assumes the table is already analyzed (i.e., using +`org-table-analyze')." + (let* ((coordinates + (cond + ((cdr (assoc ref org-table-named-field-locations))) + ((string-match "\\`@\\([1-9][0-9]*\\)\\$\\([1-9][0-9]*\\)\\'" ref) + (list (condition-case nil + (aref org-table-dlines + (string-to-number (match-string 1 ref))) + (error (user-error "Invalid row number in %s" ref))) + (string-to-number (match-string 2 ref)))) + (t (user-error "Unknown field: %s" ref)))) + (line (car coordinates)) + (column (nth 1 coordinates)) + (create-new-column (if (functionp create-column-p) + (funcall create-column-p column) + create-column-p))) + (when coordinates + (goto-char org-table-current-begin-pos) + (forward-line line) + (org-table-goto-column column nil create-new-column)))) ;;;###autoload (defun org-table-maybe-recalculate-line () @@ -2481,7 +2622,7 @@ of the new mark." (interactive) (and org-table-allow-automatic-line-recalculation (not (and (memq last-command org-recalc-commands) - (equal org-last-recalc-line (org-current-line)))) + (eq org-last-recalc-line (line-beginning-position)))) (save-excursion (beginning-of-line 1) (looking-at org-table-auto-recalculate-regexp)) (org-table-recalculate) t)) @@ -2505,20 +2646,18 @@ of the new mark." suppress-store suppress-analysis) "Replace the table field value at the cursor by the result of a calculation. -This function makes use of Dave Gillespie's Calc package, in my view the -most exciting program ever written for GNU Emacs. So you need to have Calc -installed in order to use this function. - In a table, this command replaces the value in the current field with the result of a formula. It also installs the formula as the \"current\" column formula, by storing it in a special line below the table. When called -with a `C-u' prefix, the current field must be a named field, and the -formula is installed as valid in only this specific field. +with a `\\[universal-argument]' prefix the formula is installed as a \ +field formula. -When called with two `C-u' prefixes, insert the active equation -for the field back into the current field, so that it can be -edited there. This is useful in order to use \\[org-table-show-reference] -to check the referenced fields. +When called with a `\\[universal-argument] \\[universal-argument]' prefix, \ +insert the active equation for the field +back into the current field, so that it can be edited there. This is \ +useful +in order to use \\`\\[org-table-show-reference]' to \ +check the referenced fields. When called, the command first prompts for a formula, which is read in the minibuffer. Previously entered formulas are available through the @@ -2527,7 +2666,7 @@ These stored formulas are adapted correctly when moving, inserting, or deleting columns with the corresponding commands. The formula can be any algebraic expression understood by the Calc package. -For details, see the Org-mode manual. +For details, see the Org mode manual. This function can also be called from Lisp programs and offers additional arguments: EQUATION can be the formula to apply. If this @@ -2537,13 +2676,13 @@ SUPPRESS-CONST suppresses the interpretation of constants in the formula, assuming that this has been done already outside the function. SUPPRESS-STORE means the formula should not be stored, either because it is already stored, or because it is a modified equation that should -not overwrite the stored one." +not overwrite the stored one. SUPPRESS-ANALYSIS prevents any call to +`org-table-analyze'." (interactive "P") (org-table-check-inside-data-field) - (or suppress-analysis (org-table-get-specials)) + (or suppress-analysis (org-table-analyze)) (if (equal arg '(16)) (let ((eq (org-table-current-field-formula))) - (or eq (user-error "No equation active for current field")) (org-table-get-field nil eq) (org-table-align) (setq org-table-may-need-update t)) @@ -2557,7 +2696,7 @@ not overwrite the stored one." (org-table-get-formula equation (equal arg '(4))))) (n0 (org-table-current-column)) (org-tbl-calc-modes (copy-sequence org-calc-default-modes)) - (numbers nil) ; was a variable, now fixed default + (numbers nil) ; was a variable, now fixed default (keep-empty nil) n form form0 formrpl formrg bw fmt x ev orig c lispp literal duration duration-output-format) @@ -2603,12 +2742,15 @@ not overwrite the stored one." (setq fmt (replace-match "" t t fmt))) (unless (string-match "\\S-" fmt) (setq fmt nil)))) - (if (and (not suppress-const) org-table-formula-use-constants) - (setq formula (org-table-formula-substitute-names formula))) + (when (and (not suppress-const) org-table-formula-use-constants) + (setq formula (org-table-formula-substitute-names formula))) (setq orig (or (get-text-property 1 :orig-formula formula) "?")) + (setq formula (org-table-formula-handle-first/last-rc formula)) (while (> ndown 0) (setq fields (org-split-string - (buffer-substring-no-properties (point-at-bol) (point-at-eol)) + (org-trim + (buffer-substring-no-properties + (line-beginning-position) (line-end-position))) " *| *")) ;; replace fields with duration values if relevant (if duration @@ -2641,9 +2783,10 @@ not overwrite the stored one." t t form))) ;; Check for old vertical references - (setq form (org-table-rewrite-old-row-references form)) + (org-table--error-on-old-row-references form) ;; Insert remote references - (while (string-match "\\ (length (match-string 0 form)) 1)) - (setq formrg (save-match-data - (org-table-get-range (match-string 0 form) nil n0))) + (setq formrg + (save-match-data + (org-table-get-range + (match-string 0 form) org-table-current-begin-pos n0))) (setq formrpl (save-match-data (org-table-make-reference @@ -2676,15 +2821,20 @@ not overwrite the stored one." (string-match (regexp-quote form) formrpl))) (setq form (replace-match formrpl t t form)) (user-error "Spreadsheet error: invalid reference \"%s\"" form))) - ;; Insert simple ranges - (while (string-match "\\$\\([0-9]+\\)\\.\\.\\$\\([0-9]+\\)" form) + ;; Insert simple ranges, i.e. included in the current row. + (while (string-match + "\\$\\(\\([-+]\\)?[0-9]+\\)\\.\\.\\$\\(\\([-+]\\)?[0-9]+\\)" + form) (setq form (replace-match (save-match-data (org-table-make-reference - (org-sublist - fields (string-to-number (match-string 1 form)) - (string-to-number (match-string 2 form))) + (cl-subseq fields + (+ (if (match-end 2) n0 0) + (string-to-number (match-string 1 form)) + -1) + (+ (if (match-end 4) n0 0) + (string-to-number (match-string 3 form)))) keep-empty numbers lispp)) t t form))) (setq form0 form) @@ -2692,14 +2842,16 @@ not overwrite the stored one." (while (string-match "\\$\\(\\([-+]\\)?[0-9]+\\)" form) (setq n (+ (string-to-number (match-string 1 form)) (if (match-end 2) n0 0)) - x (nth (1- (if (= n 0) n0 (max n 1))) fields)) - (unless x (user-error "Invalid field specifier \"%s\"" - (match-string 0 form))) - (setq form (replace-match - (save-match-data - (org-table-make-reference - x keep-empty numbers lispp)) - t t form))) + x (nth (1- (if (= n 0) n0 (max n 1))) fields) + formrpl (save-match-data + (org-table-make-reference + x keep-empty numbers lispp))) + (when (or (not x) + (save-match-data + (string-match (regexp-quote formula) formrpl))) + (user-error "Invalid field specifier \"%s\"" + (match-string 0 form))) + (setq form (replace-match formrpl t t form))) (if lispp (setq ev (condition-case nil @@ -2709,20 +2861,23 @@ not overwrite the stored one." ev (if duration (org-table-time-seconds-to-string (string-to-number ev) duration-output-format) ev)) - (or (fboundp 'calc-eval) - (user-error "Calc does not seem to be installed, and is needed to evaluate the formula")) - ;; Use <...> time-stamps so that Calc can handle them - (while (string-match (concat "\\[" org-ts-regexp1 "\\]") form) - (setq form (replace-match "<\\1>" nil nil form))) - ;; I18n-ize local time-stamps by setting (system-time-locale "C") - (when (string-match org-ts-regexp2 form) - (let* ((ts (match-string 0 form)) - (tsp (apply 'encode-time (save-match-data (org-parse-time-string ts)))) - (system-time-locale "C") - (tf (or (and (save-match-data (string-match "[0-9]\\{1,2\\}:[0-9]\\{2\\}" ts)) - (cdr org-time-stamp-formats)) - (car org-time-stamp-formats)))) - (setq form (replace-match (format-time-string tf tsp) t t form)))) + + ;; Use <...> time-stamps so that Calc can handle them. + (setq form + (replace-regexp-in-string org-ts-regexp-inactive "<\\1>" form)) + ;; Internationalize local time-stamps by setting locale to + ;; "C". + (setq form + (replace-regexp-in-string + org-ts-regexp + (lambda (ts) + (let ((system-time-locale "C")) + (format-time-string + (org-time-stamp-format + (string-match-p "[0-9]\\{1,2\\}:[0-9]\\{2\\}" ts)) + (apply #'encode-time + (save-match-data (org-parse-time-string ts)))))) + form t t)) (setq ev (if (and duration (string-match "^[0-9]+:[0-9]+\\(?::[0-9]+\\)?$" form)) form @@ -2742,7 +2897,7 @@ Orig: %s $xyz-> %s @r$c-> %s $1-> %s\n" orig formula form0 form)) - (if (listp ev) + (if (consp ev) (princ (format " %s^\nError: %s" (make-string (car ev) ?\-) (nth 1 ev))) (princ (format "Result: %s\nFormat: %s\nFinal: %s" @@ -2750,14 +2905,14 @@ $1-> %s\n" orig formula form0 form)) (if fmt (format fmt (string-to-number ev)) ev))))) (setq bw (get-buffer-window "*Substitution History*")) (org-fit-window-to-buffer bw) - (unless (and (org-called-interactively-p 'any) (not ndown)) + (unless (and (called-interactively-p 'any) (not ndown)) (unless (let (inhibit-redisplay) (y-or-n-p "Debugging Formula. Continue to next? ")) (org-table-align) (user-error "Abort")) (delete-window bw) (message ""))) - (if (listp ev) (setq fmt nil ev "#ERROR")) + (when (consp ev) (setq fmt nil ev "#ERROR")) (org-table-justify-field-maybe (format org-table-formula-field-format (if fmt (format fmt (string-to-number ev)) ev))) @@ -2776,146 +2931,152 @@ $1-> %s\n" orig formula form0 form)) (defun org-table-get-range (desc &optional tbeg col highlight corners-only) "Get a calc vector from a column, according to descriptor DESC. + Optional arguments TBEG and COL can give the beginning of the table and the current column, to avoid unnecessary parsing. HIGHLIGHT means just highlight the range. When CORNERS-ONLY is set, only return the corners of the range as -a list (line1 column1 line2 column2) where line1 and line2 are line numbers -in the buffer and column1 and column2 are table column numbers." - (if (not (equal (string-to-char desc) ?@)) - (setq desc (concat "@" desc))) - (save-excursion - (or tbeg (setq tbeg (org-table-begin))) - (or col (setq col (org-table-current-column))) - (let ((thisline (org-current-line)) - beg end c1 c2 r1 r2 rangep tmp) - (unless (string-match org-table-range-regexp desc) - (user-error "Invalid table range specifier `%s'" desc)) - (setq rangep (match-end 3) - r1 (and (match-end 1) (match-string 1 desc)) - r2 (and (match-end 4) (match-string 4 desc)) - c1 (and (match-end 2) (substring (match-string 2 desc) 1)) - c2 (and (match-end 5) (substring (match-string 5 desc) 1))) - - (and c1 (setq c1 (+ (string-to-number c1) - (if (memq (string-to-char c1) '(?- ?+)) col 0)))) - (and c2 (setq c2 (+ (string-to-number c2) - (if (memq (string-to-char c2) '(?- ?+)) col 0)))) - (if (equal r1 "") (setq r1 nil)) - (if (equal r2 "") (setq r2 nil)) - (if r1 (setq r1 (org-table-get-descriptor-line r1))) - (if r2 (setq r2 (org-table-get-descriptor-line r2))) - ; (setq r2 (or r2 r1) c2 (or c2 c1)) - (if (not r1) (setq r1 thisline)) - (if (not r2) (setq r2 thisline)) - (if (or (not c1) (= 0 c1)) (setq c1 col)) - (if (or (not c2) (= 0 c2)) (setq c2 col)) - (if (and (not corners-only) - (or (not rangep) (and (= r1 r2) (= c1 c2)))) - ;; just one field - (progn - (org-goto-line r1) - (while (not (looking-at org-table-dataline-regexp)) - (beginning-of-line 2)) - (prog1 (org-trim (org-table-get-field c1)) - (if highlight (org-table-highlight-rectangle (point) (point))))) - ;; A range, return a vector - ;; First sort the numbers to get a regular rectangle - (if (< r2 r1) (setq tmp r1 r1 r2 r2 tmp)) - (if (< c2 c1) (setq tmp c1 c1 c2 c2 tmp)) - (if corners-only - ;; Only return the corners of the range - (list r1 c1 r2 c2) - ;; Copy the range values into a list - (org-goto-line r1) - (while (not (looking-at org-table-dataline-regexp)) - (beginning-of-line 2)) - (org-table-goto-column c1) - (setq beg (point)) - (org-goto-line r2) - (while (not (looking-at org-table-dataline-regexp)) - (beginning-of-line 0)) - (org-table-goto-column c2) - (setq end (point)) - (if highlight - (org-table-highlight-rectangle - beg (progn (skip-chars-forward "^|\n") (point)))) - ;; return string representation of calc vector - (mapcar 'org-trim - (apply 'append (org-table-copy-region beg end)))))))) - -(defun org-table-get-descriptor-line (desc &optional cline bline table) - "Analyze descriptor DESC and retrieve the corresponding line number. -The cursor is currently in line CLINE, the table begins in line BLINE, -and TABLE is a vector with line types." - (if (string-match "^[0-9]+$" desc) +a list (line1 column1 line2 column2) where line1 and line2 are +line numbers relative to beginning of table, or TBEG, and column1 +and column2 are table column numbers." + (let* ((desc (if (string-match-p "\\`\\$[0-9]+\\.\\.\\$[0-9]+\\'" desc) + (replace-regexp-in-string "\\$" "@0$" desc) + desc)) + (col (or col (org-table-current-column))) + (tbeg (or tbeg (org-table-begin))) + (thisline (count-lines tbeg (line-beginning-position)))) + (unless (string-match org-table-range-regexp desc) + (user-error "Invalid table range specifier `%s'" desc)) + (let ((rangep (match-end 3)) + (r1 (let ((r (and (match-end 1) (match-string 1 desc)))) + (or (save-match-data + (and (org-string-nw-p r) + (org-table--descriptor-line r thisline))) + thisline))) + (r2 (let ((r (and (match-end 4) (match-string 4 desc)))) + (or (save-match-data + (and (org-string-nw-p r) + (org-table--descriptor-line r thisline))) + thisline))) + (c1 (let ((c (and (match-end 2) (substring (match-string 2 desc) 1)))) + (if (or (not c) (= (string-to-number c) 0)) col + (+ (string-to-number c) + (if (memq (string-to-char c) '(?- ?+)) col 0))))) + (c2 (let ((c (and (match-end 5) (substring (match-string 5 desc) 1)))) + (if (or (not c) (= (string-to-number c) 0)) col + (+ (string-to-number c) + (if (memq (string-to-char c) '(?- ?+)) col 0)))))) + (save-excursion + (if (and (not corners-only) + (or (not rangep) (and (= r1 r2) (= c1 c2)))) + ;; Just one field. + (progn + (forward-line (- r1 thisline)) + (while (not (looking-at org-table-dataline-regexp)) + (forward-line)) + (prog1 (org-trim (org-table-get-field c1)) + (when highlight (org-table-highlight-rectangle)))) + ;; A range, return a vector. First sort the numbers to get + ;; a regular rectangle. + (let ((first-row (min r1 r2)) + (last-row (max r1 r2)) + (first-column (min c1 c2)) + (last-column (max c1 c2))) + (if corners-only (list first-row first-column last-row last-column) + ;; Copy the range values into a list. + (forward-line (- first-row thisline)) + (while (not (looking-at org-table-dataline-regexp)) + (forward-line) + (cl-incf first-row)) + (org-table-goto-column first-column) + (let ((beg (point))) + (forward-line (- last-row first-row)) + (while (not (looking-at org-table-dataline-regexp)) + (forward-line -1)) + (org-table-goto-column last-column) + (let ((end (point))) + (when highlight + (org-table-highlight-rectangle + beg (progn (skip-chars-forward "^|\n") (point)))) + ;; Return string representation of calc vector. + (mapcar #'org-trim + (apply #'append + (org-table-copy-region beg end)))))))))))) + +(defun org-table--descriptor-line (desc cline) + "Return relative line number corresponding to descriptor DESC. +The cursor is currently in relative line number CLINE." + (if (string-match "\\`[0-9]+\\'" desc) (aref org-table-dlines (string-to-number desc)) - (setq cline (or cline (org-current-line)) - bline (or bline org-table-current-begin-line) - table (or table org-table-current-line-types)) - (if (or - (not (string-match "^\\(\\([-+]\\)?\\(I+\\)\\)?\\(\\([-+]\\)?\\([0-9]+\\)\\)?" desc)) - ;; 1 2 3 4 5 6 - (and (not (match-end 3)) (not (match-end 6))) - (and (match-end 3) (match-end 6) (not (match-end 5)))) - (user-error "Invalid row descriptor `%s'" desc)) - (let* ((hdir (and (match-end 2) (match-string 2 desc))) - (hn (if (match-end 3) (- (match-end 3) (match-beginning 3)) nil)) - (odir (and (match-end 5) (match-string 5 desc))) - (on (if (match-end 6) (string-to-number (match-string 6 desc)))) - (i (- cline bline)) + (when (or (not (string-match + "^\\(\\([-+]\\)?\\(I+\\)\\)?\\(\\([-+]\\)?\\([0-9]+\\)\\)?" + ;; 1 2 3 4 5 6 + desc)) + (and (not (match-end 3)) (not (match-end 6))) + (and (match-end 3) (match-end 6) (not (match-end 5)))) + (user-error "Invalid row descriptor `%s'" desc)) + (let* ((hn (and (match-end 3) (- (match-end 3) (match-beginning 3)))) + (hdir (match-string 2 desc)) + (odir (match-string 5 desc)) + (on (and (match-end 6) (string-to-number (match-string 6 desc)))) (rel (and (match-end 6) (or (and (match-end 1) (not (match-end 3))) (match-end 5))))) - (if (and hn (not hdir)) - (progn - (setq i 0 hdir "+") - (if (eq (aref table 0) 'hline) (setq hn (1- hn))))) - (if (and (not hn) on (not odir)) - (user-error "Should never happen");;(aref org-table-dlines on) - (if (and hn (> hn 0)) - (setq i (org-table-find-row-type table i 'hline (equal hdir "-") - nil hn cline desc))) - (if on - (setq i (org-table-find-row-type table i 'dline (equal odir "-") - rel on cline desc))) - (+ bline i))))) - -(defun org-table-find-row-type (table i type backwards relative n cline desc) - "FIXME: Needs more documentation." - (let ((l (length table))) - (while (> n 0) - (while (and (setq i (+ i (if backwards -1 1))) - (>= i 0) (< i l) - (not (eq (aref table i) type)) - (if (and relative (eq (aref table i) 'hline)) - (cond - ((eq org-table-relative-ref-may-cross-hline t) t) - ((eq org-table-relative-ref-may-cross-hline 'error) - (user-error "Row descriptor %s used in line %d crosses hline" desc cline)) - (t (setq i (- i (if backwards -1 1)) - n 1) - nil)) - t))) - (setq n (1- n))) - (if (or (< i 0) (>= i l)) - (user-error "Row descriptor %s used in line %d leads outside table" - desc cline) - i))) - -(defun org-table-rewrite-old-row-references (s) - (if (string-match "&[-+0-9I]" s) - (user-error "Formula contains old &row reference, please rewrite using @-syntax") - s)) + (when (and hn (not hdir)) + (setq cline 0) + (setq hdir "+") + (when (eq (aref org-table-current-line-types 0) 'hline) (cl-decf hn))) + (when (and (not hn) on (not odir)) (user-error "Should never happen")) + (when hn + (setq cline + (org-table--row-type 'hline hn cline (equal hdir "-") nil desc))) + (when on + (setq cline + (org-table--row-type 'dline on cline (equal odir "-") rel desc))) + cline))) + +(defun org-table--row-type (type n i backwards relative desc) + "Return relative line of Nth row with type TYPE. +Search starts from relative line I. When BACKWARDS in non-nil, +look before I. When RELATIVE is non-nil, the reference is +relative. DESC is the original descriptor that started the +search, as a string." + (let ((l (length org-table-current-line-types))) + (catch :exit + (dotimes (_ n) + (while (and (cl-incf i (if backwards -1 1)) + (>= i 0) + (< i l) + (not (eq (aref org-table-current-line-types i) type)) + ;; We are going to cross a hline. Check if this is + ;; an authorized move. + (cond + ((not relative)) + ((not (eq (aref org-table-current-line-types i) 'hline))) + ((eq org-table-relative-ref-may-cross-hline t)) + ((eq org-table-relative-ref-may-cross-hline 'error) + (user-error "Row descriptor %s crosses hline" desc)) + (t (cl-decf i (if backwards -1 1)) ; Step back. + (throw :exit nil))))))) + (cond ((or (< i 0) (>= i l)) + (user-error "Row descriptor %s leads outside table" desc)) + ;; The last hline doesn't exist. Instead, point to last row + ;; in table. + ((= i (1- l)) (1- i)) + (t i)))) + +(defun org-table--error-on-old-row-references (s) + (when (string-match "&[-+0-9I]" s) + (user-error "Formula contains old &row reference, please rewrite using @-syntax"))) (defun org-table-make-reference (elements keep-empty numbers lispp) "Convert list ELEMENTS to something appropriate to insert into formula. KEEP-EMPTY indicated to keep empty fields, default is to skip them. NUMBERS indicates that everything should be converted to numbers. LISPP non-nil means to return something appropriate for a Lisp -list, 'literal is for the format specifier L." +list, `literal' is for the format specifier L." ;; Calc nan (not a number) is used for the conversion of the empty ;; field to a reference for several reasons: (i) It is accepted in a ;; Calc formula (e. g. "" or "()" would result in a Calc error). @@ -2961,162 +3122,185 @@ list, 'literal is for the format specifier L." elements ",") "]")))) -;;;###autoload -(defun org-table-set-constants () - "Set `org-table-formula-constants-local' in the current buffer." - (let (cst consts const-str) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward "^[ \t]*#\\+CONSTANTS: \\(.*\\)" nil t) - (setq const-str (substring-no-properties (match-string 1))) - (setq consts (append consts (org-split-string const-str "[ \t]+"))) - (when consts - (let (e) - (while (setq e (pop consts)) - (when (string-match "^\\([a-zA-Z0][_a-zA-Z0-9]*\\)=\\(.*\\)" e) - (if (assoc-string (match-string 1 e) cst) - (setq cst (delete (assoc-string (match-string 1 e) cst) cst))) - (push (cons (match-string 1 e) (match-string 2 e)) cst))) - (setq org-table-formula-constants-local cst))))))) +(defun org-table-message-once-per-second (t1 &rest args) + "If there has been more than one second since T1, display message. +ARGS are passed as arguments to the `message' function. Returns +current time if a message is printed, otherwise returns T1. If +T1 is nil, always messages." + (let ((curtime (current-time))) + (if (or (not t1) (< 0 (nth 1 (time-subtract curtime t1)))) + (progn (apply 'message args) + curtime) + t1))) ;;;###autoload (defun org-table-recalculate (&optional all noalign) "Recalculate the current table line by applying all stored formulas. + With prefix arg ALL, do this for all lines in the table. -With the prefix argument ALL is `(16)' \ -\(a double \\[universal-prefix] \\[universal-prefix] prefix), or if -it is the symbol `iterate', recompute the table until it no longer changes. + +When called with a `\\[universal-argument] \\[universal-argument]' prefix, or \ +if ALL is the symbol `iterate', +recompute the table until it no longer changes. + If NOALIGN is not nil, do not re-align the table after the computations are done. This is typically used internally to save time, if it is known that the table will be realigned a little later anyway." (interactive "P") - (or (memq this-command org-recalc-commands) - (setq org-recalc-commands (cons this-command org-recalc-commands))) + (unless (memq this-command org-recalc-commands) + (push this-command org-recalc-commands)) (unless (org-at-table-p) (user-error "Not at a table")) (if (or (eq all 'iterate) (equal all '(16))) (org-table-iterate) - (org-table-get-specials) + (org-table-analyze) (let* ((eqlist (sort (org-table-get-stored-formulas) (lambda (a b) (string< (car a) (car b))))) - (eqlist1 (copy-sequence eqlist)) (inhibit-redisplay (not debug-on-error)) (line-re org-table-dataline-regexp) - (thisline (org-current-line)) - (thiscol (org-table-current-column)) - seen-fields lhs1 - beg end entry eqlnum eqlname eqlname1 eql (cnt 0) eq a name name1) - ;; Insert constants in all formulas - (setq eqlist - (mapcar (lambda (x) - (when (string-match "\\`$[<>]" (car x)) - (setq lhs1 (car x)) - (setq x (cons (substring - (org-table-formula-handle-first/last-rc - (car x)) 1) - (cdr x))) - (if (assoc (car x) eqlist1) - (user-error "\"%s=\" formula tries to overwrite existing formula for column %s" - lhs1 (car x)))) - (cons - (org-table-formula-handle-first/last-rc (car x)) - (org-table-formula-substitute-names - (org-table-formula-handle-first/last-rc (cdr x))))) - eqlist)) - ;; Split the equation list - (while (setq eq (pop eqlist)) - (if (<= (string-to-char (car eq)) ?9) - (push eq eqlnum) - (push eq eqlname))) - (setq eqlnum (nreverse eqlnum) eqlname (nreverse eqlname)) - ;; Expand ranges in lhs of formulas - (setq eqlname (org-table-expand-lhs-ranges eqlname)) - - ;; Get the correct line range to process - (if all - (progn - (setq end (move-marker (make-marker) (1+ (org-table-end)))) - (goto-char (setq beg (org-table-begin))) - (if (re-search-forward org-table-calculate-mark-regexp end t) - ;; This is a table with marked lines, compute selected lines - (setq line-re org-table-recalculate-regexp) - ;; Move forward to the first non-header line - (if (and (re-search-forward org-table-dataline-regexp end t) - (re-search-forward org-table-hline-regexp end t) - (re-search-forward org-table-dataline-regexp end t)) - (setq beg (match-beginning 0)) - nil))) ;; just leave beg where it is - (setq beg (point-at-bol) - end (move-marker (make-marker) (1+ (point-at-eol))))) - (goto-char beg) - (and all (message "Re-applying formulas to full table...")) - - ;; First find the named fields, and mark them untouchable. - ;; Also check if several field/range formulas try to set the same field. - (remove-text-properties beg end '(org-untouchable t)) - (while (setq eq (pop eqlname)) - (setq name (car eq) - a (assoc name org-table-named-field-locations)) - (setq name1 name) - (if a (setq name1 (format "@%d$%d" (org-table-line-to-dline (nth 1 a)) - (nth 2 a)))) - (when (member name1 seen-fields) - (user-error "Several field/range formulas try to set %s" name1)) - (push name1 seen-fields) - - (and (not a) - (string-match "@\\([0-9]+\\)\\$\\([0-9]+\\)" name) - (setq a (list name - (condition-case nil - (aref org-table-dlines - (string-to-number (match-string 1 name))) - (error (user-error "Invalid row number in %s" - name))) - (string-to-number (match-string 2 name))))) - (when (and a (or all (equal (nth 1 a) thisline))) - (message "Re-applying formula to field: %s" name) - (org-goto-line (nth 1 a)) - (org-table-goto-column (nth 2 a)) - (push (append a (list (cdr eq))) eqlname1) - (org-table-put-field-property :org-untouchable t))) - (setq eqlname1 (nreverse eqlname1)) - - ;; Now evaluate the column formulas, but skip fields covered by - ;; field formulas - (goto-char beg) - (while (re-search-forward line-re end t) - (unless (string-match "^ *[_^!$/] *$" (org-table-get-field 1)) - ;; Unprotected line, recalculate - (and all (message "Re-applying formulas to full table...(line %d)" - (setq cnt (1+ cnt)))) - (setq org-last-recalc-line (org-current-line)) - (setq eql eqlnum) - (while (setq entry (pop eql)) - (org-goto-line org-last-recalc-line) - (org-table-goto-column (string-to-number (car entry)) nil 'force) - (unless (get-text-property (point) :org-untouchable) - (org-table-eval-formula nil (cdr entry) - 'noalign 'nocst 'nostore 'noanalysis))))) - - ;; Now evaluate the field formulas - (while (setq eq (pop eqlname1)) - (message "Re-applying formula to field: %s" (car eq)) - (org-goto-line (nth 1 eq)) - (org-table-goto-column (nth 2 eq)) - (org-table-eval-formula nil (nth 3 eq) 'noalign 'nocst - 'nostore 'noanalysis)) - - (org-goto-line thisline) - (org-table-goto-column thiscol) - (remove-text-properties (point-min) (point-max) '(org-untouchable t)) - (or noalign (and org-table-may-need-update (org-table-align)) - (and all (message "Re-applying formulas to %d lines...done" cnt))) - - ;; back to initial position - (message "Re-applying formulas...done") - (org-goto-line thisline) - (org-table-goto-column thiscol) - (or noalign (and org-table-may-need-update (org-table-align)) - (and all (message "Re-applying formulas...done")))))) + (log-first-time (current-time)) + (log-last-time log-first-time) + (cnt 0) + beg end eqlcol eqlfield) + ;; Insert constants in all formulas. + (when eqlist + (org-table-save-field + ;; Expand equations, then split the equation list between + ;; column formulas and field formulas. + (dolist (eq eqlist) + (let* ((rhs (org-table-formula-substitute-names + (org-table-formula-handle-first/last-rc (cdr eq)))) + (old-lhs (car eq)) + (lhs + (org-table-formula-handle-first/last-rc + (cond + ((string-match "\\`@-?I+" old-lhs) + (user-error "Can't assign to hline relative reference")) + ((string-match "\\`$[<>]" old-lhs) + (let ((new (org-table-formula-handle-first/last-rc + old-lhs))) + (when (assoc new eqlist) + (user-error "\"%s=\" formula tries to overwrite \ +existing formula for column %s" + old-lhs + new)) + new)) + (t old-lhs))))) + (if (string-match-p "\\`\\$[0-9]+\\'" lhs) + (push (cons lhs rhs) eqlcol) + (push (cons lhs rhs) eqlfield)))) + (setq eqlcol (nreverse eqlcol)) + ;; Expand ranges in lhs of formulas + (setq eqlfield (org-table-expand-lhs-ranges (nreverse eqlfield))) + ;; Get the correct line range to process. + (if all + (progn + (setq end (copy-marker (org-table-end))) + (goto-char (setq beg org-table-current-begin-pos)) + (cond + ((re-search-forward org-table-calculate-mark-regexp end t) + ;; This is a table with marked lines, compute selected + ;; lines. + (setq line-re org-table-recalculate-regexp)) + ;; Move forward to the first non-header line. + ((and (re-search-forward org-table-dataline-regexp end t) + (re-search-forward org-table-hline-regexp end t) + (re-search-forward org-table-dataline-regexp end t)) + (setq beg (match-beginning 0))) + ;; Just leave BEG at the start of the table. + (t nil))) + (setq beg (line-beginning-position) + end (copy-marker (line-beginning-position 2)))) + (goto-char beg) + ;; Mark named fields untouchable. Also check if several + ;; field/range formulas try to set the same field. + (remove-text-properties beg end '(org-untouchable t)) + (let ((current-line (count-lines org-table-current-begin-pos + (line-beginning-position))) + seen-fields) + (dolist (eq eqlfield) + (let* ((name (car eq)) + (location (assoc name org-table-named-field-locations)) + (eq-line (or (nth 1 location) + (and (string-match "\\`@\\([0-9]+\\)" name) + (aref org-table-dlines + (string-to-number + (match-string 1 name)))))) + (reference + (if location + ;; Turn field coordinates associated to NAME + ;; into an absolute reference. + (format "@%d$%d" + (org-table-line-to-dline eq-line) + (nth 2 location)) + name))) + (when (member reference seen-fields) + (user-error "Several field/range formulas try to set %s" + reference)) + (push reference seen-fields) + (when (or all (eq eq-line current-line)) + (org-table-goto-field name) + (org-table-put-field-property :org-untouchable t))))) + ;; Evaluate the column formulas, but skip fields covered by + ;; field formulas. + (goto-char beg) + (while (re-search-forward line-re end t) + (unless (string-match "\\` *[_^!$/] *\\'" (org-table-get-field 1)) + ;; Unprotected line, recalculate. + (cl-incf cnt) + (when all + (setq log-last-time + (org-table-message-once-per-second + log-last-time + "Re-applying formulas to full table...(line %d)" cnt))) + (if (markerp org-last-recalc-line) + (move-marker org-last-recalc-line (line-beginning-position)) + (setq org-last-recalc-line + (copy-marker (line-beginning-position)))) + (dolist (entry eqlcol) + (goto-char org-last-recalc-line) + (org-table-goto-column + (string-to-number (substring (car entry) 1)) nil 'force) + (unless (get-text-property (point) :org-untouchable) + (org-table-eval-formula + nil (cdr entry) 'noalign 'nocst 'nostore 'noanalysis))))) + ;; Evaluate the field formulas. + (dolist (eq eqlfield) + (let ((reference (car eq)) + (formula (cdr eq))) + (setq log-last-time + (org-table-message-once-per-second + (and all log-last-time) + "Re-applying formula to field: %s" (car eq))) + (org-table-goto-field + reference + ;; Possibly create a new column, as long as + ;; `org-table-formula-create-columns' allows it. + (let ((column-count (progn (end-of-line) + (1- (org-table-current-column))))) + (lambda (column) + (when (> column 1000) + (user-error "Formula column target too large")) + (and (> column column-count) + (or (eq org-table-formula-create-columns t) + (and (eq org-table-formula-create-columns 'warn) + (progn + (org-display-warning + "Out-of-bounds formula added columns") + t)) + (and (eq org-table-formula-create-columns 'prompt) + (yes-or-no-p + "Out-of-bounds formula. Add columns? "))))))) + (org-table-eval-formula nil formula t t t t)))) + ;; Clean up markers and internal text property. + (remove-text-properties (point-min) (point-max) '(org-untouchable t)) + (set-marker end nil) + (unless noalign + (when org-table-may-need-update (org-table-align)) + (when all + (org-table-message-once-per-second + log-first-time "Re-applying formulas to %d lines... done" cnt))) + (org-table-message-once-per-second + (and all log-first-time) "Re-applying formulas... done"))))) ;;;###autoload (defun org-table-iterate (&optional arg) @@ -3145,10 +3329,15 @@ with the prefix ARG." (defun org-table-recalculate-buffer-tables () "Recalculate all tables in the current buffer." (interactive) - (save-excursion - (save-restriction - (widen) - (org-table-map-tables (lambda () (org-table-recalculate t)) t)))) + (org-with-wide-buffer + (org-table-map-tables + (lambda () + ;; Reason for separate `org-table-align': When repeating + ;; (org-table-recalculate t) `org-table-may-need-update' gets in + ;; the way. + (org-table-recalculate t t) + (org-table-align)) + t))) ;;;###autoload (defun org-table-iterate-buffer-tables () @@ -3158,85 +3347,90 @@ with the prefix ARG." (i imax) (checksum (md5 (buffer-string))) c1) - (save-excursion - (save-restriction - (widen) - (catch 'exit - (while (> i 0) - (setq i (1- i)) - (org-table-map-tables (lambda () (org-table-recalculate t)) t) - (if (equal checksum (setq c1 (md5 (buffer-string)))) - (progn - (message "Convergence after %d iterations" (- imax i)) - (throw 'exit t)) - (setq checksum c1))) - (user-error "No convergence after %d iterations" imax)))))) + (org-with-wide-buffer + (catch 'exit + (while (> i 0) + (setq i (1- i)) + (org-table-map-tables (lambda () (org-table-recalculate t t)) t) + (if (equal checksum (setq c1 (md5 (buffer-string)))) + (progn + (org-table-map-tables #'org-table-align t) + (message "Convergence after %d iterations" (- imax i)) + (throw 'exit t)) + (setq checksum c1))) + (org-table-map-tables #'org-table-align t) + (user-error "No convergence after %d iterations" imax))))) (defun org-table-calc-current-TBLFM (&optional arg) "Apply the #+TBLFM in the line at point to the table." (interactive "P") (unless (org-at-TBLFM-p) (user-error "Not at a #+TBLFM line")) (let ((formula (buffer-substring - (point-at-bol) - (point-at-eol))) - s e) + (line-beginning-position) + (line-end-position)))) (save-excursion ;; Insert a temporary formula at right after the table (goto-char (org-table-TBLFM-begin)) - (setq s (point-marker)) - (insert (concat formula "\n")) - (setq e (point-marker)) - ;; Recalculate the table - (beginning-of-line 0) ; move to the inserted line - (skip-chars-backward " \r\n\t") - (if (org-at-table-p) + (let ((s (point-marker))) + (insert formula "\n") + (let ((e (point-marker))) + ;; Recalculate the table. + (beginning-of-line 0) ; move to the inserted line + (skip-chars-backward " \r\n\t") (unwind-protect - (org-call-with-arg 'org-table-recalculate (or arg t)) - ;; delete the formula inserted temporarily - (delete-region s e)))))) + (org-call-with-arg #'org-table-recalculate (or arg t)) + ;; Delete the formula inserted temporarily. + (delete-region s e) + (set-marker s nil) + (set-marker e nil))))))) (defun org-table-TBLFM-begin () "Find the beginning of the TBLFM lines and return its position. Return nil when the beginning of TBLFM line was not found." (save-excursion (when (progn (forward-line 1) - (re-search-backward - org-table-TBLFM-begin-regexp - nil t)) - (point-at-bol 2)))) + (re-search-backward org-table-TBLFM-begin-regexp nil t)) + (line-beginning-position 2)))) (defun org-table-expand-lhs-ranges (equations) "Expand list of formulas. -If some of the RHS in the formulas are ranges or a row reference, expand -them to individual field equations for each field." - (let (e res lhs rhs range r1 r2 c1 c2) - (while (setq e (pop equations)) - (setq lhs (car e) rhs (cdr e)) - (cond - ((string-match "^@-?[-+0-9]+\\$-?[0-9]+$" lhs) - ;; This just refers to one fixed field - (push e res)) - ((string-match "^[a-zA-Z][_a-zA-Z0-9]*$" lhs) - ;; This just refers to one fixed named field - (push e res)) - ((string-match "^@[0-9]+$" lhs) - (loop for ic from 1 to org-table-current-ncol do - (push (cons (format "%s$%d" lhs ic) rhs) res) - (put-text-property 0 (length (caar res)) - :orig-eqn e (caar res)))) - (t - (setq range (org-table-get-range lhs org-table-current-begin-pos - 1 nil 'corners)) - (setq r1 (nth 0 range) c1 (nth 1 range) - r2 (nth 2 range) c2 (nth 3 range)) - (setq r1 (org-table-line-to-dline r1)) - (setq r2 (org-table-line-to-dline r2 'above)) - (loop for ir from r1 to r2 do - (loop for ic from c1 to c2 do - (push (cons (format "@%d$%d" ir ic) rhs) res) - (put-text-property 0 (length (caar res)) - :orig-eqn e (caar res))))))) - (nreverse res))) +If some of the RHS in the formulas are ranges or a row reference, +expand them to individual field equations for each field. This +function assumes the table is already analyzed (i.e., using +`org-table-analyze')." + (let (res) + (dolist (e equations (nreverse res)) + (let ((lhs (car e)) + (rhs (cdr e))) + (cond + ((string-match-p "\\`@-?[-+0-9]+\\$-?[0-9]+\\'" lhs) + ;; This just refers to one fixed field. + (push e res)) + ((string-match-p "\\`[a-zA-Z][_a-zA-Z0-9]*\\'" lhs) + ;; This just refers to one fixed named field. + (push e res)) + ((string-match-p "\\`\\$[0-9]+\\'" lhs) + ;; Column formulas are treated specially and are not + ;; expanded. + (push e res)) + ((string-match "\\`@[0-9]+\\'" lhs) + (dotimes (ic org-table-current-ncol) + (push (cons (propertize (format "%s$%d" lhs (1+ ic)) :orig-eqn e) + rhs) + res))) + (t + (let* ((range (org-table-get-range + lhs org-table-current-begin-pos 1 nil 'corners)) + (r1 (org-table-line-to-dline (nth 0 range))) + (c1 (nth 1 range)) + (r2 (org-table-line-to-dline (nth 2 range) 'above)) + (c2 (nth 3 range))) + (cl-loop for ir from r1 to r2 do + (cl-loop for ic from c1 to c2 do + (push (cons (propertize + (format "@%d$%d" ir ic) :orig-eqn e) + rhs) + res)))))))))) (defun org-table-formula-handle-first/last-rc (s) "Replace @<, @>, $<, $> with first/last row/column of the table. @@ -3262,32 +3456,40 @@ borders of the table using the @< @> $< $> makers." (- nmax len -1))) (if (or (< n 1) (> n nmax)) (user-error "Reference \"%s\" in expression \"%s\" points outside table" - (match-string 0 s) s)) + (match-string 0 s) s)) (setq start (match-beginning 0)) (setq s (replace-match (format "%s%d" (match-string 1 s) n) t t s))))) s) (defun org-table-formula-substitute-names (f) "Replace $const with values in string F." - (let ((start 0) a (f1 f) (pp (/= (string-to-char f) ?'))) - ;; First, check for column names - (while (setq start (string-match org-table-column-name-regexp f start)) - (setq start (1+ start)) - (setq a (assoc (match-string 1 f) org-table-column-names)) - (setq f (replace-match (concat "$" (cdr a)) t t f))) - ;; Parameters and constants - (setq start 0) - (while (setq start (string-match "\\$\\([a-zA-Z][_a-zA-Z0-9]*\\)\\|\\(\\]" (car entry)) 'column) - ((equal (string-to-char (car entry)) ?@) 'field) - ((string-match "^[0-9]" (car entry)) 'column) - (t 'named))) - (when (setq title (assq type titles)) - (or (bobp) (insert "\n")) - (insert (org-add-props (cdr title) nil 'face font-lock-comment-face)) - (setq titles (remove title titles))) - (if (equal key (car entry)) (setq startline (org-current-line))) - (setq s (concat (if (member (string-to-char (car entry)) '(?@ ?$)) "" "$") - (car entry) " = " (cdr entry) "\n")) - (remove-text-properties 0 (length s) '(face nil) s) - (insert s)) - (if (eq org-table-use-standard-references t) + (let ((at-tblfm (org-at-TBLFM-p))) + (unless (or at-tblfm (org-at-table-p)) + (user-error "Not at a table")) + (save-excursion + ;; Move point within the table before analyzing it. + (when at-tblfm (re-search-backward "^[ \t]*|")) + (org-table-analyze)) + (let ((key (org-table-current-field-formula 'key 'noerror)) + (eql (sort (org-table-get-stored-formulas t (and at-tblfm (point))) + #'org-table-formula-less-p)) + (pos (point-marker)) + (source (copy-marker (line-beginning-position))) + (startline 1) + (wc (current-window-configuration)) + (sel-win (selected-window)) + (titles '((column . "# Column Formulas\n") + (field . "# Field and Range Formulas\n") + (named . "# Named Field Formulas\n")))) + (org-switch-to-buffer-other-window "*Edit Formulas*") + (erase-buffer) + ;; Keep global-font-lock-mode from turning on font-lock-mode + (let ((font-lock-global-modes '(not fundamental-mode))) + (fundamental-mode)) + (setq-local font-lock-global-modes (list 'not major-mode)) + (setq-local org-pos pos) + (setq-local org-table--fedit-source source) + (setq-local org-window-configuration wc) + (setq-local org-selected-window sel-win) + (use-local-map org-table-fedit-map) + (add-hook 'post-command-hook #'org-table-fedit-post-command t t) + (easy-menu-add org-table-fedit-menu) + (setq startline (org-current-line)) + (dolist (entry eql) + (let* ((type (cond + ((string-match "\\`$\\([0-9]+\\|[<>]+\\)\\'" (car entry)) + 'column) + ((equal (string-to-char (car entry)) ?@) 'field) + (t 'named))) + (title (assq type titles))) + (when title + (unless (bobp) (insert "\n")) + (insert + (org-add-props (cdr title) nil 'face font-lock-comment-face)) + (setq titles (remove title titles))) + (when (equal key (car entry)) (setq startline (org-current-line))) + (let ((s (concat + (if (memq (string-to-char (car entry)) '(?@ ?$)) "" "$") + (car entry) " = " (cdr entry) "\n"))) + (remove-text-properties 0 (length s) '(face nil) s) + (insert s)))) + (when (eq org-table-use-standard-references t) (org-table-fedit-toggle-ref-type)) - (org-goto-line startline) - (message "%s" "Edit formulas, finish with C-c C-c or C-c '. See menu for more commands."))) + (org-goto-line startline) + (message "%s" (substitute-command-keys "\\\ +Edit formulas, finish with `\\[org-ctrl-c-ctrl-c]' or `\\[org-edit-special]'. \ +See menu for more commands."))))) (defun org-table-fedit-post-command () (when (not (memq this-command '(lisp-complete-symbol))) (let ((win (selected-window))) (save-excursion - (condition-case nil - (org-table-show-reference) - (error nil)) + (ignore-errors (org-table-show-reference)) (select-window win))))) (defun org-table-formula-to-user (s) @@ -3537,23 +3748,24 @@ minutes or seconds." (format "%.1f" (/ (float secs0) 60))) ((eq output-format 'seconds) (format "%d" secs0)) - (t (org-format-seconds "%.2h:%.2m:%.2s" secs0))))) + (t (format-seconds "%.2h:%.2m:%.2s" secs0))))) (if (< secs 0) (concat "-" res) res))) (defun org-table-fedit-convert-buffer (function) "Convert all references in this buffer, using FUNCTION." - (let ((line (org-current-line))) + (let ((origin (copy-marker (line-beginning-position)))) (goto-char (point-min)) (while (not (eobp)) - (insert (funcall function (buffer-substring (point) (point-at-eol)))) - (delete-region (point) (point-at-eol)) - (or (eobp) (forward-char 1))) - (org-goto-line line))) + (insert (funcall function (buffer-substring (point) (line-end-position)))) + (delete-region (point) (line-end-position)) + (forward-line)) + (goto-char origin) + (set-marker origin nil))) (defun org-table-fedit-toggle-ref-type () "Convert all references in the buffer from B3 to @3$2 and back." (interactive) - (org-set-local 'org-table-buffer-is-an (not org-table-buffer-is-an)) + (setq-local org-table-buffer-is-an (not org-table-buffer-is-an)) (org-table-fedit-convert-buffer (if org-table-buffer-is-an 'org-table-convert-refs-to-an 'org-table-convert-refs-to-rc)) @@ -3579,16 +3791,16 @@ minutes or seconds." (defun org-table-fedit-shift-reference (dir) (cond - ((org-at-regexp-p "\\(\\<[a-zA-Z]\\)&") + ((org-in-regexp "\\(\\<[a-zA-Z]\\)&") (if (memq dir '(left right)) (org-rematch-and-replace 1 (eq dir 'left)) (user-error "Cannot shift reference in this direction"))) - ((org-at-regexp-p "\\(\\<[a-zA-Z]\\{1,2\\}\\)\\([0-9]+\\)") + ((org-in-regexp "\\(\\<[a-zA-Z]\\{1,2\\}\\)\\([0-9]+\\)") ;; A B3-like reference (if (memq dir '(up down)) (org-rematch-and-replace 2 (eq dir 'up)) (org-rematch-and-replace 1 (eq dir 'left)))) - ((org-at-regexp-p + ((org-in-regexp "\\(@\\|\\.\\.\\)\\([-+]?\\(I+\\>\\|[0-9]+\\)\\)\\(\\$\\([-+]?[0-9]+\\)\\)?") ;; An internal reference (if (memq dir '(up down)) @@ -3649,32 +3861,31 @@ a translation reference." With prefix ARG, apply the new formulas to the table." (interactive "P") (org-table-remove-rectangle-highlight) - (if org-table-use-standard-references - (progn - (org-table-fedit-convert-buffer 'org-table-convert-refs-to-rc) - (setq org-table-buffer-is-an nil))) - (let ((pos org-pos) (sel-win org-selected-window) eql var form) + (when org-table-use-standard-references + (org-table-fedit-convert-buffer 'org-table-convert-refs-to-rc) + (setq org-table-buffer-is-an nil)) + (let ((pos org-pos) + (sel-win org-selected-window) + (source org-table--fedit-source) + eql) (goto-char (point-min)) (while (re-search-forward "^\\(@[-+I<>0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\|[<>]+\\)\\) *= *\\(.*\\(\n[ \t]+.*$\\)*\\)" nil t) - (setq var (if (match-end 2) (match-string 2) (match-string 1)) - form (match-string 3)) - (setq form (org-trim form)) - (when (not (equal form "")) - (while (string-match "[ \t]*\n[ \t]*" form) - (setq form (replace-match " " t t form))) - (when (assoc var eql) - (user-error "Double formulas for %s" var)) - (push (cons var form) eql))) - (setq org-pos nil) + (let ((var (match-string 1)) + (form (org-trim (match-string 3)))) + (unless (equal form "") + (while (string-match "[ \t]*\n[ \t]*" form) + (setq form (replace-match " " t t form))) + (when (assoc var eql) + (user-error "Double formulas for %s" var)) + (push (cons var form) eql)))) (set-window-configuration org-window-configuration) (select-window sel-win) - (goto-char pos) - (unless (org-at-table-p) - (user-error "Lost table position - cannot install formulas")) + (goto-char source) (org-table-store-formulas eql) - (move-marker pos nil) + (set-marker pos nil) + (set-marker source nil) (kill-buffer "*Edit Formulas*") (if arg (org-table-recalculate 'all) @@ -3733,9 +3944,11 @@ With prefix ARG, apply the new formulas to the table." (defvar org-show-positions nil) (defun org-table-show-reference (&optional local) - "Show the location/value of the $ expression at point." + "Show the location/value of the $ expression at point. +When LOCAL is non-nil, show references for the table at point." (interactive) (org-table-remove-rectangle-highlight) + (when local (org-table-analyze)) (catch 'exit (let ((pos (if local (point) org-pos)) (face2 'highlight) @@ -3743,41 +3956,41 @@ With prefix ARG, apply the new formulas to the table." (win (selected-window)) (org-show-positions nil) var name e what match dest) - (if local (org-table-get-specials)) (setq what (cond - ((org-at-regexp-p "^@[0-9]+[ \t=]") + ((org-in-regexp "^@[0-9]+[ \t=]") (setq match (concat (substring (match-string 0) 0 -1) "$1.." (substring (match-string 0) 0 -1) "$100")) 'range) - ((or (org-at-regexp-p org-table-range-regexp2) - (org-at-regexp-p org-table-translate-regexp) - (org-at-regexp-p org-table-range-regexp)) + ((or (org-in-regexp org-table-range-regexp2) + (org-in-regexp org-table-translate-regexp) + (org-in-regexp org-table-range-regexp)) (setq match (save-match-data (org-table-convert-refs-to-rc (match-string 0)))) 'range) - ((org-at-regexp-p "\\$[a-zA-Z][a-zA-Z0-9]*") 'name) - ((org-at-regexp-p "\\$[0-9]+") 'column) + ((org-in-regexp "\\$[a-zA-Z][a-zA-Z0-9]*") 'name) + ((org-in-regexp "\\$[0-9]+") 'column) ((not local) nil) (t (user-error "No reference at point"))) match (and what (or match (match-string 0)))) (when (and match (not (equal (match-beginning 0) (point-at-bol)))) (org-table-add-rectangle-overlay (match-beginning 0) (match-end 0) 'secondary-selection)) - (org-add-hook 'before-change-functions - 'org-table-remove-rectangle-highlight) - (if (eq what 'name) (setq var (substring match 1))) + (add-hook 'before-change-functions + #'org-table-remove-rectangle-highlight) + (when (eq what 'name) (setq var (substring match 1))) (when (eq what 'range) - (or (equal (string-to-char match) ?@) (setq match (concat "@" match))) + (unless (eq (string-to-char match) ?@) (setq match (concat "@" match))) (setq match (org-table-formula-substitute-names match))) (unless local (save-excursion - (end-of-line 1) + (end-of-line) (re-search-backward "^\\S-" nil t) - (beginning-of-line 1) - (when (looking-at "\\(\\$[0-9a-zA-Z]+\\|@[0-9]+\\$[0-9]+\\|[a-zA-Z]+\\([0-9]+\\|&\\)\\) *=") + (beginning-of-line) + (when (looking-at "\\(\\$[0-9a-zA-Z]+\\|@[0-9]+\\$[0-9]+\\|[a-zA-Z]+\ +\\([0-9]+\\|&\\)\\) *=") (setq dest (save-match-data (org-table-convert-refs-to-rc (match-string 1)))) @@ -3790,60 +4003,52 @@ With prefix ARG, apply the new formulas to the table." (marker-buffer pos))))) (goto-char pos) (org-table-force-dataline) - (when dest - (setq name (substring dest 1)) - (cond - ((string-match "^\\$[a-zA-Z][a-zA-Z0-9]*" dest) - (setq e (assoc name org-table-named-field-locations)) - (org-goto-line (nth 1 e)) - (org-table-goto-column (nth 2 e))) - ((string-match "^@\\([0-9]+\\)\\$\\([0-9]+\\)" dest) - (let ((l (string-to-number (match-string 1 dest))) - (c (string-to-number (match-string 2 dest)))) - (org-goto-line (aref org-table-dlines l)) - (org-table-goto-column c))) - (t (org-table-goto-column (string-to-number name)))) - (move-marker pos (point)) - (org-table-highlight-rectangle nil nil face2)) - (cond - ((equal dest match)) - ((not match)) - ((eq what 'range) - (condition-case nil - (save-excursion - (org-table-get-range match nil nil 'highlight)) - (error nil))) - ((setq e (assoc var org-table-named-field-locations)) - (org-goto-line (nth 1 e)) - (org-table-goto-column (nth 2 e)) - (org-table-highlight-rectangle (point) (point)) - (message "Named field, column %d of line %d" (nth 2 e) (nth 1 e))) - ((setq e (assoc var org-table-column-names)) - (org-table-goto-column (string-to-number (cdr e))) - (org-table-highlight-rectangle (point) (point)) - (goto-char (org-table-begin)) - (if (re-search-forward (concat "^[ \t]*| *! *.*?| *\\(" var "\\) *|") - (org-table-end) t) - (progn - (goto-char (match-beginning 1)) - (org-table-highlight-rectangle) - (message "Named column (column %s)" (cdr e))) - (user-error "Column name not found"))) - ((eq what 'column) - ;; column number - (org-table-goto-column (string-to-number (substring match 1))) - (org-table-highlight-rectangle (point) (point)) - (message "Column %s" (substring match 1))) - ((setq e (assoc var org-table-local-parameters)) - (goto-char (org-table-begin)) - (if (re-search-forward (concat "^[ \t]*| *\\$ *.*?| *\\(" var "=\\)") nil t) - (progn - (goto-char (match-beginning 1)) - (org-table-highlight-rectangle) - (message "Local parameter.")) - (user-error "Parameter not found"))) - (t + (let ((table-start + (if local org-table-current-begin-pos (org-table-begin)))) + (when dest + (setq name (substring dest 1)) + (cond + ((string-match-p "\\`\\$[a-zA-Z][a-zA-Z0-9]*" dest) + (org-table-goto-field dest)) + ((string-match-p "\\`@\\([1-9][0-9]*\\)\\$\\([1-9][0-9]*\\)\\'" + dest) + (org-table-goto-field dest)) + (t (org-table-goto-column (string-to-number name)))) + (move-marker pos (point)) + (org-table-highlight-rectangle nil nil face2)) (cond + ((equal dest match)) + ((not match)) + ((eq what 'range) + (ignore-errors (org-table-get-range match table-start nil 'highlight))) + ((setq e (assoc var org-table-named-field-locations)) + (org-table-goto-field var) + (org-table-highlight-rectangle) + (message "Named field, column %d of line %d" (nth 2 e) (nth 1 e))) + ((setq e (assoc var org-table-column-names)) + (org-table-goto-column (string-to-number (cdr e))) + (org-table-highlight-rectangle) + (goto-char table-start) + (if (re-search-forward (concat "^[ \t]*| *! *.*?| *\\(" var "\\) *|") + (org-table-end) t) + (progn + (goto-char (match-beginning 1)) + (org-table-highlight-rectangle) + (message "Named column (column %s)" (cdr e))) + (user-error "Column name not found"))) + ((eq what 'column) + ;; Column number. + (org-table-goto-column (string-to-number (substring match 1))) + (org-table-highlight-rectangle) + (message "Column %s" (substring match 1))) + ((setq e (assoc var org-table-local-parameters)) + (goto-char table-start) + (if (re-search-forward (concat "^[ \t]*| *\\$ *.*?| *\\(" var "=\\)") nil t) + (progn + (goto-char (match-beginning 1)) + (org-table-highlight-rectangle) + (message "Local parameter.")) + (user-error "Parameter not found"))) ((not var) (user-error "No reference at point")) ((setq e (assoc var org-table-formula-constants-local)) (message "Local Constant: $%s=%s in #+CONSTANTS line." @@ -3854,19 +4059,19 @@ With prefix ARG, apply the new formulas to the table." ((setq e (and (fboundp 'constants-get) (constants-get var))) (message "Constant: $%s=%s, from `constants.el'%s." var e (format " (%s units)" constants-unit-system))) - (t (user-error "Undefined name $%s" var))))) - (goto-char pos) - (when (and org-show-positions - (not (memq this-command '(org-table-fedit-scroll - org-table-fedit-scroll-down)))) - (push pos org-show-positions) - (push org-table-current-begin-pos org-show-positions) - (let ((min (apply 'min org-show-positions)) - (max (apply 'max org-show-positions))) - (set-window-start (selected-window) min) - (goto-char max) - (or (pos-visible-in-window-p max) - (set-window-start (selected-window) max)))) + (t (user-error "Undefined name $%s" var))) + (goto-char pos) + (when (and org-show-positions + (not (memq this-command '(org-table-fedit-scroll + org-table-fedit-scroll-down)))) + (push pos org-show-positions) + (push table-start org-show-positions) + (let ((min (apply 'min org-show-positions)) + (max (apply 'max org-show-positions))) + (set-window-start (selected-window) min) + (goto-char max) + (or (pos-visible-in-window-p max) + (set-window-start (selected-window) max))))) (select-window win)))) (defun org-table-force-dataline () @@ -3926,43 +4131,49 @@ Use COMMAND to do the motion, repeat if necessary to end up in a data line." (push ov org-table-rectangle-overlays))) (defun org-table-highlight-rectangle (&optional beg end face) - "Highlight rectangular region in a table." - (setq beg (or beg (point)) end (or end (point))) - (let ((b (min beg end)) - (e (max beg end)) - l1 c1 l2 c2 tmp) - (and (boundp 'org-show-positions) - (setq org-show-positions (cons b (cons e org-show-positions)))) - (goto-char (min beg end)) - (setq l1 (org-current-line) - c1 (org-table-current-column)) - (goto-char (max beg end)) - (setq l2 (org-current-line) - c2 (org-table-current-column)) - (if (> c1 c2) (setq tmp c1 c1 c2 c2 tmp)) - (org-goto-line l1) - (beginning-of-line 1) - (loop for line from l1 to l2 do - (when (looking-at org-table-dataline-regexp) - (org-table-goto-column c1) - (skip-chars-backward "^|\n") (setq beg (point)) - (org-table-goto-column c2) - (skip-chars-forward "^|\n") (setq end (point)) - (org-table-add-rectangle-overlay beg end face)) - (beginning-of-line 2)) - (goto-char b)) - (add-hook 'before-change-functions 'org-table-remove-rectangle-highlight)) - -(defun org-table-remove-rectangle-highlight (&rest ignore) + "Highlight rectangular region in a table. +When buffer positions BEG and END are provided, use them to +delimit the region to highlight. Otherwise, refer to point. Use +FACE, when non-nil, for the highlight." + (let* ((beg (or beg (point))) + (end (or end (point))) + (b (min beg end)) + (e (max beg end)) + (start-coordinates + (save-excursion + (goto-char b) + (cons (line-beginning-position) (org-table-current-column)))) + (end-coordinates + (save-excursion + (goto-char e) + (cons (line-beginning-position) (org-table-current-column))))) + (when (boundp 'org-show-positions) + (setq org-show-positions (cons b (cons e org-show-positions)))) + (goto-char (car start-coordinates)) + (let ((column-start (min (cdr start-coordinates) (cdr end-coordinates))) + (column-end (max (cdr start-coordinates) (cdr end-coordinates))) + (last-row (car end-coordinates))) + (while (<= (point) last-row) + (when (looking-at org-table-dataline-regexp) + (org-table-goto-column column-start) + (skip-chars-backward "^|\n") + (let ((p (point))) + (org-table-goto-column column-end) + (skip-chars-forward "^|\n") + (org-table-add-rectangle-overlay p (point) face))) + (forward-line))) + (goto-char (car start-coordinates))) + (add-hook 'before-change-functions #'org-table-remove-rectangle-highlight)) + +(defun org-table-remove-rectangle-highlight (&rest _ignore) "Remove the rectangle overlays." (unless org-inhibit-highlight-removal (remove-hook 'before-change-functions 'org-table-remove-rectangle-highlight) (mapc 'delete-overlay org-table-rectangle-overlays) (setq org-table-rectangle-overlays nil))) -(defvar org-table-coordinate-overlays nil +(defvar-local org-table-coordinate-overlays nil "Collects the coordinate grid overlays, so that they can be removed.") -(make-variable-buffer-local 'org-table-coordinate-overlays) (defun org-table-overlay-coordinates () "Add overlays to the table at point, to show row/column coordinates." @@ -4017,19 +4228,20 @@ Use COMMAND to do the motion, repeat if necessary to end up in a data line." ;;; The orgtbl minor mode ;; Define a minor mode which can be used in other modes in order to -;; integrate the org-mode table editor. - -;; This is really a hack, because the org-mode table editor uses several -;; keys which normally belong to the major mode, for example the TAB and -;; RET keys. Here is how it works: The minor mode defines all the keys -;; necessary to operate the table editor, but wraps the commands into a -;; function which tests if the cursor is currently inside a table. If that -;; is the case, the table editor command is executed. However, when any of -;; those keys is used outside a table, the function uses `key-binding' to -;; look up if the key has an associated command in another currently active -;; keymap (minor modes, major mode, global), and executes that command. -;; There might be problems if any of the keys used by the table editor is -;; otherwise used as a prefix key. +;; integrate the Org table editor. + +;; This is really a hack, because the Org table editor uses several +;; keys which normally belong to the major mode, for example the TAB +;; and RET keys. Here is how it works: The minor mode defines all the +;; keys necessary to operate the table editor, but wraps the commands +;; into a function which tests if the cursor is currently inside +;; a table. If that is the case, the table editor command is +;; executed. However, when any of those keys is used outside a table, +;; the function uses `key-binding' to look up if the key has an +;; associated command in another currently active keymap (minor modes, +;; major mode, global), and executes that command. There might be +;; problems if any of the keys used by the table editor is otherwise +;; used as a prefix key. ;; Another challenge is that the key binding for TAB can be tab or \C-i, ;; likewise the binding for RET can be return or \C-m. Orgtbl-mode @@ -4079,16 +4291,16 @@ Use COMMAND to do the motion, repeat if necessary to end up in a data line." ;; FIXME: maybe it should use emulation-mode-map-alists? (and c (setq minor-mode-map-alist (cons c (delq c minor-mode-map-alist))))) - (org-set-local (quote org-table-may-need-update) t) - (org-add-hook 'before-change-functions 'org-before-change-function - nil 'local) - (org-set-local 'org-old-auto-fill-inhibit-regexp - auto-fill-inhibit-regexp) - (org-set-local 'auto-fill-inhibit-regexp - (if auto-fill-inhibit-regexp - (concat orgtbl-line-start-regexp "\\|" - auto-fill-inhibit-regexp) - orgtbl-line-start-regexp)) + (setq-local org-table-may-need-update t) + (add-hook 'before-change-functions 'org-before-change-function + nil 'local) + (setq-local org-old-auto-fill-inhibit-regexp + auto-fill-inhibit-regexp) + (setq-local auto-fill-inhibit-regexp + (if auto-fill-inhibit-regexp + (concat orgtbl-line-start-regexp "\\|" + auto-fill-inhibit-regexp) + orgtbl-line-start-regexp)) (add-to-invisibility-spec '(org-cwidth)) (when (fboundp 'font-lock-add-keywords) (font-lock-add-keywords nil orgtbl-extra-font-lock-keywords) @@ -4188,27 +4400,26 @@ to execute outside of tables." cmd (orgtbl-make-binding fun nfunc key)) (org-defkey orgtbl-mode-map key cmd)) - ;; Special treatment needed for TAB and RET + ;; Special treatment needed for TAB, RET and DEL (org-defkey orgtbl-mode-map [(return)] (orgtbl-make-binding 'orgtbl-ret 100 [(return)] "\C-m")) (org-defkey orgtbl-mode-map "\C-m" (orgtbl-make-binding 'orgtbl-ret 101 "\C-m" [(return)])) - (org-defkey orgtbl-mode-map [(tab)] (orgtbl-make-binding 'orgtbl-tab 102 [(tab)] "\C-i")) (org-defkey orgtbl-mode-map "\C-i" (orgtbl-make-binding 'orgtbl-tab 103 "\C-i" [(tab)])) - (org-defkey orgtbl-mode-map [(shift tab)] (orgtbl-make-binding 'org-table-previous-field 104 [(shift tab)] [(tab)] "\C-i")) + (org-defkey orgtbl-mode-map [backspace] + (orgtbl-make-binding 'org-delete-backward-char 109 + [backspace] (kbd "DEL"))) - - (unless (featurep 'xemacs) - (org-defkey orgtbl-mode-map [S-iso-lefttab] - (orgtbl-make-binding 'org-table-previous-field 107 - [S-iso-lefttab] [backtab] [(shift tab)] - [(tab)] "\C-i"))) + (org-defkey orgtbl-mode-map [S-iso-lefttab] + (orgtbl-make-binding 'org-table-previous-field 107 + [S-iso-lefttab] [backtab] [(shift tab)] + [(tab)] "\C-i")) (org-defkey orgtbl-mode-map [backtab] (orgtbl-make-binding 'org-table-previous-field 108 @@ -4290,7 +4501,10 @@ to execute outside of tables." org-table-toggle-coordinate-overlays :active (org-at-table-p) :keys "C-c }" :style toggle :selected org-table-overlay-coordinates] - )) + "--" + ("Plot" + ["Ascii plot" orgtbl-ascii-plot :active (org-at-table-p) :keys "C-c \" a"] + ["Gnuplot" org-plot/gnuplot :active (org-at-table-p) :keys "C-c \" g"]))) t)) (defun orgtbl-ctrl-c-ctrl-c (arg) @@ -4316,7 +4530,6 @@ With prefix arg, also recompute table." (when (orgtbl-send-table 'maybe) (run-hooks 'orgtbl-after-send-table-hook))) ((eq action 'recalc) - (org-table-set-constants) (save-excursion (beginning-of-line 1) (skip-chars-backward " \r\n\t") @@ -4325,7 +4538,7 @@ With prefix arg, also recompute table." (t (let (orgtbl-mode) (call-interactively (key-binding "\C-c\C-c"))))))) -(defun orgtbl-create-or-convert-from-region (arg) +(defun orgtbl-create-or-convert-from-region (_arg) "Create table or convert region to table, if no conflicting binding. This installs the table binding `C-c |', but only if there is no conflicting binding to this key outside orgtbl-mode." @@ -4369,11 +4582,9 @@ overwritten, and the table is not marked as requiring realignment." (org-table-blank-field)) t) (eq N 1) - (looking-at "[^|\n]* +|")) + (looking-at "[^|\n]* \\( \\)|")) (let (org-table-may-need-update) - (goto-char (1- (match-end 0))) - (org-delete-backward-char 1) - (goto-char (match-beginning 0)) + (delete-region (match-beginning 1) (match-end 1)) (self-insert-command N)) (setq org-table-may-need-update t) (let* (orgtbl-mode @@ -4398,6 +4609,7 @@ overwritten, and the table is not marked as requiring realignment." (setq org-self-insert-command-undo-counter (1+ org-self-insert-command-undo-counter)))))))) +;;;###autoload (defvar orgtbl-exp-regexp "^\\([-+]?[0-9][0-9.]*\\)[eE]\\([-+]?[0-9]+\\)$" "Regular expression matching exponentials as produced by calc.") @@ -4418,23 +4630,24 @@ a radio table." (beginning-of-line 0))) rtn))) -(defun orgtbl-send-replace-tbl (name txt) - "Find and replace table NAME with TXT." +(defun orgtbl-send-replace-tbl (name text) + "Find and replace table NAME with TEXT." (save-excursion (goto-char (point-min)) - (unless (re-search-forward - (concat "BEGIN +RECEIVE +ORGTBL +" name "\\([ \t]\\|$\\)") nil t) - (user-error "Don't know where to insert translated table")) - (goto-char (match-beginning 0)) - (beginning-of-line 2) - (save-excursion - (let ((beg (point))) - (unless (re-search-forward - (concat "END +RECEIVE +ORGTBL +" name) nil t) - (user-error "Cannot find end of insertion region")) - (beginning-of-line 1) - (delete-region beg (point)))) - (insert txt "\n"))) + (let* ((location-flag nil) + (name (regexp-quote name)) + (begin-re (format "BEGIN +RECEIVE +ORGTBL +%s\\([ \t]\\|$\\)" name)) + (end-re (format "END +RECEIVE +ORGTBL +%s\\([ \t]\\|$\\)" name))) + (while (re-search-forward begin-re nil t) + (unless location-flag (setq location-flag t)) + (let ((beg (line-beginning-position 2))) + (unless (re-search-forward end-re nil t) + (user-error "Cannot find end of receiver location at %d" beg)) + (beginning-of-line) + (delete-region beg (point)) + (insert text "\n"))) + (unless location-flag + (user-error "No valid receiver location found in the buffer"))))) ;;;###autoload (defun org-table-to-lisp (&optional txt) @@ -4442,76 +4655,43 @@ a radio table." The structure will be a list. Each item is either the symbol `hline' for a horizontal separator line, or a list of field values as strings. The table is taken from the parameter TXT, or from the buffer at point." - (unless txt - (unless (org-at-table-p) - (user-error "No table at point"))) - (let* ((txt (or txt - (buffer-substring-no-properties (org-table-begin) - (org-table-end)))) - (lines (org-split-string txt "[ \t]*\n[ \t]*"))) - - (mapcar - (lambda (x) - (if (string-match org-table-hline-regexp x) - 'hline - (org-split-string (org-trim x) "\\s-*|\\s-*"))) - lines))) + (unless (or txt (org-at-table-p)) (user-error "No table at point")) + (let ((txt (or txt + (buffer-substring-no-properties (org-table-begin) + (org-table-end))))) + (mapcar (lambda (x) + (if (string-match org-table-hline-regexp x) 'hline + (org-split-string (org-trim x) "\\s-*|\\s-*"))) + (org-split-string txt "[ \t]*\n[ \t]*")))) (defun orgtbl-send-table (&optional maybe) - "Send a transformed version of this table to the receiver position. -With argument MAYBE, fail quietly if no transformation is defined for -this table." + "Send a transformed version of table at point to the receiver position. +With argument MAYBE, fail quietly if no transformation is defined +for this table." (interactive) (catch 'exit (unless (org-at-table-p) (user-error "Not at a table")) ;; when non-interactive, we assume align has just happened. - (when (org-called-interactively-p 'any) (org-table-align)) + (when (called-interactively-p 'any) (org-table-align)) (let ((dests (orgtbl-gather-send-defs)) - (txt (buffer-substring-no-properties (org-table-begin) - (org-table-end))) + (table (org-table-to-lisp + (buffer-substring-no-properties (org-table-begin) + (org-table-end)))) (ntbl 0)) - (unless dests (if maybe (throw 'exit nil) - (user-error "Don't know how to transform this table"))) + (unless dests + (if maybe (throw 'exit nil) + (user-error "Don't know how to transform this table"))) (dolist (dest dests) - (let* ((name (plist-get dest :name)) - (transform (plist-get dest :transform)) - (params (plist-get dest :params)) - (skip (plist-get params :skip)) - (skipcols (plist-get params :skipcols)) - (no-escape (plist-get params :no-escape)) - beg - (lines (org-table-clean-before-export - (nthcdr (or skip 0) - (org-split-string txt "[ \t]*\n[ \t]*")))) - (i0 (if org-table-clean-did-remove-column 2 1)) - (lines (if no-escape lines - (mapcar (lambda(l) (replace-regexp-in-string - "\\([&%#_^]\\)" "\\\\\\1{}" l)) lines))) - (table (mapcar - (lambda (x) - (if (string-match org-table-hline-regexp x) - 'hline - (org-remove-by-index - (org-split-string (org-trim x) "\\s-*|\\s-*") - skipcols i0))) - lines)) - (fun (if (= i0 2) 'cdr 'identity)) - (org-table-last-alignment - (org-remove-by-index (funcall fun org-table-last-alignment) - skipcols i0)) - (org-table-last-column-widths - (org-remove-by-index (funcall fun org-table-last-column-widths) - skipcols i0)) - (txt (if (fboundp transform) - (funcall transform table params) - (user-error "No such transformation function %s" transform)))) - (orgtbl-send-replace-tbl name txt)) - (setq ntbl (1+ ntbl))) + (let ((name (plist-get dest :name)) + (transform (plist-get dest :transform)) + (params (plist-get dest :params))) + (unless (fboundp transform) + (user-error "No such transformation function %s" transform)) + (orgtbl-send-replace-tbl name (funcall transform table params))) + (cl-incf ntbl)) (message "Table converted and installed at %d receiver location%s" ntbl (if (> ntbl 1) "s" "")) - (if (> ntbl 0) - ntbl - nil)))) + (and (> ntbl 0) ntbl)))) (defun org-remove-by-index (list indices &optional i0) "Remove the elements in LIST with indices in INDICES. @@ -4561,356 +4741,512 @@ First element has index 0, or I0 if given." (insert txt) (goto-char pos))) -;; Dynamically bound input and output for table formatting. -(defvar *orgtbl-table* nil - "Carries the current table through formatting routines.") -(defvar *orgtbl-rtn* nil - "Formatting routines push the output lines here.") -;; Formatting parameters for the current table section. -(defvar *orgtbl-hline* nil "Text used for horizontal lines.") -(defvar *orgtbl-sep* nil "Text used as a column separator.") -(defvar *orgtbl-default-fmt* nil "Default format for each entry.") -(defvar *orgtbl-fmt* nil "Format for each entry.") -(defvar *orgtbl-efmt* nil "Format for numbers.") -(defvar *orgtbl-lfmt* nil "Format for an entire line, overrides fmt.") -(defvar *orgtbl-llfmt* nil "Specializes lfmt for the last row.") -(defvar *orgtbl-lstart* nil "Text starting a row.") -(defvar *orgtbl-llstart* nil "Specializes lstart for the last row.") -(defvar *orgtbl-lend* nil "Text ending a row.") -(defvar *orgtbl-llend* nil "Specializes lend for the last row.") - -(defsubst orgtbl-get-fmt (fmt i) - "Retrieve the format from FMT corresponding to the Ith column." - (if (and (not (functionp fmt)) (consp fmt)) - (plist-get fmt i) - fmt)) - -(defsubst orgtbl-apply-fmt (fmt &rest args) - "Apply format FMT to arguments ARGS. -When FMT is nil, return the first argument from ARGS." - (cond ((functionp fmt) (apply fmt args)) - (fmt (apply 'format fmt args)) - (args (car args)) - (t args))) - -(defsubst orgtbl-eval-str (str) - "If STR is a function, evaluate it with no arguments." - (if (functionp str) - (funcall str) - str)) - -(defun orgtbl-format-line (line) - "Format LINE as a table row." - (if (eq line 'hline) (if *orgtbl-hline* (push *orgtbl-hline* *orgtbl-rtn*)) - (let* ((i 0) - (line - (mapcar - (lambda (f) - (setq i (1+ i)) - (let* ((efmt (orgtbl-get-fmt *orgtbl-efmt* i)) - (f (if (and efmt (string-match orgtbl-exp-regexp f)) - (orgtbl-apply-fmt efmt (match-string 1 f) - (match-string 2 f)) - f))) - (orgtbl-apply-fmt (or (orgtbl-get-fmt *orgtbl-fmt* i) - *orgtbl-default-fmt*) - f))) - line))) - (push (if *orgtbl-lfmt* - (apply #'orgtbl-apply-fmt *orgtbl-lfmt* line) - (concat (orgtbl-eval-str *orgtbl-lstart*) - (mapconcat 'identity line *orgtbl-sep*) - (orgtbl-eval-str *orgtbl-lend*))) - *orgtbl-rtn*)))) - -(defun orgtbl-format-section (section-stopper) - "Format lines until the first occurrence of SECTION-STOPPER." - (let (prevline) - (progn - (while (not (eq (car *orgtbl-table*) section-stopper)) - (if prevline (orgtbl-format-line prevline)) - (setq prevline (pop *orgtbl-table*))) - (if prevline (let ((*orgtbl-lstart* *orgtbl-llstart*) - (*orgtbl-lend* *orgtbl-llend*) - (*orgtbl-lfmt* *orgtbl-llfmt*)) - (orgtbl-format-line prevline)))))) - ;;;###autoload -(defun orgtbl-to-generic (table params &optional backend) +(defun orgtbl-to-generic (table params) "Convert the orgtbl-mode TABLE to some other format. + This generic routine can be used for many standard cases. -TABLE is a list, each entry either the symbol `hline' for a horizontal -separator line, or a list of fields for that line. -PARAMS is a property list of parameters that can influence the conversion. -A third optional argument BACKEND can be used to convert the content of -the cells using a specific export back-end. -For the generic converter, some parameters are obligatory: you need to -specify either :lfmt, or all of (:lstart :lend :sep). +TABLE is a list, each entry either the symbol `hline' for +a horizontal separator line, or a list of fields for that +line. PARAMS is a property list of parameters that can +influence the conversion. Valid parameters are: -:splice When set to t, return only table body lines, don't wrap - them into :tstart and :tend. Default is nil. When :splice - is non-nil, this also means that the exporter should not look - for and interpret header and footer sections. +:backend, :raw + + Export back-end used as a basis to transcode elements of the + table, when no specific parameter applies to it. It is also + used to translate cells contents. You can prevent this by + setting :raw property to a non-nil value. -:hline String to be inserted on horizontal separation lines. - May be nil to ignore hlines. +:splice -:sep Separator between two fields -:remove-nil-lines Do not include lines that evaluate to nil. + When non-nil, only convert rows, not the table itself. This is + equivalent to setting to the empty string both :tstart + and :tend, which see. + +:skip + + When set to an integer N, skip the first N lines of the table. + Horizontal separation lines do count for this parameter! + +:skipcols + + List of columns that should be skipped. If the table has + a column with calculation marks, that column is automatically + discarded beforehand. + +:hline + + String to be inserted on horizontal separation lines. May be + nil to ignore these lines altogether. + +:sep + + Separator between two fields, as a string. Each in the following group may be either a string or a function of no arguments returning a string: -:tstart String to start the table. Ignored when :splice is t. -:tend String to end the table. Ignored when :splice is t. -:lstart String to start a new table line. -:llstart String to start the last table line, defaults to :lstart. -:lend String to end a table line -:llend String to end the last table line, defaults to :lend. - -Each in the following group may be a string, a function of one -argument (the field or line) returning a string, or a plist -mapping columns to either of the above: - -:lfmt Format for entire line, with enough %s to capture all fields. - If this is present, :lstart, :lend, and :sep are ignored. -:llfmt Format for the entire last line, defaults to :lfmt. -:fmt A format to be used to wrap the field, should contain - %s for the original field value. For example, to wrap - everything in dollars, you could use :fmt \"$%s$\". - This may also be a property list with column numbers and - formats. For example :fmt (2 \"$%s$\" 4 \"%s%%\") -:hlstart :hllstart :hlend :hllend :hlsep :hlfmt :hllfmt :hfmt - Same as above, specific for the header lines in the table. - All lines before the first hline are treated as header. - If any of these is not present, the data line value is used. +:tstart, :tend + + Strings to start and end the table. Ignored when :splice is t. + +:lstart, :lend + + Strings to start and end a new table line. + +:llstart, :llend + + Strings to start and end the last table line. Default, + respectively, to :lstart and :lend. + +Each in the following group may be a string or a function of one +argument (either the cells in the current row, as a list of +strings, or the current cell) returning a string: + +:lfmt + + Format string for an entire row, with enough %s to capture all + fields. When non-nil, :lstart, :lend, and :sep are ignored. + +:llfmt + + Format for the entire last line, defaults to :lfmt. + +:fmt + + A format to be used to wrap the field, should contain %s for + the original field value. For example, to wrap everything in + dollars, you could use :fmt \"$%s$\". This may also be + a property list with column numbers and format strings, or + functions, e.g., + + (:fmt (2 \"$%s$\" 4 (lambda (c) (format \"$%s$\" c)))) + +:hlstart :hllstart :hlend :hllend :hsep :hlfmt :hllfmt :hfmt + + Same as above, specific for the header lines in the table. + All lines before the first hline are treated as header. If + any of these is not present, the data line value is used. This may be either a string or a function of two arguments: -:efmt Use this format to print numbers with exponentials. - The format should have %s twice for inserting mantissa - and exponent, for example \"%s\\\\times10^{%s}\". This - may also be a property list with column numbers and - formats. :fmt will still be applied after :efmt. - -In addition to this, the parameters :skip and :skipcols are always handled -directly by `orgtbl-send-table'. See manual." - (let* ((splicep (plist-get params :splice)) - (hline (plist-get params :hline)) - (skipheadrule (plist-get params :skipheadrule)) - (remove-nil-linesp (plist-get params :remove-nil-lines)) - (remove-newlines (plist-get params :remove-newlines)) - (*orgtbl-hline* hline) - (*orgtbl-table* table) - (*orgtbl-sep* (plist-get params :sep)) - (*orgtbl-efmt* (plist-get params :efmt)) - (*orgtbl-lstart* (plist-get params :lstart)) - (*orgtbl-llstart* (or (plist-get params :llstart) *orgtbl-lstart*)) - (*orgtbl-lend* (plist-get params :lend)) - (*orgtbl-llend* (or (plist-get params :llend) *orgtbl-lend*)) - (*orgtbl-lfmt* (plist-get params :lfmt)) - (*orgtbl-llfmt* (or (plist-get params :llfmt) *orgtbl-lfmt*)) - (*orgtbl-fmt* (plist-get params :fmt)) - *orgtbl-rtn*) - ;; Convert cells content to backend BACKEND - (when backend - (setq *orgtbl-table* - (mapcar - (lambda(r) - (if (listp r) - (mapcar - (lambda (c) - (org-trim (org-export-string-as c backend t '(:with-tables t)))) - r) - r)) - *orgtbl-table*))) - ;; Put header - (unless splicep - (when (plist-member params :tstart) - (let ((tstart (orgtbl-eval-str (plist-get params :tstart)))) - (if tstart (push tstart *orgtbl-rtn*))))) - ;; If we have a heading, format it and handle the trailing hline. - (if (and (not splicep) - (or (consp (car *orgtbl-table*)) - (consp (nth 1 *orgtbl-table*))) - (memq 'hline (cdr *orgtbl-table*))) - (progn - (when (eq 'hline (car *orgtbl-table*)) - ;; There is a hline before the first data line - (and hline (push hline *orgtbl-rtn*)) - (pop *orgtbl-table*)) - (let* ((*orgtbl-lstart* (or (plist-get params :hlstart) - *orgtbl-lstart*)) - (*orgtbl-llstart* (or (plist-get params :hllstart) - *orgtbl-llstart*)) - (*orgtbl-lend* (or (plist-get params :hlend) *orgtbl-lend*)) - (*orgtbl-llend* (or (plist-get params :hllend) - (plist-get params :hlend) *orgtbl-llend*)) - (*orgtbl-lfmt* (or (plist-get params :hlfmt) *orgtbl-lfmt*)) - (*orgtbl-llfmt* (or (plist-get params :hllfmt) - (plist-get params :hlfmt) *orgtbl-llfmt*)) - (*orgtbl-sep* (or (plist-get params :hlsep) *orgtbl-sep*)) - (*orgtbl-fmt* (or (plist-get params :hfmt) *orgtbl-fmt*))) - (orgtbl-format-section 'hline)) - (if (and hline (not skipheadrule)) (push hline *orgtbl-rtn*)) - (pop *orgtbl-table*))) - ;; Now format the main section. - (orgtbl-format-section nil) - (unless splicep - (when (plist-member params :tend) - (let ((tend (orgtbl-eval-str (plist-get params :tend)))) - (if tend (push tend *orgtbl-rtn*))))) - (mapconcat (if remove-newlines - (lambda (tend) - (replace-regexp-in-string "[\n\r\t\f]" "\\\\n" tend)) - 'identity) - (nreverse (if remove-nil-linesp - (remq nil *orgtbl-rtn*) - *orgtbl-rtn*)) "\n"))) +:efmt + + Use this format to print numbers with exponential. The format + should have %s twice for inserting mantissa and exponent, for + example \"%s\\\\times10^{%s}\". This may also be a property + list with column numbers and format strings or functions. + :fmt will still be applied after :efmt." + ;; Make sure `org-export-create-backend' is available. + (require 'ox) + (let* ((backend (plist-get params :backend)) + (custom-backend + ;; Build a custom back-end according to PARAMS. Before + ;; defining a translator, check if there is anything to do. + ;; When there isn't, let BACKEND handle the element. + (org-export-create-backend + :parent (or backend 'org) + :transcoders + `((table . ,(org-table--to-generic-table params)) + (table-row . ,(org-table--to-generic-row params)) + (table-cell . ,(org-table--to-generic-cell params)) + ;; Macros are not going to be expanded. However, no + ;; regular back-end has a transcoder for them. We + ;; provide one so they are not ignored, but displayed + ;; as-is instead. + (macro . (lambda (m c i) (org-element-macro-interpreter m nil)))))) + data info) + ;; Store TABLE as Org syntax in DATA. Tolerate non-string cells. + ;; Initialize communication channel in INFO. + (with-temp-buffer + (let ((org-inhibit-startup t)) (org-mode)) + (let ((standard-output (current-buffer))) + (dolist (e table) + (cond ((eq e 'hline) (princ "|--\n")) + ((consp e) + (princ "| ") (dolist (c e) (princ c) (princ " |")) + (princ "\n"))))) + ;; Add back-end specific filters, but not user-defined ones. In + ;; particular, make sure to call parse-tree filters on the + ;; table. + (setq info + (let ((org-export-filters-alist nil)) + (org-export-install-filters + (org-combine-plists + (org-export-get-environment backend nil params) + `(:back-end ,(org-export-get-backend backend)))))) + (setq data + (org-export-filter-apply-functions + (plist-get info :filter-parse-tree) + (org-element-map (org-element-parse-buffer) 'table + #'identity nil t) + info))) + (when (and backend (symbolp backend) (not (org-export-get-backend backend))) + (user-error "Unknown :backend value")) + (when (or (not backend) (plist-get info :raw)) (require 'ox-org)) + ;; Handle :skip parameter. + (let ((skip (plist-get info :skip))) + (when skip + (unless (wholenump skip) (user-error "Wrong :skip value")) + (let ((n 0)) + (org-element-map data 'table-row + (lambda (row) + (if (>= n skip) t + (org-element-extract-element row) + (cl-incf n) + nil)) + nil t)))) + ;; Handle :skipcols parameter. + (let ((skipcols (plist-get info :skipcols))) + (when skipcols + (unless (consp skipcols) (user-error "Wrong :skipcols value")) + (org-element-map data 'table + (lambda (table) + (let ((specialp (org-export-table-has-special-column-p table))) + (dolist (row (org-element-contents table)) + (when (eq (org-element-property :type row) 'standard) + (let ((c 1)) + (dolist (cell (nthcdr (if specialp 1 0) + (org-element-contents row))) + (when (memq c skipcols) + (org-element-extract-element cell)) + (cl-incf c)))))))))) + ;; Since we are going to export using a low-level mechanism, + ;; ignore special column and special rows manually. + (let ((special? (org-export-table-has-special-column-p data)) + ignore) + (org-element-map data (if special? '(table-cell table-row) 'table-row) + (lambda (datum) + (when (if (eq (org-element-type datum) 'table-row) + (org-export-table-row-is-special-p datum nil) + (org-export-first-sibling-p datum nil)) + (push datum ignore)))) + (setq info (plist-put info :ignore-list ignore))) + ;; We use a low-level mechanism to export DATA so as to skip all + ;; usual pre-processing and post-processing, i.e., hooks, Babel + ;; code evaluation, include keywords and macro expansion. Only + ;; back-end specific filters are retained. + (let ((output (org-export-data-with-backend data custom-backend info))) + ;; Remove final newline. + (if (org-string-nw-p output) (substring-no-properties output 0 -1) "")))) + +(defun org-table--generic-apply (value name &optional with-cons &rest args) + (cond ((null value) nil) + ((functionp value) `(funcall ',value ,@args)) + ((stringp value) + (cond ((consp (car args)) `(apply #'format ,value ,@args)) + (args `(format ,value ,@args)) + (t value))) + ((and with-cons (consp value)) + `(let ((val (cadr (memq column ',value)))) + (cond ((null val) contents) + ((stringp val) (format val ,@args)) + ((functionp val) (funcall val ,@args)) + (t (user-error "Wrong %s value" ,name))))) + (t (user-error "Wrong %s value" name)))) + +(defun org-table--to-generic-table (params) + "Return custom table transcoder according to PARAMS. +PARAMS is a plist. See `orgtbl-to-generic' for more +information." + (let ((backend (plist-get params :backend)) + (splice (plist-get params :splice)) + (tstart (plist-get params :tstart)) + (tend (plist-get params :tend))) + `(lambda (table contents info) + (concat + ,(and tstart (not splice) + `(concat ,(org-table--generic-apply tstart ":tstart") "\n")) + ,(if (or (not backend) tstart tend splice) 'contents + `(org-export-with-backend ',backend table contents info)) + ,(org-table--generic-apply (and (not splice) tend) ":tend"))))) + +(defun org-table--to-generic-row (params) + "Return custom table row transcoder according to PARAMS. +PARAMS is a plist. See `orgtbl-to-generic' for more +information." + (let* ((backend (plist-get params :backend)) + (lstart (plist-get params :lstart)) + (llstart (plist-get params :llstart)) + (hlstart (plist-get params :hlstart)) + (hllstart (plist-get params :hllstart)) + (lend (plist-get params :lend)) + (llend (plist-get params :llend)) + (hlend (plist-get params :hlend)) + (hllend (plist-get params :hllend)) + (lfmt (plist-get params :lfmt)) + (llfmt (plist-get params :llfmt)) + (hlfmt (plist-get params :hlfmt)) + (hllfmt (plist-get params :hllfmt))) + `(lambda (row contents info) + (if (eq (org-element-property :type row) 'rule) + ,(cond + ((plist-member params :hline) + (org-table--generic-apply (plist-get params :hline) ":hline")) + (backend `(org-export-with-backend ',backend row nil info))) + (let ((headerp (org-export-table-row-in-header-p row info)) + (lastp (not (org-export-get-next-element row info))) + (last-header-p (org-export-table-row-ends-header-p row info))) + (when contents + ;; Check if we can apply `:lfmt', `:llfmt', `:hlfmt', or + ;; `:hllfmt' to CONTENTS. Otherwise, fallback on + ;; `:lstart', `:lend' and their relatives. + ,(let ((cells + '(org-element-map row 'table-cell + (lambda (cell) + ;; Export all cells, without separators. + ;; + ;; Use `org-export-data-with-backend' + ;; instead of `org-export-data' to eschew + ;; cached values, which + ;; ignore :orgtbl-ignore-sep parameter. + (org-export-data-with-backend + cell + (plist-get info :back-end) + (org-combine-plists info '(:orgtbl-ignore-sep t)))) + info))) + `(cond + ,(and hllfmt + `(last-header-p ,(org-table--generic-apply + hllfmt ":hllfmt" nil cells))) + ,(and hlfmt + `(headerp ,(org-table--generic-apply + hlfmt ":hlfmt" nil cells))) + ,(and llfmt + `(lastp ,(org-table--generic-apply + llfmt ":llfmt" nil cells))) + (t + ,(if lfmt (org-table--generic-apply lfmt ":lfmt" nil cells) + `(concat + (cond + ,(and + (or hllstart hllend) + `(last-header-p + (concat + ,(org-table--generic-apply hllstart ":hllstart") + contents + ,(org-table--generic-apply hllend ":hllend")))) + ,(and + (or hlstart hlend) + `(headerp + (concat + ,(org-table--generic-apply hlstart ":hlstart") + contents + ,(org-table--generic-apply hlend ":hlend")))) + ,(and + (or llstart llend) + `(lastp + (concat + ,(org-table--generic-apply llstart ":llstart") + contents + ,(org-table--generic-apply llend ":llend")))) + (t + ,(cond + ((or lstart lend) + `(concat + ,(org-table--generic-apply lstart ":lstart") + contents + ,(org-table--generic-apply lend ":lend"))) + (backend + `(org-export-with-backend + ',backend row contents info)) + (t 'contents))))))))))))))) + +(defun org-table--to-generic-cell (params) + "Return custom table cell transcoder according to PARAMS. +PARAMS is a plist. See `orgtbl-to-generic' for more +information." + (let* ((backend (plist-get params :backend)) + (efmt (plist-get params :efmt)) + (fmt (plist-get params :fmt)) + (hfmt (plist-get params :hfmt)) + (sep (plist-get params :sep)) + (hsep (plist-get params :hsep))) + `(lambda (cell contents info) + (let ((headerp (org-export-table-row-in-header-p + (org-export-get-parent-element cell) info)) + (column (1+ (cdr (org-export-table-cell-address cell info))))) + ;; Make sure that contents are exported as Org data when :raw + ;; parameter is non-nil. + ,(when (and backend (plist-get params :raw)) + `(setq contents + ;; Since we don't know what are the pseudo object + ;; types defined in backend, we cannot pass them to + ;; `org-element-interpret-data'. As a consequence, + ;; they will be treated as pseudo elements, and + ;; will have newlines appended instead of spaces. + ;; Therefore, we must make sure :post-blank value + ;; is really turned into spaces. + (replace-regexp-in-string + "\n" " " + (org-trim + (org-element-interpret-data + (org-element-contents cell)))))) + (when contents + ;; Check if we can apply `:efmt' on CONTENTS. + ,(when efmt + `(when (string-match orgtbl-exp-regexp contents) + (let ((mantissa (match-string 1 contents)) + (exponent (match-string 2 contents))) + (setq contents ,(org-table--generic-apply + efmt ":efmt" t 'mantissa 'exponent))))) + ;; Check if we can apply FMT (or HFMT) on CONTENTS. + (cond + ,(and hfmt `(headerp (setq contents ,(org-table--generic-apply + hfmt ":hfmt" t 'contents)))) + ,(and fmt `(t (setq contents ,(org-table--generic-apply + fmt ":fmt" t 'contents)))))) + ;; If a separator is provided, use it instead of BACKEND's. + ;; Separators are ignored when LFMT (or equivalent) is + ;; provided. + ,(cond + ((or hsep sep) + `(if (or ,(and (not sep) '(not headerp)) + (plist-get info :orgtbl-ignore-sep) + (not (org-export-get-next-element cell info))) + ,(if (not backend) 'contents + `(org-export-with-backend ',backend cell contents info)) + (concat contents + ,(if (and sep hsep) `(if headerp ,hsep ,sep) + (or hsep sep))))) + (backend `(org-export-with-backend ',backend cell contents info)) + (t 'contents)))))) ;;;###autoload (defun orgtbl-to-tsv (table params) "Convert the orgtbl-mode table to TAB separated material." (orgtbl-to-generic table (org-combine-plists '(:sep "\t") params))) + ;;;###autoload (defun orgtbl-to-csv (table params) "Convert the orgtbl-mode table to CSV material. This does take care of the proper quoting of fields with comma or quotes." - (orgtbl-to-generic table (org-combine-plists - '(:sep "," :fmt org-quote-csv-field) - params))) + (orgtbl-to-generic table + (org-combine-plists '(:sep "," :fmt org-quote-csv-field) + params))) ;;;###autoload (defun orgtbl-to-latex (table params) "Convert the orgtbl-mode TABLE to LaTeX. -TABLE is a list, each entry either the symbol `hline' for a horizontal -separator line, or a list of fields for that line. -PARAMS is a property list of parameters that can influence the conversion. -Supports all parameters from `orgtbl-to-generic'. Most important for -LaTeX are: - -:splice When set to t, return only table body lines, don't wrap - them into a tabular environment. Default is nil. - -:fmt A format to be used to wrap the field, should contain %s for the - original field value. For example, to wrap everything in dollars, - use :fmt \"$%s$\". This may also be a property list with column - numbers and formats. For example :fmt (2 \"$%s$\" 4 \"%s%%\") - The format may also be a function that formats its one argument. - -:efmt Format for transforming numbers with exponentials. The format - should have %s twice for inserting mantissa and exponent, for - example \"%s\\\\times10^{%s}\". LaTeX default is \"%s\\\\,(%s)\". - This may also be a property list with column numbers and formats. - The format may also be a function that formats its two arguments. - -:llend If you find too much space below the last line of a table, - pass a value of \"\" for :llend to suppress the final \\\\. - -The general parameters :skip and :skipcols have already been applied when -this function is called." - (let* ((alignment (mapconcat (lambda (x) (if x "r" "l")) - org-table-last-alignment "")) - (params2 - (list - :tstart (concat "\\begin{tabular}{" alignment "}") - :tend "\\end{tabular}" - :lstart "" :lend " \\\\" :sep " & " - :efmt "%s\\,(%s)" :hline "\\hline"))) - (require 'ox-latex) - (orgtbl-to-generic table (org-combine-plists params2 params) 'latex))) + +TABLE is a list, each entry either the symbol `hline' for +a horizontal separator line, or a list of fields for that line. +PARAMS is a property list of parameters that can influence the +conversion. All parameters from `orgtbl-to-generic' are +supported. It is also possible to use the following ones: + +:booktabs + + When non-nil, use formal \"booktabs\" style. + +:environment + + Specify environment to use, as a string. If you use + \"longtable\", you may also want to specify :language property, + as a string, to get proper continuation strings." + (require 'ox-latex) + (orgtbl-to-generic + table + (org-combine-plists + ;; Provide sane default values. + (list :backend 'latex + :latex-default-table-mode 'table + :latex-tables-centered nil + :latex-tables-booktabs (plist-get params :booktabs) + :latex-table-scientific-notation nil + :latex-default-table-environment + (or (plist-get params :environment) "tabular")) + params))) ;;;###autoload (defun orgtbl-to-html (table params) "Convert the orgtbl-mode TABLE to HTML. -TABLE is a list, each entry either the symbol `hline' for a horizontal -separator line, or a list of fields for that line. -PARAMS is a property list of parameters that can influence the conversion. -Currently this function recognizes the following parameters: -:splice When set to t, return only table body lines, don't wrap - them into a environment. Default is nil. +TABLE is a list, each entry either the symbol `hline' for +a horizontal separator line, or a list of fields for that line. +PARAMS is a property list of parameters that can influence the +conversion. All parameters from `orgtbl-to-generic' are +supported. It is also possible to use the following one: -The general parameters :skip and :skipcols have already been applied when -this function is called. The function does *not* use `orgtbl-to-generic', -so you cannot specify parameters for it." +:attributes + + Attributes and values, as a plist, which will be used in +
tag." (require 'ox-html) - (let ((output (org-export-string-as - (orgtbl-to-orgtbl table nil) 'html t '(:with-tables t)))) - (if (not (plist-get params :splice)) output - (org-trim - (replace-regexp-in-string - "\\`
\n" "" - (replace-regexp-in-string "
\n*\\'" "" output)))))) + (orgtbl-to-generic + table + (org-combine-plists + ;; Provide sane default values. + (list :backend 'html + :html-table-data-tags '("" . "") + :html-table-use-header-tags-for-first-column nil + :html-table-align-individual-fields t + :html-table-row-tags '("" . "") + :html-table-attributes + (if (plist-member params :attributes) + (plist-get params :attributes) + '(:border "2" :cellspacing "0" :cellpadding "6" :rules "groups" + :frame "hsides"))) + params))) ;;;###autoload (defun orgtbl-to-texinfo (table params) - "Convert the orgtbl-mode TABLE to TeXInfo. -TABLE is a list, each entry either the symbol `hline' for a horizontal -separator line, or a list of fields for that line. -PARAMS is a property list of parameters that can influence the conversion. -Supports all parameters from `orgtbl-to-generic'. Most important for -TeXInfo are: - -:splice nil/t When set to t, return only table body lines, don't wrap - them into a multitable environment. Default is nil. - -:fmt fmt A format to be used to wrap the field, should contain - %s for the original field value. For example, to wrap - everything in @kbd{}, you could use :fmt \"@kbd{%s}\". - This may also be a property list with column numbers and - formats. For example :fmt (2 \"@kbd{%s}\" 4 \"@code{%s}\"). - Each format also may be a function that formats its one - argument. - -:cf \"f1 f2..\" The column fractions for the table. By default these - are computed automatically from the width of the columns - under org-mode. - -The general parameters :skip and :skipcols have already been applied when -this function is called." - (let* ((total (float (apply '+ org-table-last-column-widths))) - (colfrac (or (plist-get params :cf) - (mapconcat - (lambda (x) (format "%.3f" (/ (float x) total))) - org-table-last-column-widths " "))) - (params2 - (list - :tstart (concat "@multitable @columnfractions " colfrac) - :tend "@end multitable" - :lstart "@item " :lend "" :sep " @tab " - :hlstart "@headitem "))) - (require 'ox-texinfo) - (orgtbl-to-generic table (org-combine-plists params2 params) 'texinfo))) + "Convert the orgtbl-mode TABLE to Texinfo. + +TABLE is a list, each entry either the symbol `hline' for +a horizontal separator line, or a list of fields for that line. +PARAMS is a property list of parameters that can influence the +conversion. All parameters from `orgtbl-to-generic' are +supported. It is also possible to use the following one: + +:columns + + Column widths, as a string. When providing column fractions, + \"@columnfractions\" command can be omitted." + (require 'ox-texinfo) + (let ((output + (orgtbl-to-generic + table + (org-combine-plists + (list :backend 'texinfo + :texinfo-tables-verbatim nil + :texinfo-table-scientific-notation nil) + params))) + (columns (let ((w (plist-get params :columns))) + (cond ((not w) nil) + ((string-match-p "{\\|@columnfractions " w) w) + (t (concat "@columnfractions " w)))))) + (if (not columns) output + (replace-regexp-in-string + "@multitable \\(.*\\)" columns output t nil 1)))) ;;;###autoload (defun orgtbl-to-orgtbl (table params) "Convert the orgtbl-mode TABLE into another orgtbl-mode table. + +TABLE is a list, each entry either the symbol `hline' for +a horizontal separator line, or a list of fields for that line. +PARAMS is a property list of parameters that can influence the +conversion. All parameters from `orgtbl-to-generic' are +supported. + Useful when slicing one table into many. The :hline, :sep, -:lstart, and :lend provide orgtbl framing. The default nil :tstart -and :tend suppress strings without splicing; they can be set to -provide ORGTBL directives for the generated table." - (let* ((params2 - (list - :remove-newlines t - :tstart nil :tend nil - :hline "|---" - :sep " | " - :lstart "| " - :lend " |")) - (params (org-combine-plists params2 params))) - (with-temp-buffer - (insert (orgtbl-to-generic table params)) - (goto-char (point-min)) - (while (re-search-forward org-table-hline-regexp nil t) - (org-table-align)) - (buffer-substring 1 (buffer-size))))) +:lstart, and :lend provide orgtbl framing. :tstart and :tend can +be set to provide ORGTBL directives for the generated table." + (require 'ox-org) + (orgtbl-to-generic table (org-combine-plists params (list :backend 'org)))) (defun orgtbl-to-table.el (table params) - "Convert the orgtbl-mode TABLE into a table.el table." + "Convert the orgtbl-mode TABLE into a table.el table. +TABLE is a list, each entry either the symbol `hline' for +a horizontal separator line, or a list of fields for that line. +PARAMS is a property list of parameters that can influence the +conversion. All parameters from `orgtbl-to-generic' are +supported." (with-temp-buffer (insert (orgtbl-to-orgtbl table params)) (org-table-align) @@ -4920,19 +5256,137 @@ provide ORGTBL directives for the generated table." (defun orgtbl-to-unicode (table params) "Convert the orgtbl-mode TABLE into a table with unicode characters. -You need the ascii-art-to-unicode.el package for this. You can download -it here: http://gnuvola.org/software/j/aa2u/ascii-art-to-unicode.el." - (with-temp-buffer - (insert (orgtbl-to-table.el table params)) - (goto-char (point-min)) - (if (or (featurep 'ascii-art-to-unicode) - (require 'ascii-art-to-unicode nil t)) - (aa2u) - (unless (delq nil (mapcar (lambda (l) (string-match "aa2u" (car l))) org-stored-links)) - (push '("http://gnuvola.org/software/j/aa2u/ascii-art-to-unicode.el" - "Link to ascii-art-to-unicode.el") org-stored-links)) - (user-error "Please download ascii-art-to-unicode.el (use C-c C-l to insert the link to it)")) - (buffer-string))) + +TABLE is a list, each entry either the symbol `hline' for +a horizontal separator line, or a list of fields for that line. +PARAMS is a property list of parameters that can influence the +conversion. All parameters from `orgtbl-to-generic' are +supported. It is also possible to use the following ones: + +:ascii-art + + When non-nil, use \"ascii-art-to-unicode\" package to translate + the table. You can download it here: + http://gnuvola.org/software/j/aa2u/ascii-art-to-unicode.el. + +:narrow + + When non-nil, narrow columns width than provided width cookie, + using \"=>\" as an ellipsis, just like in an Org mode buffer." + (require 'ox-ascii) + (orgtbl-to-generic + table + (org-combine-plists + (list :backend 'ascii + :ascii-charset 'utf-8 + :ascii-table-widen-columns (not (plist-get params :narrow)) + :ascii-table-use-ascii-art (plist-get params :ascii-art)) + params))) + +;; Put the cursor in a column containing numerical values +;; of an Org table, +;; type C-c " a +;; A new column is added with a bar plot. +;; When the table is refreshed (C-u C-c *), +;; the plot is updated to reflect the new values. + +(defun orgtbl-ascii-draw (value min max &optional width characters) + "Draw an ascii bar in a table. +VALUE is the value to plot, it determines the width of the bar to draw. +MIN is the value that will be displayed as empty (zero width bar). +MAX is the value that will draw a bar filling all the WIDTH. +WIDTH is the span in characters from MIN to MAX. +CHARACTERS is a string that will compose the bar, with shades of grey +from pure white to pure black. It defaults to a 10 characters string +of regular ascii characters." + (let* ((width (ceiling (or width 12))) + (characters (or characters " .:;c!lhVHW")) + (len (1- (length characters))) + (value (float (if (numberp value) + value (string-to-number value)))) + (relative (/ (- value min) (- max min))) + (steps (round (* relative width len)))) + (cond ((< steps 0) "too small") + ((> steps (* width len)) "too large") + (t (let* ((int-division (/ steps len)) + (remainder (- steps (* int-division len)))) + (concat (make-string int-division (elt characters len)) + (string (elt characters remainder)))))))) + +;;;###autoload +(defun orgtbl-ascii-plot (&optional ask) + "Draw an ASCII bar plot in a column. + +With cursor in a column containing numerical values, this function +will draw a plot in a new column. + +ASK, if given, is a numeric prefix to override the default 12 +characters width of the plot. ASK may also be the `\\[universal-argument]' \ +prefix, +which will prompt for the width." + (interactive "P") + (let ((col (org-table-current-column)) + (min 1e999) ; 1e999 will be converted to infinity + (max -1e999) ; which is the desired result + (table (org-table-to-lisp)) + (length + (cond ((consp ask) + (read-number "Length of column " 12)) + ((numberp ask) ask) + (t 12)))) + ;; Skip any hline a the top of table. + (while (eq (car table) 'hline) (setq table (cdr table))) + ;; Skip table header if any. + (dolist (x (or (cdr (memq 'hline table)) table)) + (when (consp x) + (setq x (nth (1- col) x)) + (when (string-match + "^[-+]?\\([0-9]*[.]\\)?[0-9]*\\([eE][+-]?[0-9]+\\)?$" + x) + (setq x (string-to-number x)) + (when (> min x) (setq min x)) + (when (< max x) (setq max x))))) + (org-table-insert-column) + (org-table-move-column-right) + (org-table-store-formulas + (cons + (cons + (concat "$" (number-to-string (1+ col))) + (format "'(%s $%s %s %s %s)" + "orgtbl-ascii-draw" col min max length)) + (org-table-get-stored-formulas))) + (org-table-recalculate t))) + +;; Example of extension: unicode characters +;; Here are two examples of different styles. + +;; Unicode block characters are used to give a smooth effect. +;; See http://en.wikipedia.org/wiki/Block_Elements +;; Use one of those drawing functions +;; - orgtbl-ascii-draw (the default ascii) +;; - orgtbl-uc-draw-grid (unicode with a grid effect) +;; - orgtbl-uc-draw-cont (smooth unicode) + +;; This is best viewed with the "DejaVu Sans Mono" font +;; (use M-x set-default-font). + +(defun orgtbl-uc-draw-grid (value min max &optional width) + "Draw a bar in a table using block unicode characters. +It is a variant of orgtbl-ascii-draw with Unicode block +characters, for a smooth display. Bars appear as grids (to the +extent the font allows)." + ;; http://en.wikipedia.org/wiki/Block_Elements + ;; best viewed with the "DejaVu Sans Mono" font. + (orgtbl-ascii-draw value min max width + " \u258F\u258E\u258D\u258C\u258B\u258A\u2589")) + +(defun orgtbl-uc-draw-cont (value min max &optional width) + "Draw a bar in a table using block unicode characters. +It is a variant of orgtbl-ascii-draw with Unicode block +characters, for a smooth display. Bars are solid (to the extent +the font allows)." + (orgtbl-ascii-draw value min max width + " \u258F\u258E\u258D\u258C\u258B\u258A\u2589\u2588")) (defun org-table-get-remote-range (name-or-id form) "Get a field value or a list of values in a range from table at ID. @@ -4949,57 +5403,74 @@ The return value is either a single string for a single field, or a list of the fields in the rectangle." (save-match-data (let ((case-fold-search t) (id-loc nil) - ;; Protect a bunch of variables from being overwritten - ;; by the context of the remote table + ;; Protect a bunch of variables from being overwritten by + ;; the context of the remote table. org-table-column-names org-table-column-name-regexp org-table-local-parameters org-table-named-field-locations - org-table-current-line-types org-table-current-begin-line + org-table-current-line-types org-table-current-begin-pos org-table-dlines org-table-current-ncol org-table-hlines org-table-last-alignment org-table-last-column-widths org-table-last-alignment - org-table-last-column-widths tbeg + org-table-last-column-widths buffer loc) (setq form (org-table-convert-refs-to-rc form)) - (save-excursion - (save-restriction - (widen) - (save-excursion - (goto-char (point-min)) - (if (re-search-forward - (concat "^[ \t]*#\\+\\(tbl\\)?name:[ \t]*" - (regexp-quote name-or-id) "[ \t]*$") - nil t) - (setq buffer (current-buffer) loc (match-beginning 0)) - (setq id-loc (org-id-find name-or-id 'marker)) - (unless (and id-loc (markerp id-loc)) - (user-error "Can't find remote table \"%s\"" name-or-id)) - (setq buffer (marker-buffer id-loc) - loc (marker-position id-loc)) - (move-marker id-loc nil))) - (with-current-buffer buffer - (save-excursion - (save-restriction - (widen) - (goto-char loc) - (forward-char 1) - (unless (and (re-search-forward "^\\(\\*+ \\)\\|[ \t]*|" nil t) - (not (match-beginning 1))) - (user-error "Cannot find a table at NAME or ID %s" name-or-id)) - (setq tbeg (point-at-bol)) - (org-table-get-specials) - (setq form (org-table-formula-substitute-names - (org-table-formula-handle-first/last-rc form))) - (if (and (string-match org-table-range-regexp form) - (> (length (match-string 0 form)) 1)) - (save-match-data - (org-table-get-range (match-string 0 form) tbeg 1)) - form))))))))) + (org-with-wide-buffer + (goto-char (point-min)) + (if (re-search-forward + (concat "^[ \t]*#\\+\\(tbl\\)?name:[ \t]*" + (regexp-quote name-or-id) "[ \t]*$") + nil t) + (setq buffer (current-buffer) loc (match-beginning 0)) + (setq id-loc (org-id-find name-or-id 'marker)) + (unless (and id-loc (markerp id-loc)) + (user-error "Can't find remote table \"%s\"" name-or-id)) + (setq buffer (marker-buffer id-loc) + loc (marker-position id-loc)) + (move-marker id-loc nil)) + (with-current-buffer buffer + (org-with-wide-buffer + (goto-char loc) + (forward-char 1) + (unless (and (re-search-forward "^\\(\\*+ \\)\\|^[ \t]*|" nil t) + (not (match-beginning 1))) + (user-error "Cannot find a table at NAME or ID %s" name-or-id)) + (org-table-analyze) + (setq form (org-table-formula-substitute-names + (org-table-formula-handle-first/last-rc form))) + (if (and (string-match org-table-range-regexp form) + (> (length (match-string 0 form)) 1)) + (org-table-get-range + (match-string 0 form) org-table-current-begin-pos 1) + form))))))) + +(defun org-table-remote-reference-indirection (form) + "Return formula with table remote references substituted by indirection. +For example \"remote($1, @>$2)\" => \"remote(year_2013, @>$1)\". +This indirection works only with the format @ROW$COLUMN. The +format \"B3\" is not supported because it can not be +distinguished from a plain table name or ID." + (let ((regexp + ;; Same as in `org-table-eval-formula'. + (concat "\\")) (force-mode-line-update))) -(defun org-timer-cancel-timer () - "Cancel the current timer." - (interactive) - (when (eval org-timer-current-timer) - (run-hooks 'org-timer-cancel-hook) - (cancel-timer org-timer-current-timer) - (setq org-timer-current-timer nil) - (setq org-timer-timer-is-countdown nil) - (org-timer-set-mode-line 'off)) - (message "Last timer canceled")) - (defun org-timer-show-remaining-time () "Display the remaining time before the timer ends." (interactive) (require 'time) - (if (not org-timer-current-timer) + (if (not org-timer-countdown-timer) (message "No timer set") (let* ((rtime (decode-time - (time-subtract (timer--time org-timer-current-timer) + (time-subtract (timer--time org-timer-countdown-timer) (current-time)))) (rsecs (nth 0 rtime)) (rmins (nth 1 rtime))) (message "%d minute(s) %d seconds left before next time out" rmins rsecs)))) -(defvar org-clock-sound) - ;;;###autoload (defun org-timer-set-timer (&optional opt) - "Prompt for a duration and set a timer. + "Prompt for a duration in minutes or hh:mm:ss and set a timer. -If `org-timer-default-timer' is not zero, suggest this value as +If `org-timer-default-timer' is not \"0\", suggest this value as the default duration for the timer. If a timer is already set, prompt the user if she wants to replace it. Called with a numeric prefix argument, use this numeric value as -the duration of the timer. +the duration of the timer in minutes. Called with a `C-u' prefix arguments, use `org-timer-default-timer' without prompting the user for a duration. With two `C-u' prefix arguments, use `org-timer-default-timer' without prompting the user for a duration and automatically -replace any running timer." +replace any running timer. + +By default, the timer duration will be set to the number of +minutes in the Effort property, if any. You can ignore this by +using three `C-u' prefix arguments." (interactive "P") - (let ((minutes (or (and (numberp opt) (number-to-string opt)) - (and (listp opt) (not (null opt)) - (number-to-string org-timer-default-timer)) - (read-from-minibuffer - "How many minutes left? " - (if (not (eq org-timer-default-timer 0)) - (number-to-string org-timer-default-timer)))))) + (when (and org-timer-start-time + (not org-timer-countdown-timer)) + (user-error "Relative timer is running. Stop first")) + (let* ((default-timer + ;; `org-timer-default-timer' used to be a number, don't choke: + (if (numberp org-timer-default-timer) + (number-to-string org-timer-default-timer) + org-timer-default-timer)) + (effort-minutes (ignore-errors (org-get-at-eol 'effort-minutes 1))) + (minutes (or (and (numberp opt) (number-to-string opt)) + (and (not (equal opt '(64))) + effort-minutes + (number-to-string effort-minutes)) + (and (consp opt) default-timer) + (and (stringp opt) opt) + (read-from-minibuffer + "How much time left? (minutes or h:mm:ss) " + (and (not (string-equal default-timer "0")) default-timer))))) + (when (string-match "\\`[0-9]+\\'" minutes) + (setq minutes (concat minutes ":00"))) (if (not (string-match "[0-9]+" minutes)) (org-timer-show-remaining-time) - (let* ((mins (string-to-number (match-string 0 minutes))) - (secs (* mins 60)) - (hl (cond - ((string-match "Org Agenda" (buffer-name)) - (let* ((marker (or (get-text-property (point) 'org-marker) - (org-agenda-error))) - (hdmarker (or (get-text-property (point) 'org-hd-marker) - marker)) - (pos (marker-position marker))) - (with-current-buffer (marker-buffer marker) - (widen) - (goto-char pos) - (org-show-entry) - (or (ignore-errors (org-get-heading)) - (concat "File:" (file-name-nondirectory (buffer-file-name))))))) - ((derived-mode-p 'org-mode) - (or (ignore-errors (org-get-heading)) - (concat "File:" (file-name-nondirectory (buffer-file-name))))) - (t (error "Not in an Org buffer")))) - timer-set) - (if (or (and org-timer-current-timer - (or (equal opt '(16)) - (y-or-n-p "Replace current timer? "))) - (not org-timer-current-timer)) - (progn - (require 'org-clock) - (when org-timer-current-timer - (cancel-timer org-timer-current-timer)) - (setq org-timer-current-timer - (run-with-timer - secs nil `(lambda () - (setq org-timer-current-timer nil) - (org-notify ,(format "%s: time out" hl) ,org-clock-sound) - (setq org-timer-timer-is-countdown nil) - (org-timer-set-mode-line 'off) - (run-hooks 'org-timer-done-hook)))) - (run-hooks 'org-timer-set-hook) - (setq org-timer-timer-is-countdown t - org-timer-start-time - (time-add (current-time) (seconds-to-time (* mins 60)))) - (org-timer-set-mode-line 'on)) - (message "No timer set")))))) + (let ((secs (org-timer-hms-to-secs (org-timer-fix-incomplete minutes)))) + (if (and org-timer-countdown-timer + (not (or (equal opt '(16)) + (y-or-n-p "Replace current timer? ")))) + (message "No timer set") + (when (timerp org-timer-countdown-timer) + (cancel-timer org-timer-countdown-timer)) + (setq org-timer-countdown-timer-title + (org-timer--get-timer-title)) + (setq org-timer-countdown-timer + (org-timer--run-countdown-timer + secs org-timer-countdown-timer-title)) + (run-hooks 'org-timer-set-hook) + (setq org-timer-start-time + (time-add (current-time) (seconds-to-time secs))) + (setq org-timer-pause-time nil) + (org-timer-set-mode-line 'on)))))) + +(defun org-timer--run-countdown-timer (secs title) + "Start countdown timer that will last SECS. +TITLE will be appended to the notification message displayed when +time is up." + (let ((msg (format "%s: time out" title))) + (run-with-timer + secs nil `(lambda () + (setq org-timer-countdown-timer nil + org-timer-start-time nil) + (org-notify ,msg ,org-clock-sound) + (org-timer-set-mode-line 'off) + (run-hooks 'org-timer-done-hook))))) + +(defun org-timer--get-timer-title () + "Construct timer title from heading or file name of Org buffer." + (cond + ((derived-mode-p 'org-agenda-mode) + (let* ((marker (or (get-text-property (point) 'org-marker) + (org-agenda-error))) + (hdmarker (or (get-text-property (point) 'org-hd-marker) + marker))) + (with-current-buffer (marker-buffer marker) + (org-with-wide-buffer + (goto-char hdmarker) + (org-show-entry) + (or (ignore-errors (org-get-heading)) + (buffer-name (buffer-base-buffer))))))) + ((derived-mode-p 'org-mode) + (or (ignore-errors (org-get-heading)) + (buffer-name (buffer-base-buffer)))) + (t (error "Not in an Org buffer")))) (provide 'org-timer) diff --git a/lisp/org/org-version.el b/lisp/org/org-version.el index aae65cc6d3..2db3eae2d8 100644 --- a/lisp/org/org-version.el +++ b/lisp/org/org-version.el @@ -3,15 +3,15 @@ ;;; Code: ;;;###autoload (defun org-release () - "The release version of org-mode. - Inserted by installing org-mode or when a release is made." - (let ((org-release "8.2.10")) + "The release version of Org. +Inserted by installing Org mode or when a release is made." + (let ((org-release "9.0.9")) org-release)) ;;;###autoload (defun org-git-version () "The Git version of org-mode. - Inserted by installing org-mode or when a release is made." - (let ((org-git-version "release_8.2.10")) +Inserted by installing Org or when a release is made." + (let ((org-git-version "release_9.0.9")) org-git-version)) (provide 'org-version) diff --git a/lisp/org/org-w3m.el b/lisp/org/org-w3m.el index 8360bd07fe..e9bbeff37c 100644 --- a/lisp/org/org-w3m.el +++ b/lisp/org/org-w3m.el @@ -1,4 +1,4 @@ -;;; org-w3m.el --- Support from copy and paste from w3m to Org-mode +;;; org-w3m.el --- Support from Copy and Paste From w3m -*- lexical-binding: t; -*- ;; Copyright (C) 2008-2017 Free Software Foundation, Inc. @@ -25,9 +25,9 @@ ;;; Commentary: ;; This file implements copying HTML content from a w3m buffer and -;; transforming the text on the fly so that it can be pasted into -;; an org-mode buffer with hot links. It will also work for regions -;; in gnus buffers that have been washed with w3m. +;; transforming the text on the fly so that it can be pasted into an +;; Org buffer with hot links. It will also work for regions in gnus +;; buffers that have been washed with w3m. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; @@ -35,7 +35,7 @@ ;; Richard Riley ;; -;; The idea of transforming the HTML content with org-mode style is +;; The idea of transforming the HTML content with Org syntax is ;; proposed by Richard, I'm just coding it. ;; @@ -46,7 +46,7 @@ (defvar w3m-current-url) (defvar w3m-current-title) -(add-hook 'org-store-link-functions 'org-w3m-store-link) +(org-link-set-parameters "w3m" :store #'org-w3m-store-link) (defun org-w3m-store-link () "Store a link to a w3m buffer." (when (eq major-mode 'w3m-mode) @@ -60,7 +60,7 @@ "Copy current buffer content or active region with `org-mode' style links. This will encode `link-title' and `link-location' with `org-make-link-string', and insert the transformed test into the kill ring, -so that it can be yanked into an Org-mode buffer with links working correctly." +so that it can be yanked into an Org buffer with links working correctly." (interactive) (let* ((regionp (org-region-active-p)) (transform-start (point-min)) @@ -107,7 +107,7 @@ so that it can be yanked into an Org-mode buffer with links working correctly." (concat return-content (buffer-substring (point) transform-end)))) (org-kill-new return-content) - (message "Transforming links...done, use C-y to insert text into Org-mode file") + (message "Transforming links...done, use C-y to insert text into Org file") (message "Copy with link transformation complete.")))) (defun org-w3m-get-anchor-start () diff --git a/lisp/org/org.el b/lisp/org/org.el index 02a7a0c09a..22b7dbfdaf 100644 --- a/lisp/org/org.el +++ b/lisp/org/org.el @@ -1,4 +1,4 @@ -;;; org.el --- Outline-based notes management and organizer +;;; org.el --- Outline-based notes management and organizer -*- lexical-binding: t; -*- ;; Carstens outline-mode for keeping track of everything. ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -25,23 +25,24 @@ ;; ;;; Commentary: ;; -;; Org-mode is a mode for keeping notes, maintaining ToDo lists, and doing +;; Org is a mode for keeping notes, maintaining ToDo lists, and doing ;; project planning with a fast and effective plain-text system. ;; -;; Org-mode develops organizational tasks around NOTES files that contain -;; information about projects as plain text. Org-mode is implemented on -;; top of outline-mode, which makes it possible to keep the content of -;; large files well structured. Visibility cycling and structure editing -;; help to work with the tree. Tables are easily created with a built-in -;; table editor. Org-mode supports ToDo items, deadlines, time stamps, -;; and scheduling. It dynamically compiles entries into an agenda that -;; utilizes and smoothly integrates much of the Emacs calendar and diary. -;; Plain text URL-like links connect to websites, emails, Usenet -;; messages, BBDB entries, and any files related to the projects. For -;; printing and sharing of notes, an Org-mode file can be exported as a -;; structured ASCII file, as HTML, or (todo and agenda items only) as an -;; iCalendar file. It can also serve as a publishing tool for a set of -;; linked webpages. +;; Org mode develops organizational tasks around NOTES files that +;; contain information about projects as plain text. Org mode is +;; implemented on top of outline-mode, which makes it possible to keep +;; the content of large files well structured. Visibility cycling and +;; structure editing help to work with the tree. Tables are easily +;; created with a built-in table editor. Org mode supports ToDo +;; items, deadlines, time stamps, and scheduling. It dynamically +;; compiles entries into an agenda that utilizes and smoothly +;; integrates much of the Emacs calendar and diary. Plain text +;; URL-like links connect to websites, emails, Usenet messages, BBDB +;; entries, and any files related to the projects. For printing and +;; sharing of notes, an Org file can be exported as a structured ASCII +;; file, as HTML, or (todo and agenda items only) as an iCalendar +;; file. It can also serve as a publishing tool for a set of linked +;; webpages. ;; ;; Installation and Activation ;; --------------------------- @@ -51,11 +52,11 @@ ;; ;; Documentation ;; ------------- -;; The documentation of Org-mode can be found in the TeXInfo file. The +;; The documentation of Org mode can be found in the TeXInfo file. The ;; distribution also contains a PDF version of it. At the homepage of -;; Org-mode, you can read the same text online as HTML. There is also an +;; Org mode, you can read the same text online as HTML. There is also an ;; excellent reference card made by Philip Rooke. This card can be found -;; in the etc/ directory of Emacs 22. +;; in the doc/ directory. ;; ;; A list of recent changes can be found at ;; http://orgmode.org/Changes.html @@ -63,21 +64,29 @@ ;;; Code: (defvar org-inhibit-highlight-removal nil) ; dynamically scoped param -(defvar org-table-formula-constants-local nil +(defvar-local org-table-formula-constants-local nil "Local version of `org-table-formula-constants'.") -(make-variable-buffer-local 'org-table-formula-constants-local) ;;;; Require other packages -(eval-when-compile - (require 'cl) - (require 'gnus-sum)) +(require 'cl-lib) + +(eval-when-compile (require 'gnus-sum)) (require 'calendar) (require 'find-func) (require 'format-spec) -(load "org-loaddefs.el" t t t) +(or (eq this-command 'eval-buffer) + (condition-case nil + (load (concat (file-name-directory load-file-name) + "org-loaddefs.el") + nil t t t) + (error + (message "WARNING: No org-loaddefs.el file could be found from where org.el is loaded.") + (sit-for 3) + (message "You need to run \"make\" or \"make autoloads\" from Org lisp directory") + (sit-for 3)))) (require 'org-macs) (require 'org-compat) @@ -101,75 +110,87 @@ sure that we are at the beginning of the line.") "Matches a headline, putting stars and text into groups. Stars are put in group 1 and the trimmed body in group 2.") -;; Emacs 22 calendar compatibility: Make sure the new variables are available -(unless (boundp 'calendar-view-holidays-initially-flag) - (org-defvaralias 'calendar-view-holidays-initially-flag - 'view-calendar-holidays-initially)) -(unless (boundp 'calendar-view-diary-initially-flag) - (org-defvaralias 'calendar-view-diary-initially-flag - 'view-diary-entries-initially)) -(unless (boundp 'diary-fancy-buffer) - (org-defvaralias 'diary-fancy-buffer 'fancy-diary-buffer)) - +(declare-function calendar-check-holidays "holidays" (date)) +(declare-function cdlatex-environment "ext:cdlatex" (environment item)) +(declare-function isearch-no-upper-case-p "isearch" (string regexp-flag)) (declare-function org-add-archive-files "org-archive" (files)) - -(declare-function org-inlinetask-at-task-p "org-inlinetask" ()) -(declare-function org-inlinetask-outline-regexp "org-inlinetask" ()) -(declare-function org-inlinetask-toggle-visibility "org-inlinetask" ()) -(declare-function org-pop-to-buffer-same-window "org-compat" (&optional buffer-or-name norecord label)) +(declare-function org-agenda-entry-get-agenda-timestamp "org-agenda" (pom)) +(declare-function org-agenda-list "org-agenda" (&optional arg start-day span with-hour)) +(declare-function org-agenda-redo "org-agenda" (&optional all)) +(declare-function org-babel-do-in-edit-buffer "ob-core" (&rest body) t) +(declare-function org-babel-tangle-file "ob-tangle" (file &optional target-file lang)) +(declare-function org-beamer-mode "ox-beamer" (&optional prefix) t) (declare-function org-clock-get-last-clock-out-time "org-clock" ()) -(declare-function org-clock-timestamps-up "org-clock" (&optional n)) -(declare-function org-clock-timestamps-down "org-clock" (&optional n)) +(declare-function org-clock-out "org-clock" (&optional switch-to-state fail-quietly at-time)) (declare-function org-clock-remove-overlays "org-clock" (&optional beg end noremove)) +(declare-function org-clock-sum "org-clock" (&optional tstart tend headline-filter propname)) (declare-function org-clock-sum-current-item "org-clock" (&optional tstart)) +(declare-function org-clock-timestamps-down "org-clock" (&optional n)) +(declare-function org-clock-timestamps-up "org-clock" (&optional n)) (declare-function org-clock-update-time-maybe "org-clock" ()) (declare-function org-clocktable-shift "org-clock" (dir n)) - -(declare-function orgtbl-mode "org-table" (&optional arg)) -(declare-function org-clock-out "org-clock" (&optional switch-to-state fail-quietly at-time)) -(declare-function org-beamer-mode "ox-beamer" (&optional prefix) t) -(declare-function org-table-edit-field "org-table" (arg)) -(declare-function org-table-justify-field-maybe "org-table" (&optional new)) -(declare-function org-table-set-constants "org-table" ()) -(declare-function org-table-calc-current-TBLFM "org-table" (&optional arg)) -(declare-function org-id-get-create "org-id" (&optional force)) +(declare-function org-element-at-point "org-element" ()) +(declare-function org-element-cache-refresh "org-element" (pos)) +(declare-function org-element-cache-reset "org-element" (&optional all)) +(declare-function org-element-contents "org-element" (element)) +(declare-function org-element-context "org-element" (&optional element)) +(declare-function org-element-copy "org-element" (datum)) +(declare-function org-element-interpret-data "org-element" (data)) +(declare-function org-element-lineage "org-element" (blob &optional types with-self)) +(declare-function org-element-link-parser "org-element" ()) +(declare-function org-element-nested-p "org-element" (elem-a elem-b)) +(declare-function org-element-parse-buffer "org-element" (&optional granularity visible-only)) +(declare-function org-element-property "org-element" (property element)) +(declare-function org-element-put-property "org-element" (element property value)) +(declare-function org-element-swap-A-B "org-element" (elem-a elem-b)) +(declare-function org-element-type "org-element" (element)) +(declare-function org-element-update-syntax "org-element" ()) (declare-function org-id-find-id-file "org-id" (id)) -(declare-function org-tags-view "org-agenda" (&optional todo-only match)) -(declare-function org-agenda-list "org-agenda" - (&optional arg start-day span with-hour)) -(declare-function org-agenda-redo "org-agenda" (&optional all)) +(declare-function org-id-get-create "org-id" (&optional force)) +(declare-function org-inlinetask-at-task-p "org-inlinetask" ()) +(declare-function org-inlinetask-outline-regexp "org-inlinetask" ()) +(declare-function org-inlinetask-toggle-visibility "org-inlinetask" ()) +(declare-function org-plot/gnuplot "org-plot" (&optional params)) (declare-function org-table-align "org-table" ()) (declare-function org-table-begin "org-table" (&optional table-type)) +(declare-function org-table-beginning-of-field "org-table" (&optional n)) (declare-function org-table-blank-field "org-table" ()) +(declare-function org-table-calc-current-TBLFM "org-table" (&optional arg)) +(declare-function org-table-copy-region "org-table" (beg end &optional cut)) +(declare-function org-table-cut-region "org-table" (beg end)) +(declare-function org-table-edit-field "org-table" (arg)) (declare-function org-table-end "org-table" (&optional table-type)) +(declare-function org-table-end-of-field "org-table" (&optional n)) (declare-function org-table-insert-row "org-table" (&optional arg)) -(declare-function org-table-paste-rectangle "org-table" ()) +(declare-function org-table-justify-field-maybe "org-table" (&optional new)) (declare-function org-table-maybe-eval-formula "org-table" ()) (declare-function org-table-maybe-recalculate-line "org-table" ()) +(declare-function org-table-next-row "org-table" ()) +(declare-function org-table-paste-rectangle "org-table" ()) +(declare-function org-table-recalculate "org-table" (&optional all noalign)) +(declare-function org-table-wrap-region "org-table" (arg)) +(declare-function org-tags-view "org-agenda" (&optional todo-only match)) +(declare-function orgtbl-ascii-plot "org-table" (&optional ask)) +(declare-function orgtbl-mode "org-table" (&optional arg)) +(declare-function org-export-get-backend "ox" (name)) +(declare-function org-export-get-environment "ox" (&optional backend subtreep ext-plist)) +(declare-function org-latex-make-preamble "ox-latex" (info &optional template snippet?)) -(declare-function org-element--parse-objects "org-element" - (beg end acc restriction)) -(declare-function org-element-at-point "org-element" (&optional keep-trail)) -(declare-function org-element-contents "org-element" (element)) -(declare-function org-element-context "org-element" (&optional element)) -(declare-function org-element-interpret-data "org-element" - (data &optional parent)) -(declare-function org-element-map "org-element" - (data types fun &optional - info first-match no-recursion with-affiliated)) -(declare-function org-element-nested-p "org-element" (elem-a elem-b)) -(declare-function org-element-parse-buffer "org-element" - (&optional granularity visible-only)) -(declare-function org-element-property "org-element" (property element)) -(declare-function org-element-put-property "org-element" - (element property value)) -(declare-function org-element-swap-A-B "org-element" (elem-a elem-b)) -(declare-function org-element--parse-objects "org-element" - (beg end acc restriction)) -(declare-function org-element-parse-buffer "org-element" - (&optional granularity visible-only)) -(declare-function org-element-restriction "org-element" (element)) -(declare-function org-element-type "org-element" (element)) +(defsubst org-uniquify (list) + "Non-destructively remove duplicate elements from LIST." + (let ((res (copy-sequence list))) (delete-dups res))) + +(defsubst org-get-at-bol (property) + "Get text property PROPERTY at the beginning of line." + (get-text-property (point-at-bol) property)) + +(defsubst org-trim (s &optional keep-lead) + "Remove whitespace at the beginning and the end of string S. +When optional argument KEEP-LEAD is non-nil, removing blank lines +at the beginning of the string does not affect leading indentation." + (replace-regexp-in-string + (if keep-lead "\\`\\([ \t]*\n\\)+" "\\`[ \t\n\r]+") "" + (replace-regexp-in-string "[ \t\n\r]+\\'" "" s))) ;; load languages based on value of `org-babel-load-languages' (defvar org-babel-load-languages) @@ -178,28 +199,24 @@ Stars are put in group 1 and the trimmed body in group 2.") (defun org-babel-do-load-languages (sym value) "Load the languages defined in `org-babel-load-languages'." (set-default sym value) - (mapc (lambda (pair) - (let ((active (cdr pair)) (lang (symbol-name (car pair)))) - (if active - (progn - (require (intern (concat "ob-" lang)))) - (progn - (funcall 'fmakunbound - (intern (concat "org-babel-execute:" lang))) - (funcall 'fmakunbound - (intern (concat "org-babel-expand-body:" lang))))))) - org-babel-load-languages)) + (dolist (pair org-babel-load-languages) + (let ((active (cdr pair)) (lang (symbol-name (car pair)))) + (if active + (require (intern (concat "ob-" lang))) + (funcall 'fmakunbound + (intern (concat "org-babel-execute:" lang))) + (funcall 'fmakunbound + (intern (concat "org-babel-expand-body:" lang))))))) (declare-function org-babel-tangle-file "ob-tangle" (file &optional target-file lang)) ;;;###autoload (defun org-babel-load-file (file &optional compile) - "Load Emacs Lisp source code blocks in the Org-mode FILE. + "Load Emacs Lisp source code blocks in the Org FILE. This function exports the source code using `org-babel-tangle' and then loads the resulting file using `load-file'. With prefix arg (noninteractively: 2nd arg) COMPILE the tangled Emacs Lisp file to byte-code before it is loaded." (interactive "fFile to load: \nP") - (require 'ob-core) (let* ((age (lambda (file) (float-time (time-subtract (current-time) @@ -207,11 +224,13 @@ file to byte-code before it is loaded." (file-attributes file))))))) (base-name (file-name-sans-extension file)) (exported-file (concat base-name ".el"))) - ;; tangle if the org-mode file is newer than the elisp file + ;; tangle if the Org file is newer than the elisp file (unless (and (file-exists-p exported-file) (> (funcall age file) (funcall age exported-file))) + ;; Tangle-file traversal returns reversed list of tangled files + ;; and we want to evaluate the first target. (setq exported-file - (car (org-babel-tangle-file file exported-file "emacs-lisp")))) + (car (last (org-babel-tangle-file file exported-file "emacs-lisp"))))) (message "%s %s" (if compile (progn (byte-compile-file exported-file 'load) @@ -220,7 +239,7 @@ file to byte-code before it is loaded." exported-file))) (defcustom org-babel-load-languages '((emacs-lisp . t)) - "Languages which can be evaluated in Org-mode buffers. + "Languages which can be evaluated in Org buffers. This list can be used to load support for any of the languages below, note that each language will depend on a different set of system executables and/or Emacs modes. When a language is @@ -246,10 +265,12 @@ requirements) is loaded." (const :tag "Ditaa" ditaa) (const :tag "Dot" dot) (const :tag "Emacs Lisp" emacs-lisp) + (const :tag "Forth" forth) (const :tag "Fortran" fortran) (const :tag "Gnuplot" gnuplot) (const :tag "Haskell" haskell) (const :tag "IO" io) + (const :tag "J" J) (const :tag "Java" java) (const :tag "Javascript" js) (const :tag "LaTeX" latex) @@ -272,10 +293,12 @@ requirements) is loaded." (const :tag "Scala" scala) (const :tag "Scheme" scheme) (const :tag "Screen" screen) - (const :tag "Shell Script" sh) + (const :tag "Shell Script" shell) (const :tag "Shen" shen) (const :tag "Sql" sql) - (const :tag "Sqlite" sqlite)) + (const :tag "Sqlite" sqlite) + (const :tag "Stan" stan) + (const :tag "ebnf2ps" ebnf2ps)) :value-type (boolean :tag "Activate" :value t))) ;;;; Customization variables @@ -293,41 +316,318 @@ identifier." ;;;###autoload (defun org-version (&optional here full message) - "Show the org-mode version in the echo area. -With prefix argument HERE, insert it at point. -When FULL is non-nil, use a verbose version string. -When MESSAGE is non-nil, display a message with the version." - (interactive "P") - (let* ((org-dir (ignore-errors (org-find-library-dir "org"))) - (save-load-suffixes (when (boundp 'load-suffixes) load-suffixes)) - (load-suffixes (list ".el")) - (org-install-dir (ignore-errors (org-find-library-dir "org-loaddefs"))) - (org-trash (or - (and (fboundp 'org-release) (fboundp 'org-git-version)) - (org-load-noerror-mustsuffix (concat org-dir "org-version")))) - (load-suffixes save-load-suffixes) - (org-version (org-release)) - (git-version (org-git-version)) - (version (format "Org-mode version %s (%s @ %s)" - org-version - git-version - (if org-install-dir - (if (string= org-dir org-install-dir) - org-install-dir - (concat "mixed installation! " org-install-dir " and " org-dir)) - "org-loaddefs.el can not be found!"))) - (version1 (if full version org-version))) - (if (org-called-interactively-p 'interactive) - (if here - (insert version) - (message version)) - (if message (message version1)) + "Show the Org version. +Interactively, or when MESSAGE is non-nil, show it in echo area. +With prefix argument, or when HERE is non-nil, insert it at point. +In non-interactive uses, a reduced version string is output unless +FULL is given." + (interactive (list current-prefix-arg t (not current-prefix-arg))) + (let ((org-dir (ignore-errors (org-find-library-dir "org"))) + (save-load-suffixes (when (boundp 'load-suffixes) load-suffixes)) + (load-suffixes (list ".el")) + (org-install-dir + (ignore-errors (org-find-library-dir "org-loaddefs")))) + (unless (and (fboundp 'org-release) (fboundp 'org-git-version)) + (org-load-noerror-mustsuffix (concat org-dir "org-version"))) + (let* ((load-suffixes save-load-suffixes) + (release (org-release)) + (git-version (org-git-version)) + (version (format "Org mode version %s (%s @ %s)" + release + git-version + (if org-install-dir + (if (string= org-dir org-install-dir) + org-install-dir + (concat "mixed installation! " + org-install-dir + " and " + org-dir)) + "org-loaddefs.el can not be found!"))) + (version1 (if full version release))) + (when here (insert version1)) + (when message (message "%s" version1)) version1))) (defconst org-version (org-version)) -;;; Compatibility constants + +;;; Syntax Constants + +;;;; Block + +(defconst org-block-regexp + "^[ \t]*#\\+begin_?\\([^ \n]+\\)\\(\\([^\n]+\\)\\)?\n\\([^\000]+?\\)#\\+end_?\\1[ \t]*$" + "Regular expression for hiding blocks.") + +(defconst org-dblock-start-re + "^[ \t]*#\\+\\(?:BEGIN\\|begin\\):[ \t]+\\(\\S-+\\)\\([ \t]+\\(.*\\)\\)?" + "Matches the start line of a dynamic block, with parameters.") + +(defconst org-dblock-end-re "^[ \t]*#\\+\\(?:END\\|end\\)\\([: \t\r\n]\\|$\\)" + "Matches the end of a dynamic block.") + +;;;; Clock and Planning + +(defconst org-clock-string "CLOCK:" + "String used as prefix for timestamps clocking work hours on an item.") + +(defvar org-closed-string "CLOSED:" + "String used as the prefix for timestamps logging closing a TODO entry.") + +(defvar org-deadline-string "DEADLINE:" + "String to mark deadline entries. +\\ +A deadline is this string, followed by a time stamp. It must be +a word, terminated by a colon. You can insert a schedule keyword +and a timestamp with `\\[org-deadline]'.") + +(defvar org-scheduled-string "SCHEDULED:" + "String to mark scheduled TODO entries. +\\ +A schedule is this string, followed by a time stamp. It must be +a word, terminated by a colon. You can insert a schedule keyword +and a timestamp with `\\[org-schedule]'.") + +(defconst org-ds-keyword-length + (+ 2 + (apply #'max + (mapcar #'length + (list org-deadline-string org-scheduled-string + org-clock-string org-closed-string)))) + "Maximum length of the DEADLINE and SCHEDULED keywords.") + +(defconst org-planning-line-re + (concat "^[ \t]*" + (regexp-opt + (list org-closed-string org-deadline-string org-scheduled-string) + t)) + "Matches a line with planning info. +Matched keyword is in group 1.") + +(defconst org-clock-line-re + (concat "^[ \t]*" org-clock-string) + "Matches a line with clock info.") + +(defconst org-deadline-regexp (concat "\\<" org-deadline-string) + "Matches the DEADLINE keyword.") + +(defconst org-deadline-time-regexp + (concat "\\<" org-deadline-string " *<\\([^>]+\\)>") + "Matches the DEADLINE keyword together with a time stamp.") + +(defconst org-deadline-time-hour-regexp + (concat "\\<" org-deadline-string + " *<\\([^>]+[0-9]\\{1,2\\}:[0-9]\\{2\\}[0-9-+:hdwmy \t.]*\\)>") + "Matches the DEADLINE keyword together with a time-and-hour stamp.") + +(defconst org-deadline-line-regexp + (concat "\\<\\(" org-deadline-string "\\).*") + "Matches the DEADLINE keyword and the rest of the line.") + +(defconst org-scheduled-regexp (concat "\\<" org-scheduled-string) + "Matches the SCHEDULED keyword.") + +(defconst org-scheduled-time-regexp + (concat "\\<" org-scheduled-string " *<\\([^>]+\\)>") + "Matches the SCHEDULED keyword together with a time stamp.") + +(defconst org-scheduled-time-hour-regexp + (concat "\\<" org-scheduled-string + " *<\\([^>]+[0-9]\\{1,2\\}:[0-9]\\{2\\}[0-9-+:hdwmy \t.]*\\)>") + "Matches the SCHEDULED keyword together with a time-and-hour stamp.") + +(defconst org-closed-time-regexp + (concat "\\<" org-closed-string " *\\[\\([^]]+\\)\\]") + "Matches the CLOSED keyword together with a time stamp.") + +(defconst org-keyword-time-regexp + (concat "\\<" + (regexp-opt + (list org-scheduled-string org-deadline-string org-closed-string + org-clock-string) + t) + " *[[<]\\([^]>]+\\)[]>]") + "Matches any of the 4 keywords, together with the time stamp.") + +(defconst org-keyword-time-not-clock-regexp + (concat + "\\<" + (regexp-opt + (list org-scheduled-string org-deadline-string org-closed-string) t) + " *[[<]\\([^]>]+\\)[]>]") + "Matches any of the 3 keywords, together with the time stamp.") + +(defconst org-maybe-keyword-time-regexp + (concat "\\(\\<" + (regexp-opt + (list org-scheduled-string org-deadline-string org-closed-string + org-clock-string) + t) + "\\)?" + " *\\([[<][0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^]\r\n>]*?[]>]" + "\\|" + "<%%([^\r\n>]*>\\)") + "Matches a timestamp, possibly preceded by a keyword.") + +(defconst org-all-time-keywords + (mapcar (lambda (w) (substring w 0 -1)) + (list org-scheduled-string org-deadline-string + org-clock-string org-closed-string)) + "List of time keywords.") + +;;;; Drawer + +(defconst org-drawer-regexp "^[ \t]*:\\(\\(?:\\w\\|[-_]\\)+\\):[ \t]*$" + "Matches first or last line of a hidden block. +Group 1 contains drawer's name or \"END\".") + +(defconst org-property-start-re "^[ \t]*:PROPERTIES:[ \t]*$" + "Regular expression matching the first line of a property drawer.") + +(defconst org-property-end-re "^[ \t]*:END:[ \t]*$" + "Regular expression matching the last line of a property drawer.") + +(defconst org-clock-drawer-start-re "^[ \t]*:CLOCK:[ \t]*$" + "Regular expression matching the first line of a clock drawer.") + +(defconst org-clock-drawer-end-re "^[ \t]*:END:[ \t]*$" + "Regular expression matching the last line of a clock drawer.") + +(defconst org-property-drawer-re + (concat "^[ \t]*:PROPERTIES:[ \t]*\n" + "\\(?:[ \t]*:\\S-+:\\(?: .*\\)?[ \t]*\n\\)*?" + "[ \t]*:END:[ \t]*$") + "Matches an entire property drawer.") + +(defconst org-clock-drawer-re + (concat "\\(" org-clock-drawer-start-re "\\)[^\000]*?\\(" + org-clock-drawer-end-re "\\)\n?") + "Matches an entire clock drawer.") + +;;;; Headline + +(defconst org-heading-keyword-regexp-format + "^\\(\\*+\\)\\(?: +%s\\)\\(?: +\\(.*?\\)\\)?[ \t]*$" + "Printf format for a regexp matching a headline with some keyword. +This regexp will match the headline of any node which has the +exact keyword that is put into the format. The keyword isn't in +any group by default, but the stars and the body are.") + +(defconst org-heading-keyword-maybe-regexp-format + "^\\(\\*+\\)\\(?: +%s\\)?\\(?: +\\(.*?\\)\\)?[ \t]*$" + "Printf format for a regexp matching a headline, possibly with some keyword. +This regexp can match any headline with the specified keyword, or +without a keyword. The keyword isn't in any group by default, +but the stars and the body are.") + +(defconst org-archive-tag "ARCHIVE" + "The tag that marks a subtree as archived. +An archived subtree does not open during visibility cycling, and does +not contribute to the agenda listings.") + +(defconst org-comment-string "COMMENT" + "Entries starting with this keyword will never be exported. +\\ +An entry can be toggled between COMMENT and normal with +`\\[org-toggle-comment]'.") + + +;;;; LaTeX Environments and Fragments + +(defconst org-latex-regexps + '(("begin" "^[ \t]*\\(\\\\begin{\\([a-zA-Z0-9\\*]+\\)[^\000]+?\\\\end{\\2}\\)" 1 t) + ;; ("$" "\\([ \t(]\\|^\\)\\(\\(\\([$]\\)\\([^ \t\n,.$].*?\\(\n.*?\\)\\{0,5\\}[^ \t\n,.$]\\)\\4\\)\\)\\([ \t.,?;:'\")]\\|$\\)" 2 nil) + ;; \000 in the following regex is needed for org-inside-LaTeX-fragment-p + ("$1" "\\([^$]\\|^\\)\\(\\$[^ \t\r\n,;.$]\\$\\)\\(\\s.\\|\\s-\\|\\s(\\|\\s)\\|\\s\"\\|\000\\|'\\|$\\)" 2 nil) + ("$" "\\([^$]\\|^\\)\\(\\(\\$\\([^ \t\n,;.$][^$\n\r]*?\\(\n[^$\n\r]*?\\)\\{0,2\\}[^ \t\n,.$]\\)\\$\\)\\)\\(\\s.\\|\\s-\\|\\s(\\|\\s)\\|\\s\"\\|\000\\|'\\|$\\)" 2 nil) + ("\\(" "\\\\([^\000]*?\\\\)" 0 nil) + ("\\[" "\\\\\\[[^\000]*?\\\\\\]" 0 nil) + ("$$" "\\$\\$[^\000]*?\\$\\$" 0 nil)) + "Regular expressions for matching embedded LaTeX.") + +;;;; Node Property + +(defconst org-effort-property "Effort" + "The property that is being used to keep track of effort estimates. +Effort estimates given in this property need to have the format H:MM.") + +;;;; Table + +(defconst org-table-any-line-regexp "^[ \t]*\\(|\\|\\+-[-+]\\)" + "Detect an org-type or table-type table.") + +(defconst org-table-line-regexp "^[ \t]*|" + "Detect an org-type table line.") + +(defconst org-table-dataline-regexp "^[ \t]*|[^-]" + "Detect an org-type table line.") + +(defconst org-table-hline-regexp "^[ \t]*|-" + "Detect an org-type table hline.") + +(defconst org-table1-hline-regexp "^[ \t]*\\+-[-+]" + "Detect a table-type table hline.") + +(defconst org-table-any-border-regexp "^[ \t]*[^|+ \t]" + "Detect the first line outside a table when searching from within it. +This works for both table types.") + +(defconst org-TBLFM-regexp "^[ \t]*#\\+TBLFM: " + "Detect a #+TBLFM line.") + +;;;; Timestamp + +(defconst org-ts-regexp "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^\r\n>]*?\\)>" + "Regular expression for fast time stamp matching.") + +(defconst org-ts-regexp-inactive + "\\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^\r\n>]*?\\)\\]" + "Regular expression for fast inactive time stamp matching.") + +(defconst org-ts-regexp-both "[[<]\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^]\r\n>]*?\\)[]>]" + "Regular expression for fast time stamp matching.") + +(defconst org-ts-regexp0 + "\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)\\( +[^]+0-9>\r\n -]+\\)?\\( +\\([0-9]\\{1,2\\}\\):\\([0-9]\\{2\\}\\)\\)?\\)" + "Regular expression matching time strings for analysis. +This one does not require the space after the date, so it can be used +on a string that terminates immediately after the date.") + +(defconst org-ts-regexp1 "\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\) *\\([^]+0-9>\r\n -]*\\)\\( \\([0-9]\\{1,2\\}\\):\\([0-9]\\{2\\}\\)\\)?\\)" + "Regular expression matching time strings for analysis.") + +(defconst org-ts-regexp2 (concat "<" org-ts-regexp1 "[^>\n]\\{0,16\\}>") + "Regular expression matching time stamps, with groups.") + +(defconst org-ts-regexp3 (concat "[[<]" org-ts-regexp1 "[^]>\n]\\{0,16\\}[]>]") + "Regular expression matching time stamps (also [..]), with groups.") + +(defconst org-tr-regexp (concat org-ts-regexp "--?-?" org-ts-regexp) + "Regular expression matching a time stamp range.") + +(defconst org-tr-regexp-both + (concat org-ts-regexp-both "--?-?" org-ts-regexp-both) + "Regular expression matching a time stamp range.") + +(defconst org-tsr-regexp (concat org-ts-regexp "\\(--?-?" + org-ts-regexp "\\)?") + "Regular expression matching a time stamp or time stamp range.") + +(defconst org-tsr-regexp-both + (concat org-ts-regexp-both "\\(--?-?" + org-ts-regexp-both "\\)?") + "Regular expression matching a time stamp or time stamp range. +The time stamps may be either active or inactive.") +(defconst org-repeat-re + "<[0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9] [^>\n]*?\\([.+]?\\+[0-9]+[hdwmy]\\(/[0-9]+[hdwmy]\\)?\\)" + "Regular expression for specifying repeated events. +After a match, group 1 contains the repeat expression.") + +(defconst org-time-stamp-formats '("<%Y-%m-%d %a>" . "<%Y-%m-%d %a %H:%M>") + "Formats for `format-time-string' which are used for time stamps.") + + ;;; The custom variables (defgroup org nil @@ -337,7 +637,7 @@ When MESSAGE is non-nil, display a message with the version." :group 'calendar) (defcustom org-mode-hook nil - "Mode hook for Org-mode, run after the mode was turned on." + "Mode hook for Org mode, run after the mode was turned on." :group 'org :type 'hook) @@ -359,17 +659,17 @@ When MESSAGE is non-nil, display a message with the version." (defun org-load-modules-maybe (&optional force) "Load all extensions listed in `org-modules'." (when (or force (not org-modules-loaded)) - (mapc (lambda (ext) - (condition-case nil (require ext) - (error (message "Problems while trying to load feature `%s'" ext)))) - org-modules) + (dolist (ext org-modules) + (condition-case nil (require ext) + (error (message "Problems while trying to load feature `%s'" ext)))) (setq org-modules-loaded t))) (defun org-set-modules (var value) "Set VAR to VALUE and call `org-load-modules-maybe' with the force flag." (set var value) (when (featurep 'org) - (org-load-modules-maybe 'force))) + (org-load-modules-maybe 'force) + (org-element-cache-reset 'all))) (defcustom org-modules '(org-w3m org-bbdb org-bibtex org-docview org-gnus org-info org-irc org-mhe org-rmail) "Modules that should always be loaded together with org.el. @@ -397,6 +697,7 @@ For export specific modules, see also `org-export-backends'." (const :tag " crypt: Encryption of subtrees" org-crypt) (const :tag " ctags: Access to Emacs tags with links" org-ctags) (const :tag " docview: Links to doc-view buffers" org-docview) + (const :tag " eww: Store link to url of eww" org-eww) (const :tag " gnus: Links to GNUS folders/messages" org-gnus) (const :tag " habit: Track your consistency with habits" org-habit) (const :tag " id: Global IDs for identifying entries" org-id) @@ -407,52 +708,50 @@ For export specific modules, see also `org-export-backends'." (const :tag " mouse: Additional mouse support" org-mouse) (const :tag " protocol: Intercept calls from emacsclient" org-protocol) (const :tag " rmail: Links to RMAIL folders/messages" org-rmail) - (const :tag " w3m: Special cut/paste from w3m to Org-mode." org-w3m) + (const :tag " w3m: Special cut/paste from w3m to Org mode." org-w3m) (const :tag "C annotate-file: Annotate a file with org syntax" org-annotate-file) - (const :tag "C bookmark: Org-mode links to bookmarks" org-bookmark) + (const :tag "C bookmark: Org links to bookmarks" org-bookmark) (const :tag "C bullets: Add overlays to headlines stars" org-bullets) (const :tag "C checklist: Extra functions for checklists in repeated tasks" org-checklist) (const :tag "C choose: Use TODO keywords to mark decisions states" org-choose) (const :tag "C collector: Collect properties into tables" org-collector) - (const :tag "C depend: TODO dependencies for Org-mode\n\t\t\t(PARTIALLY OBSOLETE, see built-in dependency support))" org-depend) - (const :tag "C drill: Flashcards and spaced repetition for Org-mode" org-drill) - (const :tag "C elisp-symbol: Org-mode links to emacs-lisp symbols" org-elisp-symbol) + (const :tag "C depend: TODO dependencies for Org mode\n\t\t\t(PARTIALLY OBSOLETE, see built-in dependency support))" org-depend) + (const :tag "C drill: Flashcards and spaced repetition for Org mode" org-drill) + (const :tag "C elisp-symbol: Org links to emacs-lisp symbols" org-elisp-symbol) (const :tag "C eshell Support for links to working directories in eshell" org-eshell) (const :tag "C eval-light: Evaluate inbuffer-code on demand" org-eval-light) (const :tag "C eval: Include command output as text" org-eval) - (const :tag "C expiry: Expiry mechanism for Org-mode entries" org-expiry) + (const :tag "C expiry: Expiry mechanism for Org entries" org-expiry) (const :tag "C favtable: Lookup table of favorite references and links" org-favtable) (const :tag "C git-link: Provide org links to specific file version" org-git-link) (const :tag "C interactive-query: Interactive modification of tags query\n\t\t\t(PARTIALLY OBSOLETE, see secondary filtering)" org-interactive-query) - (const :tag "C invoice: Help manage client invoices in Org-mode" org-invoice) - (const :tag "C jira: Add a jira:ticket protocol to Org-mode" org-jira) + (const :tag "C invoice: Help manage client invoices in Org mode" org-invoice) (const :tag "C learn: SuperMemo's incremental learning algorithm" org-learn) (const :tag "C mac-iCal Imports events from iCal.app to the Emacs diary" org-mac-iCal) (const :tag "C mac-link: Grab links and url from various mac Applications" org-mac-link) - (const :tag "C mairix: Hook mairix search into Org-mode for different MUAs" org-mairix) - (const :tag "C man: Support for links to manpages in Org-mode" org-man) + (const :tag "C mairix: Hook mairix search into Org for different MUAs" org-mairix) + (const :tag "C man: Support for links to manpages in Org mode" org-man) (const :tag "C mew: Links to Mew folders/messages" org-mew) (const :tag "C mtags: Support for muse-like tags" org-mtags) (const :tag "C notmuch: Provide org links to notmuch searches or messages" org-notmuch) (const :tag "C panel: Simple routines for us with bad memory" org-panel) - (const :tag "C registry: A registry for Org-mode links" org-registry) - (const :tag "C screen: Visit screen sessions through Org-mode links" org-screen) + (const :tag "C registry: A registry for Org links" org-registry) + (const :tag "C screen: Visit screen sessions through Org links" org-screen) (const :tag "C secretary: Team management with org-mode" org-secretary) - (const :tag "C sqlinsert: Convert Org-mode tables to SQL insertions" orgtbl-sqlinsert) - (const :tag "C toc: Table of contents for Org-mode buffer" org-toc) - (const :tag "C track: Keep up with Org-mode development" org-track) + (const :tag "C sqlinsert: Convert Org tables to SQL insertions" orgtbl-sqlinsert) + (const :tag "C toc: Table of contents for Org buffer" org-toc) + (const :tag "C track: Keep up with Org mode development" org-track) (const :tag "C velocity Something like Notational Velocity for Org" org-velocity) (const :tag "C vm: Links to VM folders/messages" org-vm) (const :tag "C wikinodes: CamelCase wiki-like links" org-wikinodes) (const :tag "C wl: Links to Wanderlust folders/messages" org-wl) (repeat :tag "External packages" :inline t (symbol :tag "Package")))) -(defvar org-export--registered-backends) ; From ox.el. +(defvar org-export-registered-backends) ; From ox.el. (declare-function org-export-derived-backend-p "ox" (backend &rest backends)) (declare-function org-export-backend-name "ox" (backend) t) -(declare-function org-export-backend-options "ox" (cl-x) t) -(defcustom org-export-backends '(ascii html icalendar latex) +(defcustom org-export-backends '(ascii html icalendar latex odt) "List of export back-ends that should be always available. If a description starts with , the file is not part of Emacs @@ -469,8 +768,8 @@ interface or run the following code, where VAL stands for the new value of the variable, after updating it: (progn - (setq org-export--registered-backends - (org-remove-if-not + (setq org-export-registered-backends + (cl-remove-if-not (lambda (backend) (let ((name (org-export-backend-name backend))) (or (memq name val) @@ -478,9 +777,9 @@ value of the variable, after updating it: (dolist (b val) (and (org-export-derived-backend-p b name) (throw \\='parentp t))))))) - org-export--registered-backends)) - (let ((new-list (mapcar \\='org-export-backend-name - org-export--registered-backends))) + org-export-registered-backends)) + (let ((new-list (mapcar #\\='org-export-backend-name + org-export-registered-backends))) (dolist (backend val) (cond ((not (load (format \"ox-%s\" backend) t t)) @@ -493,16 +792,16 @@ Adding a back-end to this list will also pull the back-end it depends on, if any." :group 'org :group 'org-export - :version "24.4" - :package-version '(Org . "8.0") + :version "26.1" + :package-version '(Org . "9.0") :initialize 'custom-initialize-set :set (lambda (var val) (if (not (featurep 'ox)) (set-default var val) ;; Any back-end not required anymore (not present in VAL and not ;; a parent of any back-end in the new value) is removed from the ;; list of registered back-ends. - (setq org-export--registered-backends - (org-remove-if-not + (setq org-export-registered-backends + (cl-remove-if-not (lambda (backend) (let ((name (org-export-backend-name backend))) (or (memq name val) @@ -510,11 +809,11 @@ depends on, if any." (dolist (b val) (and (org-export-derived-backend-p b name) (throw 'parentp t))))))) - org-export--registered-backends)) + org-export-registered-backends)) ;; Now build NEW-LIST of both new back-ends and required ;; parents. - (let ((new-list (mapcar 'org-export-backend-name - org-export--registered-backends))) + (let ((new-list (mapcar #'org-export-backend-name + org-export-registered-backends))) (dolist (backend val) (cond ((not (load (format "ox-%s" backend) t t)) @@ -544,19 +843,18 @@ depends on, if any." (const :tag "C taskjuggler Export buffer to TaskJuggler format" taskjuggler))) (eval-after-load 'ox - '(mapc - (lambda (backend) - (condition-case nil (require (intern (format "ox-%s" backend))) - (error (message "Problems while trying to load export back-end `%s'" - backend)))) - org-export-backends)) + '(dolist (backend org-export-backends) + (condition-case nil (require (intern (format "ox-%s" backend))) + (error (message "Problems while trying to load export back-end `%s'" + backend))))) (defcustom org-support-shift-select nil "Non-nil means make shift-cursor commands select text when possible. +\\\ In Emacs 23, when `shift-select-mode' is on, shifted cursor keys start selecting a region, or enlarge regions started in this way. -In Org-mode, in special contexts, these same keys are used for +In Org mode, in special contexts, these same keys are used for other purposes, important enough to compete with shift selection. Org tries to balance these needs by supporting `shift-select-mode' outside these special contexts, under control of this variable. @@ -571,7 +869,7 @@ cursor keys will then execute Org commands in the following contexts: Outside these contexts, the commands will throw an error. When this variable is t and the cursor is not in a special -context, Org-mode will support shift-selection for making and +context, Org mode will support shift-selection for making and enlarging regions. To make this more effective, the bullet cycling will no longer happen anywhere in an item line, but only if the cursor is exactly on the bullet. @@ -579,16 +877,16 @@ if the cursor is exactly on the bullet. If you set this variable to the symbol `always', then the keys will not be special in headlines, property lines, and item lines, to make shift selection work there as well. If this is what you -want, you can use the following alternative commands: `C-c C-t' -and `C-c ,' to change TODO state and priority, `C-u C-u C-c C-t' -can be used to switch TODO sets, `C-c -' to cycle item bullet -types, and properties can be edited by hand or in column view. +want, you can use the following alternative commands: +`\\[org-todo]' and `\\[org-priority]' \ +to change TODO state and priority, +`\\[universal-argument] \\[universal-argument] \\[org-todo]' \ +can be used to switch TODO sets, +`\\[org-ctrl-c-minus]' to cycle item bullet types, +and properties can be edited by hand or in column view. However, when the cursor is on a timestamp, shift-cursor commands -will still edit the time stamp - this is just too good to give up. - -XEmacs user should have this variable set to nil, because -`shift-select-mode' is in Emacs 23 or later only." +will still edit the time stamp - this is just too good to give up." :group 'org :type '(choice (const :tag "Never" nil) @@ -622,12 +920,13 @@ already archived entries." :group 'org-archive) (defgroup org-startup nil - "Options concerning startup of Org-mode." + "Options concerning startup of Org mode." :tag "Org Startup" :group 'org) (defcustom org-startup-folded t - "Non-nil means entering Org-mode will switch to OVERVIEW. + "Non-nil means entering Org mode will switch to OVERVIEW. + This can also be configured on a per-file basis by adding one of the following lines anywhere in the buffer: @@ -636,9 +935,9 @@ the following lines anywhere in the buffer: #+STARTUP: content #+STARTUP: showeverything -By default, this option is ignored when Org opens agenda files -for the first time. If you want the agenda to honor the startup -option, set `org-agenda-inhibit-startup' to nil." +Set `org-agenda-inhibit-startup' to a non-nil value if you want +to ignore this option when Org opens agenda files for the first +time." :group 'org-startup :type '(choice (const :tag "nofold: show all" nil) @@ -647,9 +946,18 @@ option, set `org-agenda-inhibit-startup' to nil." (const :tag "show everything, even drawers" showeverything))) (defcustom org-startup-truncated t - "Non-nil means entering Org-mode will set `truncate-lines'. + "Non-nil means entering Org mode will set `truncate-lines'. This is useful since some lines containing links can be very long and -uninteresting. Also tables look terrible when wrapped." +uninteresting. Also tables look terrible when wrapped. + +The variable `org-startup-truncated' allows to configure +truncation for Org mode different to the other modes that use the +variable `truncate-lines' and as a shortcut instead of putting +the variable `truncate-lines' into the `org-mode-hook'. If one +wants to configure truncation for Org mode not statically but +dynamically e. g. in a hook like `ediff-prepare-buffer-hook' then +the variable `truncate-lines' has to be used because in such a +case it is too late to set the variable `org-startup-truncated'." :group 'org-startup :type 'boolean) @@ -742,26 +1050,26 @@ the following lines anywhere in the buffer: :type 'boolean) (defcustom org-insert-mode-line-in-empty-file nil - "Non-nil means insert the first line setting Org-mode in empty files. + "Non-nil means insert the first line setting Org mode in empty files. When the function `org-mode' is called interactively in an empty file, this -normally means that the file name does not automatically trigger Org-mode. -To ensure that the file will always be in Org-mode in the future, a -line enforcing Org-mode will be inserted into the buffer, if this option +normally means that the file name does not automatically trigger Org mode. +To ensure that the file will always be in Org mode in the future, a +line enforcing Org mode will be inserted into the buffer, if this option has been set." :group 'org-startup :type 'boolean) (defcustom org-replace-disputed-keys nil "Non-nil means use alternative key bindings for some keys. -Org-mode uses S- keys for changing timestamps and priorities. +Org mode uses S- keys for changing timestamps and priorities. These keys are also used by other packages like shift-selection-mode' \(built into Emacs 23), `CUA-mode' or `windmove.el'. -If you want to use Org-mode together with one of these other modes, -or more generally if you would like to move some Org-mode commands to +If you want to use Org mode together with one of these other modes, +or more generally if you would like to move some Org mode commands to other keys, set this variable and configure the keys with the variable `org-disputed-keys'. -This option is only relevant at load-time of Org-mode, and must be set +This option is only relevant at load-time of Org mode, and must be set *before* org.el is loaded. Changing it requires a restart of Emacs to become effective." :group 'org-startup @@ -769,18 +1077,13 @@ become effective." (defcustom org-use-extra-keys nil "Non-nil means use extra key sequence definitions for certain commands. -This happens automatically if you run XEmacs or if `window-system' -is nil. This variable lets you do the same manually. You must -set it before loading org. - -Example: on Carbon Emacs 22 running graphically, with an external -keyboard on a Powerbook, the default way of setting M-left might -not work for either Alt or ESC. Setting this variable will make -it work for ESC." +This happens automatically if `window-system' is nil. This +variable lets you do the same manually. You must set it before +loading Org." :group 'org-startup :type 'boolean) -(org-defvaralias 'org-CUA-compatible 'org-replace-disputed-keys) +(defvaralias 'org-CUA-compatible 'org-replace-disputed-keys) (defcustom org-disputed-keys '(([(shift up)] . [(meta p)]) @@ -789,90 +1092,52 @@ it work for ESC." ([(shift right)] . [(meta +)]) ([(control shift right)] . [(meta shift +)]) ([(control shift left)] . [(meta shift -)])) - "Keys for which Org-mode and other modes compete. + "Keys for which Org mode and other modes compete. This is an alist, cars are the default keys, second element specifies the alternative to use when `org-replace-disputed-keys' is t. Keys can be specified in any syntax supported by `define-key'. -The value of this option takes effect only at Org-mode's startup, +The value of this option takes effect only at Org mode startup, therefore you'll have to restart Emacs to apply it after changing." :group 'org-startup :type 'alist) (defun org-key (key) "Select key according to `org-replace-disputed-keys' and `org-disputed-keys'. -Or return the original if not disputed. -Also apply the translations defined in `org-xemacs-key-equivalents'." +Or return the original if not disputed." (when org-replace-disputed-keys (let* ((nkey (key-description key)) - (x (org-find-if (lambda (x) - (equal (key-description (car x)) nkey)) - org-disputed-keys))) + (x (cl-find-if (lambda (x) (equal (key-description (car x)) nkey)) + org-disputed-keys))) (setq key (if x (cdr x) key)))) - (when (featurep 'xemacs) - (setq key (or (cdr (assoc key org-xemacs-key-equivalents)) key))) key) -(defun org-find-if (predicate seq) - (catch 'exit - (while seq - (if (funcall predicate (car seq)) - (throw 'exit (car seq)) - (pop seq))))) - (defun org-defkey (keymap key def) "Define a key, possibly translated, as returned by `org-key'." (define-key keymap (org-key key) def)) (defcustom org-ellipsis nil - "The ellipsis to use in the Org-mode outline. + "The ellipsis to use in the Org mode outline. + When nil, just use the standard three dots. When a string, use that string instead. -When a face, use the standard 3 dots, but with the specified face. -The change affects only Org-mode (which will then use its own display table). + +The change affects only Org mode (which will then use its own display table). Changing this requires executing `\\[org-mode]' in a buffer to become effective." :group 'org-startup :type '(choice (const :tag "Default" nil) - (face :tag "Face" :value org-warning) - (string :tag "String" :value "...#"))) + (string :tag "String" :value "...#")) + :safe #'string-or-null-p) (defvar org-display-table nil "The display table for org-mode, in case `org-ellipsis' is non-nil.") (defgroup org-keywords nil - "Keywords in Org-mode." + "Keywords in Org mode." :tag "Org Keywords" :group 'org) -(defcustom org-deadline-string "DEADLINE:" - "String to mark deadline entries. -A deadline is this string, followed by a time stamp. Should be a word, -terminated by a colon. You can insert a schedule keyword and -a timestamp with \\[org-deadline]. -Changes become only effective after restarting Emacs." - :group 'org-keywords - :type 'string) - -(defcustom org-scheduled-string "SCHEDULED:" - "String to mark scheduled TODO entries. -A schedule is this string, followed by a time stamp. Should be a word, -terminated by a colon. You can insert a schedule keyword and -a timestamp with \\[org-schedule]. -Changes become only effective after restarting Emacs." - :group 'org-keywords - :type 'string) - -(defcustom org-closed-string "CLOSED:" - "String used as the prefix for timestamps logging closing a TODO entry." - :group 'org-keywords - :type 'string) - -(defcustom org-clock-string "CLOCK:" - "String used as prefix for timestamps clocking work hours on an item." - :group 'org-keywords - :type 'string) - (defcustom org-closed-keep-when-no-todo nil "Remove CLOSED: time-stamp when switching back to a non-todo state?" :group 'org-todo @@ -881,37 +1146,8 @@ Changes become only effective after restarting Emacs." :package-version '(Org . "8.0") :type 'boolean) -(defconst org-planning-or-clock-line-re (concat "^[ \t]*\\(" - org-scheduled-string "\\|" - org-deadline-string "\\|" - org-closed-string "\\|" - org-clock-string "\\)") - "Matches a line with planning or clock info.") - -(defcustom org-comment-string "COMMENT" - "Entries starting with this keyword will never be exported. -An entry can be toggled between COMMENT and normal with -\\[org-toggle-comment]. -Changes become only effective after restarting Emacs." - :group 'org-keywords - :type 'string) - -(defcustom org-quote-string "QUOTE" - "Entries starting with this keyword will be exported in fixed-width font. -Quoting applies only to the text in the entry following the headline, and does -not extend beyond the next headline, even if that is lower level. -An entry can be toggled between QUOTE and normal with -\\[org-toggle-fixed-width-section]." - :group 'org-keywords - :type 'string) - -(defconst org-repeat-re - "<[0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9] [^>\n]*?\\([.+]?\\+[0-9]+[hdwmy]\\(/[0-9]+[hdwmy]\\)?\\)" - "Regular expression for specifying repeated events. -After a match, group 1 contains the repeat expression.") - (defgroup org-structure nil - "Options concerning the general structure of Org-mode files." + "Options concerning the general structure of Org files." :tag "Org Structure" :group 'org) @@ -920,92 +1156,88 @@ After a match, group 1 contains the repeat expression.") :tag "Org Reveal Location" :group 'org-structure) -(defconst org-context-choice - '(choice - (const :tag "Always" t) - (const :tag "Never" nil) - (repeat :greedy t :tag "Individual contexts" - (cons - (choice :tag "Context" - (const agenda) - (const org-goto) - (const occur-tree) - (const tags-tree) - (const link-search) - (const mark-goto) - (const bookmark-jump) - (const isearch) - (const default)) - (boolean)))) - "Contexts for the reveal options.") - -(defcustom org-show-hierarchy-above '((default . t)) - "Non-nil means show full hierarchy when revealing a location. -Org-mode often shows locations in an org-mode file which might have -been invisible before. When this is set, the hierarchy of headings -above the exposed location is shown. -Turning this off for example for sparse trees makes them very compact. -Instead of t, this can also be an alist specifying this option for different -contexts. Valid contexts are +(defcustom org-show-context-detail '((agenda . local) + (bookmark-jump . lineage) + (isearch . lineage) + (default . ancestors)) + "Alist between context and visibility span when revealing a location. + +\\Some actions may move point into invisible +locations. As a consequence, Org always expose a neighborhood +around point. How much is shown depends on the initial action, +or context. Valid contexts are + agenda when exposing an entry from the agenda - org-goto when using the command `org-goto' on key C-c C-j - occur-tree when using the command `org-occur' on key C-c / + org-goto when using the command `org-goto' (`\\[org-goto]') + occur-tree when using the command `org-occur' (`\\[org-sparse-tree] /') tags-tree when constructing a sparse tree based on tags matches link-search when exposing search matches associated with a link mark-goto when exposing the jump goal of a mark bookmark-jump when exposing a bookmark location isearch when exiting from an incremental search - default default for all contexts not set explicitly" - :group 'org-reveal-location - :type org-context-choice) - -(defcustom org-show-following-heading '((default . nil)) - "Non-nil means show following heading when revealing a location. -Org-mode often shows locations in an org-mode file which might have -been invisible before. When this is set, the heading following the -match is shown. -Turning this off for example for sparse trees makes them very compact, -but makes it harder to edit the location of the match. In such a case, -use the command \\[org-reveal] to show more context. -Instead of t, this can also be an alist specifying this option for different -contexts. See `org-show-hierarchy-above' for valid contexts." - :group 'org-reveal-location - :type org-context-choice) - -(defcustom org-show-siblings '((default . nil) (isearch t) (bookmark-jump t)) - "Non-nil means show all sibling heading when revealing a location. -Org-mode often shows locations in an org-mode file which might have -been invisible before. When this is set, the sibling of the current entry -heading are all made visible. If `org-show-hierarchy-above' is t, -the same happens on each level of the hierarchy above the current entry. - -By default this is on for the isearch context, off for all other contexts. -Turning this off for example for sparse trees makes them very compact, -but makes it harder to edit the location of the match. In such a case, -use the command \\[org-reveal] to show more context. -Instead of t, this can also be an alist specifying this option for different -contexts. See `org-show-hierarchy-above' for valid contexts." - :group 'org-reveal-location - :type org-context-choice - :version "24.4" - :package-version '(Org . "8.0")) + default default for all contexts not set explicitly + +Allowed visibility spans are + + minimal show current headline; if point is not on headline, + also show entry -(defcustom org-show-entry-below '((default . nil)) - "Non-nil means show the entry below a headline when revealing a location. -Org-mode often shows locations in an org-mode file which might have -been invisible before. When this is set, the text below the headline that is -exposed is also shown. + local show current headline, entry and next headline -By default this is off for all contexts. -Instead of t, this can also be an alist specifying this option for different -contexts. See `org-show-hierarchy-above' for valid contexts." + ancestors show current headline and its direct ancestors; if + point is not on headline, also show entry + + lineage show current headline, its direct ancestors and all + their children; if point is not on headline, also show + entry and first child + + tree show current headline, its direct ancestors and all + their children; if point is not on headline, also show + entry and all children + + canonical show current headline, its direct ancestors along with + their entries and children; if point is not located on + the headline, also show current entry and all children + +As special cases, a nil or t value means show all contexts in +`minimal' or `canonical' view, respectively. + +Some views can make displayed information very compact, but also +make it harder to edit the location of the match. In such +a case, use the command `org-reveal' (`\\[org-reveal]') to show +more context." :group 'org-reveal-location - :type org-context-choice) + :version "26.1" + :package-version '(Org . "9.0") + :type '(choice + (const :tag "Canonical" t) + (const :tag "Minimal" nil) + (repeat :greedy t :tag "Individual contexts" + (cons + (choice :tag "Context" + (const agenda) + (const org-goto) + (const occur-tree) + (const tags-tree) + (const link-search) + (const mark-goto) + (const bookmark-jump) + (const isearch) + (const default)) + (choice :tag "Detail level" + (const minimal) + (const local) + (const ancestors) + (const lineage) + (const tree) + (const canonical)))))) (defcustom org-indirect-buffer-display 'other-window "How should indirect tree buffers be displayed? + This applies to indirect buffers created with the commands -\\[org-tree-to-indirect-buffer] and \\[org-agenda-tree-to-indirect-buffer]. +`org-tree-to-indirect-buffer' and `org-agenda-tree-to-indirect-buffer'. + Valid values are: current-window Display in the current window other-window Just display in another window. @@ -1024,7 +1256,13 @@ new-frame Make a new frame each time. Note that in this case (defcustom org-use-speed-commands nil "Non-nil means activate single letter commands at beginning of a headline. This may also be a function to test for appropriate locations where speed -commands should be active." +commands should be active. + +For example, to activate speed commands when the point is on any +star at the beginning of the headline, you can do this: + + (setq org-use-speed-commands + (lambda () (and (looking-at org-outline-regexp) (looking-back \"^\\**\"))))" :group 'org-structure :type '(choice (const :tag "Never" nil) @@ -1054,10 +1292,10 @@ commands in the Help buffer using the `?' speed command." (sexp)))))) (defcustom org-bookmark-names-plist - '(:last-capture "org-capture-last-stored" - :last-refile "org-refile-last-stored" - :last-capture-marker "org-capture-last-stored-marker") - "Names for bookmarks automatically set by some Org commands. + '(:last-capture "org-capture-last-stored" + :last-refile "org-refile-last-stored" + :last-capture-marker "org-capture-last-stored-marker") + "Names for bookmarks automatically set by some Org commands. This can provide strings as names for a number of bookmarks Org sets automatically. The following keys are currently implemented: :last-capture @@ -1065,11 +1303,11 @@ automatically. The following keys are currently implemented: :last-refile When a key does not show up in the property list, the corresponding bookmark is not set." - :group 'org-structure - :type 'plist) + :group 'org-structure + :type 'plist) (defgroup org-cycle nil - "Options concerning visibility cycling in Org-mode." + "Options concerning visibility cycling in Org mode." :tag "Org Cycle" :group 'org-structure) @@ -1093,25 +1331,8 @@ than its value." (const :tag "No limit" nil) (integer :tag "Maximum level"))) -(defcustom org-drawers '("PROPERTIES" "CLOCK" "LOGBOOK" "RESULTS") - "Names of drawers. Drawers are not opened by cycling on the headline above. -Drawers only open with a TAB on the drawer line itself. A drawer looks like -this: - :DRAWERNAME: - ..... - :END: -The drawer \"PROPERTIES\" is special for capturing properties through -the property API. - -Drawers can be defined on the per-file basis with a line like: - -#+DRAWERS: HIDDEN STATE PROPERTIES" - :group 'org-structure - :group 'org-cycle - :type '(repeat (string :tag "Drawer Name"))) - (defcustom org-hide-block-startup nil - "Non-nil means entering Org-mode will fold all blocks. + "Non-nil means entering Org mode will fold all blocks. This can also be set in on a per-file basis with #+STARTUP: hideblocks @@ -1122,12 +1343,17 @@ This can also be set in on a per-file basis with (defcustom org-cycle-global-at-bob nil "Cycle globally if cursor is at beginning of buffer and not at a headline. -This makes it possible to do global cycling without having to use S-TAB or -\\[universal-argument] TAB. For this special case to work, the first line -of the buffer must not be a headline -- it may be empty or some other text. + +This makes it possible to do global cycling without having to use `S-TAB' +or `\\[universal-argument] TAB'. For this special case to work, the first \ +line of the buffer +must not be a headline -- it may be empty or some other text. + When used in this way, `org-cycle-hook' is disabled temporarily to make -sure the cursor stays at the beginning of the buffer. When this option is -nil, don't do anything special at the beginning of the buffer." +sure the cursor stays at the beginning of the buffer. + +When this option is nil, don't do anything special at the beginning of +the buffer." :group 'org-cycle :type 'boolean) @@ -1166,7 +1392,7 @@ visibility is cycled." "Number of empty lines needed to keep an empty line between collapsed trees. If you leave an empty line between the end of a subtree and the following headline, this empty line is hidden when the subtree is folded. -Org-mode will leave (exactly) one empty line visible if the number of +Org mode will leave (exactly) one empty line visible if the number of empty lines is equal or larger to the number given in this variable. So the default 2 means at least 2 empty lines after the end of a subtree are needed to produce free space between a collapsed subtree and the @@ -1192,7 +1418,6 @@ the values `folded', `children', or `subtree'." (defcustom org-cycle-hook '(org-cycle-hide-archived-subtrees org-cycle-hide-drawers - org-cycle-hide-inline-tasks org-cycle-show-empty-lines org-optimize-window-after-visibility-change) "Hook that is run after `org-cycle' has changed the buffer visibility. @@ -1202,10 +1427,12 @@ argument is a symbol. After a global state change, it can have the values `overview', `contents', or `all'. After a local state change, it can have the values `folded', `children', or `subtree'." :group 'org-cycle - :type 'hook) + :type 'hook + :version "26.1" + :package-version '(Org . "8.3")) (defgroup org-edit-structure nil - "Options concerning structure editing in Org-mode." + "Options concerning structure editing in Org mode." :tag "Org Edit Structure" :group 'org-structure) @@ -1229,23 +1456,25 @@ lines to the buffer: "Non-nil means adapt indentation to outline node level. When this variable is set, Org assumes that you write outlines by -indenting text in each node to align with the headline (after the stars). -The following issues are influenced by this variable: +indenting text in each node to align with the headline (after the +stars). The following issues are influenced by this variable: -- When this is set and the *entire* text in an entry is indented, the - indentation is increased by one space in a demotion command, and - decreased by one in a promotion command. If any line in the entry - body starts with text at column 0, indentation is not changed at all. +- The indentation is increased by one space in a demotion + command, and decreased by one in a promotion command. However, + in the latter case, if shifting some line in the entry body + would alter document structure (e.g., insert a new headline), + indentation is not changed at all. -- Property drawers and planning information is inserted indented when - this variable s set. When nil, they will not be indented. +- Property drawers and planning information is inserted indented + when this variable is set. When nil, they will not be indented. -- TAB indents a line relative to context. The lines below a headline - will be indented when this variable is set. +- TAB indents a line relative to current level. The lines below + a headline will be indented when this variable is set. -Note that this is all about true indentation, by adding and removing -space characters. See also `org-indent.el' which does level-dependent -indentation in a virtual way, i.e. at display time in Emacs." +Note that this is all about true indentation, by adding and +removing space characters. See also `org-indent.el' which does +level-dependent indentation in a virtual way, i.e. at display +time in Emacs." :group 'org-edit-structure :type 'boolean) @@ -1286,7 +1515,7 @@ This may also be a cons cell where the behavior for `C-a' and (const :tag "off" nil) (const :tag "on: before tags first" t) (const :tag "reversed: after tags first" reversed))))) -(org-defvaralias 'org-special-ctrl-a 'org-special-ctrl-a/e) +(defvaralias 'org-special-ctrl-a 'org-special-ctrl-a/e) (defcustom org-special-ctrl-k nil "Non-nil means `C-k' will behave specially in headlines. @@ -1386,9 +1615,11 @@ default the value to be used for all contexts not explicitly (defcustom org-insert-heading-respect-content nil "Non-nil means insert new headings after the current subtree. +\\ When nil, the new heading is created directly after the current line. -The commands \\[org-insert-heading-respect-content] and \\[org-insert-todo-heading-respect-content] turn -this variable on for the duration of the command." +The commands `\\[org-insert-heading-respect-content]' and \ +`\\[org-insert-todo-heading-respect-content]' turn this variable on +for the duration of the command." :group 'org-structure :type 'boolean) @@ -1398,11 +1629,7 @@ this variable on for the duration of the command." The value is an alist, with `heading' and `plain-list-item' as CAR, and a boolean flag as CDR. The cdr may also be the symbol `auto', in which case Org will look at the surrounding headings/items and try to -make an intelligent decision whether to insert a blank line or not. - -For plain lists, if `org-list-empty-line-terminates-plain-lists' is set, -the setting here is ignored and no empty line is inserted to avoid breaking -the list structure." +make an intelligent decision whether to insert a blank line or not." :group 'org-edit-structure :type '(list (cons (const heading) @@ -1422,8 +1649,7 @@ the list structure." (defcustom org-enable-fixed-width-editor t "Non-nil means lines starting with \":\" are treated as fixed-width. This currently only means they are never auto-wrapped. -When nil, such lines will be treated like ordinary lines. -See also the QUOTE keyword." +When nil, such lines will be treated like ordinary lines." :group 'org-edit-structure :type 'boolean) @@ -1441,7 +1667,7 @@ When nil, you can use these keybindings to navigate the buffer: :type 'boolean) (defgroup org-sparse-trees nil - "Options concerning sparse trees in Org-mode." + "Options concerning sparse trees in Org mode." :tag "Org Sparse Trees" :group 'org-structure) @@ -1454,14 +1680,26 @@ changed by an edit command." (defcustom org-remove-highlights-with-change t "Non-nil means any change to the buffer will remove temporary highlights. +\\\ Such highlights are created by `org-occur' and `org-clock-display'. -When nil, `C-c C-c' needs to be used to get rid of the highlights. -The highlights created by `org-preview-latex-fragment' always need -`C-c C-c' to be removed." +When nil, `\\[org-ctrl-c-ctrl-c]' needs to be used \ +to get rid of the highlights. +The highlights created by `org-toggle-latex-fragment' always need +`\\[org-toggle-latex-fragment]' to be removed." :group 'org-sparse-trees :group 'org-time :type 'boolean) +(defcustom org-occur-case-fold-search t + "Non-nil means `org-occur' should be case-insensitive. +If set to `smart' the search will be case-insensitive only if it +doesn't specify any upper case character." + :group 'org-sparse-trees + :version "26.1" + :type '(choice + (const :tag "Case-sensitive" nil) + (const :tag "Case-insensitive" t) + (const :tag "Case-insensitive for lower case searches only" 'smart))) (defcustom org-occur-hook '(org-first-headline-recenter) "Hook that is run after `org-occur' has constructed a sparse tree. @@ -1471,18 +1709,18 @@ as possible." :type 'hook) (defgroup org-imenu-and-speedbar nil - "Options concerning imenu and speedbar in Org-mode." + "Options concerning imenu and speedbar in Org mode." :tag "Org Imenu and Speedbar" :group 'org-structure) (defcustom org-imenu-depth 2 - "The maximum level for Imenu access to Org-mode headlines. + "The maximum level for Imenu access to Org headlines. This also applied for speedbar access." :group 'org-imenu-and-speedbar :type 'integer) (defgroup org-table nil - "Options concerning tables in Org-mode." + "Options concerning tables in Org mode." :tag "Org Table" :group 'org) @@ -1499,12 +1737,12 @@ do the following: TAB or RET are pressed to move to another field. With optimization this happens only if changes to a field might have changed the column width. Optimization requires replacing the functions `self-insert-command', -`delete-char', and `backward-delete-char' in Org-mode buffers, with a -slight (in fact: unnoticeable) speed impact for normal typing. Org-mode is -very good at guessing when a re-align will be necessary, but you can always -force one with \\[org-ctrl-c-ctrl-c]. +`delete-char', and `backward-delete-char' in Org buffers, with a +slight (in fact: unnoticeable) speed impact for normal typing. Org is very +good at guessing when a re-align will be necessary, but you can always +force one with `\\[org-ctrl-c-ctrl-c]'. -If you would like to use the optimized version in Org-mode, but the +If you would like to use the optimized version in Org mode, but the un-optimized version in OrgTbl-mode, see the variable `orgtbl-optimized'. This variable can be used to turn on and off the table editor during a session, @@ -1517,8 +1755,7 @@ See also the variable `org-table-auto-blank-field'." (const :tag "on" t) (const :tag "on, optimized" optimized))) -(defcustom org-self-insert-cluster-for-undo (or (featurep 'xemacs) - (version<= emacs-version "24.1")) +(defcustom org-self-insert-cluster-for-undo nil "Non-nil means cluster self-insert commands for undo when possible. If this is set, then, like in the Emacs command loop, 20 consecutive characters will be undone together. @@ -1534,24 +1771,95 @@ calls `table-recognize-table'." :type 'boolean) (defgroup org-link nil - "Options concerning links in Org-mode." + "Options concerning links in Org mode." :tag "Org Link" :group 'org) -(defvar org-link-abbrev-alist-local nil +(defvar-local org-link-abbrev-alist-local nil "Buffer-local version of `org-link-abbrev-alist', which see. The value of this is taken from the #+LINK lines.") -(make-variable-buffer-local 'org-link-abbrev-alist-local) + +(defcustom org-link-parameters + '(("doi" :follow org--open-doi-link) + ("elisp" :follow org--open-elisp-link) + ("file" :complete org-file-complete-link) + ("ftp" :follow (lambda (path) (browse-url (concat "ftp:" path)))) + ("help" :follow org--open-help-link) + ("http" :follow (lambda (path) (browse-url (concat "http:" path)))) + ("https" :follow (lambda (path) (browse-url (concat "https:" path)))) + ("mailto" :follow (lambda (path) (browse-url (concat "mailto:" path)))) + ("message" :follow (lambda (path) (browse-url (concat "message:" path)))) + ("news" :follow (lambda (path) (browse-url (concat "news:" path)))) + ("shell" :follow org--open-shell-link)) + "An alist of properties that defines all the links in Org mode. +The key in each association is a string of the link type. +Subsequent optional elements make up a p-list of link properties. + +:follow - A function that takes the link path as an argument. + +:export - A function that takes the link path, description and +export-backend as arguments. + +:store - A function responsible for storing the link. See the +function `org-store-link-functions'. + +:complete - A function that inserts a link with completion. The +function takes one optional prefix arg. + +:face - A face for the link, or a function that returns a face. +The function takes one argument which is the link path. The +default face is `org-link'. + +:mouse-face - The mouse-face. The default is `highlight'. + +:display - `full' will not fold the link in descriptive +display. Default is `org-link'. + +:help-echo - A string or function that takes (window object position) +as arguments and returns a string. + +:keymap - A keymap that is active on the link. The default is +`org-mouse-map'. + +:htmlize-link - A function for the htmlize-link. Defaults +to (list :uri \"type:path\") + +:activate-func - A function to run at the end of font-lock +activation. The function must accept (link-start link-end path bracketp) +as arguments." + :group 'org-link + :type '(alist :tag "Link display parameters" + :value-type plist)) + +(defun org-link-get-parameter (type key) + "Get TYPE link property for KEY. +TYPE is a string and KEY is a plist keyword." + (plist-get + (cdr (assoc type org-link-parameters)) + key)) + +(defun org-link-set-parameters (type &rest parameters) + "Set link TYPE properties to PARAMETERS. + PARAMETERS should be :key val pairs." + (let ((data (assoc type org-link-parameters))) + (if data (setcdr data (org-combine-plists (cdr data) parameters)) + (push (cons type parameters) org-link-parameters) + (org-make-link-regexps) + (org-element-update-syntax)))) + +(defun org-link-types () + "Return a list of known link types." + (mapcar #'car org-link-parameters)) (defcustom org-link-abbrev-alist nil "Alist of link abbreviations. The car of each element is a string, to be replaced at the start of a link. The cdrs are replacement values, like (\"linkkey\" . REPLACE). Abbreviated -links in Org-mode buffers can have an optional tag after a double colon, e.g. +links in Org buffers can have an optional tag after a double colon, e.g., [[linkkey:tag][description]] -The `linkkey' must be a word word, starting with a letter, followed +The `linkkey' must be a single word, starting with a letter, followed by letters, numbers, `-' or `_'. If REPLACE is a string, the tag will simply be appended to create the link. @@ -1603,11 +1911,18 @@ adaptive Use relative path for files in the current directory and sub- (const noabbrev) (const adaptive))) -(defcustom org-activate-links '(bracket angle plain radio tag date footnote) - "Types of links that should be activated in Org-mode files. -This is a list of symbols, each leading to the activation of a certain link -type. In principle, it does not hurt to turn on most link types - there may -be a small gain when turning off unused link types. The types are: +(defvaralias 'org-activate-links 'org-highlight-links) +(defcustom org-highlight-links '(bracket angle plain radio tag date footnote) + "Types of links that should be highlighted in Org files. + +This is a list of symbols, each one of them leading to the +highlighting of a certain link type. + +You can still open links that are not highlighted. + +In principle, it does not hurt to turn on highlighting for all +link types. There may be a small gain when turning off unused +link types. The types are: bracket The recommended [[link][description]] or [[link]] links with hiding. angle Links in angular brackets that may contain whitespace like @@ -1618,8 +1933,10 @@ tag Tag settings in a headline (link to tag search). date Time stamps (link to calendar). footnote Footnote labels. -Changing this variable requires a restart of Emacs to become effective." +If you set this variable during an Emacs session, use `org-mode-restart' +in the Org buffer so that the change takes effect." :group 'org-link + :group 'org-appearance :type '(set :greedy t (const :tag "Double bracket links" bracket) (const :tag "Angular bracket links" angle) @@ -1639,7 +1956,7 @@ return the description to use." :type '(choice (const nil) (function))) (defgroup org-link-store nil - "Options concerning storing links in Org-mode." + "Options concerning storing links in Org mode." :tag "Org Store Link" :group 'org-link) @@ -1684,32 +2001,36 @@ It should match if the message is from the user him/herself." (defcustom org-context-in-file-links t "Non-nil means file links from `org-store-link' contain context. -A search string will be added to the file name with :: as separator and -used to find the context when the link is activated by the command +\\ +A search string will be added to the file name with :: as separator +and used to find the context when the link is activated by the command `org-open-at-point'. When this option is t, the entire active region will be placed in the search string of the file link. If set to a positive integer, only the first n lines of context will be stored. -Using a prefix arg to the command \\[org-store-link] (`org-store-link') +Using a prefix arg to the command `org-store-link' (`\\[universal-argument] \ +\\[org-store-link]') negates this setting for the duration of the command." :group 'org-link-store :type '(choice boolean integer)) (defcustom org-keep-stored-link-after-insertion nil "Non-nil means keep link in list for entire session. - +\\ The command `org-store-link' adds a link pointing to the current location to an internal list. These links accumulate during a session. The command `org-insert-link' can be used to insert links into any -Org-mode file (offering completion for all stored links). When this -option is nil, every link which has been inserted once using \\[org-insert-link] -will be removed from the list, to make completing the unused links -more efficient." +Org file (offering completion for all stored links). + +When this option is nil, every link which has been inserted once using +`\\[org-insert-link]' will be removed from the list, to make completing the \ +unused +links more efficient." :group 'org-link-store :type 'boolean) (defgroup org-link-follow nil - "Options concerning following links in Org-mode." + "Options concerning following links in Org mode." :tag "Org Follow Link" :group 'org-link) @@ -1749,8 +2070,8 @@ In tables, the special behavior of RET has precedence." (defcustom org-mouse-1-follows-link (if (boundp 'mouse-1-click-follows-link) mouse-1-click-follows-link t) "Non-nil means mouse-1 on a link will follow the link. -A longer mouse click will still set point. Does not work on XEmacs. -Needs to be set before org.el is loaded." +A longer mouse click will still set point. Needs to be set +before org.el is loaded." :group 'org-link-follow :version "24.4" :package-version '(Org . "8.3") @@ -1766,16 +2087,22 @@ Changing this requires a restart of Emacs to work correctly." :type 'integer) (defcustom org-link-search-must-match-exact-headline 'query-to-create - "Non-nil means internal links in Org files must exactly match a headline. -When nil, the link search tries to match a phrase with all words -in the search text." + "Non-nil means internal fuzzy links can only match headlines. + +When nil, the a fuzzy link may point to a target or a named +construct in the document. When set to the special value +`query-to-create', offer to create a new headline when none +matched. + +Spaces and statistics cookies are ignored during heading searches." :group 'org-link-follow :version "24.1" :type '(choice (const :tag "Use fuzzy text search" nil) (const :tag "Match only exact headline" t) (const :tag "Match exact headline or query to create it" - query-to-create))) + query-to-create)) + :safe #'symbolp) (defcustom org-link-frame-setup '((vm . vm-visit-folder-other-frame) @@ -1836,7 +2163,7 @@ another window." "Non-nil means use indirect buffer to display infile links. Activating internal links (from one location in a file to another location in the same file) normally just jumps to the location. When the link is -activated with a \\[universal-argument] prefix (or with mouse-3), the link \ +activated with a `\\[universal-argument]' prefix (or with mouse-3), the link \ is displayed in another window. When this option is set, the other window actually displays an indirect buffer clone of the current buffer, to avoid any visibility @@ -1860,26 +2187,13 @@ window on that directory." :group 'org-link-follow :type 'boolean) -(defcustom org-link-mailto-program '(browse-url "mailto:%a?subject=%s") - "Function and arguments to call for following mailto links. -This is a list with the first element being a Lisp function, and the -remaining elements being arguments to the function. In string arguments, -%a will be replaced by the address, and %s will be replaced by the subject -if one was given like in ." - :group 'org-link-follow - :type '(choice - (const :tag "browse-url" (browse-url-mail "mailto:%a?subject=%s")) - (const :tag "compose-mail" (compose-mail "%a" "%s")) - (const :tag "message-mail" (message-mail "%a" "%s")) - (cons :tag "other" (function) (repeat :tag "argument" sexp)))) - (defcustom org-confirm-shell-link-function 'yes-or-no-p "Non-nil means ask for confirmation before executing shell links. Shell links can be dangerous: just think about a link [[shell:rm -rf ~/*][Google Search]] -This link would show up in your Org-mode document as \"Google Search\", +This link would show up in your Org document as \"Google Search\", but really it would remove your entire home directory. Therefore we advise against setting this variable to nil. Just change it to `y-or-n-p' if you want to confirm with a @@ -1891,7 +2205,7 @@ single keystroke rather than having to type \"yes\"." (const :tag "no confirmation (dangerous)" nil))) (put 'org-confirm-shell-link-function 'safe-local-variable - #'(lambda (x) (member x '(yes-or-no-p y-or-n-p)))) + (lambda (x) (member x '(yes-or-no-p y-or-n-p)))) (defcustom org-confirm-shell-link-not-regexp "" "A regexp to skip confirmation for shell links." @@ -1905,7 +2219,7 @@ Elisp links can be dangerous: just think about a link [[elisp:(shell-command \"rm -rf ~/*\")][Google Search]] -This link would show up in your Org-mode document as \"Google Search\", +This link would show up in your Org document as \"Google Search\", but really it would remove your entire home directory. Therefore we advise against setting this variable to nil. Just change it to `y-or-n-p' if you want to confirm with a @@ -1917,7 +2231,7 @@ single keystroke rather than having to type \"yes\"." (const :tag "no confirmation (dangerous)" nil))) (put 'org-confirm-shell-link-function 'safe-local-variable - #'(lambda (x) (member x '(yes-or-no-p y-or-n-p)))) + (lambda (x) (member x '(yes-or-no-p y-or-n-p)))) (defcustom org-confirm-elisp-link-not-regexp "" "A regexp to skip confirmation for Elisp links." @@ -1934,30 +2248,23 @@ See `org-file-apps'.") (defconst org-file-apps-defaults-macosx '((remote . emacs) - (t . "open %s") (system . "open %s") ("ps.gz" . "gv %s") ("eps.gz" . "gv %s") ("dvi" . "xdvi %s") - ("fig" . "xfig %s")) + ("fig" . "xfig %s") + (t . "open %s")) "Default file applications on a macOS system. The system \"open\" is known as a default, but we use X11 applications for some files for which the OS does not have a good default. See `org-file-apps'.") (defconst org-file-apps-defaults-windowsnt - (list - '(remote . emacs) - (cons t - (list (if (featurep 'xemacs) - 'mswindows-shell-execute - 'w32-shell-execute) - "open" 'file)) - (cons 'system - (list (if (featurep 'xemacs) - 'mswindows-shell-execute - 'w32-shell-execute) - "open" 'file))) + (list '(remote . emacs) + (cons 'system (lambda (file _path) + (with-no-warnings (w32-shell-execute "open" file)))) + (cons t (lambda (file _path) + (with-no-warnings (w32-shell-execute "open" file))))) "Default file applications on a Windows NT system. The system \"open\" is used for most files. See `org-file-apps'.") @@ -1968,11 +2275,15 @@ See `org-file-apps'.") ("\\.x?html?\\'" . default) ("\\.pdf\\'" . default)) "External applications for opening `file:path' items in a document. -Org-mode uses system defaults for different file types, but +\\\ + +Org mode uses system defaults for different file types, but you can use this variable to set the application for a given file extension. The entries in this list are cons cells where the car identifies -files and the cdr the corresponding command. Possible values for the -file identifier are +files and the cdr the corresponding command. + +Possible values for the file identifier are: + \"string\" A string as a file identifier can be interpreted in different ways, depending on its contents: @@ -1985,8 +2296,8 @@ file identifier are filename matches the regexp. If you want to use groups here, use shy groups. - Example: (\"\\.x?html\\\\='\" . \"firefox %s\") - (\"\\(?:xhtml\\|html\\)\" . \"firefox %s\") + Example: (\"\\\\.x?html\\\\\\='\" . \"firefox %s\") + (\"\\\\(?:xhtml\\\\|html\\\\)\\\\\\='\" . \"firefox %s\") to open *.html and *.xhtml with firefox. - Regular expression which contains (non-shy) groups: @@ -1998,10 +2309,11 @@ file identifier are that does not use any of the group matches, this case is handled identically to the second one (i.e. match against file name only). - In a custom lisp form, you can access the group matches with + In a custom function, you can access the group matches with (match-string n link). - Example: (\"\\.pdf::\\(\\d+\\)\\\\='\" . \"evince -p %1 %s\") + Example: (\"\\\\.pdf::\\\\(\\\\d+\\\\)\\\\\\='\" . \ +\"evince -p %1 %s\") to open [[file:document.pdf::5]] with evince at page 5. `directory' Matches a directory @@ -2013,28 +2325,32 @@ file identifier are command `emacs' will open most files in Emacs. Beware that this will also open html files inside Emacs, unless you add (\"html\" . default) to the list as well. - t Default for files not matched by any of the other options. `system' The system command to open files, like `open' on Windows and macOS, and mailcap under GNU/Linux. This is the command - that will be selected if you call `C-c C-o' with a double - \\[universal-argument] \\[universal-argument] prefix. + that will be selected if you call `org-open-at-point' with a + double prefix argument (`\\[universal-argument] \ +\\[universal-argument] \\[org-open-at-point]'). + t Default for files not matched by any of the other options. Possible values for the command are: + `emacs' The file will be visited by the current Emacs process. `default' Use the default application for this file type, which is the association for t in the list, most likely in the system-specific - part. - This can be used to overrule an unwanted setting in the + part. This can be used to overrule an unwanted setting in the system-specific variable. `system' Use the system command for opening files, like \"open\". This command is specified by the entry whose car is `system'. Most likely, the system-specific version of this variable does define this command, but you can overrule/replace it here. +`mailcap' Use command specified in the mailcaps. string A command to be executed by a shell; %s will be replaced by the path to the file. - sexp A Lisp form which will be evaluated. The file path will - be available in the Lisp variable `file'. + function A Lisp function, which will be called with two arguments: + the file path and the original link string, without the + \"file:\" prefix. + For more examples, see the system specific constants `org-file-apps-defaults-macosx' `org-file-apps-defaults-windowsnt' @@ -2054,7 +2370,7 @@ For more examples, see the system specific constants (const :tag "Use default" default) (const :tag "Use the system command" system) (string :tag "Command") - (sexp :tag "Lisp form"))))) + (function :tag "Function"))))) (defcustom org-doi-server-url "http://dx.doi.org/" "The URL of the DOI server." @@ -2063,22 +2379,22 @@ For more examples, see the system specific constants :group 'org-link-follow) (defgroup org-refile nil - "Options concerning refiling entries in Org-mode." + "Options concerning refiling entries in Org mode." :tag "Org Refile" :group 'org) (defcustom org-directory "~/org" - "Directory with org files. + "Directory with Org files. This is just a default location to look for Org files. There is no need -at all to put your files into this directory. It is only used in the +at all to put your files into this directory. It is used in the following situations: 1. When a capture template specifies a target file that is not an absolute path. The path will then be interpreted relative to `org-directory' -2. When a capture note is filed away in an interactive way (when exiting the - note buffer with `C-1 C-c C-c'. The user is prompted for an org file, - with `org-directory' as the default path." +2. When the value of variable `org-agenda-files' is a single file, any + relative paths in this file will be taken as relative to + `org-directory'." :group 'org-refile :group 'org-capture :type 'directory) @@ -2089,9 +2405,7 @@ Used as a fall back file for org-capture.el, for templates that do not specify a target file." :group 'org-refile :group 'org-capture - :type '(choice - (const :tag "Default from remember-data-file" nil) - file)) + :type 'file) (defcustom org-goto-interface 'outline "The default interface to be used for `org-goto'. @@ -2154,7 +2468,7 @@ will temporarily be changed to `time'." (const :tag "Record timestamp with note." note))) (defcustom org-refile-targets nil - "Targets for refiling entries with \\[org-refile]. + "Targets for refiling entries with `\\[org-refile]'. This is a list of cons cells. Each cell contains: - a specification of the files to be considered, either a list of files, or a symbol whose function or variable value will be used to retrieve @@ -2218,12 +2532,15 @@ of the subtree." (defcustom org-refile-use-cache nil "Non-nil means cache refile targets to speed up the process. +\\\ The cache for a particular file will be updated automatically when the buffer has been killed, or when any of the marker used for flagging refile targets no longer points at a live buffer. If you have added new entries to a buffer that might themselves be targets, -you need to clear the cache manually by pressing `C-0 C-c C-w' or, if you -find that easier, `C-u C-u C-u C-c C-w'." +you need to clear the cache manually by pressing `C-0 \\[org-refile]' or, +if you find that easier, \ +`\\[universal-argument] \\[universal-argument] \\[universal-argument] \ +\\[org-refile]'." :group 'org-refile :version "24.1" :type 'boolean) @@ -2246,13 +2563,13 @@ When `full-file-path', include the full file path." (defcustom org-outline-path-complete-in-steps t "Non-nil means complete the outline path in hierarchical steps. -When Org-mode uses the refile interface to select an outline path -\(see variable `org-refile-use-outline-path'), the completion of -the path can be done is a single go, or if can be done in steps down -the headline hierarchy. Going in steps is probably the best if you -do not use a special completion package like `ido' or `icicles'. -However, when using these packages, going in one step can be very -fast, while still showing the whole path to the entry." +When Org uses the refile interface to select an outline path (see +`org-refile-use-outline-path'), the completion of the path can be +done in a single go, or it can be done in steps down the headline +hierarchy. Going in steps is probably the best if you do not use +a special completion package like `ido' or `icicles'. However, +when using these packages, going in one step can be very fast, +while still showing the whole path to the entry." :group 'org-refile :type 'boolean) @@ -2285,12 +2602,12 @@ converted to a headline before refiling." :type 'boolean) (defgroup org-todo nil - "Options concerning TODO items in Org-mode." + "Options concerning TODO items in Org mode." :tag "Org TODO" :group 'org) (defgroup org-progress nil - "Options concerning Progress logging in Org-mode." + "Options concerning Progress logging in Org mode." :tag "Org Progress" :group 'org-time) @@ -2308,12 +2625,12 @@ Each sequence starts with a symbol, either `sequence' or `type', indicating if the keywords should be interpreted as a sequence of action steps, or as different types of TODO items. The first keywords are states requiring action - these states will select a headline -for inclusion into the global TODO list Org-mode produces. If one of -the \"keywords\" is the vertical bar, \"|\", the remaining keywords +for inclusion into the global TODO list Org produces. If one of the +\"keywords\" is the vertical bar, \"|\", the remaining keywords signify that no further action is necessary. If \"|\" is not found, the last keyword is treated as the only DONE state of the sequence. -The command \\[org-todo] cycles an entry through these states, and one +The command `\\[org-todo]' cycles an entry through these states, and one additional state where no keyword is present. For details about this cycling, see the manual. @@ -2356,44 +2673,37 @@ taken from the (otherwise obsolete) variable `org-todo-interpretation'." (lambda (widget) (widget-put widget :args (mapcar - #'(lambda (x) - (widget-convert - (cons 'const x))) + (lambda (x) + (widget-convert + (cons 'const x))) org-todo-interpretation-widgets)) widget)) (repeat (string :tag "Keyword")))))) -(defvar org-todo-keywords-1 nil +(defvar-local org-todo-keywords-1 nil "All TODO and DONE keywords active in a buffer.") -(make-variable-buffer-local 'org-todo-keywords-1) (defvar org-todo-keywords-for-agenda nil) (defvar org-done-keywords-for-agenda nil) -(defvar org-drawers-for-agenda nil) (defvar org-todo-keyword-alist-for-agenda nil) (defvar org-tag-alist-for-agenda nil "Alist of all tags from all agenda files.") (defvar org-tag-groups-alist-for-agenda nil "Alist of all groups tags from all current agenda files.") -(defvar org-tag-groups-alist nil) -(make-variable-buffer-local 'org-tag-groups-alist) +(defvar-local org-tag-groups-alist nil) (defvar org-agenda-contributing-files nil) -(defvar org-not-done-keywords nil) -(make-variable-buffer-local 'org-not-done-keywords) -(defvar org-done-keywords nil) -(make-variable-buffer-local 'org-done-keywords) -(defvar org-todo-heads nil) -(make-variable-buffer-local 'org-todo-heads) -(defvar org-todo-sets nil) -(make-variable-buffer-local 'org-todo-sets) -(defvar org-todo-log-states nil) -(make-variable-buffer-local 'org-todo-log-states) -(defvar org-todo-kwd-alist nil) -(make-variable-buffer-local 'org-todo-kwd-alist) -(defvar org-todo-key-alist nil) -(make-variable-buffer-local 'org-todo-key-alist) -(defvar org-todo-key-trigger nil) -(make-variable-buffer-local 'org-todo-key-trigger) +(defvar-local org-current-tag-alist nil + "Alist of all tag groups in current buffer. +This variable takes into consideration `org-tag-alist', +`org-tag-persistent-alist' and TAGS keywords in the buffer.") +(defvar-local org-not-done-keywords nil) +(defvar-local org-done-keywords nil) +(defvar-local org-todo-heads nil) +(defvar-local org-todo-sets nil) +(defvar-local org-todo-log-states nil) +(defvar-local org-todo-kwd-alist nil) +(defvar-local org-todo-key-alist nil) +(defvar-local org-todo-key-trigger nil) (defcustom org-todo-interpretation 'sequence "Controls how TODO keywords are interpreted. @@ -2407,7 +2717,8 @@ more information." (const type))) (defcustom org-use-fast-todo-selection t - "Non-nil means use the fast todo selection scheme with C-c C-t. + "\\\ +Non-nil means use the fast todo selection scheme with `\\[org-todo]'. This variable describes if and under what circumstances the cycling mechanism for TODO keywords will be replaced by a single-key, direct selection scheme. @@ -2415,8 +2726,9 @@ selection scheme. When nil, fast selection is never used. When the symbol `prefix', it will be used when `org-todo' is called -with a prefix argument, i.e. `C-u C-c C-t' in an Org-mode buffer, and -`C-u t' in an agenda buffer. +with a prefix argument, i.e. `\\[universal-argument] \\[org-todo]' \ +in an Org buffer, and +`\\[universal-argument] t' in an agenda buffer. When t, fast selection is used by default. In this case, the prefix argument forces cycling instead. @@ -2436,6 +2748,9 @@ ALL-HEADLINES means update todo statistics by including headlines with no TODO keyword as well, counting them as not done. A list of TODO keywords means the same, but skip keywords that are not in this list. +When set to a list of two lists, the first list contains keywords +to consider as TODO keywords, the second list contains keywords +to consider as DONE keywords. When this is set, todo statistics is updated in the parent of the current entry each time a todo state is changed." @@ -2445,6 +2760,9 @@ current entry each time a todo state is changed." (const :tag "Yes, including all entries" all-headlines) (repeat :tag "Yes, for TODOs in this list" (string :tag "TODO keyword")) + (list :tag "Yes, for TODOs and DONEs in these lists" + (repeat (string :tag "TODO keyword")) + (repeat (string :tag "DONE keyword"))) (other :tag "No TODO statistics" nil))) (defcustom org-hierarchical-todo-statistics t @@ -2529,7 +2847,7 @@ to change is while Emacs is running is through the customize interface." (defcustom org-treat-insert-todo-heading-as-state-change nil "Non-nil means inserting a TODO heading is treated as state change. -So when the command \\[org-insert-todo-heading] is used, state change +So when the command `\\[org-insert-todo-heading]' is used, state change logging will apply if appropriate. When nil, the new TODO item will be inserted directly, and no logging will take place." :group 'org-todo @@ -2667,20 +2985,23 @@ When nil, only the date will be recorded." (refile . "Refiled on %t") (clock-out . "")) "Headings for notes added to entries. -The value is an alist, with the car being a symbol indicating the note -context, and the cdr is the heading to be used. The heading may also be the -empty string. -%t in the heading will be replaced by a time stamp. -%T will be an active time stamp instead the default inactive one -%d will be replaced by a short-format time stamp. -%D will be replaced by an active short-format time stamp. -%s will be replaced by the new TODO state, in double quotes. -%S will be replaced by the old TODO state, in double quotes. -%u will be replaced by the user name. -%U will be replaced by the full user name. - -In fact, it is not a good idea to change the `state' entry, because -agenda log mode depends on the format of these entries." + +The value is an alist, with the car being a symbol indicating the +note context, and the cdr is the heading to be used. The heading +may also be the empty string. The following placeholders can be +used: + + %t a time stamp. + %T an active time stamp instead the default inactive one + %d a short-format time stamp. + %D an active short-format time stamp. + %s the new TODO state or time stamp (inactive), in double quotes. + %S the old TODO state or time stamp (inactive), in double quotes. + %u the user name. + %U full user name. + +In fact, it is not a good idea to change the `state' entry, +because Agenda Log mode depends on the format of these entries." :group 'org-todo :group 'org-progress :type '(list :greedy t @@ -2719,7 +3040,10 @@ If this variable is set, `org-log-state-notes-insert-after-drawers' will be ignored. You can set the property LOG_INTO_DRAWER to overrule this setting for -a subtree." +a subtree. + +Do not check directly this variable in a Lisp program. Call +function `org-log-into-drawer' instead." :group 'org-todo :group 'org-progress :type '(choice @@ -2727,18 +3051,20 @@ a subtree." (const :tag "LOGBOOK" t) (string :tag "Other"))) -(org-defvaralias 'org-log-state-notes-into-drawer 'org-log-into-drawer) +(defvaralias 'org-log-state-notes-into-drawer 'org-log-into-drawer) (defun org-log-into-drawer () - "Return the value of `org-log-into-drawer', but let properties overrule. -If the current entry has or inherits a LOG_INTO_DRAWER property, it will be -used instead of the default value." + "Name of the log drawer, as a string, or nil. +This is the value of `org-log-into-drawer'. However, if the +current entry has or inherits a LOG_INTO_DRAWER property, it will +be used instead of the default value." (let ((p (org-entry-get nil "LOG_INTO_DRAWER" 'inherit t))) - (cond - ((not p) org-log-into-drawer) - ((equal p "nil") nil) - ((equal p "t") "LOGBOOK") - (t p)))) + (cond ((equal p "nil") nil) + ((equal p "t") "LOGBOOK") + ((stringp p) p) + (p "LOGBOOK") + ((stringp org-log-into-drawer) org-log-into-drawer) + (org-log-into-drawer "LOGBOOK")))) (defcustom org-log-state-notes-insert-after-drawers nil "Non-nil means insert state change notes after any drawers in entry. @@ -2804,7 +3130,7 @@ property to one or more of these keywords." (defgroup org-priorities nil - "Priorities in Org-mode." + "Priorities in Org mode." :tag "Org Priorities" :group 'org-todo) @@ -2862,24 +3188,13 @@ as an argument and return the numeric priority." (function))) (defgroup org-time nil - "Options concerning time stamps and deadlines in Org-mode." + "Options concerning time stamps and deadlines in Org mode." :tag "Org Time" :group 'org) -(defcustom org-insert-labeled-timestamps-at-point nil - "Non-nil means SCHEDULED and DEADLINE timestamps are inserted at point. -When nil, these labeled time stamps are forces into the second line of an -entry, just after the headline. When scheduling from the global TODO list, -the time stamp will always be forced into the second line." - :group 'org-time - :type 'boolean) - -(defconst org-time-stamp-formats '("<%Y-%m-%d %a>" . "<%Y-%m-%d %a %H:%M>") - "Formats for `format-time-string' which are used for time stamps. -It is not recommended to change this constant.") - (defcustom org-time-stamp-rounding-minutes '(0 5) "Number of minutes to round time stamps to. +\\\ These are two values, the first applies when first creating a time stamp. The second applies when changing it with the commands `S-up' and `S-down'. When changing the time stamp, this means that it will change in steps @@ -2889,14 +3204,15 @@ When a setting is 0 or 1, insert the time unmodified. Useful rounding numbers should be factors of 60, so for example 5, 10, 15. When this is larger than 1, you can still force an exact time stamp by using -a double prefix argument to a time stamp command like `C-c .' or `C-c !', +a double prefix argument to a time stamp command like \ +`\\[org-time-stamp]' or `\\[org-time-stamp-inactive], and by using a prefix arg to `S-up/down' to specify the exact number of minutes to shift." :group 'org-time - :get #'(lambda (var) ; Make sure both elements are there - (if (integerp (default-value var)) - (list (default-value var) 5) - (default-value var))) + :get (lambda (var) ; Make sure both elements are there + (if (integerp (default-value var)) + (list (default-value var) 5) + (default-value var))) :type '(list (integer :tag "when inserting times") (integer :tag "when modifying times"))) @@ -3013,7 +3329,7 @@ in minutes (even for durations longer than an hour)." (const t))))) (defcustom org-time-clocksum-use-fractional nil - "When non-nil, \\[org-clock-display] uses fractional times. + "When non-nil, `\\[org-clock-display]' uses fractional times. See `org-time-clocksum-format' for more on time clock formats." :group 'org-time :group 'org-clock @@ -3021,7 +3337,7 @@ See `org-time-clocksum-format' for more on time clock formats." :type 'boolean) (defcustom org-time-clocksum-use-effort-durations nil - "When non-nil, \\[org-clock-display] uses effort durations. + "When non-nil, `\\[org-clock-display]' uses effort durations. E.g. by default, one day is considered to be a 8 hours effort, so a task that has been clocked for 16 hours will be displayed as during 2 days in the clock display or in the clocktable. @@ -3052,9 +3368,9 @@ is used." :group 'org-time :type '(choice (string :tag "Format string") (set (group :inline t (const :tag "Years" :years) - (string :tag "Format string")) + (string :tag "Format string")) (group :inline t (const :tag "Months" :months) - (string :tag "Format string")) + (string :tag "Format string")) (group :inline t (const :tag "Weeks" :weeks) (string :tag "Format string")) (group :inline t (const :tag "Days" :days) @@ -3097,8 +3413,8 @@ This affects the following situations: For example, if it is April and you enter \"feb 2\", this will be read as Feb 2, *next* year. \"May 5\", however, will be this year. 2. The user gives a day, but no month. - For example, if today is the 15th, and you enter \"3\", Org-mode will - read this as the third of *next* month. However, if you enter \"17\", + For example, if today is the 15th, and you enter \"3\", Org will read + this as the third of *next* month. However, if you enter \"17\", it will be considered as *this* month. If you set this variable to the symbol `time', then also the following @@ -3176,22 +3492,9 @@ In the calendar, the date can be selected with mouse-1. However, the minibuffer will also be active, and you can simply enter the date as well. When nil, only the minibuffer will be available." :group 'org-time - :type 'boolean) -(org-defvaralias 'org-popup-calendar-for-date-prompt - 'org-read-date-popup-calendar) - -(make-obsolete-variable - 'org-read-date-minibuffer-setup-hook - "Set `org-read-date-minibuffer-local-map' instead." "24.4") -(defcustom org-read-date-minibuffer-setup-hook nil - "Hook to be used to set up keys for the date/time interface. -Add key definitions to `minibuffer-local-map', which will be a -temporary copy. - -WARNING: This option is obsolete, you should use -`org-read-date-minibuffer-local-map' to set up keys." - :group 'org-time - :type 'hook) + :type 'boolean) +(defvaralias 'org-popup-calendar-for-date-prompt + 'org-read-date-popup-calendar) (defcustom org-extend-today-until 0 "The hour when your day really ends. Must be an integer. @@ -3240,52 +3543,76 @@ moved to the new date." :type 'boolean) (defgroup org-tags nil - "Options concerning tags in Org-mode." + "Options concerning tags in Org mode." :tag "Org Tags" :group 'org) (defcustom org-tag-alist nil - "List of tags allowed in Org-mode files. -When this list is nil, Org-mode will base TAG input on what is already in the -buffer. -The value of this variable is an alist, the car of each entry must be a -keyword as a string, the cdr may be a character that is used to select -that tag through the fast-tag-selection interface. -See the manual for details." + "Default tags available in Org files. + +The value of this variable is an alist. Associations either: + + (TAG) + (TAG . SELECT) + (SPECIAL) + +where TAG is a tag as a string, SELECT is character, used to +select that tag through the fast tag selection interface, and +SPECIAL is one of the following keywords: `:startgroup', +`:startgrouptag', `:grouptags', `:engroup', `:endgrouptag' or +`:newline'. These keywords are used to define a hierarchy of +tags. See manual for details. + +When this variable is nil, Org mode bases tag input on what is +already in the buffer. The value can be overridden locally by +using a TAGS keyword, e.g., + + #+TAGS: tag1 tag2 + +See also `org-tag-persistent-alist' to sidestep this behavior." :group 'org-tags :type '(repeat (choice (cons (string :tag "Tag name") (character :tag "Access char")) - (list :tag "Start radio group" - (const :startgroup) - (option (string :tag "Group description"))) - (list :tag "Group tags delimiter" - (const :grouptags)) - (list :tag "End radio group" - (const :endgroup) - (option (string :tag "Group description"))) + (const :tag "Start radio group" (:startgroup)) + (const :tag "Start tag group, non distinct" (:startgrouptag)) + (const :tag "Group tags delimiter" (:grouptags)) + (const :tag "End radio group" (:endgroup)) + (const :tag "End tag group, non distinct" (:endgrouptag)) (const :tag "New line" (:newline))))) (defcustom org-tag-persistent-alist nil - "List of tags that will always appear in all Org-mode files. -This is in addition to any in buffer settings or customizations -of `org-tag-alist'. -When this list is nil, Org-mode will base TAG input on `org-tag-alist'. -The value of this variable is an alist, the car of each entry must be a -keyword as a string, the cdr may be a character that is used to select -that tag through the fast-tag-selection interface. -See the manual for details. -To disable these tags on a per-file basis, insert anywhere in the file: - #+STARTUP: noptag" + "Tags always available in Org files. + +The value of this variable is an alist. Associations either: + + (TAG) + (TAG . SELECT) + (SPECIAL) + +where TAG is a tag as a string, SELECT is a character, used to +select that tag through the fast tag selection interface, and +SPECIAL is one of the following keywords: `:startgroup', +`:startgrouptag', `:grouptags', `:engroup', `:endgrouptag' or +`:newline'. These keywords are used to define a hierarchy of +tags. See manual for details. + +Unlike to `org-tag-alist', tags defined in this variable do not +depend on a local TAGS keyword. Instead, to disable these tags +on a per-file basis, insert anywhere in the file: + + #+STARTUP: noptag" :group 'org-tags :type '(repeat (choice - (cons (string :tag "Tag name") - (character :tag "Access char")) + (cons (string :tag "Tag name") + (character :tag "Access char")) (const :tag "Start radio group" (:startgroup)) + (const :tag "Start tag group, non distinct" (:startgrouptag)) (const :tag "Group tags delimiter" (:grouptags)) (const :tag "End radio group" (:endgroup)) + (const :tag "End tag group, non distinct" (:endgrouptag)) (const :tag "New line" (:newline))))) (defcustom org-complete-tags-always-offer-all-agenda-tags nil @@ -3296,9 +3623,7 @@ tags in that file can be created dynamically (there are none). (add-hook \\='org-capture-mode-hook (lambda () - (set (make-local-variable - \\='org-complete-tags-always-offer-all-agenda-tags) - t)))" + (setq-local org-complete-tags-always-offer-all-agenda-tags t)))" :group 'org-tags :version "24.1" :type 'boolean) @@ -3340,7 +3665,7 @@ displaying the tags menu is not even shown, until you press C-c again." "Non-nil means fast tags selection interface will also offer TODO states. This is an undocumented feature, you should not rely on it.") -(defcustom org-tags-column (if (featurep 'xemacs) -76 -77) +(defcustom org-tags-column -77 "The column to which tags should be indented in a headline. If this number is positive, it specifies the column. If it is negative, it means that the tags should be flushright to that column. For example, @@ -3437,7 +3762,7 @@ is better to limit inheritance to certain tags using the variables "Hook that is run after the tags in a line have changed.") (defgroup org-properties nil - "Options concerning properties in Org-mode." + "Options concerning properties in Org mode." :tag "Org Properties" :group 'org) @@ -3504,14 +3829,14 @@ in this variable)." (regexp :tag "Properties matched by regexp"))) (defun org-property-inherit-p (property) - "Check if PROPERTY is one that should be inherited." + "Return a non-nil value if PROPERTY should be inherited." (cond ((eq org-use-property-inheritance t) t) ((not org-use-property-inheritance) nil) ((stringp org-use-property-inheritance) (string-match org-use-property-inheritance property)) ((listp org-use-property-inheritance) - (member property org-use-property-inheritance)) + (member-ignore-case property org-use-property-inheritance)) (t (error "Invalid setting of `org-use-property-inheritance'")))) (defcustom org-columns-default-format "%25ITEM %TODO %3PRIORITY %TAGS" @@ -3532,26 +3857,6 @@ ellipses string, only part of the ellipses string will be shown." :group 'org-properties :type 'string) -(defcustom org-columns-modify-value-for-display-function nil - "Function that modifies values for display in column view. -For example, it can be used to cut out a certain part from a time stamp. -The function must take 2 arguments: - -column-title The title of the column (*not* the property name) -value The value that should be modified. - -The function should return the value that should be displayed, -or nil if the normal value should be used." - :group 'org-properties - :type '(choice (const nil) (function))) - -(defcustom org-effort-property "Effort" - "The property that is being used to keep track of effort estimates. -Effort estimates given in this property need to have the format H:MM." - :group 'org-properties - :group 'org-progress - :type '(string :tag "Property")) - (defconst org-global-properties-fixed '(("VISIBILITY_ALL" . "folded children content all") ("CLOCK_MODELINE_TOTAL_ALL" . "current today repeat all auto")) @@ -3582,18 +3887,17 @@ You can set buffer-local values for the same purpose in the variable (cons (string :tag "Property") (string :tag "Value")))) -(defvar org-file-properties nil +(defvar-local org-file-properties nil "List of property/value pairs that can be inherited by any entry. Valid for the current buffer. This variable is populated from #+PROPERTY lines.") -(make-variable-buffer-local 'org-file-properties) (defgroup org-agenda nil - "Options concerning agenda views in Org-mode." + "Options concerning agenda views in Org mode." :tag "Org Agenda" :group 'org) -(defvar org-category nil +(defvar-local org-category nil "Variable used by org files to set a category for agenda display. Such files should use a file variable to set it, for example @@ -3605,22 +3909,22 @@ or contain a special line If the file does not specify a category, then file's base name is used instead.") -(make-variable-buffer-local 'org-category) -(put 'org-category 'safe-local-variable #'(lambda (x) (or (symbolp x) (stringp x)))) +(put 'org-category 'safe-local-variable (lambda (x) (or (symbolp x) (stringp x)))) (defcustom org-agenda-files nil "The files to be used for agenda display. -Entries may be added to this list with \\[org-agenda-file-to-front] and removed with -\\[org-remove-file]. You can also use customize to edit the list. -If an entry is a directory, all files in that directory that are matched by -`org-agenda-file-regexp' will be part of the file list. +If an entry is a directory, all files in that directory that are matched +by `org-agenda-file-regexp' will be part of the file list. If the value of the variable is not a list but a single file name, then -the list of agenda files is actually stored and maintained in that file, one -agenda file per line. In this file paths can be given relative to +the list of agenda files is actually stored and maintained in that file, +one agenda file per line. In this file paths can be given relative to `org-directory'. Tilde expansion and environment variable substitution -are also made." +are also made. + +Entries may be added to this list with `\\[org-agenda-file-to-front]' +and removed with `\\[org-remove-file]'." :group 'org-agenda :type '(choice (repeat :tag "List of files and directories" file) @@ -3637,7 +3941,8 @@ regular expression will be included." (defcustom org-agenda-text-search-extra-files nil "List of extra files to be searched by text search commands. These files will be searched in addition to the agenda files by the -commands `org-search-view' (`C-c a s') and `org-occur-in-agenda-files'. +commands `org-search-view' (`\\[org-agenda] s') \ +and `org-occur-in-agenda-files'. Note that these files will only be searched for text search commands, not for the other agenda views like todo lists, tag searches or the weekly agenda. This variable is intended to list notes and possibly archive files @@ -3650,7 +3955,7 @@ scope." (const :tag "Agenda Archives" agenda-archives) (repeat :inline t (file)))) -(org-defvaralias 'org-agenda-multi-occur-extra-files +(defvaralias 'org-agenda-multi-occur-extra-files 'org-agenda-text-search-extra-files) (defcustom org-agenda-skip-unavailable-files nil @@ -3670,7 +3975,7 @@ forth between agenda and calendar." (defcustom org-calendar-insert-diary-entry-key [?i] "The key to be installed in `calendar-mode-map' for adding diary entries. This option is irrelevant until `org-agenda-diary-file' has been configured -to point to an Org-mode file. When that is the case, the command +to point to an Org file. When that is the case, the command `org-agenda-diary-entry' will be bound to the key given here, by default `i'. In the calendar, `i' normally adds entries to `diary-file'. So if you want to continue doing this, you need to change this to a different @@ -3700,7 +4005,7 @@ points to a file, `org-agenda-diary-entry' will be used instead." 'org-agenda-diary-entry)))))) (defgroup org-latex nil - "Options for embedding LaTeX code into Org-mode." + "Options for embedding LaTeX code into Org mode." :tag "Org LaTeX" :group 'org) @@ -3755,39 +4060,131 @@ Replace format-specifiers in the command as noted below and use `shell-command' to convert LaTeX to MathML. %j: Executable file in fully expanded form as specified by `org-latex-to-mathml-jar-file'. -%I: Input LaTeX file in fully expanded form -%o: Output MathML file +%I: Input LaTeX file in fully expanded form. +%i: The latex fragment to be converted. +%o: Output MathML file. + This command is used by `org-create-math-formula'. -When using MathToWeb as the converter, set this to -\"java -jar %j -unicode -force -df %o %I\"." +When using MathToWeb as the converter, set this option to +\"java -jar %j -unicode -force -df %o %I\". + +When using LaTeXML set this option to +\"latexmlmath \"%i\" --presentationmathml=%o\"." :group 'org-latex :version "24.1" :type '(choice (const :tag "None" nil) (string :tag "\nShell command"))) -(defcustom org-latex-create-formula-image-program 'dvipng - "Program to convert LaTeX fragments with. - -dvipng Process the LaTeX fragments to dvi file, then convert - dvi files to png files using dvipng. - This will also include processing of non-math environments. -imagemagick Convert the LaTeX fragments to pdf files and use imagemagick - to convert pdf files to png files" +(defcustom org-preview-latex-default-process 'dvipng + "The default process to convert LaTeX fragments to image files. +All available processes and theirs documents can be found in +`org-preview-latex-process-alist', which see." :group 'org-latex - :version "24.1" - :type '(choice - (const :tag "dvipng" dvipng) - (const :tag "imagemagick" imagemagick))) + :version "26.1" + :package-version '(Org . "9.0") + :type 'symbol) + +(defcustom org-preview-latex-process-alist + '((dvipng + :programs ("latex" "dvipng") + :description "dvi > png" + :message "you need to install the programs: latex and dvipng." + :image-input-type "dvi" + :image-output-type "png" + :image-size-adjust (1.0 . 1.0) + :latex-compiler ("latex -interaction nonstopmode -output-directory %o %f") + :image-converter ("dvipng -fg %F -bg %B -D %D -T tight -o %O %f")) + (dvisvgm + :programs ("latex" "dvisvgm") + :description "dvi > svg" + :message "you need to install the programs: latex and dvisvgm." + :use-xcolor t + :image-input-type "dvi" + :image-output-type "svg" + :image-size-adjust (1.7 . 1.5) + :latex-compiler ("latex -interaction nonstopmode -output-directory %o %f") + :image-converter ("dvisvgm %f -n -b min -c %S -o %O")) + (imagemagick + :programs ("latex" "convert") + :description "pdf > png" + :message "you need to install the programs: latex and imagemagick." + :use-xcolor t + :image-input-type "pdf" + :image-output-type "png" + :image-size-adjust (1.0 . 1.0) + :latex-compiler ("pdflatex -interaction nonstopmode -output-directory %o %f") + :image-converter + ("convert -density %D -trim -antialias %f -quality 100 %O"))) + "Definitions of external processes for LaTeX previewing. +Org mode can use some external commands to generate TeX snippet's images for +previewing or inserting into HTML files, e.g., \"dvipng\". This variable tells +`org-create-formula-image' how to call them. + +The value is an alist with the pattern (NAME . PROPERTIES). NAME is a symbol. +PROPERTIES accepts the following attributes: + + :programs list of strings, required programs. + :description string, describe the process. + :message string, message it when required programs cannot be found. + :image-input-type string, input file type of image converter (e.g., \"dvi\"). + :image-output-type string, output file type of image converter (e.g., \"png\"). + :use-xcolor boolean, when non-nil, LaTeX \"xcolor\" macro is used to + deal with background and foreground color of image. + Otherwise, dvipng style background and foregroud color + format are generated. You may then refer to them in + command options with \"%F\" and \"%B\". + :image-size-adjust cons of numbers, the car element is used to adjust LaTeX + image size showed in buffer and the cdr element is for + HTML file. This option is only useful for process + developers, users should use variable + `org-format-latex-options' instead. + :post-clean list of strings, files matched are to be cleaned up once + the image is generated. When nil, the files with \".dvi\", + \".xdv\", \".pdf\", \".tex\", \".aux\", \".log\", \".svg\", + \".png\", \".jpg\", \".jpeg\" or \".out\" extension will + be cleaned up. + :latex-header list of strings, the LaTeX header of the snippet file. + When nil, the fallback value is used instead, which is + controlled by `org-format-latex-header', + `org-latex-default-packages-alist' and + `org-latex-packages-alist', which see. + :latex-compiler list of LaTeX commands, as strings. Each of them is given + to the shell. Place-holders \"%t\", \"%b\" and \"%o\" are + replaced with values defined below. + :image-converter list of image converter commands strings. Each of them is + given to the shell and supports any of the following + place-holders defined below. + +Place-holders used by `:image-converter' and `:latex-compiler': + + %f input file name + %b base name of input file + %o base directory of input file + %O absolute output file name + +Place-holders only used by `:image-converter': + + %F foreground of image + %B background of image + %D dpi, which is used to adjust image size by some processing commands. + %S the image size scale ratio, which is used to adjust image size by some + processing commands." + :group 'org-latex + :version "26.1" + :package-version '(Org . "9.0") + :type '(alist :tag "LaTeX to image backends" + :value-type (plist))) -(defcustom org-latex-preview-ltxpng-directory "ltxpng/" +(defcustom org-preview-latex-image-directory "ltximg/" "Path to store latex preview images. A relative path here creates many directories relative to the processed org files paths. An absolute path puts all preview images at the same place." :group 'org-latex - :version "24.3" + :version "26.1" + :package-version '(Org . "9.0") :type 'string) (defun org-format-latex-mathml-available-p () @@ -3805,8 +4202,8 @@ images at the same place." (defcustom org-format-latex-header "\\documentclass{article} \\usepackage[usenames]{color} -[PACKAGES] -[DEFAULT-PACKAGES] +\[PACKAGES] +\[DEFAULT-PACKAGES] \\pagestyle{empty} % do not remove % The settings below are copied from fullpage.sty \\setlength{\\textwidth}{\\paperwidth} @@ -3847,22 +4244,19 @@ header, or they will be appended." (default-value var))) (defcustom org-latex-default-packages-alist - '(("AUTO" "inputenc" t) - ("T1" "fontenc" t) - ("" "fixltx2e" nil) + '(("AUTO" "inputenc" t ("pdflatex")) + ("T1" "fontenc" t ("pdflatex")) ("" "graphicx" t) + ("" "grffile" t) ("" "longtable" nil) - ("" "float" nil) ("" "wrapfig" nil) ("" "rotating" nil) ("normalem" "ulem" t) ("" "amsmath" t) ("" "textcomp" t) - ("" "marvosym" t) - ("" "wasysym" t) ("" "amssymb" t) - ("" "hyperref" nil) - "\\tolerance=1000") + ("" "capt-of" nil) + ("" "hyperref" nil)) "Alist of default packages to be inserted in the header. Change this only if one of the packages here causes an @@ -3872,16 +4266,17 @@ The packages in this list are needed by one part or another of Org mode to function properly: - inputenc, fontenc: for basic font and character selection -- fixltx2e: Important patches of LaTeX itself - graphicx: for including images +- grffile: allow periods and spaces in graphics file names - longtable: For multipage tables -- float, wrapfig: for figure placement +- wrapfig: for figure placement - rotating: for sideways figures and tables - ulem: for underline and strike-through - amsmath: for subscript and superscript and math environments -- textcomp, marvosymb, wasysym, amssymb: for various symbols used +- textcomp, amssymb: for various symbols used for interpreting the entities in `org-entities'. You can skip some of these packages if you don't use any of their symbols. +- capt-of: for captions outside of floats - hyperref: for cross references Therefore you should not modify this variable unless you know @@ -3890,20 +4285,24 @@ you might be loading some other package that conflicts with one of the default packages. Each element is either a cell or a string. -A cell is of the format: +A cell is of the format - ( \"options\" \"package\" SNIPPET-FLAG). + (\"options\" \"package\" SNIPPET-FLAG COMPILERS) If SNIPPET-FLAG is non-nil, the package also needs to be included when compiling LaTeX snippets into images for inclusion into -non-LaTeX output. +non-LaTeX output. COMPILERS is a list of compilers that should +include the package, see `org-latex-compiler'. If the document +compiler is not in the list, and the list is non-nil, the package +will not be inserted in the final document. A string will be inserted as-is in the header of the document." :group 'org-latex :group 'org-export-latex :set 'org-set-packages-alist :get 'org-get-packages-alist - :version "24.1" + :version "26.1" + :package-version '(Org . "8.3") :type '(repeat (choice (list :tag "options/package pair" @@ -3947,7 +4346,7 @@ Make sure that you only list packages here which: (string :tag "A line of LaTeX")))) (defgroup org-appearance nil - "Settings for Org-mode appearance." + "Settings for Org mode appearance." :tag "Org Appearance" :group 'org) @@ -4038,6 +4437,11 @@ following symbols: :group 'org-appearance :type 'boolean) +(defcustom org-hide-macro-markers nil + "Non-nil mean font-lock should hide the brackets marking macro calls." + :group 'org-appearance + :type 'boolean) + (defcustom org-pretty-entities nil "Non-nil means show entities as UTF8 characters. When nil, the \\name form remains in the buffer." @@ -4124,7 +4528,7 @@ After a match, the match groups contain these elements: ;; set this option proved cumbersome. See this message/thread: ;; http://article.gmane.org/gmane.emacs.orgmode/68681 (defvar org-emphasis-regexp-components - '(" \t('\"{" "- \t.,:!?;'\")}\\" " \t\r\n,\"'" "." 1) + '(" \t('\"{" "- \t.,:!?;'\")}\\[" " \t\r\n" "." 1) "Components used to build the regular expression for emphasis. This is a list with five entries. Terminology: In an emphasis string like \" *strong word* \", we call the initial space PREMATCH, the final @@ -4142,17 +4546,17 @@ newline The maximum number of newlines allowed in an emphasis exp. You need to reload Org or to restart Emacs after customizing this.") (defcustom org-emphasis-alist - `(("*" bold) + '(("*" bold) ("/" italic) ("_" underline) ("=" org-verbatim verbatim) ("~" org-code verbatim) - ("+" ,(if (featurep 'xemacs) 'org-table '(:strike-through t)))) + ("+" (:strike-through t))) "Alist of characters and faces to emphasize text. Text starting and ending with a special character will be emphasized, for example *bold*, _underlined_ and /italic/. This variable sets the marker characters and the face to be used by font-lock for highlighting -in Org-mode Emacs buffers. +in Org buffers. You need to reload Org or to restart Emacs after customizing this." :group 'org-appearance @@ -4167,122 +4571,68 @@ You need to reload Org or to restart Emacs after customizing this." (plist :tag "Face property list")) (option (const verbatim))))) -(defvar org-protecting-blocks - '("src" "example" "latex" "ascii" "html" "ditaa" "dot" "r" "R") +(defvar org-protecting-blocks '("src" "example" "export") "Blocks that contain text that is quoted, i.e. not processed as Org syntax. This is needed for font-lock setup.") -;;; Miscellaneous options - -(defgroup org-completion nil - "Completion in Org-mode." - :tag "Org Completion" - :group 'org) - -(defcustom org-completion-use-ido nil - "Non-nil means use ido completion wherever possible. -Note that `ido-mode' must be active for this variable to be relevant. -If you decide to turn this variable on, you might well want to turn off -`org-outline-path-complete-in-steps'. -See also `org-completion-use-iswitchb'." - :group 'org-completion - :type 'boolean) - -(defcustom org-completion-use-iswitchb nil - "Non-nil means use iswitchb completion wherever possible. -Note that `iswitchb-mode' must be active for this variable to be relevant. -If you decide to turn this variable on, you might well want to turn off -`org-outline-path-complete-in-steps'. -Note that this variable has only an effect if `org-completion-use-ido' is nil." - :group 'org-completion - :type 'boolean) - -(defcustom org-completion-fallback-command 'hippie-expand - "The expansion command called by \\[pcomplete] in normal context. -Normal means, no org-mode-specific context." - :group 'org-completion - :type 'function) - ;;; Functions and variables from their packages ;; Declared here to avoid compiler warnings - -;; XEmacs only -(defvar outline-mode-menu-heading) -(defvar outline-mode-menu-show) -(defvar outline-mode-menu-hide) -(defvar zmacs-regions) ; XEmacs regions - -;; Emacs only (defvar mark-active) ;; Various packages -(declare-function calendar-iso-to-absolute "cal-iso" (date)) -(declare-function calendar-forward-day "cal-move" (arg)) -(declare-function calendar-goto-date "cal-move" (date)) -(declare-function calendar-goto-today "cal-move" ()) -(declare-function calendar-iso-from-absolute "cal-iso" (date)) -(defvar calc-embedded-close-formula) -(defvar calc-embedded-open-formula) -(declare-function cdlatex-tab "ext:cdlatex" ()) +(declare-function calc-eval "calc" (str &optional separator &rest args)) +(declare-function calendar-forward-day "cal-move" (arg)) +(declare-function calendar-goto-date "cal-move" (date)) +(declare-function calendar-goto-today "cal-move" ()) +(declare-function calendar-iso-from-absolute "cal-iso" (date)) +(declare-function calendar-iso-to-absolute "cal-iso" (date)) (declare-function cdlatex-compute-tables "ext:cdlatex" ()) -(declare-function dired-get-filename "dired" (&optional localp no-error-if-not-filep)) -(defvar font-lock-unfontify-region-function) -(declare-function iswitchb-read-buffer "iswitchb" - (prompt &optional - default require-match _predicate start matches-set)) -(defvar iswitchb-temp-buflist) -(declare-function org-gnus-follow-link "org-gnus" (&optional group article)) -(defvar org-agenda-tags-todo-honor-ignore-options) -(declare-function org-agenda-skip "org-agenda" ()) -(declare-function - org-agenda-format-item "org-agenda" - (extra txt &optional level category tags dotime remove-re habitp)) -(declare-function org-agenda-new-marker "org-agenda" (&optional pos)) -(declare-function org-agenda-change-all-lines "org-agenda" +(declare-function cdlatex-tab "ext:cdlatex" ()) +(declare-function dired-get-filename + "dired" + (&optional localp no-error-if-not-filep)) +(declare-function iswitchb-read-buffer + "iswitchb" + (prompt &optional + default require-match _predicate start matches-set)) +(declare-function org-agenda-change-all-lines + "org-agenda" (newhead hdmarker &optional fixface just-this)) -(declare-function org-agenda-set-restriction-lock "org-agenda" (&optional type)) +(declare-function org-agenda-check-for-timestamp-as-reason-to-ignore-todo-item + "org-agenda" + (&optional end)) +(declare-function org-agenda-copy-local-variable "org-agenda" (var)) +(declare-function org-agenda-format-item + "org-agenda" + (extra txt &optional level category tags dotime + remove-re habitp)) (declare-function org-agenda-maybe-redo "org-agenda" ()) -(declare-function org-agenda-save-markers-for-cut-and-paste "org-agenda" +(declare-function org-agenda-new-marker "org-agenda" (&optional pos)) +(declare-function org-agenda-save-markers-for-cut-and-paste + "org-agenda" (beg end)) -(declare-function org-agenda-copy-local-variable "org-agenda" (var)) -(declare-function org-agenda-check-for-timestamp-as-reason-to-ignore-todo-item - "org-agenda" (&optional end)) -(declare-function org-inlinetask-remove-END-maybe "org-inlinetask" ()) -(declare-function org-inlinetask-in-task-p "org-inlinetask" ()) +(declare-function org-agenda-set-restriction-lock "org-agenda" (&optional type)) +(declare-function org-agenda-skip "org-agenda" ()) +(declare-function org-attach-reveal "org-attach" (&optional if-exists)) +(declare-function org-gnus-follow-link "org-gnus" (&optional group article)) +(declare-function org-indent-mode "org-indent" (&optional arg)) (declare-function org-inlinetask-goto-beginning "org-inlinetask" ()) (declare-function org-inlinetask-goto-end "org-inlinetask" ()) -(declare-function org-indent-mode "org-indent" (&optional arg)) -(declare-function parse-time-string "parse-time" (string)) -(declare-function org-attach-reveal "org-attach" (&optional if-exists)) +(declare-function org-inlinetask-in-task-p "org-inlinetask" ()) +(declare-function org-inlinetask-remove-END-maybe "org-inlinetask" ()) (declare-function orgtbl-send-table "org-table" (&optional maybe)) -(defvar remember-data-file) -(defvar texmathp-why) +(declare-function parse-time-string "parse-time" (string)) (declare-function speedbar-line-directory "speedbar" (&optional depth)) -(declare-function table--at-cell-p "table" (position &optional object at-column)) - -(defvar org-latex-regexps) - -;;; Autoload and prepare some org modules - -;; Some table stuff that needs to be defined here, because it is used -;; by the functions setting up org-mode or checking for table context. - -(defconst org-table-any-line-regexp "^[ \t]*\\(|\\|\\+-[-+]\\)" - "Detect an org-type or table-type table.") -(defconst org-table-line-regexp "^[ \t]*|" - "Detect an org-type table line.") -(defconst org-table-dataline-regexp "^[ \t]*|[^-]" - "Detect an org-type table line.") -(defconst org-table-hline-regexp "^[ \t]*|-" - "Detect an org-type table hline.") -(defconst org-table1-hline-regexp "^[ \t]*\\+-[-+]" - "Detect a table-type table hline.") -(defconst org-table-any-border-regexp "^[ \t]*[^|+ \t]" - "Detect the first line outside a table when searching from within it. -This works for both table types.") -(defconst org-TBLFM-regexp "^[ \t]*#\\+TBLFM: " - "Detect a #+TBLFM line.") +(defvar align-mode-rules-list) +(defvar calc-embedded-close-formula) +(defvar calc-embedded-open-formula) +(defvar calc-embedded-open-mode) +(defvar font-lock-unfontify-region-function) +(defvar iswitchb-temp-buflist) +(defvar org-agenda-tags-todo-honor-ignore-options) +(defvar remember-data-file) +(defvar texmathp-why) ;;;###autoload (defun turn-on-orgtbl () @@ -4291,75 +4641,50 @@ This works for both table types.") (orgtbl-mode 1)) (defun org-at-table-p (&optional table-type) - "Return t if the cursor is inside an org-type table. -If TABLE-TYPE is non-nil, also check for table.el-type tables." - (if org-enable-table-editor - (save-excursion - (beginning-of-line 1) - (looking-at (if table-type org-table-any-line-regexp - org-table-line-regexp))) - nil)) -(defsubst org-table-p () (org-at-table-p)) + "Non-nil if the cursor is inside an Org table. +If TABLE-TYPE is non-nil, also check for table.el-type tables. +If `org-enable-table-editor' is nil, return nil unconditionally." + (and + org-enable-table-editor + (save-excursion + (beginning-of-line) + (looking-at-p (if table-type "[ \t]*[|+]" "[ \t]*|"))) + (or (not (derived-mode-p 'org-mode)) + (let ((e (org-element-lineage (org-element-at-point) '(table) t))) + (and e (or table-type (eq (org-element-property :type e) 'org))))))) (defun org-at-table.el-p () - "Return t if and only if we are at a table.el table." - (and (org-at-table-p 'any) - (save-excursion - (goto-char (org-table-begin 'any)) - (looking-at org-table1-hline-regexp)))) - -(defun org-table-recognize-table.el () - "If there is a table.el table nearby, recognize it and move into it." - (if org-table-tab-recognizes-table.el - (if (org-at-table.el-p) - (progn - (beginning-of-line 1) - (if (looking-at org-table-dataline-regexp) - nil - (if (looking-at org-table1-hline-regexp) - (progn - (beginning-of-line 2) - (if (looking-at org-table-any-border-regexp) - (beginning-of-line -1))))) - (if (re-search-forward "|" (org-table-end t) t) - (progn - (require 'table) - (if (table--at-cell-p (point)) - t - (message "recognizing table.el table...") - (table-recognize-table) - (message "recognizing table.el table...done"))) - (error "This should not happen")) - t) - nil) - nil)) + "Non-nil when point is at a table.el table." + (and (save-excursion (beginning-of-line) (looking-at "[ \t]*[|+]")) + (let ((element (org-element-at-point))) + (and (eq (org-element-type element) 'table) + (eq (org-element-property :type element) 'table.el))))) (defun org-at-table-hline-p () - "Return t if the cursor is inside a hline in a table." - (if org-enable-table-editor - (save-excursion - (beginning-of-line 1) - (looking-at org-table-hline-regexp)) - nil)) + "Non-nil when point is inside a hline in a table. +Assume point is already in a table. If `org-enable-table-editor' +is nil, return nil unconditionally." + (and org-enable-table-editor + (save-excursion + (beginning-of-line) + (looking-at org-table-hline-regexp)))) (defun org-table-map-tables (function &optional quietly) "Apply FUNCTION to the start of all tables in the buffer." - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while (re-search-forward org-table-any-line-regexp nil t) - (unless quietly - (message "Mapping tables: %d%%" - (floor (* 100.0 (point)) (buffer-size)))) - (beginning-of-line 1) - (when (and (looking-at org-table-line-regexp) - ;; Exclude tables in src/example/verbatim/clocktable blocks - (not (org-in-block-p '("src" "example" "verbatim" "clocktable")))) - (save-excursion (funcall function)) - (or (looking-at org-table-line-regexp) - (forward-char 1))) - (re-search-forward org-table-any-border-regexp nil 1)))) + (org-with-wide-buffer + (goto-char (point-min)) + (while (re-search-forward org-table-any-line-regexp nil t) + (unless quietly + (message "Mapping tables: %d%%" + (floor (* 100.0 (point)) (buffer-size)))) + (beginning-of-line 1) + (when (and (looking-at org-table-line-regexp) + ;; Exclude tables in src/example/verbatim/clocktable blocks + (not (org-in-block-p '("src" "example" "verbatim" "clocktable")))) + (save-excursion (funcall function)) + (or (looking-at org-table-line-regexp) + (forward-char 1))) + (re-search-forward org-table-any-border-regexp nil 1))) (unless quietly (message "Mapping tables: done"))) (declare-function org-clock-save-markers-for-cut-and-paste "org-clock" (beg end)) @@ -4368,12 +4693,12 @@ If TABLE-TYPE is non-nil, also check for table.el-type tables." (&optional also-non-dangling-p prompt last-valid)) (defun org-at-TBLFM-p (&optional pos) - "Return t when point (or POS) is in #+TBLFM line." + "Non-nil when point (or POS) is in #+TBLFM line." (save-excursion - (let ((pos pos))) (goto-char (or pos (point))) - (beginning-of-line 1) - (looking-at org-TBLFM-regexp))) + (beginning-of-line) + (and (let ((case-fold-search t)) (looking-at org-TBLFM-regexp)) + (eq (org-element-type (org-element-at-point)) 'table)))) (defvar org-clock-start-time) (defvar org-clock-marker (make-marker) @@ -4410,7 +4735,7 @@ If yes, offer to stop it and to save the buffer with the changes." (add-hook 'kill-emacs-hook 'org-clock-save)) (defgroup org-archive nil - "Options concerning archiving in Org-mode." + "Options concerning archiving in Org mode." :tag "Org Archive" :group 'org-structure) @@ -4425,7 +4750,7 @@ When the filename is omitted, archiving happens in the same file. %s in the filename will be replaced by the current file name (without the directory part). Archiving to a different file is useful to keep archived entries from contributing to the -Org-mode Agenda. +Org Agenda. The archived entries will be filed as subtrees of the specified headline. When the headline is omitted, the subtrees are simply @@ -4473,16 +4798,6 @@ the hierarchy, it will be used." :group 'org-archive :type 'string) -(defcustom org-archive-tag "ARCHIVE" - "The tag that marks a subtree as archived. -An archived subtree does not open during visibility cycling, and does -not contribute to the agenda listings. -After changing this, font-lock must be restarted in the relevant buffers to -get the proper fontification." - :group 'org-archive - :group 'org-keywords - :type 'string) - (defcustom org-agenda-skip-archived-trees t "Non-nil means the agenda will skip any items located in archived trees. An archived tree is a tree marked with the tag ARCHIVE. The use of this @@ -4515,24 +4830,25 @@ collapsed state." :group 'org-sparse-trees :type 'boolean) -(defcustom org-sparse-tree-default-date-type 'scheduled-or-deadline +(defcustom org-sparse-tree-default-date-type nil "The default date type when building a sparse tree. When this is nil, a date is a scheduled or a deadline timestamp. Otherwise, these types are allowed: all: all timestamps active: only active timestamps (<...>) - inactive: only inactive timestamps (<...) + inactive: only inactive timestamps ([...]) scheduled: only scheduled timestamps deadline: only deadline timestamps" - :type '(choice (const :tag "Scheduled or deadline" scheduled-or-deadline) + :type '(choice (const :tag "Scheduled or deadline" nil) (const :tag "All timestamps" all) (const :tag "Only active timestamps" active) (const :tag "Only inactive timestamps" inactive) (const :tag "Only scheduled timestamps" scheduled) (const :tag "Only deadline timestamps" deadline) (const :tag "Only closed timestamps" closed)) - :version "24.3" + :version "26.1" + :package-version '(Org . "8.3") :group 'org-sparse-trees) (defun org-cycle-hide-archived-subtrees (state) @@ -4545,9 +4861,10 @@ Otherwise, these types are allowed: (end (if globalp (point-max) (org-end-of-subtree t)))) (org-hide-archived-subtrees beg end) (goto-char beg) - (if (looking-at (concat ".*:" org-archive-tag ":")) - (message "%s" (substitute-command-keys - "Subtree is archived and stays closed. Use \\[org-force-cycle-archived] to cycle it anyway."))))))) + (when (looking-at-p (concat ".*:" org-archive-tag ":")) + (message "%s" (substitute-command-keys + "Subtree is archived and stays closed. Use \ +`\\[org-force-cycle-archived]' to cycle it anyway."))))))) (defun org-force-cycle-archived () "Cycle subtree even if it is archived." @@ -4558,13 +4875,16 @@ Otherwise, these types are allowed: (defun org-hide-archived-subtrees (beg end) "Re-hide all archived subtrees after a visibility state change." - (save-excursion - (let* ((re (concat ":" org-archive-tag ":"))) - (goto-char beg) - (while (re-search-forward re end t) - (when (org-at-heading-p) - (org-flag-subtree t) - (org-end-of-subtree t)))))) + (org-with-wide-buffer + (let ((case-fold-search nil) + (re (concat org-outline-regexp-bol ".*:" org-archive-tag ":"))) + (goto-char beg) + ;; Include headline point is currently on. + (beginning-of-line) + (while (and (< (point) end) (re-search-forward re end t)) + (when (member org-archive-tag (org-get-tags)) + (org-flag-subtree t) + (org-end-of-subtree t)))))) (declare-function outline-end-of-heading "outline" ()) (declare-function outline-flag-region "outline" (from to flag)) @@ -4580,7 +4900,6 @@ Otherwise, these types are allowed: ;; Declare Column View Code -(declare-function org-columns-number-to-string "org-colview" (n fmt &optional printf)) (declare-function org-columns-get-format-and-top-level "org-colview" ()) (declare-function org-columns-compute "org-colview" (property)) @@ -4593,79 +4912,47 @@ Otherwise, these types are allowed: ;;; Variables for pre-computed regular expressions, all buffer local -(defvar org-drawer-regexp "^[ \t]*:PROPERTIES:[ \t]*$" - "Matches first line of a hidden block.") -(make-variable-buffer-local 'org-drawer-regexp) -(defvar org-todo-regexp nil - "Matches any of the TODO state keywords.") -(make-variable-buffer-local 'org-todo-regexp) -(defvar org-not-done-regexp nil - "Matches any of the TODO state keywords except the last one.") -(make-variable-buffer-local 'org-not-done-regexp) -(defvar org-not-done-heading-regexp nil - "Matches a TODO headline that is not done.") -(make-variable-buffer-local 'org-not-done-regexp) -(defvar org-todo-line-regexp nil - "Matches a headline and puts TODO state into group 2 if present.") -(make-variable-buffer-local 'org-todo-line-regexp) -(defvar org-complex-heading-regexp nil +(defvar-local org-todo-regexp nil + "Matches any of the TODO state keywords. +Since TODO keywords are case-sensitive, `case-fold-search' is +expected to be bound to nil when matching against this regexp.") + +(defvar-local org-not-done-regexp nil + "Matches any of the TODO state keywords except the last one. +Since TODO keywords are case-sensitive, `case-fold-search' is +expected to be bound to nil when matching against this regexp.") + +(defvar-local org-not-done-heading-regexp nil + "Matches a TODO headline that is not done. +Since TODO keywords are case-sensitive, `case-fold-search' is +expected to be bound to nil when matching against this regexp.") + +(defvar-local org-todo-line-regexp nil + "Matches a headline and puts TODO state into group 2 if present. +Since TODO keywords are case-sensitive, `case-fold-search' is +expected to be bound to nil when matching against this regexp.") + +(defvar-local org-complex-heading-regexp nil "Matches a headline and puts everything into groups: -group 1: the stars -group 2: The todo keyword, maybe + +group 1: Stars +group 2: The TODO keyword, maybe group 3: Priority cookie group 4: True headline -group 5: Tags") -(make-variable-buffer-local 'org-complex-heading-regexp) -(defvar org-complex-heading-regexp-format nil +group 5: Tags + +Since TODO keywords are case-sensitive, `case-fold-search' is +expected to be bound to nil when matching against this regexp.") + +(defvar-local org-complex-heading-regexp-format nil "Printf format to make regexp to match an exact headline. This regexp will match the headline of any node which has the exact headline text that is put into the format, but may have any TODO state, priority and tags.") -(make-variable-buffer-local 'org-complex-heading-regexp-format) -(defvar org-todo-line-tags-regexp nil + +(defvar-local org-todo-line-tags-regexp nil "Matches a headline and puts TODO state into group 2 if present. Also put tags into group 4 if tags are present.") -(make-variable-buffer-local 'org-todo-line-tags-regexp) -(defvar org-ds-keyword-length 12 - "Maximum length of the DEADLINE and SCHEDULED keywords.") -(make-variable-buffer-local 'org-ds-keyword-length) -(defvar org-deadline-regexp nil - "Matches the DEADLINE keyword.") -(make-variable-buffer-local 'org-deadline-regexp) -(defvar org-deadline-time-regexp nil - "Matches the DEADLINE keyword together with a time stamp.") -(make-variable-buffer-local 'org-deadline-time-regexp) -(defvar org-deadline-time-hour-regexp nil - "Matches the DEADLINE keyword together with a time-and-hour stamp.") -(make-variable-buffer-local 'org-deadline-time-hour-regexp) -(defvar org-deadline-line-regexp nil - "Matches the DEADLINE keyword and the rest of the line.") -(make-variable-buffer-local 'org-deadline-line-regexp) -(defvar org-scheduled-regexp nil - "Matches the SCHEDULED keyword.") -(make-variable-buffer-local 'org-scheduled-regexp) -(defvar org-scheduled-time-regexp nil - "Matches the SCHEDULED keyword together with a time stamp.") -(make-variable-buffer-local 'org-scheduled-time-regexp) -(defvar org-scheduled-time-hour-regexp nil - "Matches the SCHEDULED keyword together with a time-and-hour stamp.") -(make-variable-buffer-local 'org-scheduled-time-hour-regexp) -(defvar org-closed-time-regexp nil - "Matches the CLOSED keyword together with a time stamp.") -(make-variable-buffer-local 'org-closed-time-regexp) - -(defvar org-keyword-time-regexp nil - "Matches any of the 4 keywords, together with the time stamp.") -(make-variable-buffer-local 'org-keyword-time-regexp) -(defvar org-keyword-time-not-clock-regexp nil - "Matches any of the 3 keywords, together with the time stamp.") -(make-variable-buffer-local 'org-keyword-time-not-clock-regexp) -(defvar org-maybe-keyword-time-regexp nil - "Matches a timestamp, possibly preceded by a keyword.") -(make-variable-buffer-local 'org-maybe-keyword-time-regexp) -(defvar org-all-time-keywords nil - "List of time keywords.") -(make-variable-buffer-local 'org-all-time-keywords) (defconst org-plain-time-of-day-regexp (concat @@ -4771,32 +5058,6 @@ in the #+STARTUP line), the corresponding variable, and the value to set this variable to if the option is found. An optional forth element PUSH means to push this value onto the list in the variable.") -(defun org-update-property-plist (key val props) - "Update PROPS with KEY and VAL." - (let* ((appending (string= "+" (substring key (- (length key) 1)))) - (key (if appending (substring key 0 (- (length key) 1)) key)) - (remainder (org-remove-if (lambda (p) (string= (car p) key)) props)) - (previous (cdr (assoc key props)))) - (if appending - (cons (cons key (if previous (concat previous " " val) val)) remainder) - (cons (cons key val) remainder)))) - -(defconst org-block-regexp - "^[ \t]*#\\+begin_?\\([^ \n]+\\)\\(\\([^\n]+\\)\\)?\n\\([^\000]+?\\)#\\+end_?\\1[ \t]*$" - "Regular expression for hiding blocks.") -(defconst org-heading-keyword-regexp-format - "^\\(\\*+\\)\\(?: +%s\\)\\(?: +\\(.*?\\)\\)?[ \t]*$" - "Printf format for a regexp matching a headline with some keyword. -This regexp will match the headline of any node which has the -exact keyword that is put into the format. The keyword isn't in -any group by default, but the stars and the body are.") -(defconst org-heading-keyword-maybe-regexp-format - "^\\(\\*+\\)\\(?: +%s\\)?\\(?: +\\(.*?\\)\\)?[ \t]*$" - "Printf format for a regexp matching a headline, possibly with some keyword. -This regexp can match any headline with the specified keyword, or -without a keyword. The keyword isn't in any group by default, -but the stars and the body are.") - (defcustom org-group-tags t "When non-nil (the default), use group tags. This can be turned on/off through `org-toggle-tags-groups'." @@ -4820,386 +5081,378 @@ Support for group tags is controlled by the option (message "Groups tags support has been turned %s" (if org-group-tags "on" "off"))) -(defun org-set-regexps-and-options-for-tags () - "Precompute variables used for tags." - (when (derived-mode-p 'org-mode) - (org-set-local 'org-file-tags nil) - (let ((re (org-make-options-regexp '("FILETAGS" "TAGS"))) - (splitre "[ \t]+") - (start 0) - tags ftags key value) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while (re-search-forward re nil t) - (setq key (upcase (org-match-string-no-properties 1)) - value (org-match-string-no-properties 2)) - (if (stringp value) (setq value (org-trim value))) - (cond - ((equal key "TAGS") - (setq tags (append tags (if tags '("\\n") nil) - (org-split-string value splitre)))) - ((equal key "FILETAGS") - (when (string-match "\\S-" value) - (setq ftags - (append - ftags - (apply 'append - (mapcar (lambda (x) (org-split-string x ":")) - (org-split-string value))))))))))) - ;; Process the file tags. - (and ftags (org-set-local 'org-file-tags - (mapcar 'org-add-prop-inherited ftags))) - (org-set-local 'org-tag-groups-alist nil) - ;; Process the tags. - (when (and (not tags) org-tag-alist) - (setq tags - (mapcar - (lambda (tg) (cond ((eq (car tg) :startgroup) "{") - ((eq (car tg) :endgroup) "}") - ((eq (car tg) :grouptags) ":") - ((eq (car tg) :newline) "\n") - (t (concat (car tg) - (if (characterp (cdr tg)) - (format "(%s)" (char-to-string (cdr tg))) ""))))) - org-tag-alist))) - (let (tgs g) - (dolist (e tags) - (cond - ((equal e "{") - (progn (push '(:startgroup) tgs) - (when (equal (nth 1 tags) ":") - (push (list (replace-regexp-in-string - "(.+)$" "" (nth 0 tags))) - org-tag-groups-alist) - (setq g 0)))) - ((equal e ":") (push '(:grouptags) tgs)) - ((equal e "}") (push '(:endgroup) tgs) (if g (setq g nil))) - ((equal e "\\n") (push '(:newline) tgs)) - ((string-match (org-re "^\\([[:alnum:]_@#%]+\\)(\\(.\\))$") e) - (push (cons (match-string 1 e) - (string-to-char (match-string 2 e))) - tgs) - (if (and g (> g 0)) - (setcar org-tag-groups-alist - (append (car org-tag-groups-alist) - (list (match-string 1 e))))) - (if g (setq g (1+ g)))) - (t (push (list e) tgs) - (if (and g (> g 0)) - (setcar org-tag-groups-alist - (append (car org-tag-groups-alist) (list e)))) - (if g (setq g (1+ g)))))) - (org-set-local 'org-tag-alist nil) - (dolist (e tgs) - (or (and (stringp (car e)) - (assoc (car e) org-tag-alist)) - (push e org-tag-alist))) - ;; Return a list with tag variables - (list org-file-tags org-tag-alist org-tag-groups-alist))))) - -(defvar org-ota nil) -(defun org-set-regexps-and-options () - "Precompute regular expressions used in the current buffer." +(defun org-set-regexps-and-options (&optional tags-only) + "Precompute regular expressions used in the current buffer. +When optional argument TAGS-ONLY is non-nil, only compute tags +related expressions." (when (derived-mode-p 'org-mode) - (org-set-local 'org-todo-kwd-alist nil) - (org-set-local 'org-todo-key-alist nil) - (org-set-local 'org-todo-key-trigger nil) - (org-set-local 'org-todo-keywords-1 nil) - (org-set-local 'org-done-keywords nil) - (org-set-local 'org-todo-heads nil) - (org-set-local 'org-todo-sets nil) - (org-set-local 'org-todo-log-states nil) - (org-set-local 'org-file-properties nil) - (let ((re (org-make-options-regexp - '("CATEGORY" "TODO" "COLUMNS" "STARTUP" "ARCHIVE" - "LINK" "PRIORITIES" "CONSTANTS" "PROPERTY" "DRAWERS" - "SETUPFILE" "OPTIONS") - "\\(?:[a-zA-Z][0-9a-zA-Z_]*_TODO\\)")) - (splitre "[ \t]+") - (scripts org-use-sub-superscripts) - kwds kws0 kwsa key log value cat arch const links hw dws - tail sep kws1 prio props drawers ext-setup-or-nil setup-contents - (start 0)) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while - (or (and - ext-setup-or-nil - (not org-ota) - (let (ret) - (with-temp-buffer - (insert ext-setup-or-nil) - (let ((major-mode 'org-mode) org-ota) - (setq ret (save-match-data - (org-set-regexps-and-options-for-tags))))) - ;; Append setupfile tags to existing tags - (setq org-ota t) - (setq org-file-tags - (delq nil (append org-file-tags (nth 0 ret))) - org-tag-alist - (delq nil (append org-tag-alist (nth 1 ret))) - org-tag-groups-alist - (delq nil (append org-tag-groups-alist (nth 2 ret)))))) - (and ext-setup-or-nil - (string-match re ext-setup-or-nil start) - (setq start (match-end 0))) - (and (setq ext-setup-or-nil nil start 0) - (re-search-forward re nil t))) - (setq key (upcase (match-string 1 ext-setup-or-nil)) - value (org-match-string-no-properties 2 ext-setup-or-nil)) - (if (stringp value) (setq value (org-trim value))) - (cond - ((equal key "CATEGORY") - (setq cat value)) - ((member key '("SEQ_TODO" "TODO")) - (push (cons 'sequence (org-split-string value splitre)) kwds)) - ((equal key "TYP_TODO") - (push (cons 'type (org-split-string value splitre)) kwds)) - ((string-match "\\`\\([a-zA-Z][0-9a-zA-Z_]*\\)_TODO\\'" key) - ;; general TODO-like setup - (push (cons (intern (downcase (match-string 1 key))) - (org-split-string value splitre)) - kwds)) - ((equal key "COLUMNS") - (org-set-local 'org-columns-default-format value)) - ((equal key "LINK") - (when (string-match "^\\(\\S-+\\)[ \t]+\\(.+\\)" value) - (push (cons (match-string 1 value) - (org-trim (match-string 2 value))) - links))) - ((equal key "PRIORITIES") - (setq prio (org-split-string value " +"))) - ((equal key "PROPERTY") - (when (string-match "\\(\\S-+\\)\\s-+\\(.*\\)" value) - (setq props (org-update-property-plist (match-string 1 value) - (match-string 2 value) - props)))) - ((equal key "DRAWERS") - (setq drawers (delete-dups (append org-drawers (org-split-string value splitre))))) - ((equal key "CONSTANTS") - (org-table-set-constants)) - ((equal key "STARTUP") - (let ((opts (org-split-string value splitre)) - var val) - (dolist (l opts) - (when (setq l (assoc l org-startup-options)) - (setq var (nth 1 l) val (nth 2 l)) - (if (not (nth 3 l)) - (set (make-local-variable var) val) - (if (not (listp (symbol-value var))) - (set (make-local-variable var) nil)) - (set (make-local-variable var) (symbol-value var)) - (add-to-list var val)))))) - ((equal key "ARCHIVE") - (setq arch value) - (remove-text-properties 0 (length arch) - '(face t fontified t) arch)) - ((equal key "OPTIONS") - (if (string-match "\\([ \t]\\|\\`\\)\\^:\\(t\\|nil\\|{}\\)" value) - (setq scripts (read (match-string 2 value))))) - ((and (equal key "SETUPFILE") - ;; Prevent checking in Gnus messages - (not buffer-read-only)) - (setq setup-contents (org-file-contents - (expand-file-name - (org-remove-double-quotes value)) - 'noerror)) - (if (not ext-setup-or-nil) - (setq ext-setup-or-nil setup-contents start 0) - (setq ext-setup-or-nil - (concat (substring ext-setup-or-nil 0 start) - "\n" setup-contents "\n" - (substring ext-setup-or-nil start))))))) - ;; search for property blocks - (goto-char (point-min)) - (while (re-search-forward org-block-regexp nil t) - (when (equal "PROPERTY" (upcase (match-string 1))) - (setq value (replace-regexp-in-string - "[\n\r]" " " (match-string 4))) - (when (string-match "\\(\\S-+\\)\\s-+\\(.*\\)" value) - (setq props (org-update-property-plist (match-string 1 value) - (match-string 2 value) - props))))))) - (org-set-local 'org-use-sub-superscripts scripts) - (when cat - (org-set-local 'org-category (intern cat)) - (push (cons "CATEGORY" cat) props)) - (when prio - (if (< (length prio) 3) (setq prio '("A" "C" "B"))) - (setq prio (mapcar 'string-to-char prio)) - (org-set-local 'org-highest-priority (nth 0 prio)) - (org-set-local 'org-lowest-priority (nth 1 prio)) - (org-set-local 'org-default-priority (nth 2 prio))) - (and props (org-set-local 'org-file-properties (nreverse props))) - (and drawers (org-set-local 'org-drawers drawers)) - (and arch (org-set-local 'org-archive-location arch)) - (and links (setq org-link-abbrev-alist-local (nreverse links))) - ;; Process the TODO keywords - (unless kwds - ;; Use the global values as if they had been given locally. - (setq kwds (default-value 'org-todo-keywords)) - (if (stringp (car kwds)) - (setq kwds (list (cons org-todo-interpretation - (default-value 'org-todo-keywords))))) - (setq kwds (reverse kwds))) - (setq kwds (nreverse kwds)) - (let (inter kw) - (dolist (kws kwds) - (let ((kws (or - (run-hook-with-args-until-success - 'org-todo-setup-filter-hook kws) - kws))) - (setq inter (pop kws) sep (member "|" kws) - kws0 (delete "|" (copy-sequence kws)) - kwsa nil - kws1 (mapcar - (lambda (x) - ;; 1 2 - (if (string-match "^\\(.*?\\)\\(?:(\\([^!@/]\\)?.*?)\\)?$" x) - (progn - (setq kw (match-string 1 x) - key (and (match-end 2) (match-string 2 x)) - log (org-extract-log-state-settings x)) - (push (cons kw (and key (string-to-char key))) kwsa) - (and log (push log org-todo-log-states)) - kw) - (error "Invalid TODO keyword %s" x))) - kws0) - kwsa (if kwsa (append '((:startgroup)) - (nreverse kwsa) - '((:endgroup)))) - hw (car kws1) - dws (if sep (org-remove-keyword-keys (cdr sep)) (last kws1)) - tail (list inter hw (car dws) (org-last dws)))) - (add-to-list 'org-todo-heads hw 'append) - (push kws1 org-todo-sets) - (setq org-done-keywords (append org-done-keywords dws nil)) - (setq org-todo-key-alist (append org-todo-key-alist kwsa)) - (mapc (lambda (x) (push (cons x tail) org-todo-kwd-alist)) kws1) - (setq org-todo-keywords-1 (append org-todo-keywords-1 kws1 nil))) + (let ((alist (org--setup-collect-keywords + (org-make-options-regexp + (append '("FILETAGS" "TAGS" "SETUPFILE") + (and (not tags-only) + '("ARCHIVE" "CATEGORY" "COLUMNS" "CONSTANTS" + "LINK" "OPTIONS" "PRIORITIES" "PROPERTY" + "SEQ_TODO" "STARTUP" "TODO" "TYP_TODO"))))))) + ;; Startup options. Get this early since it does change + ;; behavior for other options (e.g., tags). + (let ((startup (cdr (assq 'startup alist)))) + (dolist (option startup) + (let ((entry (assoc-string option org-startup-options t))) + (when entry + (let ((var (nth 1 entry)) + (val (nth 2 entry))) + (if (not (nth 3 entry)) (set (make-local-variable var) val) + (unless (listp (symbol-value var)) + (set (make-local-variable var) nil)) + (add-to-list var val))))))) + (setq-local org-file-tags + (mapcar #'org-add-prop-inherited + (cdr (assq 'filetags alist)))) + (setq org-current-tag-alist + (append org-tag-persistent-alist + (let ((tags (cdr (assq 'tags alist)))) + (if tags (org-tag-string-to-alist tags) + org-tag-alist)))) + (setq org-tag-groups-alist + (org-tag-alist-to-groups org-current-tag-alist)) + (unless tags-only + ;; File properties. + (setq-local org-file-properties (cdr (assq 'property alist))) + ;; Archive location. + (let ((archive (cdr (assq 'archive alist)))) + (when archive (setq-local org-archive-location archive))) + ;; Category. + (let ((cat (org-string-nw-p (cdr (assq 'category alist))))) + (when cat + (setq-local org-category (intern cat)) + (setq-local org-file-properties + (org--update-property-plist + "CATEGORY" cat org-file-properties)))) + ;; Columns. + (let ((column (cdr (assq 'columns alist)))) + (when column (setq-local org-columns-default-format column))) + ;; Constants. + (setq org-table-formula-constants-local (cdr (assq 'constants alist))) + ;; Link abbreviations. + (let ((links (cdr (assq 'link alist)))) + (when links (setq org-link-abbrev-alist-local (nreverse links)))) + ;; Priorities. + (let ((priorities (cdr (assq 'priorities alist)))) + (when priorities + (setq-local org-highest-priority (nth 0 priorities)) + (setq-local org-lowest-priority (nth 1 priorities)) + (setq-local org-default-priority (nth 2 priorities)))) + ;; Scripts. + (let ((scripts (assq 'scripts alist))) + (when scripts + (setq-local org-use-sub-superscripts (cdr scripts)))) + ;; TODO keywords. + (setq-local org-todo-kwd-alist nil) + (setq-local org-todo-key-alist nil) + (setq-local org-todo-key-trigger nil) + (setq-local org-todo-keywords-1 nil) + (setq-local org-done-keywords nil) + (setq-local org-todo-heads nil) + (setq-local org-todo-sets nil) + (setq-local org-todo-log-states nil) + (let ((todo-sequences + (or (nreverse (cdr (assq 'todo alist))) + (let ((d (default-value 'org-todo-keywords))) + (if (not (stringp (car d))) d + ;; XXX: Backward compatibility code. + (list (cons org-todo-interpretation d))))))) + (dolist (sequence todo-sequences) + (let* ((sequence (or (run-hook-with-args-until-success + 'org-todo-setup-filter-hook sequence) + sequence)) + (sequence-type (car sequence)) + (keywords (cdr sequence)) + (sep (member "|" keywords)) + names alist) + (dolist (k (remove "|" keywords)) + (unless (string-match "^\\(.*?\\)\\(?:(\\([^!@/]\\)?.*?)\\)?$" + k) + (error "Invalid TODO keyword %s" k)) + (let ((name (match-string 1 k)) + (key (match-string 2 k)) + (log (org-extract-log-state-settings k))) + (push name names) + (push (cons name (and key (string-to-char key))) alist) + (when log (push log org-todo-log-states)))) + (let* ((names (nreverse names)) + (done (if sep (org-remove-keyword-keys (cdr sep)) + (last names))) + (head (car names)) + (tail (list sequence-type head (car done) (org-last done)))) + (add-to-list 'org-todo-heads head 'append) + (push names org-todo-sets) + (setq org-done-keywords (append org-done-keywords done nil)) + (setq org-todo-keywords-1 (append org-todo-keywords-1 names nil)) + (setq org-todo-key-alist + (append org-todo-key-alist + (and alist + (append '((:startgroup)) + (nreverse alist) + '((:endgroup)))))) + (dolist (k names) (push (cons k tail) org-todo-kwd-alist)))))) (setq org-todo-sets (nreverse org-todo-sets) org-todo-kwd-alist (nreverse org-todo-kwd-alist) - org-todo-key-trigger (delq nil (mapcar 'cdr org-todo-key-alist)) - org-todo-key-alist (org-assign-fast-keys org-todo-key-alist))) - ;; Compute the regular expressions and other local variables. - ;; Using `org-outline-regexp-bol' would complicate them much, - ;; because of the fixed white space at the end of that string. - (if (not org-done-keywords) - (setq org-done-keywords (and org-todo-keywords-1 - (list (org-last org-todo-keywords-1))))) - (setq org-ds-keyword-length (+ 2 (max (length org-deadline-string) - (length org-scheduled-string) - (length org-clock-string) - (length org-closed-string))) - org-drawer-regexp - (concat "^[ \t]*:\\(" - (mapconcat 'regexp-quote org-drawers "\\|") - "\\):[ \t]*$") - org-not-done-keywords - (org-delete-all org-done-keywords (copy-sequence org-todo-keywords-1)) - org-todo-regexp - (concat "\\(" - (mapconcat 'regexp-quote org-todo-keywords-1 "\\|") - "\\)") - org-not-done-regexp - (concat "\\(" - (mapconcat 'regexp-quote org-not-done-keywords "\\|") - "\\)") - org-not-done-heading-regexp - (format org-heading-keyword-regexp-format org-not-done-regexp) - org-todo-line-regexp - (format org-heading-keyword-maybe-regexp-format org-todo-regexp) - org-complex-heading-regexp - (concat "^\\(\\*+\\)" - "\\(?: +" org-todo-regexp "\\)?" - "\\(?: +\\(\\[#.\\]\\)\\)?" - "\\(?: +\\(.*?\\)\\)??" - (org-re "\\(?:[ \t]+\\(:[[:alnum:]_@#%:]+:\\)\\)?") - "[ \t]*$") - org-complex-heading-regexp-format - (concat "^\\(\\*+\\)" - "\\(?: +" org-todo-regexp "\\)?" - "\\(?: +\\(\\[#.\\]\\)\\)?" - "\\(?: +" - ;; Stats cookies can be stuck to body. - "\\(?:\\[[0-9%%/]+\\] *\\)*" - "\\(%s\\)" - "\\(?: *\\[[0-9%%/]+\\]\\)*" - "\\)" - (org-re "\\(?:[ \t]+\\(:[[:alnum:]_@#%%:]+:\\)\\)?") - "[ \t]*$") - org-todo-line-tags-regexp - (concat "^\\(\\*+\\)" - "\\(?: +" org-todo-regexp "\\)?" - "\\(?: +\\(.*?\\)\\)??" - (org-re "\\(?:[ \t]+\\(:[[:alnum:]:_@#%]+:\\)\\)?") - "[ \t]*$") - org-deadline-regexp (concat "\\<" org-deadline-string) - org-deadline-time-regexp - (concat "\\<" org-deadline-string " *<\\([^>]+\\)>") - org-deadline-time-hour-regexp - (concat "\\<" org-deadline-string - " *<\\([^>]+[0-9]\\{1,2\\}:[0-9]\\{2\\}[0-9-+:hdwmy \t.]*\\)>") - org-deadline-line-regexp - (concat "\\<\\(" org-deadline-string "\\).*") - org-scheduled-regexp - (concat "\\<" org-scheduled-string) - org-scheduled-time-regexp - (concat "\\<" org-scheduled-string " *<\\([^>]+\\)>") - org-scheduled-time-hour-regexp - (concat "\\<" org-scheduled-string - " *<\\([^>]+[0-9]\\{1,2\\}:[0-9]\\{2\\}[0-9-+:hdwmy \t.]*\\)>") - org-closed-time-regexp - (concat "\\<" org-closed-string " *\\[\\([^]]+\\)\\]") - org-keyword-time-regexp - (concat "\\<\\(" org-scheduled-string - "\\|" org-deadline-string - "\\|" org-closed-string - "\\|" org-clock-string "\\)" - " *[[<]\\([^]>]+\\)[]>]") - org-keyword-time-not-clock-regexp - (concat "\\<\\(" org-scheduled-string - "\\|" org-deadline-string - "\\|" org-closed-string - "\\)" - " *[[<]\\([^]>]+\\)[]>]") - org-maybe-keyword-time-regexp - (concat "\\(\\<\\(" org-scheduled-string - "\\|" org-deadline-string - "\\|" org-closed-string - "\\|" org-clock-string "\\)\\)?" - " *\\([[<][0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^]\r\n>]*?[]>]\\|<%%([^\r\n>]*>\\)") - org-all-time-keywords - (mapcar (lambda (w) (substring w 0 -1)) - (list org-scheduled-string org-deadline-string - org-clock-string org-closed-string))) - (setq org-ota nil) - (org-compute-latex-and-related-regexp)))) + org-todo-key-trigger (delq nil (mapcar #'cdr org-todo-key-alist)) + org-todo-key-alist (org-assign-fast-keys org-todo-key-alist)) + ;; Compute the regular expressions and other local variables. + ;; Using `org-outline-regexp-bol' would complicate them much, + ;; because of the fixed white space at the end of that string. + (unless org-done-keywords + (setq org-done-keywords + (and org-todo-keywords-1 (last org-todo-keywords-1)))) + (setq org-not-done-keywords + (org-delete-all org-done-keywords + (copy-sequence org-todo-keywords-1)) + org-todo-regexp (regexp-opt org-todo-keywords-1 t) + org-not-done-regexp (regexp-opt org-not-done-keywords t) + org-not-done-heading-regexp + (format org-heading-keyword-regexp-format org-not-done-regexp) + org-todo-line-regexp + (format org-heading-keyword-maybe-regexp-format org-todo-regexp) + org-complex-heading-regexp + (concat "^\\(\\*+\\)" + "\\(?: +" org-todo-regexp "\\)?" + "\\(?: +\\(\\[#.\\]\\)\\)?" + "\\(?: +\\(.*?\\)\\)??" + "\\(?:[ \t]+\\(:[[:alnum:]_@#%:]+:\\)\\)?" + "[ \t]*$") + org-complex-heading-regexp-format + (concat "^\\(\\*+\\)" + "\\(?: +" org-todo-regexp "\\)?" + "\\(?: +\\(\\[#.\\]\\)\\)?" + "\\(?: +" + ;; Stats cookies can be stuck to body. + "\\(?:\\[[0-9%%/]+\\] *\\)*" + "\\(%s\\)" + "\\(?: *\\[[0-9%%/]+\\]\\)*" + "\\)" + "\\(?:[ \t]+\\(:[[:alnum:]_@#%%:]+:\\)\\)?" + "[ \t]*$") + org-todo-line-tags-regexp + (concat "^\\(\\*+\\)" + "\\(?: +" org-todo-regexp "\\)?" + "\\(?: +\\(.*?\\)\\)??" + "\\(?:[ \t]+\\(:[[:alnum:]:_@#%]+:\\)\\)?" + "[ \t]*$")) + (org-compute-latex-and-related-regexp))))) + +(defun org--setup-collect-keywords (regexp &optional files alist) + "Return setup keywords values as an alist. + +REGEXP matches a subset of setup keywords. FILES is a list of +file names already visited. It is used to avoid circular setup +files. ALIST, when non-nil, is the alist computed so far. + +Return value contains the following keys: `archive', `category', +`columns', `constants', `filetags', `link', `priorities', +`property', `scripts', `startup', `tags' and `todo'." + (org-with-wide-buffer + (goto-char (point-min)) + (let ((case-fold-search t)) + (while (re-search-forward regexp nil t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'keyword) + (let ((key (org-element-property :key element)) + (value (org-element-property :value element))) + (cond + ((equal key "ARCHIVE") + (when (org-string-nw-p value) + (push (cons 'archive value) alist))) + ((equal key "CATEGORY") (push (cons 'category value) alist)) + ((equal key "COLUMNS") (push (cons 'columns value) alist)) + ((equal key "CONSTANTS") + (let* ((constants (assq 'constants alist)) + (store (cdr constants))) + (dolist (pair (org-split-string value)) + (when (string-match "^\\([a-zA-Z0][_a-zA-Z0-9]*\\)=\\(.*\\)" + pair) + (let* ((name (match-string 1 pair)) + (value (match-string 2 pair)) + (old (assoc name store))) + (if old (setcdr old value) + (push (cons name value) store))))) + (if constants (setcdr constants store) + (push (cons 'constants store) alist)))) + ((equal key "FILETAGS") + (when (org-string-nw-p value) + (let ((old (assq 'filetags alist)) + (new (apply #'nconc + (mapcar (lambda (x) (org-split-string x ":")) + (org-split-string value))))) + (if old (setcdr old (append new (cdr old))) + (push (cons 'filetags new) alist))))) + ((equal key "LINK") + (when (string-match "\\`\\(\\S-+\\)[ \t]+\\(.+\\)" value) + (let ((links (assq 'link alist)) + (pair (cons (match-string-no-properties 1 value) + (match-string-no-properties 2 value)))) + (if links (push pair (cdr links)) + (push (list 'link pair) alist))))) + ((equal key "OPTIONS") + (when (and (org-string-nw-p value) + (string-match "\\^:\\(t\\|nil\\|{}\\)" value)) + (push (cons 'scripts (read (match-string 1 value))) alist))) + ((equal key "PRIORITIES") + (push (cons 'priorities + (let ((prio (org-split-string value))) + (if (< (length prio) 3) '(?A ?C ?B) + (mapcar #'string-to-char prio)))) + alist)) + ((equal key "PROPERTY") + (when (string-match "\\(\\S-+\\)[ \t]+\\(.*\\)" value) + (let* ((property (assq 'property alist)) + (value (org--update-property-plist + (match-string-no-properties 1 value) + (match-string-no-properties 2 value) + (cdr property)))) + (if property (setcdr property value) + (push (cons 'property value) alist))))) + ((equal key "STARTUP") + (let ((startup (assq 'startup alist))) + (if startup + (setcdr startup + (append (cdr startup) (org-split-string value))) + (push (cons 'startup (org-split-string value)) alist)))) + ((equal key "TAGS") + (let ((tag-cell (assq 'tags alist))) + (if tag-cell + (setcdr tag-cell (concat (cdr tag-cell) "\n" value)) + (push (cons 'tags value) alist)))) + ((member key '("TODO" "SEQ_TODO" "TYP_TODO")) + (let ((todo (assq 'todo alist)) + (value (cons (if (equal key "TYP_TODO") 'type 'sequence) + (org-split-string value)))) + (if todo (push value (cdr todo)) + (push (list 'todo value) alist)))) + ((equal key "SETUPFILE") + (unless buffer-read-only ; Do not check in Gnus messages. + (let ((f (and (org-string-nw-p value) + (expand-file-name + (org-unbracket-string "\"" "\"" value))))) + (when (and f (file-readable-p f) (not (member f files))) + (with-temp-buffer + (setq default-directory (file-name-directory f)) + (insert-file-contents f) + (setq alist + ;; Fake Org mode to benefit from cache + ;; without recurring needlessly. + (let ((major-mode 'org-mode)) + (org--setup-collect-keywords + regexp (cons f files) alist))))))))))))))) + alist) + +(defun org-tag-string-to-alist (s) + "Return tag alist associated to string S. +S is a value for TAGS keyword or produced with +`org-tag-alist-to-string'. Return value is an alist suitable for +`org-tag-alist' or `org-tag-persistent-alist'." + (let ((lines (mapcar #'split-string (split-string s "\n" t))) + (tag-re (concat "\\`\\([[:alnum:]_@#%]+" + "\\|{.+?}\\)" ; regular expression + "\\(?:(\\(.\\))\\)?\\'")) + alist group-flag) + (dolist (tokens lines (cdr (nreverse alist))) + (push '(:newline) alist) + (while tokens + (let ((token (pop tokens))) + (pcase token + ("{" + (push '(:startgroup) alist) + (when (equal (nth 1 tokens) ":") (setq group-flag t))) + ("}" + (push '(:endgroup) alist) + (setq group-flag nil)) + ("[" + (push '(:startgrouptag) alist) + (when (equal (nth 1 tokens) ":") (setq group-flag t))) + ("]" + (push '(:endgrouptag) alist) + (setq group-flag nil)) + (":" + (push '(:grouptags) alist)) + ((guard (string-match tag-re token)) + (let ((tag (match-string 1 token)) + (key (and (match-beginning 2) + (string-to-char (match-string 2 token))))) + ;; Push all tags in groups, no matter if they already + ;; appear somewhere else in the list. + (when (or group-flag (not (assoc tag alist))) + (push (cons tag key) alist)))))))))) + +(defun org-tag-alist-to-string (alist &optional skip-key) + "Return tag string associated to ALIST. + +ALIST is an alist, as defined in `org-tag-alist' or +`org-tag-persistent-alist', or produced with +`org-tag-string-to-alist'. + +Return value is a string suitable as a value for \"TAGS\" +keyword. + +When optional argument SKIP-KEY is non-nil, skip selection keys +next to tags." + (mapconcat (lambda (token) + (pcase token + (`(:startgroup) "{") + (`(:endgroup) "}") + (`(:startgrouptag) "[") + (`(:endgrouptag) "]") + (`(:grouptags) ":") + (`(:newline) "\\n") + ((and + (guard (not skip-key)) + `(,(and tag (pred stringp)) . ,(and key (pred characterp)))) + (format "%s(%c)" tag key)) + (`(,(and tag (pred stringp)) . ,_) tag) + (_ (user-error "Invalid tag token: %S" token)))) + alist + " ")) + +(defun org-tag-alist-to-groups (alist) + "Return group alist from tag ALIST. +ALIST is an alist, as defined in `org-tag-alist' or +`org-tag-persistent-alist', or produced with +`org-tag-string-to-alist'. Return value is an alist following +the pattern (GROUP-TAG TAGS) where GROUP-TAG is the tag, as +a string, summarizing TAGS, as a list of strings." + (let (groups group-status current-group) + (dolist (token alist (nreverse groups)) + (pcase token + (`(,(or :startgroup :startgrouptag)) (setq group-status t)) + (`(,(or :endgroup :endgrouptag)) + (when (eq group-status 'append) + (push (nreverse current-group) groups)) + (setq group-status nil)) + (`(:grouptags) (setq group-status 'append)) + ((and `(,tag . ,_) (guard group-status)) + (if (eq group-status 'append) (push tag current-group) + (setq current-group (list tag)))) + (_ nil))))) (defun org-file-contents (file &optional noerror) "Return the contents of FILE, as a string." - (if (or (not file) (not (file-readable-p file))) - (if (not noerror) - (error "Cannot read file \"%s\"" file) - (message "Cannot read file \"%s\"" file) - "") - (with-temp-buffer - (insert-file-contents file) - (buffer-string)))) + (if (and file (file-readable-p file)) + (with-temp-buffer + (insert-file-contents file) + (buffer-string)) + (funcall (if noerror 'message 'error) + "Cannot read file \"%s\"%s" + file + (let ((from (buffer-file-name (buffer-base-buffer)))) + (if from (concat " (referenced in file \"" from "\")") ""))))) (defun org-extract-log-state-settings (x) "Extract the log state setting from a TODO keyword string. This will extract info from a string like \"WAIT(w@/!)\"." - (let (kw key log1 log2) - (when (string-match "^\\(.*?\\)\\(?:(\\([^!@/]\\)?\\([!@]\\)?\\(?:/\\([!@]\\)\\)?)\\)?$" x) - (setq kw (match-string 1 x) - key (and (match-end 2) (match-string 2 x)) - log1 (and (match-end 3) (match-string 3 x)) - log2 (and (match-end 4) (match-string 4 x))) + (when (string-match "^\\(.*?\\)\\(?:(\\([^!@/]\\)?\\([!@]\\)?\\(?:/\\([!@]\\)\\)?)\\)?$" x) + (let ((kw (match-string 1 x)) + (log1 (and (match-end 3) (match-string 3 x))) + (log2 (and (match-end 4) (match-string 4 x)))) (and (or log1 log2) (list kw (and log1 (if (equal log1 "!") 'time 'note)) @@ -5216,8 +5469,8 @@ This will extract info from a string like \"WAIT(w@/!)\"." (defun org-assign-fast-keys (alist) "Assign fast keys to a keyword-key alist. Respect keys that are already there." - (let (new (alt ?0)) - (dolist (e alist) + (let (new e (alt ?0)) + (while (setq e (pop alist)) (if (or (memq (car e) '(:newline :grouptags :endgroup :startgroup)) (cdr e)) ;; Key already assigned. (push e new) @@ -5229,7 +5482,7 @@ Respect keys that are already there." (pop clist)) (unless clist (while (rassoc alt used) - (incf alt))) + (cl-incf alt))) (push (cons (car e) (or (car clist) alt)) new)))) (nreverse new))) @@ -5242,13 +5495,7 @@ Respect keys that are already there." (defvar org-finish-function nil "Function to be called when `C-c C-c' is used. This is for getting out of special buffers like capture.") - - -;; FIXME: Occasionally check by commenting these, to make sure -;; no other functions uses these, forgetting to let-bind them. -(org-no-warnings (defvar entry)) ;; unprefixed, from calendar.el (defvar org-last-state) -(org-no-warnings (defvar date)) ;; unprefixed, from calendar.el ;; Defined somewhere in this file, but used before definition. (defvar org-entities) ;; defined in org-entities.el @@ -5256,7 +5503,7 @@ This is for getting out of special buffers like capture.") (defvar org-org-menu) (defvar org-tbl-menu) -;;;; Define the Org-mode +;;;; Define the Org mode ;; We use a before-change function to check if a table might need ;; an update. @@ -5264,7 +5511,7 @@ This is for getting out of special buffers like capture.") "Indicates that a table might need an update. This variable is set by `org-before-change-function'. `org-table-align' sets it back to nil.") -(defun org-before-change-function (beg end) +(defun org-before-change-function (_beg _end) "Every change indicates that a table might need an update." (setq org-table-may-need-update t)) (defvar org-mode-map) @@ -5278,13 +5525,12 @@ This variable is set by `org-before-change-function'. (defvar buffer-face-mode-face) (require 'outline) -(if (and (not (keymapp outline-mode-map)) (featurep 'allout)) - (error "Conflict with outdated version of allout.el. Load org.el before allout.el, or upgrade to newer allout, for example by switching to Emacs 22")) -(require 'noutline "noutline" 'noerror) ;; stock XEmacs does not have it ;; Other stuff we need. (require 'time-date) +(unless (fboundp 'time-subtract) (defalias 'time-subtract 'subtract-time)) (require 'easymenu) +(autoload 'easy-menu-add "easymenu") (require 'overlay) ;; (require 'org-macs) moved higher up in the file before it is first used @@ -5305,15 +5551,15 @@ This variable is set by `org-before-change-function'. "Outline-based notes management and organizer, alias \"Carsten's outline-mode for keeping track of everything.\" -Org-mode develops organizational tasks around a NOTES file which -contains information about projects as plain text. Org-mode is -implemented on top of outline-mode, which is ideal to keep the content +Org mode develops organizational tasks around a NOTES file which +contains information about projects as plain text. Org mode is +implemented on top of Outline mode, which is ideal to keep the content of large files well structured. It supports ToDo items, deadlines and time stamps, which magically appear in the diary listing of the Emacs calendar. Tables are easily created with a built-in table editor. Plain text URL-like links connect to websites, emails (VM), Usenet messages (Gnus), BBDB entries, and any files related to the project. -For printing and sharing of notes, an Org-mode file (or a part of it) +For printing and sharing of notes, an Org file (or a part of it) can be exported as a structured ASCII or HTML file. The following commands are available: @@ -5323,29 +5569,18 @@ The following commands are available: ;; Get rid of Outline menus, they are not needed ;; Need to do this here because define-derived-mode sets up ;; the keymap so late. Still, it is a waste to call this each time - ;; we switch another buffer into org-mode. - (if (featurep 'xemacs) - (when (boundp 'outline-mode-menu-heading) - ;; Assume this is Greg's port, it uses easymenu - (easy-menu-remove outline-mode-menu-heading) - (easy-menu-remove outline-mode-menu-show) - (easy-menu-remove outline-mode-menu-hide)) - (define-key org-mode-map [menu-bar headings] 'undefined) - (define-key org-mode-map [menu-bar hide] 'undefined) - (define-key org-mode-map [menu-bar show] 'undefined)) + ;; we switch another buffer into Org mode. + (define-key org-mode-map [menu-bar headings] 'undefined) + (define-key org-mode-map [menu-bar hide] 'undefined) + (define-key org-mode-map [menu-bar show] 'undefined) (org-load-modules-maybe) - (when (featurep 'xemacs) - (easy-menu-add org-org-menu) - (easy-menu-add org-tbl-menu)) (org-install-agenda-files-menu) - (if org-descriptive-links (add-to-invisibility-spec '(org-link))) + (when org-descriptive-links (add-to-invisibility-spec '(org-link))) (add-to-invisibility-spec '(org-cwidth)) (add-to-invisibility-spec '(org-hide-block . t)) - (when (featurep 'xemacs) - (org-set-local 'line-move-ignore-invisible t)) - (org-set-local 'outline-regexp org-outline-regexp) - (org-set-local 'outline-level 'org-outline-level) + (setq-local outline-regexp org-outline-regexp) + (setq-local outline-level 'org-outline-level) (setq bidi-paragraph-direction 'left-to-right) (when (and org-ellipsis (fboundp 'set-display-table-slot) (boundp 'buffer-display-table) @@ -5354,55 +5589,50 @@ The following commands are available: (setq org-display-table (make-display-table))) (set-display-table-slot org-display-table 4 - (vconcat (mapcar - (lambda (c) (make-glyph-code c (and (not (stringp org-ellipsis)) - org-ellipsis))) - (if (stringp org-ellipsis) org-ellipsis "...")))) + (vconcat (mapcar (lambda (c) (make-glyph-code c 'org-ellipsis)) + (if (stringp org-ellipsis) org-ellipsis "...")))) (setq buffer-display-table org-display-table)) - (org-set-regexps-and-options-for-tags) (org-set-regexps-and-options) (org-set-font-lock-defaults) (when (and org-tag-faces (not org-tags-special-faces-re)) ;; tag faces set outside customize.... force initialization. (org-set-tag-faces 'org-tag-faces org-tag-faces)) ;; Calc embedded - (org-set-local 'calc-embedded-open-mode "# ") + (setq-local calc-embedded-open-mode "# ") ;; Modify a few syntax entries (modify-syntax-entry ?@ "w") (modify-syntax-entry ?\" "\"") (modify-syntax-entry ?\\ "_") (modify-syntax-entry ?~ "_") - (if org-startup-truncated (setq truncate-lines t)) - (when org-startup-indented (require 'org-indent) (org-indent-mode 1)) - (org-set-local 'font-lock-unfontify-region-function - 'org-unfontify-region) + (setq-local font-lock-unfontify-region-function 'org-unfontify-region) ;; Activate before-change-function - (org-set-local 'org-table-may-need-update t) - (org-add-hook 'before-change-functions 'org-before-change-function nil - 'local) + (setq-local org-table-may-need-update t) + (add-hook 'before-change-functions 'org-before-change-function nil 'local) ;; Check for running clock before killing a buffer - (org-add-hook 'kill-buffer-hook 'org-check-running-clock nil 'local) + (add-hook 'kill-buffer-hook 'org-check-running-clock nil 'local) ;; Initialize macros templates. (org-macro-initialize-templates) ;; Initialize radio targets. (org-update-radio-target-regexp) ;; Indentation. - (org-set-local 'indent-line-function 'org-indent-line) - (org-set-local 'indent-region-function 'org-indent-region) + (setq-local indent-line-function 'org-indent-line) + (setq-local indent-region-function 'org-indent-region) ;; Filling and auto-filling. (org-setup-filling) ;; Comments. (org-setup-comments-handling) + ;; Initialize cache. + (org-element-cache-reset) ;; Beginning/end of defun - (org-set-local 'beginning-of-defun-function 'org-backward-element) - (org-set-local 'end-of-defun-function - (lambda () - (if (not (org-at-heading-p)) - (org-forward-element) - (org-forward-element) - (forward-char -1)))) + (setq-local beginning-of-defun-function 'org-backward-element) + (setq-local end-of-defun-function + (lambda () + (if (not (org-at-heading-p)) + (org-forward-element) + (org-forward-element) + (forward-char -1)))) ;; Next error for sparse trees - (org-set-local 'next-error-function 'org-occur-next-match) + (setq-local next-error-function 'org-occur-next-match) ;; Make sure dependence stuff works reliably, even for users who set it ;; too late :-( (if org-enforce-todo-dependencies @@ -5417,78 +5647,65 @@ The following commands are available: 'org-block-todo-from-checkboxes)) ;; Align options lines - (org-set-local - 'align-mode-rules-list + (setq-local + align-mode-rules-list '((org-in-buffer-settings - (regexp . "^#\\+[A-Z_]+:\\(\\s-*\\)\\S-+") + (regexp . "^[ \t]*#\\+[A-Z_]+:\\(\\s-*\\)\\S-+") (modes . '(org-mode))))) ;; Imenu - (org-set-local 'imenu-create-index-function - 'org-imenu-get-tree) + (setq-local imenu-create-index-function 'org-imenu-get-tree) ;; Make isearch reveal context - (if (or (featurep 'xemacs) - (not (boundp 'outline-isearch-open-invisible-function))) - ;; Emacs 21 and XEmacs make use of the hook - (org-add-hook 'isearch-mode-end-hook 'org-isearch-end 'append 'local) - ;; Emacs 22 deals with this through a special variable - (org-set-local 'outline-isearch-open-invisible-function - (lambda (&rest ignore) (org-show-context 'isearch)))) + (setq-local outline-isearch-open-invisible-function + (lambda (&rest _) (org-show-context 'isearch))) ;; Setup the pcomplete hooks - (set (make-local-variable 'pcomplete-command-completion-function) - 'org-pcomplete-initial) - (set (make-local-variable 'pcomplete-command-name-function) - 'org-command-at-point) - (set (make-local-variable 'pcomplete-default-completion-function) - 'ignore) - (set (make-local-variable 'pcomplete-parse-arguments-function) - 'org-parse-arguments) - (set (make-local-variable 'pcomplete-termination-string) "") - (when (>= emacs-major-version 23) - (set (make-local-variable 'buffer-face-mode-face) 'org-default)) - - ;; If empty file that did not turn on org-mode automatically, make it to. - (if (and org-insert-mode-line-in-empty-file - (org-called-interactively-p 'any) - (= (point-min) (point-max))) - (insert "# -*- mode: org -*-\n\n")) + (setq-local pcomplete-command-completion-function 'org-pcomplete-initial) + (setq-local pcomplete-command-name-function 'org-command-at-point) + (setq-local pcomplete-default-completion-function 'ignore) + (setq-local pcomplete-parse-arguments-function 'org-parse-arguments) + (setq-local pcomplete-termination-string "") + (setq-local buffer-face-mode-face 'org-default) + + ;; If empty file that did not turn on Org mode automatically, make + ;; it to. + (when (and org-insert-mode-line-in-empty-file + (called-interactively-p 'any) + (= (point-min) (point-max))) + (insert "# -*- mode: org -*-\n\n")) (unless org-inhibit-startup (org-unmodified - (and org-startup-with-beamer-mode (org-beamer-mode)) + (when org-startup-with-beamer-mode (org-beamer-mode)) (when org-startup-align-all-tables - (org-table-map-tables 'org-table-align 'quietly)) - (when org-startup-with-inline-images - (org-display-inline-images)) - (when org-startup-with-latex-preview - (org-preview-latex-fragment)) - (unless org-inhibit-startup-visibility-stuff - (org-set-startup-visibility)))) - ;; Try to set org-hide correctly + (org-table-map-tables #'org-table-align t)) + (when org-startup-with-inline-images (org-display-inline-images)) + (when org-startup-with-latex-preview (org-toggle-latex-fragment '(16))) + (unless org-inhibit-startup-visibility-stuff (org-set-startup-visibility)) + (when org-startup-truncated (setq truncate-lines t)) + (when org-startup-indented (require 'org-indent) (org-indent-mode 1)) + (org-refresh-effort-properties))) + ;; Try to set `org-hide' face correctly. (let ((foreground (org-find-invisible-foreground))) - (if foreground - (set-face-foreground 'org-hide foreground)))) + (when foreground + (set-face-foreground 'org-hide foreground)))) ;; Update `customize-package-emacs-version-alist' (add-to-list 'customize-package-emacs-version-alist '(Org ("6.21b" . "23.1") ("6.33x" . "23.2") ("7.8.11" . "24.1") ("7.9.4" . "24.3") - ("8.2.6" . "24.4"))) + ("8.2.6" . "24.4") ("8.2.10" . "24.5") + ("9.0" . "26.1"))) (defvar org-mode-transpose-word-syntax-table - (let ((st (make-syntax-table))) - (mapc (lambda(c) (modify-syntax-entry - (string-to-char (car c)) "w p" st)) - org-emphasis-alist) - st)) + (let ((st (make-syntax-table text-mode-syntax-table))) + (dolist (c org-emphasis-alist st) + (modify-syntax-entry (string-to-char (car c)) "w p" st)))) (when (fboundp 'abbrev-table-put) (abbrev-table-put org-mode-abbrev-table :parents (list text-mode-abbrev-table))) -(put 'org-mode 'flyspell-mode-predicate 'org-mode-flyspell-verify) - (defun org-find-invisible-foreground () (let ((candidates (remove "unspecified-bg" @@ -5498,7 +5715,7 @@ The following commands are available: (mapcar (lambda (alist) (when (boundp alist) - (cdr (assoc 'background-color (symbol-value alist))))) + (cdr (assq 'background-color (symbol-value alist))))) '(default-frame-alist initial-frame-alist window-system-default-frame-alist)) (list (face-foreground 'org-hide)))))) (car (remove nil candidates)))) @@ -5541,8 +5758,6 @@ the rounding returns a past time." (require 'font-lock) (defconst org-non-link-chars "]\t\n\r<>") -(defvar org-link-types '("http" "https" "ftp" "mailto" "file" "news" - "shell" "elisp" "doi" "message")) (defvar org-link-types-re nil "Matches a link that has a url-like prefix like \"http:\"") (defvar org-link-re-with-space nil @@ -5591,27 +5806,26 @@ stacked delimiters is N. Escaping delimiters is not possible." next (concat "\\(?:" nothing left next right "\\)+" nothing))) (concat left "\\(" re "\\)" right))) -(defvar org-match-substring-regexp +(defconst org-match-substring-regexp (concat "\\(\\S-\\)\\([_^]\\)\\(" - "\\(" (org-create-multibrace-regexp "{" "}" org-match-sexp-depth) "\\)" + "\\(?:" (org-create-multibrace-regexp "{" "}" org-match-sexp-depth) "\\)" "\\|" - "\\(" (org-create-multibrace-regexp "(" ")" org-match-sexp-depth) "\\)" + "\\(?:" (org-create-multibrace-regexp "(" ")" org-match-sexp-depth) "\\)" "\\|" - "\\(\\(?:\\*\\|[-+]?[^-+*!@#$%^_ \t\r\n,:\"?<>~;./{}=()]+\\)\\)\\)") + "\\(?:\\*\\|[+-]?[[:alnum:].,\\]*[[:alnum:]]\\)\\)") "The regular expression matching a sub- or superscript.") -(defvar org-match-substring-with-braces-regexp +(defconst org-match-substring-with-braces-regexp (concat - "\\(\\S-\\)\\([_^]\\)\\(" - "\\(" (org-create-multibrace-regexp "{" "}" org-match-sexp-depth) "\\)" - "\\)") + "\\(\\S-\\)\\([_^]\\)" + "\\(" (org-create-multibrace-regexp "{" "}" org-match-sexp-depth) "\\)") "The regular expression matching a sub- or superscript, forcing braces.") (defun org-make-link-regexps () "Update the link regular expressions. -This should be called after the variable `org-link-types' has changed." - (let ((types-re (regexp-opt org-link-types t))) +This should be called after the variable `org-link-parameters' has changed." + (let ((types-re (regexp-opt (org-link-types) t))) (setq org-link-types-re (concat "\\`" types-re ":") org-link-re-with-space @@ -5629,14 +5843,12 @@ This should be called after the variable `org-link-types' has changed." "\\([^" org-non-link-chars " ]" "[^\t\n\r]*\\)") org-angle-link-re - (concat "<" types-re ":" - "\\([^" org-non-link-chars " ]" - "[^" org-non-link-chars "]*" - "\\)>") + (format "<%s:\\([^>\n]*\\(?:\n[ \t]*[^> \t\n][^>\n]*\\)*\\)>" + types-re) org-plain-link-re (concat "\\<" types-re ":" - (org-re "\\([^ \t\n()<>]+\\(?:([[:word:]0-9_]+)\\|\\([^[:punct:] \t\n]\\|/\\)\\)\\)")) + "\\([^][ \t\n()<>]+\\(?:([[:word:]0-9_]+)\\|\\([^[:punct:] \t\n]\\|/\\)\\)\\)") ;; "\\([^]\t\n\r<>() ]+[^]\t\n\r<>,.;() ]\\)") org-bracket-link-regexp "\\[\\[\\([^][]+\\)\\]\\(\\[\\([^][]+\\)\\]\\)?\\]" @@ -5651,77 +5863,46 @@ This should be called after the variable `org-link-types' has changed." org-bracket-link-analytic-regexp++ (concat "\\[\\[" - "\\(" (regexp-opt (cons "coderef" org-link-types) t) ":\\)?" + "\\(" (regexp-opt (cons "coderef" (org-link-types)) t) ":\\)?" "\\([^]]+\\)" "\\]" "\\(\\[" "\\([^]]+\\)" "\\]\\)?" - "\\]") - org-any-link-re - (concat "\\(" org-bracket-link-regexp "\\)\\|\\(" - org-angle-link-re "\\)\\|\\(" - org-plain-link-re "\\)")))) - -(org-make-link-regexps) - -(defconst org-ts-regexp "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^\r\n>]*?\\)>" - "Regular expression for fast time stamp matching.") -(defconst org-ts-regexp-both "[[<]\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^]\r\n>]*?\\)[]>]" - "Regular expression for fast time stamp matching.") -(defconst org-ts-regexp0 - "\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)\\( +[^]+0-9>\r\n -]+\\)?\\( +\\([0-9]\\{1,2\\}\\):\\([0-9]\\{2\\}\\)\\)?\\)" - "Regular expression matching time strings for analysis. -This one does not require the space after the date, so it can be used -on a string that terminates immediately after the date.") -(defconst org-ts-regexp1 "\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\) *\\([^]+0-9>\r\n -]*\\)\\( \\([0-9]\\{1,2\\}\\):\\([0-9]\\{2\\}\\)\\)?\\)" - "Regular expression matching time strings for analysis.") -(defconst org-ts-regexp2 (concat "<" org-ts-regexp1 "[^>\n]\\{0,16\\}>") - "Regular expression matching time stamps, with groups.") -(defconst org-ts-regexp3 (concat "[[<]" org-ts-regexp1 "[^]>\n]\\{0,16\\}[]>]") - "Regular expression matching time stamps (also [..]), with groups.") -(defconst org-tr-regexp (concat org-ts-regexp "--?-?" org-ts-regexp) - "Regular expression matching a time stamp range.") -(defconst org-tr-regexp-both - (concat org-ts-regexp-both "--?-?" org-ts-regexp-both) - "Regular expression matching a time stamp range.") -(defconst org-tsr-regexp (concat org-ts-regexp "\\(--?-?" - org-ts-regexp "\\)?") - "Regular expression matching a time stamp or time stamp range.") -(defconst org-tsr-regexp-both - (concat org-ts-regexp-both "\\(--?-?" - org-ts-regexp-both "\\)?") - "Regular expression matching a time stamp or time stamp range. -The time stamps may be either active or inactive.") + "\\]") + org-any-link-re + (concat "\\(" org-bracket-link-regexp "\\)\\|\\(" + org-angle-link-re "\\)\\|\\(" + org-plain-link-re "\\)")))) + +(org-make-link-regexps) (defvar org-emph-face nil) (defun org-do-emphasis-faces (limit) - "Run through the buffer and add overlays to emphasized strings." + "Run through the buffer and emphasize strings." (let (rtn a) (while (and (not rtn) (re-search-forward org-emph-re limit t)) (let* ((border (char-after (match-beginning 3))) (bre (regexp-quote (char-to-string border)))) - (if (and (not (= border (char-after (match-beginning 4)))) - (not (save-match-data - (string-match (concat bre ".*" bre) - (replace-regexp-in-string - "\n" " " - (substring (match-string 2) 1 -1)))))) - (progn - (setq rtn t) - (setq a (assoc (match-string 3) org-emphasis-alist)) - (font-lock-prepend-text-property (match-beginning 2) (match-end 2) - 'face - (nth 1 a)) - (and (nth 2 a) - (org-remove-flyspell-overlays-in - (match-beginning 0) (match-end 0))) - (add-text-properties (match-beginning 2) (match-end 2) - '(font-lock-multiline t org-emphasis t)) - (when org-hide-emphasis-markers - (add-text-properties (match-end 4) (match-beginning 5) - '(invisible org-link)) - (add-text-properties (match-beginning 3) (match-end 3) - '(invisible org-link)))))) + (when (and (not (= border (char-after (match-beginning 4)))) + (not (string-match-p (concat bre ".*" bre) + (replace-regexp-in-string + "\n" " " + (substring (match-string 2) 1 -1))))) + (setq rtn t) + (setq a (assoc (match-string 3) org-emphasis-alist)) + (font-lock-prepend-text-property (match-beginning 2) (match-end 2) + 'face + (nth 1 a)) + (and (nth 2 a) + (org-remove-flyspell-overlays-in + (match-beginning 0) (match-end 0))) + (add-text-properties (match-beginning 2) (match-end 2) + '(font-lock-multiline t org-emphasis t)) + (when org-hide-emphasis-markers + (add-text-properties (match-end 4) (match-beginning 5) + '(invisible org-link)) + (add-text-properties (match-beginning 3) (match-end 3) + '(invisible org-link))))) (goto-char (1+ (match-beginning 0)))) rtn)) @@ -5736,19 +5917,20 @@ If CHAR is not given (for example in an interactive call) it will be prompted for." (interactive) (let ((erc org-emphasis-regexp-components) - (prompt "") - (string "") beg end move c s) + (string "") beg end move s) (if (org-region-active-p) - (setq beg (region-beginning) end (region-end) + (setq beg (region-beginning) + end (region-end) string (buffer-substring beg end)) (setq move t)) (unless char (message "Emphasis marker or tag: [%s]" - (mapconcat (lambda(e) (car e)) org-emphasis-alist "")) + (mapconcat #'car org-emphasis-alist "")) (setq char (read-char-exclusive))) - (if (equal char ?\ ) - (setq s "" move nil) + (if (equal char ?\s) + (setq s "" + move nil) (unless (assoc (char-to-string char) org-emphasis-alist) (user-error "No such emphasis marker: \"%c\"" char)) (setq s (char-to-string char))) @@ -5757,7 +5939,7 @@ prompted for." (assoc (substring string 0 1) org-emphasis-alist)) (setq string (substring string 1 -1))) (setq string (concat s string s)) - (if beg (delete-region beg end)) + (when beg (delete-region beg end)) (unless (or (bolp) (string-match (concat "[" (nth 0 erc) "\n]") (char-to-string (char-before (point))))) @@ -5775,37 +5957,86 @@ prompted for." (defsubst org-rear-nonsticky-at (pos) (add-text-properties (1- pos) pos (list 'rear-nonsticky org-nonsticky-props))) -(defun org-activate-plain-links (limit) - "Run through the buffer and add overlays to links." - (let (f hl) - (when (and (re-search-forward (concat org-plain-link-re) limit t) - (not (org-in-src-block-p))) - (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) - (setq f (get-text-property (match-beginning 0) 'face)) - (setq hl (org-match-string-no-properties 0)) - (if (or (eq f 'org-tag) - (and (listp f) (memq 'org-tag f))) - nil - (add-text-properties (match-beginning 0) (match-end 0) - (list 'mouse-face 'highlight - 'face 'org-link - 'htmlize-link `(:uri ,hl) - 'keymap org-mouse-map)) - (org-rear-nonsticky-at (match-end 0))) - t))) +(defun org-activate-links (limit) + "Add link properties to links. +This includes angle, plain, and bracket links." + (catch :exit + (while (re-search-forward org-any-link-re limit t) + (let* ((start (match-beginning 0)) + (end (match-end 0)) + (style (cond ((eq ?< (char-after start)) 'angle) + ((eq ?\[ (char-after (1+ start))) 'bracket) + (t 'plain)))) + (when (and (memq style org-highlight-links) + ;; Do not confuse plain links with tags. + (not (and (eq style 'plain) + (let ((face (get-text-property + (max (1- start) (point-min)) 'face))) + (if (consp face) (memq 'org-tag face) + (eq 'org-tag face)))))) + (let* ((link-object (save-excursion + (goto-char start) + (save-match-data (org-element-link-parser)))) + (link (org-element-property :raw-link link-object)) + (type (org-element-property :type link-object)) + (path (org-element-property :path link-object)) + (properties ;for link's visible part + (list + 'face (pcase (org-link-get-parameter type :face) + ((and (pred functionp) face) (funcall face path)) + ((and (pred facep) face) face) + ((and (pred consp) face) face) ;anonymous + (_ 'org-link)) + 'mouse-face (or (org-link-get-parameter type :mouse-face) + 'highlight) + 'keymap (or (org-link-get-parameter type :keymap) + org-mouse-map) + 'help-echo (pcase (org-link-get-parameter type :help-echo) + ((and (pred stringp) echo) echo) + ((and (pred functionp) echo) echo) + (_ (concat "LINK: " link))) + 'htmlize-link (pcase (org-link-get-parameter type + :htmlize-link) + ((and (pred functionp) f) (funcall f)) + (_ `(:uri ,link))) + 'font-lock-multiline t))) + (org-remove-flyspell-overlays-in start end) + (org-rear-nonsticky-at end) + (if (not (eq 'bracket style)) + (add-text-properties start end properties) + ;; Handle invisible parts in bracket links. + (remove-text-properties start end '(invisible nil)) + (let ((hidden + (append `(invisible + ,(or (org-link-get-parameter type :display) + 'org-link)) + properties)) + (visible-start (or (match-beginning 4) (match-beginning 2))) + (visible-end (or (match-end 4) (match-end 2)))) + (add-text-properties start visible-start hidden) + (add-text-properties visible-start visible-end properties) + (add-text-properties visible-end end hidden) + (org-rear-nonsticky-at visible-start) + (org-rear-nonsticky-at visible-end))) + (let ((f (org-link-get-parameter type :activate-func))) + (when (functionp f) + (funcall f start end path (eq style 'bracket)))) + (throw :exit t))))) ;signal success + nil)) (defun org-activate-code (limit) - (if (re-search-forward "^[ \t]*\\(:\\(?: .*\\|$\\)\n?\\)" limit t) - (progn - (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) - (remove-text-properties (match-beginning 0) (match-end 0) - '(display t invisible t intangible t)) - t))) + (when (re-search-forward "^[ \t]*\\(:\\(?: .*\\|$\\)\n?\\)" limit t) + (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) + (remove-text-properties (match-beginning 0) (match-end 0) + '(display t invisible t intangible t)) + t)) -(defcustom org-src-fontify-natively nil - "When non-nil, fontify code in code blocks." +(defcustom org-src-fontify-natively t + "When non-nil, fontify code in code blocks. +See also the `org-block' face." :type 'boolean - :version "24.1" + :version "24.4" + :package-version '(Org . "8.3") :group 'org-appearance :group 'org-babel) @@ -5820,221 +6051,248 @@ by a #." (defun org-fontify-meta-lines-and-blocks (limit) (condition-case nil (org-fontify-meta-lines-and-blocks-1 limit) - (error (message "org-mode fontification error")))) + (error (message "org-mode fontification error in %S at %d" + (current-buffer) + (line-number-at-pos))))) (defun org-fontify-meta-lines-and-blocks-1 (limit) "Fontify #+ lines and blocks." (let ((case-fold-search t)) - (if (re-search-forward - "^\\([ \t]*#\\(\\(\\+[a-zA-Z]+:?\\| \\|$\\)\\(_\\([a-zA-Z]+\\)\\)?\\)[ \t]*\\(\\([^ \t\n]*\\)[ \t]*\\(.*\\)\\)\\)" - limit t) - (let ((beg (match-beginning 0)) - (block-start (match-end 0)) - (block-end nil) - (lang (match-string 7)) - (beg1 (line-beginning-position 2)) - (dc1 (downcase (match-string 2))) - (dc3 (downcase (match-string 3))) - end end1 quoting block-type ovl) - (cond - ((member dc1 '("+html:" "+ascii:" "+latex:")) - ;; a single line of backend-specific content - (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) - (remove-text-properties (match-beginning 0) (match-end 0) - '(display t invisible t intangible t)) - (add-text-properties (match-beginning 1) (match-end 3) - '(font-lock-fontified t face org-meta-line)) - (add-text-properties (match-beginning 6) (+ (match-end 6) 1) - '(font-lock-fontified t face org-block)) - ; for backend-specific code - t) - ((and (match-end 4) (equal dc3 "+begin")) - ;; Truly a block - (setq block-type (downcase (match-string 5)) - quoting (member block-type org-protecting-blocks)) - (when (re-search-forward - (concat "^[ \t]*#\\+end" (match-string 4) "\\>.*") - nil t) ;; on purpose, we look further than LIMIT - (setq end (min (point-max) (match-end 0)) - end1 (min (point-max) (1- (match-beginning 0)))) - (setq block-end (match-beginning 0)) - (when quoting - (remove-text-properties beg end - '(display t invisible t intangible t))) - (add-text-properties - beg end - '(font-lock-fontified t font-lock-multiline t)) - (add-text-properties beg beg1 '(face org-meta-line)) - (add-text-properties end1 (min (point-max) (1+ end)) - '(face org-meta-line)) ; for end_src - (cond - ((and lang (not (string= lang "")) org-src-fontify-natively) - (org-src-font-lock-fontify-block lang block-start block-end) - ;; remove old background overlays - (mapc (lambda (ov) - (if (eq (overlay-get ov 'face) 'org-block-background) - (delete-overlay ov))) - (overlays-at (/ (+ beg1 block-end) 2))) - ;; add a background overlay - (setq ovl (make-overlay beg1 block-end)) - (overlay-put ovl 'face 'org-block-background) - (overlay-put ovl 'evaporate t)) ;; make it go away when empty - (quoting - (add-text-properties beg1 (min (point-max) (1+ end1)) - '(face org-block))) ; end of source block - ((not org-fontify-quote-and-verse-blocks)) - ((string= block-type "quote") - (add-text-properties beg1 (min (point-max) (1+ end1)) '(face org-quote))) - ((string= block-type "verse") - (add-text-properties beg1 (min (point-max) (1+ end1)) '(face org-verse)))) - (add-text-properties beg beg1 '(face org-block-begin-line)) - (add-text-properties (min (point-max) (1+ end)) (min (point-max) (1+ end1)) - '(face org-block-end-line)) - t)) - ((member dc1 '("+title:" "+author:" "+email:" "+date:")) - (add-text-properties - beg (match-end 3) - (if (member (intern (substring dc1 1 -1)) org-hidden-keywords) - '(font-lock-fontified t invisible t) - '(font-lock-fontified t face org-document-info-keyword))) - (add-text-properties - (match-beginning 6) (min (point-max) (1+ (match-end 6))) - (if (string-equal dc1 "+title:") - '(font-lock-fontified t face org-document-title) - '(font-lock-fontified t face org-document-info)))) - ((or (equal dc1 "+results") - (member dc1 '("+begin:" "+end:" "+caption:" "+label:" - "+orgtbl:" "+tblfm:" "+tblname:" "+results:" - "+call:" "+header:" "+headers:" "+name:")) - (and (match-end 4) (equal dc3 "+attr"))) - (add-text-properties - beg (match-end 0) - '(font-lock-fontified t face org-meta-line)) - t) - ((member dc3 '(" " "")) - (add-text-properties - beg (match-end 0) - '(font-lock-fontified t face font-lock-comment-face))) - ((not (member (char-after beg) '(?\ ?\t))) - ;; just any other in-buffer setting, but not indented + (when (re-search-forward + "^\\([ \t]*#\\(\\(\\+[a-zA-Z]+:?\\| \\|$\\)\\(_\\([a-zA-Z]+\\)\\)?\\)[ \t]*\\(\\([^ \t\n]*\\)[ \t]*\\(.*\\)\\)\\)" + limit t) + (let ((beg (match-beginning 0)) + (block-start (match-end 0)) + (block-end nil) + (lang (match-string 7)) + (beg1 (line-beginning-position 2)) + (dc1 (downcase (match-string 2))) + (dc3 (downcase (match-string 3))) + end end1 quoting block-type) + (cond + ((and (match-end 4) (equal dc3 "+begin")) + ;; Truly a block + (setq block-type (downcase (match-string 5)) + quoting (member block-type org-protecting-blocks)) + (when (re-search-forward + (concat "^[ \t]*#\\+end" (match-string 4) "\\>.*") + nil t) ;; on purpose, we look further than LIMIT + (setq end (min (point-max) (match-end 0)) + end1 (min (point-max) (1- (match-beginning 0)))) + (setq block-end (match-beginning 0)) + (when quoting + (org-remove-flyspell-overlays-in beg1 end1) + (remove-text-properties beg end + '(display t invisible t intangible t))) (add-text-properties - beg (match-end 0) - '(font-lock-fontified t face org-meta-line)) - t) - (t nil)))))) - -(defun org-activate-angle-links (limit) - "Run through the buffer and add overlays to links." - (if (and (re-search-forward org-angle-link-re limit t) - (not (org-in-src-block-p))) - (progn - (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) - (add-text-properties (match-beginning 0) (match-end 0) - (list 'mouse-face 'highlight - 'keymap org-mouse-map)) - (org-rear-nonsticky-at (match-end 0)) - t))) + beg end '(font-lock-fontified t font-lock-multiline t)) + (add-text-properties beg beg1 '(face org-meta-line)) + (org-remove-flyspell-overlays-in beg beg1) + (add-text-properties ; For end_src + end1 (min (point-max) (1+ end)) '(face org-meta-line)) + (org-remove-flyspell-overlays-in end1 end) + (cond + ((and lang (not (string= lang "")) org-src-fontify-natively) + (org-src-font-lock-fontify-block lang block-start block-end) + (add-text-properties beg1 block-end '(src-block t))) + (quoting + (add-text-properties beg1 (min (point-max) (1+ end1)) + (list 'face + (list :inherit + (let ((face-name + (intern (format "org-block-%s" lang)))) + (append (and (facep face-name) (list face-name)) + '(org-block))))))) ; end of source block + ((not org-fontify-quote-and-verse-blocks)) + ((string= block-type "quote") + (add-face-text-property + beg1 (min (point-max) (1+ end1)) 'org-quote t)) + ((string= block-type "verse") + (add-face-text-property + beg1 (min (point-max) (1+ end1)) 'org-verse t))) + (add-text-properties beg beg1 '(face org-block-begin-line)) + (add-text-properties (min (point-max) (1+ end)) (min (point-max) (1+ end1)) + '(face org-block-end-line)) + t)) + ((member dc1 '("+title:" "+author:" "+email:" "+date:")) + (org-remove-flyspell-overlays-in + (match-beginning 0) + (if (equal "+title:" dc1) (match-end 2) (match-end 0))) + (add-text-properties + beg (match-end 3) + (if (member (intern (substring dc1 1 -1)) org-hidden-keywords) + '(font-lock-fontified t invisible t) + '(font-lock-fontified t face org-document-info-keyword))) + (add-text-properties + (match-beginning 6) (min (point-max) (1+ (match-end 6))) + (if (string-equal dc1 "+title:") + '(font-lock-fontified t face org-document-title) + '(font-lock-fontified t face org-document-info)))) + ((string-prefix-p "+caption" dc1) + (org-remove-flyspell-overlays-in (match-end 2) (match-end 0)) + (remove-text-properties (match-beginning 0) (match-end 0) + '(display t invisible t intangible t)) + ;; Handle short captions. + (save-excursion + (beginning-of-line) + (looking-at "\\([ \t]*#\\+caption\\(?:\\[.*\\]\\)?:\\)[ \t]*")) + (add-text-properties (line-beginning-position) (match-end 1) + '(font-lock-fontified t face org-meta-line)) + (add-text-properties (match-end 0) (line-end-position) + '(font-lock-fontified t face org-block)) + t) + ((member dc3 '(" " "")) + (org-remove-flyspell-overlays-in beg (match-end 0)) + (add-text-properties + beg (match-end 0) + '(font-lock-fontified t face font-lock-comment-face))) + (t ;; just any other in-buffer setting, but not indented + (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) + (remove-text-properties (match-beginning 0) (match-end 0) + '(display t invisible t intangible t)) + (add-text-properties beg (match-end 0) + '(font-lock-fontified t face org-meta-line)) + t)))))) + +(defun org-fontify-drawers (limit) + "Fontify drawers." + (when (re-search-forward org-drawer-regexp limit t) + (add-text-properties + (match-beginning 0) (match-end 0) + '(font-lock-fontified t face org-special-keyword)) + (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) + t)) + +(defun org-fontify-macros (limit) + "Fontify macros." + (when (re-search-forward "\\({{{\\).+?\\(}}}\\)" limit t) + (add-text-properties + (match-beginning 0) (match-end 0) + '(font-lock-fontified t face org-macro)) + (when org-hide-macro-markers + (add-text-properties (match-end 2) (match-beginning 2) + '(invisible t)) + (add-text-properties (match-beginning 1) (match-end 1) + '(invisible t))) + (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) + t)) (defun org-activate-footnote-links (limit) - "Run through the buffer and add overlays to footnotes." + "Add text properties for footnotes." (let ((fn (org-footnote-next-reference-or-definition limit))) (when fn - (let ((beg (nth 1 fn)) (end (nth 2 fn))) - (org-remove-flyspell-overlays-in beg end) + (let* ((beg (nth 1 fn)) + (end (nth 2 fn)) + (label (car fn)) + (referencep (/= (line-beginning-position) beg))) + (when (and referencep (nth 3 fn)) + (save-excursion + (goto-char beg) + (search-forward (or label "fn:")) + (org-remove-flyspell-overlays-in beg (match-end 0)))) (add-text-properties beg end (list 'mouse-face 'highlight 'keymap org-mouse-map 'help-echo - (if (= (point-at-bol) beg) - "Footnote definition" - "Footnote reference") + (if referencep "Footnote reference" + "Footnote definition") 'font-lock-fontified t 'font-lock-multiline t 'face 'org-footnote)))))) -(defun org-activate-bracket-links (limit) - "Run through the buffer and add overlays to bracketed links." - (if (and (re-search-forward org-bracket-link-regexp limit t) - (not (org-in-src-block-p))) - (let* ((hl (org-match-string-no-properties 1)) - (help (concat "LINK: " (save-match-data (org-link-unescape hl)))) - (ip (org-maybe-intangible - (list 'invisible 'org-link - 'keymap org-mouse-map 'mouse-face 'highlight - 'font-lock-multiline t 'help-echo help - 'htmlize-link `(:uri ,hl)))) - (vp (list 'keymap org-mouse-map 'mouse-face 'highlight - 'font-lock-multiline t 'help-echo help - 'htmlize-link `(:uri ,hl)))) - ;; We need to remove the invisible property here. Table narrowing - ;; may have made some of this invisible. - (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) - (remove-text-properties (match-beginning 0) (match-end 0) - '(invisible nil)) - (if (match-end 3) - (progn - (add-text-properties (match-beginning 0) (match-beginning 3) ip) - (org-rear-nonsticky-at (match-beginning 3)) - (add-text-properties (match-beginning 3) (match-end 3) vp) - (org-rear-nonsticky-at (match-end 3)) - (add-text-properties (match-end 3) (match-end 0) ip) - (org-rear-nonsticky-at (match-end 0))) - (add-text-properties (match-beginning 0) (match-beginning 1) ip) - (org-rear-nonsticky-at (match-beginning 1)) - (add-text-properties (match-beginning 1) (match-end 1) vp) - (org-rear-nonsticky-at (match-end 1)) - (add-text-properties (match-end 1) (match-end 0) ip) - (org-rear-nonsticky-at (match-end 0))) - t))) - (defun org-activate-dates (limit) - "Run through the buffer and add overlays to dates." - (if (and (re-search-forward org-tsr-regexp-both limit t) - (not (equal (char-before (match-beginning 0)) 91))) - (progn - (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) - (add-text-properties (match-beginning 0) (match-end 0) - (list 'mouse-face 'highlight - 'keymap org-mouse-map)) - (org-rear-nonsticky-at (match-end 0)) - (when org-display-custom-times - (if (match-end 3) - (org-display-custom-time (match-beginning 3) (match-end 3))) - (org-display-custom-time (match-beginning 1) (match-end 1))) - t))) - -(defvar org-target-link-regexp nil + "Add text properties for dates." + (when (and (re-search-forward org-tsr-regexp-both limit t) + (not (equal (char-before (match-beginning 0)) 91))) + (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) + (add-text-properties (match-beginning 0) (match-end 0) + (list 'mouse-face 'highlight + 'keymap org-mouse-map)) + (org-rear-nonsticky-at (match-end 0)) + (when org-display-custom-times + (if (match-end 3) + (org-display-custom-time (match-beginning 3) (match-end 3)) + (org-display-custom-time (match-beginning 1) (match-end 1)))) + t)) + +(defvar-local org-target-link-regexp nil "Regular expression matching radio targets in plain text.") -(make-variable-buffer-local 'org-target-link-regexp) -(defvar org-target-regexp "<<\\([^<>\n\r]+\\)>>" + +(defconst org-target-regexp (let ((border "[^<>\n\r \t]")) + (format "<<\\(%s\\|%s[^<>\n\r]*%s\\)>>" + border border border)) "Regular expression matching a link target.") -(defvar org-radio-target-regexp "<<<\\([^<>\n\r]+\\)>>>" + +(defconst org-radio-target-regexp (format "<%s>" org-target-regexp) "Regular expression matching a radio target.") -(defvar org-any-target-regexp "<<\n\r]+\\)>>>?" ; FIXME, not exact, would match <<> as a radio target. + +(defconst org-any-target-regexp + (format "%s\\|%s" org-radio-target-regexp org-target-regexp) "Regular expression matching any target.") (defun org-activate-target-links (limit) - "Run through the buffer and add overlays to target matches." + "Add text properties for target matches." (when org-target-link-regexp (let ((case-fold-search t)) - (if (re-search-forward org-target-link-regexp limit t) - (progn - (org-remove-flyspell-overlays-in (match-beginning 0) (match-end 0)) - (add-text-properties (match-beginning 0) (match-end 0) - (list 'mouse-face 'highlight - 'keymap org-mouse-map - 'help-echo "Radio target link" - 'org-linked-text t)) - (org-rear-nonsticky-at (match-end 0)) - t))))) + (when (re-search-forward org-target-link-regexp limit t) + (org-remove-flyspell-overlays-in (match-beginning 1) (match-end 1)) + (add-text-properties (match-beginning 1) (match-end 1) + (list 'mouse-face 'highlight + 'keymap org-mouse-map + 'help-echo "Radio target link" + 'org-linked-text t)) + (org-rear-nonsticky-at (match-end 1)) + t)))) (defun org-update-radio-target-regexp () - "Find all radio targets in this file and update the regular expression." + "Find all radio targets in this file and update the regular expression. +Also refresh fontification if needed." (interactive) - (when (memq 'radio org-activate-links) + (let ((old-regexp org-target-link-regexp) + (before-re "\\(?:^\\|[^[:alnum:]]\\)\\(") + (after-re "\\)\\(?:$\\|[^[:alnum:]]\\)") + (targets + (org-with-wide-buffer + (goto-char (point-min)) + (let (rtn) + (while (re-search-forward org-radio-target-regexp nil t) + ;; Make sure point is really within the object. + (backward-char) + (let ((obj (org-element-context))) + (when (eq (org-element-type obj) 'radio-target) + (cl-pushnew (org-element-property :value obj) rtn + :test #'equal)))) + rtn)))) (setq org-target-link-regexp - (org-make-target-link-regexp (org-all-targets 'radio))) - (org-restart-font-lock))) + (and targets + (concat before-re + (mapconcat + (lambda (x) + (replace-regexp-in-string + " +" "\\s-+" (regexp-quote x) t t)) + targets + "\\|") + after-re))) + (unless (equal old-regexp org-target-link-regexp) + ;; Clean-up cache. + (let ((regexp (cond ((not old-regexp) org-target-link-regexp) + ((not org-target-link-regexp) old-regexp) + (t + (concat before-re + (mapconcat + (lambda (re) + (substring re (length before-re) + (- (length after-re)))) + (list old-regexp org-target-link-regexp) + "\\|") + after-re))))) + (org-with-wide-buffer + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (org-element-cache-refresh (match-beginning 1))))) + ;; Re fontify buffer. + (when (memq 'radio org-highlight-links) + (org-restart-font-lock))))) (defun org-hide-wide-columns (limit) (let (s e) @@ -6042,20 +6300,18 @@ by a #." 'org-cwidth t)) (when s (setq e (next-single-property-change s 'org-cwidth)) - (add-text-properties s e (org-maybe-intangible '(invisible org-cwidth))) + (add-text-properties s e '(invisible org-cwidth)) (goto-char e) t))) (defvar org-latex-and-related-regexp nil "Regular expression for highlighting LaTeX, entities and sub/superscript.") -(defvar org-match-substring-regexp) -(defvar org-match-substring-with-braces-regexp) (defun org-compute-latex-and-related-regexp () "Compute regular expression for LaTeX, entities and sub/superscript. Result depends on variable `org-highlight-latex-and-related'." - (org-set-local - 'org-latex-and-related-regexp + (setq-local + org-latex-and-related-regexp (let* ((re-sub (cond ((not (memq 'script org-highlight-latex-and-related)) nil) ((eq org-use-sub-superscripts '{}) @@ -6081,9 +6337,13 @@ done, nil otherwise." (when (org-string-nw-p org-latex-and-related-regexp) (catch 'found (while (re-search-forward org-latex-and-related-regexp limit t) - (unless (memq (car-safe (get-text-property (1+ (match-beginning 0)) - 'face)) - '(org-code org-verbatim underline)) + (unless + (cl-some + (lambda (f) + (memq f '(org-code org-verbatim underline org-special-keyword))) + (save-excursion + (goto-char (1+ (match-beginning 0))) + (face-at-point nil t))) (let ((offset (if (memq (char-after (1+ (match-beginning 0))) '(?_ ?^)) 1 @@ -6102,63 +6362,32 @@ done, nil otherwise." (font-lock-mode -1) (font-lock-mode 1))) -(defun org-all-targets (&optional radio) - "Return a list of all targets in this file. -When optional argument RADIO is non-nil, only find radio -targets." - (let ((re (if radio org-radio-target-regexp org-target-regexp)) rtn) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward re nil t) - ;; Make sure point is really within the object. - (backward-char) - (let ((obj (org-element-context))) - (when (memq (org-element-type obj) '(radio-target target)) - (add-to-list 'rtn (downcase (org-element-property :value obj)))))) - rtn))) - -(defun org-make-target-link-regexp (targets) - "Make regular expression matching all strings in TARGETS. -The regular expression finds the targets also if there is a line break -between words." - (and targets - (concat - "\\_<\\(" - (mapconcat - (lambda (x) - (setq x (regexp-quote x)) - (while (string-match " +" x) - (setq x (replace-match "\\s-+" t t x))) - x) - targets - "\\|") - "\\)\\_>"))) - (defun org-activate-tags (limit) - (if (re-search-forward (org-re "^\\*+.*[ \t]\\(:[[:alnum:]_@#%:]+:\\)[ \r\n]") limit t) - (progn - (org-remove-flyspell-overlays-in (match-beginning 1) (match-end 1)) - (add-text-properties (match-beginning 1) (match-end 1) - (list 'mouse-face 'highlight - 'keymap org-mouse-map)) - (org-rear-nonsticky-at (match-end 1)) - t))) + (when (re-search-forward + "^\\*+.*[ \t]\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" limit t) + (org-remove-flyspell-overlays-in (match-beginning 1) (match-end 1)) + (add-text-properties (match-beginning 1) (match-end 1) + (list 'mouse-face 'highlight + 'keymap org-mouse-map)) + (org-rear-nonsticky-at (match-end 1)) + t)) (defun org-outline-level () "Compute the outline level of the heading at point. -If this is called at a normal headline, the level is the number of stars. -Use `org-reduced-level' to remove the effect of `org-odd-levels'." - (save-excursion - (if (not (condition-case nil - (org-back-to-heading t) - (error nil))) - 0 - (looking-at org-outline-regexp) - (1- (- (match-end 0) (match-beginning 0)))))) + +If this is called at a normal headline, the level is the number +of stars. Use `org-reduced-level' to remove the effect of +`org-odd-levels'. Unlike to `org-current-level', this function +takes into consideration inlinetasks." + (org-with-wide-buffer + (end-of-line) + (if (re-search-backward org-outline-regexp-bol nil t) + (1- (- (match-end 0) (match-beginning 0))) + 0))) (defvar org-font-lock-keywords nil) -(defsubst org-re-property (property &optional literal allow-null) +(defsubst org-re-property (property &optional literal allow-null value) "Return a regexp matching a PROPERTY line. When optional argument LITERAL is non-nil, do not quote PROPERTY. @@ -6166,17 +6395,25 @@ This is useful when PROPERTY is a regexp. When ALLOW-NULL is non-nil, match properties even without a value. Match group 3 is set to the value when it exists. If there is no -value and ALLOW-NULL is non-nil, it is set to the empty string." +value and ALLOW-NULL is non-nil, it is set to the empty string. + +With optional argument VALUE, match only property lines with +that value; in this case, ALLOW-NULL is ignored. VALUE is quoted +unless LITERAL is non-nil." (concat "^\\(?4:[ \t]*\\)" (format "\\(?1::\\(?2:%s\\):\\)" (if literal property (regexp-quote property))) - (if allow-null - "\\(?:\\(?3:$\\)\\|[ \t]+\\(?3:.*?\\)\\)\\(?5:[ \t]*\\)$" - "[ \t]+\\(?3:[^ \r\t\n]+.*?\\)\\(?5:[ \t]*\\)$"))) + (cond (value + (format "[ \t]+\\(?3:%s\\)\\(?5:[ \t]*\\)$" + (if literal value (regexp-quote value)))) + (allow-null + "\\(?:\\(?3:$\\)\\|[ \t]+\\(?3:.*?\\)\\)\\(?5:[ \t]*\\)$") + (t + "[ \t]+\\(?3:[^ \r\t\n]+.*?\\)\\(?5:[ \t]*\\)$")))) (defconst org-property-re - (org-re-property ".*?" 'literal t) + (org-re-property "\\S-+" 'literal t) "Regular expression matching a property line. There are four matching groups: 1: :PROPKEY: including the leading and trailing colon, @@ -6188,6 +6425,8 @@ There are four matching groups: (defvar org-font-lock-hook nil "Functions to be called for special font lock stuff.") +(defvar org-font-lock-extra-keywords nil) ;Dynamically scoped. + (defvar org-font-lock-set-keywords-hook nil "Functions that can manipulate `org-font-lock-extra-keywords'. This is called after `org-font-lock-extra-keywords' is defined, but before @@ -6201,7 +6440,7 @@ needs to be inserted at a specific position in the font-lock sequence.") (defun org-set-font-lock-defaults () "Set font lock defaults for the current buffer." (let* ((em org-fontify-emphasized-text) - (lk org-activate-links) + (lk org-highlight-links) (org-font-lock-extra-keywords (list ;; Call the hook @@ -6222,26 +6461,23 @@ needs to be inserted at a specific position in the font-lock sequence.") '("^[ \t]*|\\( *\\([$!_^/]\\) *|.*\\)|" (1 'org-formula t)) '("| *\\(<[lrc]?[0-9]*>\\)" (1 'org-formula t)) ;; Drawers - (list org-drawer-regexp '(0 'org-special-keyword t)) - (list "^[ \t]*:END:" '(0 'org-special-keyword t)) + '(org-fontify-drawers) ;; Properties (list org-property-re '(1 'org-special-keyword t) '(3 'org-property-value t)) - ;; Links - (if (memq 'tag lk) '(org-activate-tags (1 'org-tag prepend))) - (if (memq 'angle lk) '(org-activate-angle-links (0 'org-link t))) - (if (memq 'plain lk) '(org-activate-plain-links (0 'org-link t))) - (if (memq 'bracket lk) '(org-activate-bracket-links (0 'org-link t))) - (if (memq 'radio lk) '(org-activate-target-links (0 'org-link t))) - (if (memq 'date lk) '(org-activate-dates (0 'org-date t))) - (if (memq 'footnote lk) '(org-activate-footnote-links)) + ;; Link related fontification. + '(org-activate-links) + (when (memq 'tag lk) '(org-activate-tags (1 'org-tag prepend))) + (when (memq 'radio lk) '(org-activate-target-links (1 'org-link t))) + (when (memq 'date lk) '(org-activate-dates (0 'org-date t))) + (when (memq 'footnote lk) '(org-activate-footnote-links)) ;; Targets. (list org-any-target-regexp '(0 'org-target t)) ;; Diary sexps. '("^&?%%(.*\\|<%%([^>\n]*?>" (0 'org-sexp-date t)) ;; Macro - '("{{{.+}}}" (0 'org-macro t)) + '(org-fontify-macros) '(org-hide-wide-columns (0 nil append)) ;; TODO keyword (list (format org-heading-keyword-regexp-format @@ -6261,27 +6497,24 @@ needs to be inserted at a specific position in the font-lock sequence.") ;; Tags '(org-font-lock-add-tag-faces) ;; Tags groups - (if (and org-group-tags org-tag-groups-alist) - (list (concat org-outline-regexp-bol ".+\\(:" - (regexp-opt (mapcar 'car org-tag-groups-alist)) - ":\\).*$") - '(1 'org-tag-group prepend))) + (when (and org-group-tags org-tag-groups-alist) + (list (concat org-outline-regexp-bol ".+\\(:" + (regexp-opt (mapcar 'car org-tag-groups-alist)) + ":\\).*$") + '(1 'org-tag-group prepend))) ;; Special keywords (list (concat "\\<" org-deadline-string) '(0 'org-special-keyword t)) (list (concat "\\<" org-scheduled-string) '(0 'org-special-keyword t)) (list (concat "\\<" org-closed-string) '(0 'org-special-keyword t)) (list (concat "\\<" org-clock-string) '(0 'org-special-keyword t)) ;; Emphasis - (if em - (if (featurep 'xemacs) - '(org-do-emphasis-faces (0 nil append)) - '(org-do-emphasis-faces))) + (when em '(org-do-emphasis-faces)) ;; Checkboxes '("^[ \t]*\\(?:[-+*]\\|[0-9]+[.)]\\)[ \t]+\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]*\\)?\\(\\[[- X]\\]\\)" 1 'org-checkbox prepend) - (if (cdr (assq 'checkbox org-list-automatic-rules)) - '("\\[\\([0-9]*%\\)\\]\\|\\[\\([0-9]*\\)/\\([0-9]*\\)\\]" - (0 (org-get-checkbox-statistics-face) t))) + (when (cdr (assq 'checkbox org-list-automatic-rules)) + '("\\[\\([0-9]*%\\)\\]\\|\\[\\([0-9]*\\)/\\([0-9]*\\)\\]" + (0 (org-get-checkbox-statistics-face) t))) ;; Description list items '("^[ \t]*[-+*][ \t]+\\(.*?[ \t]+::\\)\\([ \t]+\\|$\\)" 1 'org-list-dt prepend) @@ -6297,83 +6530,92 @@ needs to be inserted at a specific position in the font-lock sequence.") ;; Code '(org-activate-code (1 'org-code t)) ;; COMMENT - (list (format org-heading-keyword-regexp-format - (concat "\\(" - org-comment-string "\\|" org-quote-string - "\\)")) - '(2 'org-special-keyword t)) + (list (format + "^\\*+\\(?: +%s\\)?\\(?: +\\[#[A-Z0-9]\\]\\)? +\\(?9:%s\\)\\(?: \\|$\\)" + org-todo-regexp + org-comment-string) + '(9 'org-special-keyword t)) ;; Blocks and meta lines '(org-fontify-meta-lines-and-blocks)))) (setq org-font-lock-extra-keywords (delq nil org-font-lock-extra-keywords)) (run-hooks 'org-font-lock-set-keywords-hook) ;; Now set the full font-lock-keywords - (org-set-local 'org-font-lock-keywords org-font-lock-extra-keywords) - (org-set-local 'font-lock-defaults - '(org-font-lock-keywords t nil nil backward-paragraph)) - (kill-local-variable 'font-lock-keywords) nil)) + (setq-local org-font-lock-keywords org-font-lock-extra-keywords) + (setq-local font-lock-defaults + '(org-font-lock-keywords t nil nil backward-paragraph)) + (kill-local-variable 'font-lock-keywords) + nil)) (defun org-toggle-pretty-entities () "Toggle the composition display of entities as UTF8 characters." (interactive) - (org-set-local 'org-pretty-entities (not org-pretty-entities)) + (setq-local org-pretty-entities (not org-pretty-entities)) (org-restart-font-lock) (if org-pretty-entities (message "Entities are now displayed as UTF8 characters") (save-restriction (widen) - (org-decompose-region (point-min) (point-max)) + (decompose-region (point-min) (point-max)) (message "Entities are now displayed as plain text")))) -(defvar org-custom-properties-overlays nil +(defvar-local org-custom-properties-overlays nil "List of overlays used for custom properties.") -(make-variable-buffer-local 'org-custom-properties-overlays) (defun org-toggle-custom-properties-visibility () "Display or hide properties in `org-custom-properties'." (interactive) (if org-custom-properties-overlays - (progn (mapc 'delete-overlay org-custom-properties-overlays) + (progn (mapc #'delete-overlay org-custom-properties-overlays) (setq org-custom-properties-overlays nil)) - (unless (not org-custom-properties) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while (re-search-forward org-property-re nil t) - (mapc (lambda(p) - (when (equal p (substring (match-string 1) 1 -1)) - (let ((o (make-overlay (match-beginning 0) (1+ (match-end 0))))) - (overlay-put o 'invisible t) - (overlay-put o 'org-custom-property t) - (push o org-custom-properties-overlays)))) - org-custom-properties))))))) + (when org-custom-properties + (org-with-wide-buffer + (goto-char (point-min)) + (let ((regexp (org-re-property (regexp-opt org-custom-properties) t t))) + (while (re-search-forward regexp nil t) + (let ((end (cdr (save-match-data (org-get-property-block))))) + (when (and end (< (point) end)) + ;; Hide first custom property in current drawer. + (let ((o (make-overlay (match-beginning 0) (1+ (match-end 0))))) + (overlay-put o 'invisible t) + (overlay-put o 'org-custom-property t) + (push o org-custom-properties-overlays)) + ;; Hide additional custom properties in the same drawer. + (while (re-search-forward regexp end t) + (let ((o (make-overlay (match-beginning 0) (1+ (match-end 0))))) + (overlay-put o 'invisible t) + (overlay-put o 'org-custom-property t) + (push o org-custom-properties-overlays))))) + ;; Each entry is limited to a single property drawer. + (outline-next-heading))))))) (defun org-fontify-entities (limit) "Find an entity to fontify." (let (ee) (when org-pretty-entities (catch 'match + ;; "\_ "-family is left out on purpose. Only the first one, + ;; i.e., "\_ ", could be fontified anyway, and it would be + ;; confusing when adding a second white space character. (while (re-search-forward "\\\\\\(there4\\|sup[123]\\|frac[13][24]\\|[a-zA-Z]+\\)\\($\\|{}\\|[^[:alpha:]\n]\\)" limit t) - (if (and (not (org-in-indented-comment-line)) - (setq ee (org-entity-get (match-string 1))) - (= (length (nth 6 ee)) 1)) - (let* - ((end (if (equal (match-string 2) "{}") + (when (and (not (org-at-comment-p)) + (setq ee (org-entity-get (match-string 1))) + (= (length (nth 6 ee)) 1)) + (let* ((end (if (equal (match-string 2) "{}") (match-end 2) (match-end 1)))) - (add-text-properties - (match-beginning 0) end - (list 'font-lock-fontified t)) - (compose-region (match-beginning 0) end - (nth 6 ee) nil) - (backward-char 1) - (throw 'match t)))) + (add-text-properties + (match-beginning 0) end + (list 'font-lock-fontified t)) + (compose-region (match-beginning 0) end + (nth 6 ee) nil) + (backward-char 1) + (throw 'match t)))) nil)))) (defun org-fontify-like-in-org-mode (s &optional odd-levels) - "Fontify string S like in Org-mode." + "Fontify string S like in Org mode." (with-temp-buffer (insert s) (let ((org-odd-levels-only odd-levels)) @@ -6387,33 +6629,55 @@ needs to be inserted at a specific position in the font-lock sequence.") (defun org-get-level-face (n) "Get the right face for match N in font-lock matching of headlines." (setq org-l (- (match-end 2) (match-beginning 1) 1)) - (if org-odd-levels-only (setq org-l (1+ (/ org-l 2)))) + (when org-odd-levels-only (setq org-l (1+ (/ org-l 2)))) (if org-cycle-level-faces (setq org-f (nth (% (1- org-l) org-n-level-faces) org-level-faces)) (setq org-f (nth (1- (min org-l org-n-level-faces)) org-level-faces))) (cond ((eq n 1) (if org-hide-leading-stars 'org-hide org-f)) ((eq n 2) org-f) - (t (if org-level-color-stars-only nil org-f)))) + (t (unless org-level-color-stars-only org-f)))) +(defun org-face-from-face-or-color (context inherit face-or-color) + "Create a face list that inherits INHERIT, but sets the foreground color. +When FACE-OR-COLOR is not a string, just return it." + (if (stringp face-or-color) + (list :inherit inherit + (cdr (assoc context org-faces-easy-properties)) + face-or-color) + face-or-color)) (defun org-get-todo-face (kwd) "Get the right face for a TODO keyword KWD. If KWD is a number, get the corresponding match group." - (if (numberp kwd) (setq kwd (match-string kwd))) + (when (numberp kwd) (setq kwd (match-string kwd))) (or (org-face-from-face-or-color 'todo 'org-todo (cdr (assoc kwd org-todo-keyword-faces))) (and (member kwd org-done-keywords) 'org-done) 'org-todo)) -(defun org-face-from-face-or-color (context inherit face-or-color) - "Create a face list that inherits INHERIT, but sets the foreground color. -When FACE-OR-COLOR is not a string, just return it." - (if (stringp face-or-color) - (list :inherit inherit - (cdr (assoc context org-faces-easy-properties)) - face-or-color) - face-or-color)) +(defun org-get-priority-face (priority) + "Get the right face for PRIORITY. +PRIORITY is a character." + (or (org-face-from-face-or-color + 'priority 'org-priority (cdr (assq priority org-priority-faces))) + 'org-priority)) + +(defun org-get-tag-face (tag) + "Get the right face for TAG. +If TAG is a number, get the corresponding match group." + (let ((tag (if (wholenump tag) (match-string tag) tag))) + (or (org-face-from-face-or-color + 'tag 'org-tag (cdr (assoc tag org-tag-faces))) + 'org-tag))) + +(defun org-font-lock-add-priority-faces (limit) + "Add the special priority faces." + (while (re-search-forward "^\\*+ .*?\\(\\[#\\(.\\)\\]\\)" limit t) + (add-text-properties + (match-beginning 1) (match-end 1) + (list 'face (org-get-priority-face (string-to-char (match-string 2))) + 'font-lock-fontified t)))) (defun org-font-lock-add-tag-faces (limit) "Add the special tag faces." @@ -6424,39 +6688,18 @@ When FACE-OR-COLOR is not a string, just return it." 'font-lock-fontified t)) (backward-char 1)))) -(defun org-font-lock-add-priority-faces (limit) - "Add the special priority faces." - (while (re-search-forward "\\[#\\([A-Z0-9]\\)\\]" limit t) - (when (save-match-data (org-at-heading-p)) - (add-text-properties - (match-beginning 0) (match-end 0) - (list 'face (or (org-face-from-face-or-color - 'priority 'org-priority - (cdr (assoc (char-after (match-beginning 1)) - org-priority-faces))) - 'org-priority) - 'font-lock-fontified t))))) - -(defun org-get-tag-face (kwd) - "Get the right face for a TODO keyword KWD. -If KWD is a number, get the corresponding match group." - (if (numberp kwd) (setq kwd (match-string kwd))) - (or (org-face-from-face-or-color - 'tag 'org-tag (cdr (assoc kwd org-tag-faces))) - 'org-tag)) - -(defun org-unfontify-region (beg end &optional maybe_loudly) +(defun org-unfontify-region (beg end &optional _maybe_loudly) "Remove fontification and activation overlays from links." (font-lock-default-unfontify-region beg end) (let* ((buffer-undo-list t) (inhibit-read-only t) (inhibit-point-motion-hooks t) (inhibit-modification-hooks t) deactivate-mark buffer-file-name buffer-file-truename) - (org-decompose-region beg end) + (decompose-region beg end) (remove-text-properties beg end '(mouse-face t keymap t org-linked-text t invisible t intangible t - org-no-flyspell t org-emphasis t)) + org-emphasis t)) (org-remove-font-lock-display-properties beg end))) (defconst org-script-display '(((raise -0.3) (height 0.7)) @@ -6473,59 +6716,56 @@ and subscripts." (while (< beg end) (setq next (next-single-property-change beg 'display nil end) prop (get-text-property beg 'display)) - (if (member prop org-script-display) - (put-text-property beg next 'display nil)) + (when (member prop org-script-display) + (put-text-property beg next 'display nil)) (setq beg next)))) (defun org-raise-scripts (limit) "Add raise properties to sub/superscripts." - (when (and org-pretty-entities org-pretty-entities-include-sub-superscripts) - (if (re-search-forward - (if (eq org-use-sub-superscripts t) - org-match-substring-regexp - org-match-substring-with-braces-regexp) - limit t) - (let* ((pos (point)) table-p comment-p - (mpos (match-beginning 3)) - (emph-p (get-text-property mpos 'org-emphasis)) - (link-p (get-text-property mpos 'mouse-face)) - (keyw-p (eq 'org-special-keyword (get-text-property mpos 'face)))) - (goto-char (point-at-bol)) - (setq table-p (org-looking-at-p org-table-dataline-regexp) - comment-p (org-looking-at-p "^[ \t]*#[ +]")) - (goto-char pos) - ;; Handle a_b^c - (if (member (char-after) '(?_ ?^)) (goto-char (1- pos))) - (if (or comment-p emph-p link-p keyw-p) - t - (put-text-property (match-beginning 3) (match-end 0) - 'display - (if (equal (char-after (match-beginning 2)) ?^) - (nth (if table-p 3 1) org-script-display) - (nth (if table-p 2 0) org-script-display))) - (add-text-properties (match-beginning 2) (match-end 2) - (list 'invisible t - 'org-dwidth t 'org-dwidth-n 1)) - (if (and (eq (char-after (match-beginning 3)) ?{) - (eq (char-before (match-end 3)) ?})) - (progn - (add-text-properties - (match-beginning 3) (1+ (match-beginning 3)) - (list 'invisible t 'org-dwidth t 'org-dwidth-n 1)) - (add-text-properties - (1- (match-end 3)) (match-end 3) - (list 'invisible t 'org-dwidth t 'org-dwidth-n 1)))) - t))))) + (when (and org-pretty-entities org-pretty-entities-include-sub-superscripts + (re-search-forward + (if (eq org-use-sub-superscripts t) + org-match-substring-regexp + org-match-substring-with-braces-regexp) + limit t)) + (let* ((pos (point)) table-p comment-p + (mpos (match-beginning 3)) + (emph-p (get-text-property mpos 'org-emphasis)) + (link-p (get-text-property mpos 'mouse-face)) + (keyw-p (eq 'org-special-keyword (get-text-property mpos 'face)))) + (goto-char (point-at-bol)) + (setq table-p (looking-at-p org-table-dataline-regexp) + comment-p (looking-at-p "^[ \t]*#[ +]")) + (goto-char pos) + ;; Handle a_b^c + (when (member (char-after) '(?_ ?^)) (goto-char (1- pos))) + (unless (or comment-p emph-p link-p keyw-p) + (put-text-property (match-beginning 3) (match-end 0) + 'display + (if (equal (char-after (match-beginning 2)) ?^) + (nth (if table-p 3 1) org-script-display) + (nth (if table-p 2 0) org-script-display))) + (add-text-properties (match-beginning 2) (match-end 2) + (list 'invisible t + 'org-dwidth t 'org-dwidth-n 1)) + (if (and (eq (char-after (match-beginning 3)) ?{) + (eq (char-before (match-end 3)) ?})) + (progn + (add-text-properties + (match-beginning 3) (1+ (match-beginning 3)) + (list 'invisible t 'org-dwidth t 'org-dwidth-n 1)) + (add-text-properties + (1- (match-end 3)) (match-end 3) + (list 'invisible t 'org-dwidth t 'org-dwidth-n 1))))) + t))) ;;;; Visibility cycling, including org-goto and indirect buffer ;;; Cycling -(defvar org-cycle-global-status nil) -(make-variable-buffer-local 'org-cycle-global-status) +(defvar-local org-cycle-global-status nil) (put 'org-cycle-global-status 'org-state t) -(defvar org-cycle-subtree-status nil) -(make-variable-buffer-local 'org-cycle-subtree-status) +(defvar-local org-cycle-subtree-status nil) (put 'org-cycle-subtree-status 'org-state t) (defvar org-inlinetask-min-level) @@ -6537,52 +6777,58 @@ and subscripts." ;;;###autoload (defun org-cycle (&optional arg) - "TAB-action and visibility cycling for Org-mode. + "TAB-action and visibility cycling for Org mode. -This is the command invoked in Org-mode by the TAB key. Its main purpose -is outline visibility cycling, but it also invokes other actions +This is the command invoked in Org mode by the `TAB' key. Its main +purpose is outline visibility cycling, but it also invokes other actions in special contexts. -- When this function is called with a prefix argument, rotate the entire - buffer through 3 states (global cycling) +When this function is called with a `\\[universal-argument]' prefix, rotate \ +the entire +buffer through 3 states (global cycling) 1. OVERVIEW: Show only top-level headlines. 2. CONTENTS: Show all headlines of all levels, but no body text. 3. SHOW ALL: Show everything. - When called with two `C-u C-u' prefixes, switch to the startup visibility, - determined by the variable `org-startup-folded', and by any VISIBILITY - properties in the buffer. - When called with three `C-u C-u C-u' prefixed, show the entire buffer, - including any drawers. -- When inside a table, re-align the table and move to the next field. +With a `\\[universal-argument] \\[universal-argument]' prefix argument, \ +switch to the startup visibility, +determined by the variable `org-startup-folded', and by any VISIBILITY +properties in the buffer. + +With a `\\[universal-argument] \\[universal-argument] \ +\\[universal-argument]' prefix argument, show the entire buffer, including +any drawers. -- When point is at the beginning of a headline, rotate the subtree started - by this line through 3 different states (local cycling) +When inside a table, re-align the table and move to the next field. + +When point is at the beginning of a headline, rotate the subtree started +by this line through 3 different states (local cycling) 1. FOLDED: Only the main headline is shown. 2. CHILDREN: The main headline and the direct children are shown. From this state, you can move to one of the children and zoom in further. 3. SUBTREE: Show the entire subtree, including body text. - If there is no subtree, switch directly from CHILDREN to FOLDED. - -- When point is at the beginning of an empty headline and the variable - `org-cycle-level-after-item/entry-creation' is set, cycle the level - of the headline by demoting and promoting it to likely levels. This - speeds up creation document structure by pressing TAB once or several - times right after creating a new headline. - -- When there is a numeric prefix, go up to a heading with level ARG, do - a `show-subtree' and return to the previous cursor position. If ARG - is negative, go up that many levels. - -- When point is not at the beginning of a headline, execute the global - binding for TAB, which is re-indenting the line. See the option - `org-cycle-emulate-tab' for details. - -- Special case: if point is at the beginning of the buffer and there is - no headline in line 1, this function will act as if called with prefix arg - (C-u TAB, same as S-TAB) also when called without prefix arg. - But only if also the variable `org-cycle-global-at-bob' is t." +If there is no subtree, switch directly from CHILDREN to FOLDED. + +When point is at the beginning of an empty headline and the variable +`org-cycle-level-after-item/entry-creation' is set, cycle the level +of the headline by demoting and promoting it to likely levels. This +speeds up creation document structure by pressing `TAB' once or several +times right after creating a new headline. + +When there is a numeric prefix, go up to a heading with level ARG, do +a `show-subtree' and return to the previous cursor position. If ARG +is negative, go up that many levels. + +When point is not at the beginning of a headline, execute the global +binding for `TAB', which is re-indenting the line. See the option +`org-cycle-emulate-tab' for details. + +As a special case, if point is at the beginning of the buffer and there is +no headline in line 1, this function will act as if called with prefix arg +\(`\\[universal-argument] TAB', same as `S-TAB') also when called without \ +prefix arg, but only +if the variable `org-cycle-global-at-bob' is t." (interactive "P") (org-load-modules-maybe) (unless (or (run-hook-with-args-until-success 'org-tab-first-hook) @@ -6611,10 +6857,6 @@ in special contexts. org-cycle-hook)) (pos (point))) - (if (or bob-special (equal arg '(4))) - ;; special case: use global cycling - (setq arg t)) - (cond ((equal arg '(16)) @@ -6623,32 +6865,36 @@ in special contexts. (org-unlogged-message "Startup visibility, plus VISIBILITY properties")) ((equal arg '(64)) - (show-all) + (outline-show-all) (org-unlogged-message "Entire buffer visible, including drawers")) + ((equal arg '(4)) (org-cycle-internal-global)) + + ;; Try hiding block at point. + ((org-hide-block-toggle-maybe)) + ;; Try cdlatex TAB completion ((org-try-cdlatex-tab)) ;; Table: enter it or move to the next field. ((org-at-table-p 'any) (if (org-at-table.el-p) - (message "%s" "Use C-c ' to edit table.el tables") + (message "%s" (substitute-command-keys "\\\ +Use `\\[org-edit-special]' to edit table.el tables")) (if arg (org-table-edit-field t) (org-table-justify-field-maybe) (call-interactively 'org-table-next-field)))) - ((run-hook-with-args-until-success - 'org-tab-after-check-for-table-hook)) + ((run-hook-with-args-until-success 'org-tab-after-check-for-table-hook)) ;; Global cycling: delegate to `org-cycle-internal-global'. - ((eq arg t) (org-cycle-internal-global)) + (bob-special (org-cycle-internal-global)) ;; Drawers: delegate to `org-flag-drawer'. - ((and org-drawers org-drawer-regexp - (save-excursion - (beginning-of-line 1) - (looking-at org-drawer-regexp))) - (org-flag-drawer ; toggle block visibility + ((save-excursion + (beginning-of-line 1) + (looking-at org-drawer-regexp)) + (org-flag-drawer ; toggle block visibility (not (get-char-property (match-end 0) 'invisible)))) ;; Show-subtree, ARG levels up from here. @@ -6667,7 +6913,7 @@ in special contexts. ;; At an item/headline: delegate to `org-cycle-internal-local'. ((and (or (and org-cycle-include-plain-lists (org-at-item-p)) - (save-excursion (beginning-of-line 1) + (save-excursion (move-beginning-of-line 1) (looking-at org-outline-regexp))) (or (bolp) (not (eq org-cycle-emulate-tab 'exc-hl-bol)))) (org-cycle-internal-local)) @@ -6722,7 +6968,7 @@ in special contexts. (eq org-cycle-global-status 'contents)) ;; We just showed the table of contents - now show everything (run-hook-with-args 'org-pre-cycle-hook 'all) - (show-all) + (outline-show-all) (unless ga (org-unlogged-message "SHOW ALL")) (setq org-cycle-global-status 'all) (run-hook-with-args 'org-cycle-hook 'all)) @@ -6738,6 +6984,11 @@ in special contexts. (defvar org-called-with-limited-levels nil "Non-nil when `org-with-limited-levels' is currently active.") +(defun org-invisible-p (&optional pos) + "Non-nil if the character after POS is invisible. +If POS is nil, use `point' instead." + (get-char-property (or pos (point)) 'invisible)) + (defun org-cycle-internal-local () "Do the local cycling action." (let ((goal-column 0) eoh eol eos has-children children-skipped struct) @@ -6765,15 +7016,10 @@ in special contexts. (org-list-search-forward (org-item-beginning-re) eos t))))) ;; Determine end invisible part of buffer (EOL) (beginning-of-line 2) - ;; XEmacs doesn't have `next-single-char-property-change' - (if (featurep 'xemacs) - (while (and (not (eobp)) ;; this is like `next-line' - (get-char-property (1- (point)) 'invisible)) - (beginning-of-line 2)) - (while (and (not (eobp)) ;; this is like `next-line' - (get-char-property (1- (point)) 'invisible)) - (goto-char (next-single-char-property-change (point) 'invisible)) - (and (eolp) (beginning-of-line 2)))) + (while (and (not (eobp)) ;This is like `next-line'. + (get-char-property (1- (point)) 'invisible)) + (goto-char (next-single-char-property-change (point) 'invisible)) + (and (eolp) (beginning-of-line 2))) (setq eol (point))) ;; Find out what to do next and set `this-command' (cond @@ -6786,7 +7032,7 @@ in special contexts. (save-excursion (goto-char eos) (outline-next-heading) - (if (outline-invisible-p) (org-flag-heading nil)))) + (when (org-invisible-p) (org-flag-heading nil)))) ((and (or (>= eol eos) (not (string-match "\\S-" (buffer-substring eol eos)))) (or has-children @@ -6798,7 +7044,7 @@ in special contexts. (if (org-at-item-p) (org-list-set-item-visibility (point-at-bol) struct 'children) (org-show-entry) - (org-with-limited-levels (show-children)) + (org-with-limited-levels (org-show-children)) ;; FIXME: This slows down the func way too much. ;; How keep drawers hidden in subtree anyway? ;; (when (memq 'org-cycle-hide-drawers org-cycle-hook) @@ -6813,14 +7059,14 @@ in special contexts. (let* ((struct (org-list-struct)) (prevs (org-list-prevs-alist struct)) (end (org-list-get-bottom-point struct))) - (mapc (lambda (e) (org-list-set-item-visibility e struct 'folded)) - (org-list-get-all-items (point) struct prevs)) + (dolist (e (org-list-get-all-items (point) struct prevs)) + (org-list-set-item-visibility e struct 'folded)) (goto-char (if (< end eos) end eos))))))) (org-unlogged-message "CHILDREN") (save-excursion (goto-char eos) (outline-next-heading) - (if (outline-invisible-p) (org-flag-heading nil))) + (when (org-invisible-p) (org-flag-heading nil))) (setq org-cycle-subtree-status 'children) (unless (org-before-first-heading-p) (run-hook-with-args 'org-cycle-hook 'children))) @@ -6849,15 +7095,15 @@ in special contexts. ;;;###autoload (defun org-global-cycle (&optional arg) "Cycle the global visibility. For details see `org-cycle'. -With \\[universal-argument] prefix arg, switch to startup visibility. +With `\\[universal-argument]' prefix ARG, switch to startup visibility. With a numeric prefix, show all headlines up to that level." (interactive "P") (let ((org-cycle-include-plain-lists (if (derived-mode-p 'org-mode) org-cycle-include-plain-lists nil))) (cond ((integerp arg) - (show-all) - (hide-sublevels arg) + (outline-show-all) + (outline-hide-sublevels arg) (setq org-cycle-global-status 'contents)) ((equal arg '(4)) (org-set-startup-visibility) @@ -6874,9 +7120,9 @@ With a numeric prefix, show all headlines up to that level." (org-content)) ((or (eq org-startup-folded 'showeverything) (eq org-startup-folded nil)) - (show-all))) + (outline-show-all))) (unless (eq org-startup-folded 'showeverything) - (if org-hide-block-startup (org-hide-block-all)) + (when org-hide-block-startup (org-hide-block-all)) (org-set-visibility-according-to-property 'no-cleanup) (org-cycle-hide-archived-subtrees 'all) (org-cycle-hide-drawers 'all) @@ -6885,34 +7131,32 @@ With a numeric prefix, show all headlines up to that level." (defun org-set-visibility-according-to-property (&optional no-cleanup) "Switch subtree visibilities according to :VISIBILITY: property." (interactive) - (let (org-show-entry-below state) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward - "^[ \t]*:VISIBILITY:[ \t]+\\([a-z]+\\)" - nil t) - (setq state (match-string 1)) - (save-excursion - (org-back-to-heading t) - (hide-subtree) - (org-reveal) - (cond - ((equal state '("fold" "folded")) - (hide-subtree)) - ((equal state "children") - (org-show-hidden-entry) - (show-children)) - ((equal state "content") - (save-excursion - (save-restriction - (org-narrow-to-subtree) - (org-content)))) - ((member state '("all" "showall")) - (show-subtree))))) - (unless no-cleanup - (org-cycle-hide-archived-subtrees 'all) - (org-cycle-hide-drawers 'all) - (org-cycle-show-empty-lines 'all))))) + (org-with-wide-buffer + (goto-char (point-min)) + (while (re-search-forward "^[ \t]*:VISIBILITY:" nil t) + (if (not (org-at-property-p)) (outline-next-heading) + (let ((state (match-string 3))) + (save-excursion + (org-back-to-heading t) + (outline-hide-subtree) + (org-reveal) + (cond + ((equal state "folded") + (outline-hide-subtree)) + ((equal state "children") + (org-show-hidden-entry) + (org-show-children)) + ((equal state "content") + (save-excursion + (save-restriction + (org-narrow-to-subtree) + (org-content)))) + ((member state '("all" "showall")) + (outline-show-subtree))))))) + (unless no-cleanup + (org-cycle-hide-archived-subtrees 'all) + (org-cycle-hide-drawers 'all) + (org-cycle-show-empty-lines 'all)))) ;; This function uses outline-regexp instead of the more fundamental ;; org-outline-regexp so that org-cycle-global works outside of Org @@ -6928,11 +7172,10 @@ results." (let ((level (save-excursion (goto-char (point-min)) - (if (re-search-forward (concat "^" outline-regexp) nil t) - (progn - (goto-char (match-beginning 0)) - (funcall outline-level)))))) - (and level (hide-sublevels level))))) + (when (re-search-forward (concat "^" outline-regexp) nil t) + (goto-char (match-beginning 0)) + (funcall outline-level))))) + (and level (outline-hide-sublevels level))))) (defun org-content (&optional arg) "Show all headlines in the buffer, like a table of contents. @@ -6950,9 +7193,9 @@ With numerical argument N, show content up to level N." t) (looking-at org-outline-regexp)) (if (integerp arg) - (show-children (1- arg)) - (show-branches)) - (if (bobp) (throw 'exit nil)))))) + (org-show-children (1- arg)) + (outline-show-branches)) + (when (bobp) (throw 'exit nil)))))) (defun org-optimize-window-after-visibility-change (state) "Adjust the window after a change in outline visibility. @@ -6967,13 +7210,11 @@ This function is the default value of the hook `org-cycle-hook'." (defun org-remove-empty-overlays-at (pos) "Remove outline overlays that do not contain non-white stuff." - (mapc - (lambda (o) - (and (eq 'outline (overlay-get o 'invisible)) - (not (string-match "\\S-" (buffer-substring (overlay-start o) - (overlay-end o)))) - (delete-overlay o))) - (overlays-at pos))) + (dolist (o (overlays-at pos)) + (and (eq 'outline (overlay-get o 'invisible)) + (not (string-match "\\S-" (buffer-substring (overlay-start o) + (overlay-end o)))) + (delete-overlay o)))) (defun org-clean-visibility-after-subtree-move () "Fix visibility issues after moving a subtree." @@ -6991,7 +7232,7 @@ This function is the default value of the hook `org-cycle-hook'." (point-at-eol) (point)))) (level (looking-at "\\*+")) - (re (if level (concat "^" (regexp-quote (match-string 0)) " ")))) + (re (when level (concat "^" (regexp-quote (match-string 0)) " ")))) (save-excursion (save-restriction (narrow-to-region beg end) @@ -6999,10 +7240,10 @@ This function is the default value of the hook `org-cycle-hook'." ;; Properly fold already folded siblings (goto-char (point-min)) (while (re-search-forward re nil t) - (if (and (not (outline-invisible-p)) - (save-excursion - (goto-char (point-at-eol)) (outline-invisible-p))) - (hide-entry)))) + (when (and (not (org-invisible-p)) + (save-excursion + (goto-char (point-at-eol)) (org-invisible-p))) + (outline-hide-entry)))) (org-cycle-show-empty-lines 'overview) (org-cycle-hide-drawers 'overview))))) @@ -7012,7 +7253,7 @@ The region to be covered depends on STATE when called through `org-cycle-hook'. Lisp program can use t for STATE to get the entire buffer covered. Note that an empty line is only shown if there are at least `org-cycle-separator-lines' empty lines before the headline." - (when (not (= org-cycle-separator-lines 0)) + (when (/= org-cycle-separator-lines 0) (save-excursion (let* ((n (abs org-cycle-separator-lines)) (re (cond @@ -7021,38 +7262,34 @@ are at least `org-cycle-separator-lines' empty lines before the headline." (t (let ((ns (number-to-string (- n 2)))) (concat "^\\(?:[ \t]*\n\\)\\{" ns "," ns "\\}" "[ \t]*\\(\n[ \t]*\n\\*+\\) "))))) - beg end b e) + beg end) (cond ((memq state '(overview contents t)) (setq beg (point-min) end (point-max))) ((memq state '(children folded)) - (setq beg (point) end (progn (org-end-of-subtree t t) - (beginning-of-line 2) - (point))))) + (setq beg (point) + end (progn (org-end-of-subtree t t) + (line-beginning-position 2))))) (when beg (goto-char beg) (while (re-search-forward re end t) (unless (get-char-property (match-end 1) 'invisible) - (setq e (match-end 1)) - (if (< org-cycle-separator-lines 0) - (setq b (save-excursion - (goto-char (match-beginning 0)) - (org-back-over-empty-lines) - (if (save-excursion - (goto-char (max (point-min) (1- (point)))) - (org-at-heading-p)) - (1- (point)) - (point)))) - (setq b (match-beginning 1))) - (outline-flag-region b e nil))))))) + (let ((e (match-end 1)) + (b (if (>= org-cycle-separator-lines 0) + (match-beginning 1) + (save-excursion + (goto-char (match-beginning 0)) + (skip-chars-backward " \t\n") + (line-end-position))))) + (outline-flag-region b e nil)))))))) ;; Never hide empty lines at the end of the file. (save-excursion (goto-char (point-max)) (outline-previous-heading) (outline-end-of-heading) - (if (and (looking-at "[ \t\n]+") - (= (match-end 0) (point-max))) - (outline-flag-region (point) (match-end 0) nil)))) + (when (and (looking-at "[ \t\n]+") + (= (match-end 0) (point-max))) + (outline-flag-region (point) (match-end 0) nil)))) (defun org-show-empty-lines-in-parent () "Move to the parent and re-show empty lines before visible headlines." @@ -7061,28 +7298,28 @@ are at least `org-cycle-separator-lines' empty lines before the headline." (org-cycle-show-empty-lines context)))) (defun org-files-list () - "Return `org-agenda-files' list, plus all open org-mode files. + "Return `org-agenda-files' list, plus all open Org files. This is useful for operations that need to scan all of a user's open and agenda-wise Org files." (let ((files (mapcar 'expand-file-name (org-agenda-files)))) (dolist (buf (buffer-list)) (with-current-buffer buf - (if (and (derived-mode-p 'org-mode) (buffer-file-name)) - (let ((file (expand-file-name (buffer-file-name)))) - (unless (member file files) - (push file files)))))) + (when (and (derived-mode-p 'org-mode) (buffer-file-name)) + (cl-pushnew (expand-file-name (buffer-file-name)) files)))) files)) (defsubst org-entry-beginning-position () "Return the beginning position of the current entry." - (save-excursion (outline-back-to-heading t) (point))) + (save-excursion (org-back-to-heading t) (point))) (defsubst org-entry-end-position () "Return the end position of the current entry." (save-excursion (outline-next-heading) (point))) -(defun org-cycle-hide-drawers (state) - "Re-hide all drawers after a visibility state change." +(defun org-cycle-hide-drawers (state &optional exceptions) + "Re-hide all drawers after a visibility state change. +When non-nil, optional argument EXCEPTIONS is a list of strings +specifying which drawers should not be hidden." (when (and (derived-mode-p 'org-mode) (not (memq state '(overview folded contents)))) (save-excursion @@ -7093,36 +7330,39 @@ open and agenda-wise Org files." (save-excursion (outline-next-heading) (point)) (org-end-of-subtree t))))) (goto-char beg) - (while (re-search-forward org-drawer-regexp end t) - (org-flag-drawer t)))))) - -(defun org-cycle-hide-inline-tasks (state) - "Re-hide inline tasks when switching to `contents' or `children' -visibility state." - (case state - (contents - (when (org-bound-and-true-p org-inlinetask-min-level) - (hide-sublevels (1- org-inlinetask-min-level)))) - (children - (when (featurep 'org-inlinetask) - (save-excursion - (while (and (outline-next-heading) - (org-inlinetask-at-task-p)) - (org-inlinetask-toggle-visibility) - (org-inlinetask-goto-end))))))) - -(defun org-flag-drawer (flag) - "When FLAG is non-nil, hide the drawer we are within. -Otherwise make it visible." - (save-excursion - (beginning-of-line 1) - (when (looking-at "^[ \t]*:[a-zA-Z][a-zA-Z0-9]*:") - (let ((b (match-end 0))) - (if (re-search-forward - "^[ \t]*:END:" - (save-excursion (outline-next-heading) (point)) t) - (outline-flag-region b (point-at-eol) flag) - (user-error ":END: line missing at position %s" b)))))) + (while (re-search-forward org-drawer-regexp (max end (point)) t) + (unless (member-ignore-case (match-string 1) exceptions) + (let ((drawer (org-element-at-point))) + (when (memq (org-element-type drawer) '(drawer property-drawer)) + (org-flag-drawer t drawer) + ;; Make sure to skip drawer entirely or we might flag + ;; it another time when matching its ending line with + ;; `org-drawer-regexp'. + (goto-char (org-element-property :end drawer)))))))))) + +(defun org-flag-drawer (flag &optional element) + "When FLAG is non-nil, hide the drawer we are at. +Otherwise make it visible. When optional argument ELEMENT is +a parsed drawer, as returned by `org-element-at-point', hide or +show that drawer instead." + (let ((drawer (or element + (and (save-excursion + (beginning-of-line) + (looking-at-p org-drawer-regexp)) + (org-element-at-point))))) + (when (memq (org-element-type drawer) '(drawer property-drawer)) + (let ((post (org-element-property :post-affiliated drawer))) + (save-excursion + (outline-flag-region + (progn (goto-char post) (line-end-position)) + (progn (goto-char (org-element-property :end drawer)) + (skip-chars-backward " \r\t\n") + (line-end-position)) + flag)) + ;; When the drawer is hidden away, make sure point lies in + ;; a visible part of the buffer. + (when (and flag (> (line-beginning-position) post)) + (goto-char post)))))) (defun org-subtree-end-visible-p () "Is the end of the current subtree visible?" @@ -7131,9 +7371,11 @@ Otherwise make it visible." (defun org-first-headline-recenter () "Move cursor to the first headline and recenter the headline." - (goto-char (point-min)) - (when (re-search-forward (concat "^\\(" org-outline-regexp "\\)") nil t) - (set-window-start (selected-window) (point-at-bol)))) + (let ((window (get-buffer-window))) + (when window + (goto-char (point-min)) + (when (re-search-forward (concat "^\\(" org-outline-regexp "\\)") nil t) + (set-window-start window (line-beginning-position)))))) ;;; Saving and restoring visibility @@ -7144,38 +7386,30 @@ The return value is a list of cons cells, with start and stop positions for each overlay. If USE-MARKERS is set, return the positions as markers." (let (beg end) - (save-excursion - (save-restriction - (widen) - (delq nil - (mapcar (lambda (o) - (when (eq (overlay-get o 'invisible) 'outline) - (setq beg (overlay-start o) - end (overlay-end o)) - (and beg end (> end beg) - (if use-markers - (cons (move-marker (make-marker) beg) - (move-marker (make-marker) end)) - (cons beg end))))) - (overlays-in (point-min) (point-max)))))))) + (org-with-wide-buffer + (delq nil + (mapcar (lambda (o) + (when (eq (overlay-get o 'invisible) 'outline) + (setq beg (overlay-start o) + end (overlay-end o)) + (and beg end (> end beg) + (if use-markers + (cons (copy-marker beg) + (copy-marker end t)) + (cons beg end))))) + (overlays-in (point-min) (point-max))))))) (defun org-set-outline-overlay-data (data) "Create visibility overlays for all positions in DATA. DATA should have been made by `org-outline-overlay-data'." - (let (o) - (save-excursion - (save-restriction - (widen) - (show-all) - (mapc (lambda (c) - (outline-flag-region (car c) (cdr c) t)) - data))))) + (org-with-wide-buffer + (outline-show-all) + (dolist (c data) (outline-flag-region (car c) (cdr c) t)))) ;;; Folding of blocks -(defvar org-hide-block-overlays nil +(defvar-local org-hide-block-overlays nil "Overlays hiding blocks.") -(make-variable-buffer-local 'org-hide-block-overlays) (defun org-block-map (function &optional start end) "Call FUNCTION at the head of all source blocks in the current buffer. @@ -7192,74 +7426,85 @@ Optional arguments START and END can be used to limit the range." (defun org-hide-block-toggle-all () "Toggle the visibility of all blocks in the current buffer." - (org-block-map #'org-hide-block-toggle)) + (org-block-map 'org-hide-block-toggle)) (defun org-hide-block-all () "Fold all blocks in the current buffer." (interactive) (org-show-block-all) - (org-block-map #'org-hide-block-toggle-maybe)) + (org-block-map 'org-hide-block-toggle-maybe)) (defun org-show-block-all () "Unfold all blocks in the current buffer." (interactive) - (mapc 'delete-overlay org-hide-block-overlays) + (mapc #'delete-overlay org-hide-block-overlays) (setq org-hide-block-overlays nil)) (defun org-hide-block-toggle-maybe () - "Toggle visibility of block at point." + "Toggle visibility of block at point. +Unlike to `org-hide-block-toggle', this function does not throw +an error. Return a non-nil value when toggling is successful." (interactive) - (let ((case-fold-search t)) - (if (save-excursion - (beginning-of-line 1) - (looking-at org-block-regexp)) - (progn (org-hide-block-toggle) - t) ;; to signal that we took action - nil))) ;; to signal that we did not + (ignore-errors (org-hide-block-toggle))) (defun org-hide-block-toggle (&optional force) - "Toggle the visibility of the current block." + "Toggle the visibility of the current block. +When optional argument FORCE is `off', make block visible. If it +is non-nil, hide it unconditionally. Throw an error when not at +a block. Return a non-nil value when toggling is successful." (interactive) - (save-excursion - (beginning-of-line) - (if (re-search-forward org-block-regexp nil t) - (let ((start (- (match-beginning 4) 1)) ;; beginning of body - (end (match-end 0)) ;; end of entire body - ov) - (if (memq t (mapcar (lambda (overlay) - (eq (overlay-get overlay 'invisible) - 'org-hide-block)) - (overlays-at start))) - (if (or (not force) (eq force 'off)) - (mapc (lambda (ov) - (when (member ov org-hide-block-overlays) - (setq org-hide-block-overlays - (delq ov org-hide-block-overlays))) - (when (eq (overlay-get ov 'invisible) - 'org-hide-block) - (delete-overlay ov))) - (overlays-at start))) - (setq ov (make-overlay start end)) - (overlay-put ov 'invisible 'org-hide-block) - ;; make the block accessible to isearch - (overlay-put - ov 'isearch-open-invisible - (lambda (ov) - (when (member ov org-hide-block-overlays) - (setq org-hide-block-overlays - (delq ov org-hide-block-overlays))) - (when (eq (overlay-get ov 'invisible) - 'org-hide-block) - (delete-overlay ov)))) - (push ov org-hide-block-overlays))) - (user-error "Not looking at a source block")))) - -;; org-tab-after-check-for-cycling-hook -(add-hook 'org-tab-first-hook 'org-hide-block-toggle-maybe) + (let ((element (org-element-at-point))) + (unless (memq (org-element-type element) + '(center-block comment-block dynamic-block example-block + export-block quote-block special-block + src-block verse-block)) + (user-error "Not at a block")) + (let* ((start (save-excursion + (goto-char (org-element-property :post-affiliated element)) + (line-end-position))) + (end (save-excursion + (goto-char (org-element-property :end element)) + (skip-chars-backward " \r\t\n") + (line-end-position))) + (overlays (overlays-at start))) + (cond + ;; Do nothing when not before or at the block opening line or + ;; at the block closing line. + ((let ((eol (line-end-position))) (and (> eol start) (/= eol end))) nil) + ((and (not (eq force 'off)) + (not (memq t (mapcar + (lambda (o) + (eq (overlay-get o 'invisible) 'org-hide-block)) + overlays)))) + (let ((ov (make-overlay start end))) + (overlay-put ov 'invisible 'org-hide-block) + ;; Make the block accessible to `isearch'. + (overlay-put + ov 'isearch-open-invisible + (lambda (ov) + (when (memq ov org-hide-block-overlays) + (setq org-hide-block-overlays (delq ov org-hide-block-overlays))) + (when (eq (overlay-get ov 'invisible) 'org-hide-block) + (delete-overlay ov)))) + (push ov org-hide-block-overlays) + ;; When the block is hidden away, make sure point is left in + ;; a visible part of the buffer. + (when (> (line-beginning-position) start) + (goto-char start) + (beginning-of-line)) + ;; Signal successful toggling. + t)) + ((or (not force) (eq force 'off)) + (dolist (ov overlays t) + (when (memq ov org-hide-block-overlays) + (setq org-hide-block-overlays (delq ov org-hide-block-overlays))) + (when (eq (overlay-get ov 'invisible) 'org-hide-block) + (delete-overlay ov)))))))) + ;; Remove overlays when changing major mode (add-hook 'org-mode-hook - (lambda () (org-add-hook 'change-major-mode-hook - 'org-show-block-all 'append 'local))) + (lambda () (add-hook 'change-major-mode-hook + 'org-show-block-all 'append 'local))) ;;; Org-goto @@ -7305,7 +7550,7 @@ Optional arguments START and END can be used to limit the range." (defconst org-goto-help "Browse buffer copy, to find location or copy text.%s RET=jump to location C-g=quit and return to previous location -[Up]/[Down]=next/prev headline TAB=cycle visibility [/] org-occur") +\[Up]/[Down]=next/prev headline TAB=cycle visibility [/] org-occur") (defvar org-goto-start-pos) ; dynamically scoped parameter @@ -7343,23 +7588,23 @@ With a prefix argument, use the alternative interface: e.g., if (selected-point (if (eq interface 'outline) (car (org-get-location (current-buffer) org-goto-help)) - (let ((pa (org-refile-get-location "Goto" nil nil t))) + (let ((pa (org-refile-get-location "Goto"))) (org-refile-check-position pa) (nth 3 pa))))) (if selected-point (progn (org-mark-ring-push org-goto-start-pos) (goto-char selected-point) - (if (or (outline-invisible-p) (org-invisible-p2)) - (org-show-context 'org-goto))) + (when (or (org-invisible-p) (org-invisible-p2)) + (org-show-context 'org-goto))) (message "Quit")))) (defvar org-goto-selected-point nil) ; dynamically scoped parameter (defvar org-goto-exit-command nil) ; dynamically scoped parameter (defvar org-goto-local-auto-isearch-map) ; defined below -(defun org-get-location (buf help) - "Let the user select a location in the Org-mode buffer BUF. +(defun org-get-location (_buf help) + "Let the user select a location in current buffer. This function uses a recursive edit. It returns the selected position or nil." (org-no-popups @@ -7372,7 +7617,7 @@ or nil." (save-window-excursion (delete-other-windows) (and (get-buffer "*org-goto*") (kill-buffer "*org-goto*")) - (org-pop-to-buffer-same-window + (pop-to-buffer-same-window (condition-case nil (make-indirect-buffer (current-buffer) "*org-goto*") (error (make-indirect-buffer (current-buffer) "*org-goto*")))) @@ -7390,11 +7635,9 @@ or nil." (setq buffer-read-only t) (if (and (boundp 'org-goto-start-pos) (integer-or-marker-p org-goto-start-pos)) - (let ((org-show-hierarchy-above t) - (org-show-siblings t) - (org-show-following-heading t)) - (goto-char org-goto-start-pos) - (and (outline-invisible-p) (org-show-context))) + (progn (goto-char org-goto-start-pos) + (when (org-invisible-p) + (org-show-set-visibility 'lineage))) (goto-char (point-min))) (let (org-special-ctrl-a/e) (org-beginning-of-line)) (message "Select location and press RET") @@ -7405,8 +7648,14 @@ or nil." (defvar org-goto-local-auto-isearch-map (make-sparse-keymap)) (set-keymap-parent org-goto-local-auto-isearch-map isearch-mode-map) -(define-key org-goto-local-auto-isearch-map "\C-i" 'isearch-other-control-char) -(define-key org-goto-local-auto-isearch-map "\C-m" 'isearch-other-control-char) +;; `isearch-other-control-char' was removed in Emacs 24.4. +(if (fboundp 'isearch-other-control-char) + (progn + (define-key org-goto-local-auto-isearch-map "\C-i" 'isearch-other-control-char) + (define-key org-goto-local-auto-isearch-map "\C-m" 'isearch-other-control-char)) + (define-key org-goto-local-auto-isearch-map "\C-i" nil) + (define-key org-goto-local-auto-isearch-map "\C-m" nil) + (define-key org-goto-local-auto-isearch-map [return] nil)) (defun org-goto-local-search-headings (string bound noerror) "Search and make sure that any matches are in headlines." @@ -7414,9 +7663,12 @@ or nil." (while (if isearch-forward (search-forward string bound noerror) (search-backward string bound noerror)) - (when (let ((context (mapcar 'car (save-match-data (org-context))))) - (and (member :headline context) - (not (member :tags context)))) + (when (save-match-data + (and (save-excursion + (beginning-of-line) + (looking-at org-complex-heading-regexp)) + (or (not (match-beginning 5)) + (< (point) (match-beginning 5))))) (throw 'return (point)))))) (defun org-goto-local-auto-isearch () @@ -7428,11 +7680,11 @@ or nil." (isearch-mode t) (isearch-process-search-char (string-to-char keys))))) -(defun org-goto-ret (&optional arg) +(defun org-goto-ret (&optional _arg) "Finish `org-goto' by going to the new location." (interactive "P") - (setq org-goto-selected-point (point) - org-goto-exit-command 'return) + (setq org-goto-selected-point (point)) + (setq org-goto-exit-command 'return) (throw 'exit nil)) (defun org-goto-left () @@ -7471,17 +7723,18 @@ or nil." (defun org-tree-to-indirect-buffer (&optional arg) "Create indirect buffer and narrow it to current subtree. + With a numerical prefix ARG, go up to this level and then take that tree. If ARG is negative, go up that many levels. If `org-indirect-buffer-display' is not `new-frame', the command removes the indirect buffer previously made with this command, to avoid proliferation of indirect buffers. However, when you call the command with a \ -\\[universal-argument] prefix, or -when `org-indirect-buffer-display' is `new-frame', the last buffer -is kept so that you can work with several indirect buffers at the same time. -If `org-indirect-buffer-display' is `dedicated-frame', the \ -\\[universal-argument] prefix also +`\\[universal-argument]' prefix, or +when `org-indirect-buffer-display' is `new-frame', the last buffer is kept +so that you can work with several indirect buffers at the same time. If +`org-indirect-buffer-display' is `dedicated-frame', the \ +`\\[universal-argument]' prefix also requests that a new frame be made for the new buffer, so that the dedicated frame is not changed." (interactive "P") @@ -7493,26 +7746,26 @@ frame is not changed." (org-back-to-heading t) (when (numberp arg) (setq level (org-outline-level)) - (if (< arg 0) (setq arg (+ level arg))) + (when (< arg 0) (setq arg (+ level arg))) (while (> (setq level (org-outline-level)) arg) (org-up-heading-safe))) (setq beg (point) - heading (org-get-heading)) + heading (org-get-heading 'no-tags)) (org-end-of-subtree t t) - (if (org-at-heading-p) (backward-char 1)) + (when (org-at-heading-p) (backward-char 1)) (setq end (point))) - (if (and (buffer-live-p org-last-indirect-buffer) - (not (eq org-indirect-buffer-display 'new-frame)) - (not arg)) - (kill-buffer org-last-indirect-buffer)) - (setq ibuf (org-get-indirect-buffer cbuf) + (when (and (buffer-live-p org-last-indirect-buffer) + (not (eq org-indirect-buffer-display 'new-frame)) + (not arg)) + (kill-buffer org-last-indirect-buffer)) + (setq ibuf (org-get-indirect-buffer cbuf heading) org-last-indirect-buffer ibuf) (cond ((or (eq org-indirect-buffer-display 'new-frame) (and arg (eq org-indirect-buffer-display 'dedicated-frame))) (select-frame (make-frame)) (delete-other-windows) - (org-pop-to-buffer-same-window ibuf) + (pop-to-buffer-same-window ibuf) (org-set-frame-title heading)) ((eq org-indirect-buffer-display 'dedicated-frame) (raise-frame @@ -7521,26 +7774,28 @@ frame is not changed." org-indirect-dedicated-frame) (setq org-indirect-dedicated-frame (make-frame))))) (delete-other-windows) - (org-pop-to-buffer-same-window ibuf) + (pop-to-buffer-same-window ibuf) (org-set-frame-title (concat "Indirect: " heading))) ((eq org-indirect-buffer-display 'current-window) - (org-pop-to-buffer-same-window ibuf)) + (pop-to-buffer-same-window ibuf)) ((eq org-indirect-buffer-display 'other-window) (pop-to-buffer ibuf)) (t (error "Invalid value"))) - (if (featurep 'xemacs) - (save-excursion (org-mode) (turn-on-font-lock))) (narrow-to-region beg end) - (show-all) + (outline-show-all) (goto-char pos) (run-hook-with-args 'org-cycle-hook 'all) (and (window-live-p cwin) (select-window cwin)))) -(defun org-get-indirect-buffer (&optional buffer) +(defun org-get-indirect-buffer (&optional buffer heading) (setq buffer (or buffer (current-buffer))) (let ((n 1) (base (buffer-name buffer)) bname) (while (buffer-live-p - (get-buffer (setq bname (concat base "-" (number-to-string n))))) + (get-buffer + (setq bname + (concat base "-" + (if heading (concat heading "-" (number-to-string n)) + (number-to-string n)))))) (setq n (1+ n))) (condition-case nil (make-indirect-buffer buffer bname 'clone) @@ -7548,57 +7803,70 @@ frame is not changed." (defun org-set-frame-title (title) "Set the title of the current frame to the string TITLE." - ;; FIXME: how to name a single frame in XEmacs??? - (unless (featurep 'xemacs) - (modify-frame-parameters (selected-frame) (list (cons 'name title))))) + (modify-frame-parameters (selected-frame) (list (cons 'name title)))) ;;;; Structure editing ;;; Inserting headlines -(defun org-previous-line-empty-p (&optional next) - "Is the previous line a blank line? -When NEXT is non-nil, check the next line instead." +(defun org--line-empty-p (n) + "Is the Nth next line empty? + +Counts the current line as N = 1 and the previous line as N = 0; +see `beginning-of-line'." (save-excursion (and (not (bobp)) - (or (beginning-of-line (if next 2 0)) t) + (or (beginning-of-line n) t) (save-match-data (looking-at "[ \t]*$"))))) -(defun org-insert-heading (&optional arg invisible-ok) - "Insert a new heading or an item with the same depth at point. - -If point is at the beginning of a heading or a list item, insert -a new heading or a new item above the current one. If point is -at the beginning of a normal line, turn the line into a heading. +(defun org-previous-line-empty-p () + "Is the previous line a blank line? +When NEXT is non-nil, check the next line instead." + (org--line-empty-p 0)) -If point is in the middle of a headline or a list item, split the -headline or the item and create a new headline/item with the text -in the current line after point \(see `org-M-RET-may-split-line' -on how to modify this behavior). +(defun org-next-line-empty-p () + "Is the previous line a blank line? +When NEXT is non-nil, check the next line instead." + (org--line-empty-p 2)) -With one universal prefix argument, set the user option -`org-insert-heading-respect-content' to t for the duration of -the command. This modifies the behavior described above in this -ways: on list items and at the beginning of normal lines, force -the insertion of a heading after the current subtree. +(defun org-insert-heading (&optional arg invisible-ok top) + "Insert a new heading or an item with the same depth at point. -With two universal prefix arguments, insert the heading at the -end of the grandparent subtree. For example, if point is within -a 2nd-level heading, then it will insert a 2nd-level heading at -the end of the 1st-level parent heading. +If point is at the beginning of a heading or a list item, insert +a new heading or a new item above the current one. When at the +beginning of a regular line of text, turn it into a heading. + +If point is in the middle of a line, split it and create a new +headline/item with the text in the current line after point (see +`org-M-RET-may-split-line' on how to modify this behavior). As +a special case, on a headline, splitting can only happen on the +title itself. E.g., this excludes breaking stars or tags. + +With a `\\[universal-argument]' prefix, set \ +`org-insert-heading-respect-content' to +a non-nil value for the duration of the command. This forces the +insertion of a heading after the current subtree, independently +on the location of point. + +With a `\\[universal-argument] \\[universal-argument]' prefix, \ +insert the heading at the end of the tree +above the current heading. For example, if point is within a +2nd-level heading, then it will insert a 2nd-level heading at +the end of the 1st-level parent subtree. When INVISIBLE-OK is set, stop at invisible headlines when going back. This is important for non-interactive uses of the -command." +command. + +When optional argument TOP is non-nil, insert a level 1 heading, +unconditionally." (interactive "P") - (if (org-called-interactively-p 'any) (org-reveal)) - (let ((itemp (org-in-item-p)) + (let ((itemp (and (not top) (org-in-item-p))) (may-split (org-get-alist-option org-M-RET-may-split-line 'headline)) (respect-content (or org-insert-heading-respect-content (equal arg '(4)))) - (initial-content "") - (adjust-empty-lines t)) + (initial-content "")) (cond @@ -7621,9 +7889,7 @@ command." (insert "\n* "))) (run-hooks 'org-insert-heading-hook)) - ((and itemp (not (member arg '((4) (16))))) - ;; Insert an item - (org-insert-item)) + ((and itemp (not (member arg '((4) (16)))) (org-insert-item))) (t ;; Maybe move at the end of the subtree @@ -7639,25 +7905,26 @@ command." (org-previous-line-empty-p) ;; We will decide later nil)) - ;; Get a level string to fall back on + ;; Get a level string to fall back on. (fix-level (if (org-before-first-heading-p) "*" (save-excursion (org-back-to-heading t) - (if (org-previous-line-empty-p) (setq empty-line-p t)) + (when (org-previous-line-empty-p) (setq empty-line-p t)) (looking-at org-outline-regexp) (make-string (1- (length (match-string 0))) ?*)))) (stars (save-excursion (condition-case nil - (progn + (if top "* " (org-back-to-heading invisible-ok) (when (and (not on-heading) (featurep 'org-inlinetask) (integerp org-inlinetask-min-level) (>= (length (match-string 0)) org-inlinetask-min-level)) - ;; Find a heading level before the inline task + ;; Find a heading level before the inline + ;; task. (while (and (setq level (org-up-heading-safe)) (>= level org-inlinetask-min-level))) (if (org-at-heading-p) @@ -7668,23 +7935,22 @@ command." (org-backward-heading-same-level 1 invisible-ok)) (= (point) (match-beginning 0))) - (not (org-previous-line-empty-p t))) + (not (org-next-line-empty-p))) (setq empty-line-p (or empty-line-p (org-previous-line-empty-p)))) (match-string 0)) (error (or fix-level "* "))))) (blank-a (cdr (assq 'heading org-blank-before-new-entry))) - (blank (if (eq blank-a 'auto) empty-line-p blank-a)) - pos hide-previous previous-pos) + (blank (if (eq blank-a 'auto) empty-line-p blank-a))) - ;; If we insert after content, move there and clean up whitespace - (when (and respect-content - (not (org-looking-at-p org-outline-regexp-bol))) + ;; If we insert after content, move there and clean up + ;; whitespace. + (when respect-content (if (not (org-before-first-heading-p)) (org-end-of-subtree nil t) (re-search-forward org-outline-regexp-bol) (beginning-of-line 0)) - (skip-chars-backward " \r\n") + (skip-chars-backward " \r\t\n") (and (not (looking-back "^\\*+" (line-beginning-position))) (looking-at "[ \t]+") (replace-match "")) (unless (eobp) (forward-char 1)) @@ -7692,14 +7958,17 @@ command." (unless (bobp) (backward-char 1)) (insert "\n"))) - ;; If we are splitting, grab the text that should be moved to the new headline + ;; If we are splitting, grab the text that should be moved + ;; to the new headline. (when may-split - (if (org-on-heading-p) - ;; This is a heading, we split intelligently (keeping tags) + (if (org-at-heading-p) + ;; This is a heading: split intelligently (keeping + ;; tags). (let ((pos (point))) - (goto-char (point-at-bol)) - (unless (looking-at org-complex-heading-regexp) - (error "This should not happen")) + (beginning-of-line) + (let ((case-fold-search nil)) + (unless (looking-at org-complex-heading-regexp) + (error "This should not happen"))) (when (and (match-beginning 4) (> pos (match-beginning 4)) (< pos (match-end 4))) @@ -7708,31 +7977,35 @@ command." (delete-region (point) (match-end 4)) (if (looking-at "[ \t]*$") (replace-match "") - (insert (make-string (length initial-content) ?\ ))) + (insert (make-string (length initial-content) ?\s))) (setq initial-content (org-trim initial-content))) (goto-char pos)) - ;; a normal line + ;; A normal line. (setq initial-content - (org-trim (buffer-substring (point) (point-at-eol)))) - (delete-region (point) (point-at-eol)))) + (org-trim + (delete-and-extract-region (point) (line-end-position)))))) - ;; If we are at the beginning of the line, insert before it. Else after + ;; If we are at the beginning of the line, insert before it. + ;; Otherwise, after it. (cond ((and (bolp) (looking-at "[ \t]*$"))) - ((and (bolp) (not (looking-at "[ \t]*$"))) - (open-line 1)) - (t - (goto-char (point-at-eol)) - (insert "\n"))) + ((bolp) (save-excursion (insert "\n"))) + (t (end-of-line) + (insert "\n"))) ;; Insert the new heading (insert stars) (just-one-space) (insert initial-content) - (when adjust-empty-lines - (if (or (not blank) - (and blank (not (org-previous-line-empty-p)))) - (org-N-empty-lines-before-current (if blank 1 0)))) + (unless (and blank (org-previous-line-empty-p)) + (org-N-empty-lines-before-current (if blank 1 0))) + ;; Adjust visibility, which may be messed up if we removed + ;; blank lines while previous entry was hidden. + (let ((bol (line-beginning-position))) + (dolist (o (overlays-at (1- bol))) + (when (and (eq (overlay-get o 'invisible) 'outline) + (eq (overlay-end o) bol)) + (move-overlay o (overlay-start o) (1- bol))))) (run-hooks 'org-insert-heading-hook))))))) (defun org-N-empty-lines-before-current (N) @@ -7752,20 +8025,23 @@ When NO-TAGS is non-nil, don't include tags. When NO-TODO is non-nil, don't include TODO keywords." (save-excursion (org-back-to-heading t) - (cond - ((and no-tags no-todo) - (looking-at org-complex-heading-regexp) - (match-string 4)) - (no-tags - (looking-at (concat org-outline-regexp - "\\(.*?\\)" - "\\(?:[ \t]+:[[:alnum:]:_@#%]+:\\)?[ \t]*$")) - (match-string 1)) - (no-todo - (looking-at org-todo-line-regexp) - (match-string 3)) - (t (looking-at org-heading-regexp) - (match-string 2))))) + (let ((case-fold-search nil)) + (cond + ((and no-tags no-todo) + (looking-at org-complex-heading-regexp) + ;; Return value has to be a string, but match group 4 is + ;; optional. + (or (match-string 4) "")) + (no-tags + (looking-at (concat org-outline-regexp + "\\(.*?\\)" + "\\(?:[ \t]+:[[:alnum:]:_@#%]+:\\)?[ \t]*$")) + (match-string 1)) + (no-todo + (looking-at org-todo-line-regexp) + (match-string 3)) + (t (looking-at org-heading-regexp) + (match-string 2)))))) (defvar orgstruct-mode) ; defined below @@ -7780,24 +8056,24 @@ This is a list with the following elements: - the tags string, or nil." (save-excursion (org-back-to-heading t) - (if (let (case-fold-search) - (looking-at - (if orgstruct-mode - org-heading-regexp - org-complex-heading-regexp))) - (if orgstruct-mode - (list (length (match-string 1)) - (org-reduced-level (length (match-string 1))) - nil - nil - (match-string 2) - nil) - (list (length (match-string 1)) - (org-reduced-level (length (match-string 1))) - (org-match-string-no-properties 2) - (and (match-end 3) (aref (match-string 3) 2)) - (org-match-string-no-properties 4) - (org-match-string-no-properties 5)))))) + (when (let (case-fold-search) + (looking-at + (if orgstruct-mode + org-heading-regexp + org-complex-heading-regexp))) + (if orgstruct-mode + (list (length (match-string 1)) + (org-reduced-level (length (match-string 1))) + nil + nil + (match-string 2) + nil) + (list (length (match-string 1)) + (org-reduced-level (length (match-string 1))) + (match-string-no-properties 2) + (and (match-end 3) (aref (match-string 3) 2)) + (match-string-no-properties 4) + (match-string-no-properties 5)))))) (defun org-get-entry () "Get the entry text, after heading, entire subtree." @@ -7805,6 +8081,24 @@ This is a list with the following elements: (org-back-to-heading t) (buffer-substring (point-at-bol 2) (org-end-of-subtree t)))) +(defun org-edit-headline (&optional heading) + "Edit the current headline. +Set it to HEADING when provided." + (interactive) + (org-with-wide-buffer + (org-back-to-heading t) + (let ((case-fold-search nil)) + (when (looking-at org-complex-heading-regexp) + (let* ((old (match-string-no-properties 4)) + (new (save-match-data + (org-trim (or heading (read-string "Edit: " old)))))) + (unless (equal old new) + (if old (replace-match new t t nil 4) + (goto-char (or (match-end 3) (match-end 2) (match-end 1))) + (insert " " new)) + (org-set-tags nil t) + (when (looking-at "[ \t]*$") (replace-match "")))))))) + (defun org-insert-heading-after-current () "Insert a new heading with same level as current, after current subtree." (interactive) @@ -7825,9 +8119,14 @@ This is a list with the following elements: (defun org-insert-todo-heading (arg &optional force-heading) "Insert a new heading with the same level and TODO state as current heading. -If the heading has no TODO state, or if the state is DONE, use the first -state (TODO by default). Also with one prefix arg, force first state. With -two prefix args, force inserting at the end of the parent subtree." + +If the heading has no TODO state, or if the state is DONE, use +the first state (TODO by default). Also with one prefix arg, +force first state. With two prefix args, force inserting at the +end of the parent subtree. + +When called at a plain list item, insert a new item with an +unchecked check box." (interactive "P") (when (or force-heading (not (org-insert-item 'checkbox))) (org-insert-heading (or (and (equal arg '(16)) '(16)) @@ -7835,19 +8134,18 @@ two prefix args, force inserting at the end of the parent subtree." (save-excursion (org-back-to-heading) (outline-previous-heading) - (looking-at org-todo-line-regexp)) - (let* - ((new-mark-x - (if (or (equal arg '(4)) - (not (match-beginning 2)) - (member (match-string 2) org-done-keywords)) - (car org-todo-keywords-1) - (match-string 2))) - (new-mark - (or - (run-hook-with-args-until-success - 'org-todo-get-default-hook new-mark-x nil) - new-mark-x))) + (let ((case-fold-search nil)) (looking-at org-todo-line-regexp))) + (let* ((new-mark-x + (if (or (equal arg '(4)) + (not (match-beginning 2)) + (member (match-string 2) org-done-keywords)) + (car org-todo-keywords-1) + (match-string 2))) + (new-mark + (or + (run-hook-with-args-until-success + 'org-todo-get-default-hook new-mark-x nil) + new-mark-x))) (beginning-of-line 1) (and (looking-at org-outline-regexp) (goto-char (match-end 0)) (if org-treat-insert-todo-heading-as-state-change @@ -7895,18 +8193,17 @@ See also `org-promote'." (org-fix-position-after-promote)) (defun org-demote-subtree () - "Demote the entire subtree. See `org-demote'. -See also `org-promote'." + "Demote the entire subtree. +See `org-demote' and `org-promote'." (interactive) (save-excursion (org-with-limited-levels (org-map-tree 'org-demote))) (org-fix-position-after-promote)) - (defun org-do-promote () "Promote the current heading higher up the tree. -If the region is active in `transient-mark-mode', promote all headings -in the region." +If the region is active in `transient-mark-mode', promote all +headings in the region." (interactive) (save-excursion (if (org-region-active-p) @@ -7916,8 +8213,8 @@ in the region." (defun org-do-demote () "Demote the current heading lower down the tree. -If the region is active in `transient-mark-mode', demote all headings -in the region." +If the region is active in `transient-mark-mode', demote all +headings in the region." (interactive) (save-excursion (if (org-region-active-p) @@ -7926,23 +8223,24 @@ in the region." (org-fix-position-after-promote)) (defun org-fix-position-after-promote () - "Make sure that after pro/demotion cursor position is right." + "Fix cursor position and indentation after demoting/promoting." (let ((pos (point))) (when (save-excursion - (beginning-of-line 1) - (looking-at org-todo-line-regexp) - (or (equal pos (match-end 1)) (equal pos (match-end 2)))) + (beginning-of-line) + (let ((case-fold-search nil)) (looking-at org-todo-line-regexp)) + (or (eq pos (match-end 1)) (eq pos (match-end 2)))) (cond ((eobp) (insert " ")) ((eolp) (insert " ")) - ((equal (char-after) ?\ ) (forward-char 1)))))) + ((equal (char-after) ?\s) (forward-char 1)))))) (defun org-current-level () "Return the level of the current entry, or nil if before the first headline. -The level is the number of stars at the beginning of the headline." - (save-excursion - (org-with-limited-levels - (if (ignore-errors (org-back-to-heading t)) - (funcall outline-level))))) +The level is the number of stars at the beginning of the +headline. Use `org-reduced-level' to remove the effect of +`org-odd-levels'. Unlike to `org-outline-level', this function +ignores inlinetasks." + (let ((level (org-with-limited-levels (org-outline-level)))) + (and (> level 0) level))) (defun org-get-previous-line-level () "Return the outline depth of the last headline before the current line. @@ -7978,50 +8276,39 @@ even level numbers will become the next higher odd number." ((< change 0) (max 1 (1+ (* 2 (/ (+ level (* 2 change)) 2)))))) (max 1 (+ level (or change 0))))) -(if (boundp 'define-obsolete-function-alias) - (if (or (featurep 'xemacs) (< emacs-major-version 23)) - (define-obsolete-function-alias 'org-get-legal-level - 'org-get-valid-level) - (define-obsolete-function-alias 'org-get-legal-level - 'org-get-valid-level "23.1"))) - (defun org-promote () - "Promote the current heading higher up the tree. -If the region is active in `transient-mark-mode', promote all headings -in the region." - (org-back-to-heading t) - (let* ((level (save-match-data (funcall outline-level))) - (after-change-functions (remove 'flyspell-after-change-function - after-change-functions)) - (up-head (concat (make-string (org-get-valid-level level -1) ?*) " ")) - (diff (abs (- level (length up-head) -1)))) - (cond ((and (= level 1) org-called-with-limited-levels - org-allow-promoting-top-level-subtree) - (replace-match "# " nil t)) - ((= level 1) - (user-error "Cannot promote to level 0. UNDO to recover if necessary")) - (t (replace-match up-head nil t))) - ;; Fixup tag positioning - (unless (= level 1) - (and org-auto-align-tags (org-set-tags nil 'ignore-column)) - (if org-adapt-indentation (org-fixup-indentation (- diff)))) - (run-hooks 'org-after-promote-entry-hook))) + "Promote the current heading higher up the tree." + (org-with-wide-buffer + (org-back-to-heading t) + (let* ((after-change-functions (remq 'flyspell-after-change-function + after-change-functions)) + (level (save-match-data (funcall outline-level))) + (up-head (concat (make-string (org-get-valid-level level -1) ?*) " ")) + (diff (abs (- level (length up-head) -1)))) + (cond + ((and (= level 1) org-allow-promoting-top-level-subtree) + (replace-match "# " nil t)) + ((= level 1) + (user-error "Cannot promote to level 0. UNDO to recover if necessary")) + (t (replace-match up-head nil t))) + (unless (= level 1) + (when org-auto-align-tags (org-set-tags nil 'ignore-column)) + (when org-adapt-indentation (org-fixup-indentation (- diff)))) + (run-hooks 'org-after-promote-entry-hook)))) (defun org-demote () - "Demote the current heading lower down the tree. -If the region is active in `transient-mark-mode', demote all headings -in the region." - (org-back-to-heading t) - (let* ((level (save-match-data (funcall outline-level))) - (after-change-functions (remove 'flyspell-after-change-function - after-change-functions)) - (down-head (concat (make-string (org-get-valid-level level 1) ?*) " ")) - (diff (abs (- level (length down-head) -1)))) - (replace-match down-head nil t) - ;; Fixup tag positioning - (and org-auto-align-tags (org-set-tags nil 'ignore-column)) - (if org-adapt-indentation (org-fixup-indentation diff)) - (run-hooks 'org-after-demote-entry-hook))) + "Demote the current heading lower down the tree." + (org-with-wide-buffer + (org-back-to-heading t) + (let* ((after-change-functions (remq 'flyspell-after-change-function + after-change-functions)) + (level (save-match-data (funcall outline-level))) + (down-head (concat (make-string (org-get-valid-level level 1) ?*) " ")) + (diff (abs (- level (length down-head) -1)))) + (replace-match down-head nil t) + (when org-auto-align-tags (org-set-tags nil 'ignore-column)) + (when org-adapt-indentation (org-fixup-indentation diff)) + (run-hooks 'org-after-demote-entry-hook)))) (defun org-cycle-level () "Cycle the level of an empty headline through possible states. @@ -8036,32 +8323,32 @@ After top level, it switches back to sibling level." (cond ;; If first headline in file, promote to top-level. ((= prev-level 0) - (loop repeat (/ (- cur-level 1) (org-level-increment)) - do (org-do-promote))) + (cl-loop repeat (/ (- cur-level 1) (org-level-increment)) + do (org-do-promote))) ;; If same level as prev, demote one. ((= prev-level cur-level) (org-do-demote)) ;; If parent is top-level, promote to top level if not already. ((= prev-level 1) - (loop repeat (/ (- cur-level 1) (org-level-increment)) - do (org-do-promote))) + (cl-loop repeat (/ (- cur-level 1) (org-level-increment)) + do (org-do-promote))) ;; If top-level, return to prev-level. ((= cur-level 1) - (loop repeat (/ (- prev-level 1) (org-level-increment)) - do (org-do-demote))) + (cl-loop repeat (/ (- prev-level 1) (org-level-increment)) + do (org-do-demote))) ;; If less than prev-level, promote one. ((< cur-level prev-level) (org-do-promote)) ;; If deeper than prev-level, promote until higher than ;; prev-level. ((> cur-level prev-level) - (loop repeat (+ 1 (/ (- cur-level prev-level) (org-level-increment))) - do (org-do-promote)))) + (cl-loop repeat (+ 1 (/ (- cur-level prev-level) (org-level-increment))) + do (org-do-promote)))) t)))) (defun org-map-tree (fun) "Call FUN for every heading underneath the current one." - (org-back-to-heading) + (org-back-to-heading t) (let ((level (funcall outline-level))) (save-excursion (funcall fun) @@ -8077,39 +8364,123 @@ After top level, it switches back to sibling level." (save-excursion (setq end (copy-marker end)) (goto-char beg) - (if (and (re-search-forward org-outline-regexp-bol nil t) - (< (point) end)) - (funcall fun)) + (when (and (re-search-forward org-outline-regexp-bol nil t) + (< (point) end)) + (funcall fun)) (while (and (progn (outline-next-heading) (< (point) end)) (not (eobp))) (funcall fun))))) -(defvar org-property-end-re) ; silence byte-compiler (defun org-fixup-indentation (diff) "Change the indentation in the current entry by DIFF. -However, if any line in the current entry has no indentation, or if it -would end up with no indentation after the change, nothing at all is done." - (save-excursion - (let ((end (save-excursion (outline-next-heading) - (point-marker))) - (prohibit (if (> diff 0) - "^\\S-" - (concat "^ \\{0," (int-to-string (- diff)) "\\}\\S-"))) - col) - (unless (save-excursion (end-of-line 1) - (re-search-forward prohibit end t)) - (while (and (< (point) end) - (re-search-forward "^[ \t]+" end t)) - (goto-char (match-end 0)) - (setq col (current-column)) - (if (< diff 0) (replace-match "")) - (org-indent-to-column (+ diff col)))) - (move-marker end nil)))) + +DIFF is an integer. Indentation is done according to the +following rules: + + - Planning information and property drawers are always indented + according to the new level of the headline; + + - Footnote definitions and their contents are ignored; + + - Inlinetasks' boundaries are not shifted; + + - Empty lines are ignored; + + - Other lines' indentation are shifted by DIFF columns, unless + it would introduce a structural change in the document, in + which case no shifting is done at all. + +Assume point is at a heading or an inlinetask beginning." + (org-with-wide-buffer + (narrow-to-region (line-beginning-position) + (save-excursion + (if (org-with-limited-levels (org-at-heading-p)) + (org-with-limited-levels (outline-next-heading)) + (org-inlinetask-goto-end)) + (point))) + (forward-line) + ;; Indent properly planning info and property drawer. + (when (looking-at-p org-planning-line-re) + (org-indent-line) + (forward-line)) + (when (looking-at org-property-drawer-re) + (goto-char (match-end 0)) + (forward-line) + (save-excursion (org-indent-region (match-beginning 0) (match-end 0)))) + (catch 'no-shift + (when (zerop diff) (throw 'no-shift nil)) + ;; If DIFF is negative, first check if a shift is possible at all + ;; (e.g., it doesn't break structure). This can only happen if + ;; some contents are not properly indented. + (let ((case-fold-search t)) + (when (< diff 0) + (let ((diff (- diff)) + (forbidden-re (concat org-outline-regexp + "\\|" + (substring org-footnote-definition-re 1)))) + (save-excursion + (while (not (eobp)) + (cond + ((looking-at-p "[ \t]*$") (forward-line)) + ((and (looking-at-p org-footnote-definition-re) + (let ((e (org-element-at-point))) + (and (eq (org-element-type e) 'footnote-definition) + (goto-char (org-element-property :end e)))))) + ((looking-at-p org-outline-regexp) (forward-line)) + ;; Give up if shifting would move before column 0 or + ;; if it would introduce a headline or a footnote + ;; definition. + (t + (skip-chars-forward " \t") + (let ((ind (current-column))) + (when (or (< ind diff) + (and (= ind diff) (looking-at-p forbidden-re))) + (throw 'no-shift nil))) + ;; Ignore contents of example blocks and source + ;; blocks if their indentation is meant to be + ;; preserved. Jump to block's closing line. + (beginning-of-line) + (or (and (looking-at-p "[ \t]*#\\+BEGIN_\\(EXAMPLE\\|SRC\\)") + (let ((e (org-element-at-point))) + (and (memq (org-element-type e) + '(example-block src-block)) + (or org-src-preserve-indentation + (org-element-property :preserve-indent e)) + (goto-char (org-element-property :end e)) + (progn (skip-chars-backward " \r\t\n") + (beginning-of-line) + t)))) + (forward-line)))))))) + ;; Shift lines but footnote definitions, inlinetasks boundaries + ;; by DIFF. Also skip contents of source or example blocks + ;; when indentation is meant to be preserved. + (while (not (eobp)) + (cond + ((and (looking-at-p org-footnote-definition-re) + (let ((e (org-element-at-point))) + (and (eq (org-element-type e) 'footnote-definition) + (goto-char (org-element-property :end e)))))) + ((looking-at-p org-outline-regexp) (forward-line)) + ((looking-at-p "[ \t]*$") (forward-line)) + (t + (indent-line-to (+ (org-get-indentation) diff)) + (beginning-of-line) + (or (and (looking-at-p "[ \t]*#\\+BEGIN_\\(EXAMPLE\\|SRC\\)") + (let ((e (org-element-at-point))) + (and (memq (org-element-type e) + '(example-block src-block)) + (or org-src-preserve-indentation + (org-element-property :preserve-indent e)) + (goto-char (org-element-property :end e)) + (progn (skip-chars-backward " \r\t\n") + (beginning-of-line) + t)))) + (forward-line))))))))) (defun org-convert-to-odd-levels () - "Convert an org-mode file with all levels allowed to one with odd levels. + "Convert an Org file with all levels allowed to one with odd levels. This will leave level 1 alone, convert level 2 to level 3, level 3 to level 5 etc." (interactive) @@ -8125,7 +8496,7 @@ level 5 etc." (end-of-line 1)))))) (defun org-convert-to-oddeven-levels () - "Convert an org-mode file with only odd levels to one with odd/even levels. + "Convert an Org file with only odd levels to one with odd/even levels. This promotes level 3 to level 2, level 5 to level 3 etc. If the file contains a section with an even level, conversion would destroy the structure of the file. An error is signaled in this @@ -8134,7 +8505,7 @@ case." (goto-char (point-min)) ;; First check if there are no even levels (when (re-search-forward "^\\(\\*\\*\\)+ " nil t) - (org-show-context t) + (org-show-set-visibility 'canonical) (error "Not all levels are odd in this file. Conversion not possible")) (when (yes-or-no-p "Are you sure you want to globally change levels to odd-even? ") (let ((outline-regexp org-outline-regexp) @@ -8177,7 +8548,7 @@ case." (setq beg (point))) (save-match-data (save-excursion (outline-end-of-heading) - (setq folded (outline-invisible-p))) + (setq folded (org-invisible-p))) (progn (org-end-of-subtree nil t) (unless (eobp) (backward-char)))) (outline-next-heading) @@ -8196,12 +8567,12 @@ case." (progn (goto-char beg0) (user-error "Cannot move past superior level or buffer limit"))) (setq cnt (1- cnt))) - (if (> arg 0) - ;; Moving forward - still need to move over subtree - (progn (org-end-of-subtree t t) - (save-excursion - (org-back-over-empty-lines) - (or (bolp) (newline))))) + (when (> arg 0) + ;; Moving forward - still need to move over subtree + (org-end-of-subtree t t) + (save-excursion + (org-back-over-empty-lines) + (or (bolp) (newline)))) (setq ne-ins (org-back-over-empty-lines)) (move-marker ins-point (point)) (setq txt (buffer-substring beg end)) @@ -8230,9 +8601,9 @@ case." (insert (make-string (- ne-ins ne-beg) ?\n))) (move-marker ins-point nil) (if folded - (hide-subtree) + (outline-hide-subtree) (org-show-entry) - (show-children) + (org-show-children) (org-cycle-hide-drawers 'children)) (org-clean-visibility-after-subtree-move) ;; move back to the initial column we were at @@ -8264,7 +8635,7 @@ of some markers in the region, even if CUT is non-nil. This is useful if the caller implements cut-and-paste as copy-then-paste-then-cut." (interactive "p") (let (beg end folded (beg0 (point))) - (if (org-called-interactively-p 'any) + (if (called-interactively-p 'any) (org-back-to-heading nil) ; take what looks like a subtree (org-back-to-heading t)) ; take what is really there (setq beg (point)) @@ -8273,11 +8644,14 @@ useful if the caller implements cut-and-paste as copy-then-paste-then-cut." (if nosubtrees (outline-next-heading) (save-excursion (outline-end-of-heading) - (setq folded (outline-invisible-p))) - (condition-case nil - (org-forward-heading-same-level (1- n) t) - (error nil)) + (setq folded (org-invisible-p))) + (ignore-errors (org-forward-heading-same-level (1- n) t)) (org-end-of-subtree t t))) + ;; Include the end of an inlinetask + (when (and (featurep 'org-inlinetask) + (looking-at-p (concat (org-inlinetask-outline-regexp) + "END[ \t]*$"))) + (end-of-line)) (setq end (point)) (goto-char beg0) (when (> end beg) @@ -8290,7 +8664,7 @@ useful if the caller implements cut-and-paste as copy-then-paste-then-cut." (if cut "Cut" "Copied") (length org-subtree-clip))))) -(defun org-paste-subtree (&optional level tree for-yank) +(defun org-paste-subtree (&optional level tree for-yank remove) "Paste the clipboard as a subtree, with modification of headline level. The entire subtree is promoted or demoted in order to match a new headline level. @@ -8313,15 +8687,17 @@ If optional TREE is given, use this text instead of the kill ring. When FOR-YANK is set, this is called by `org-yank'. In this case, do not move back over whitespace before inserting, and move point to the end of -the inserted text when done." +the inserted text when done. + +When REMOVE is non-nil, remove the subtree from the clipboard." (interactive "P") (setq tree (or tree (and kill-ring (current-kill 0)))) (unless (org-kill-is-subtree-p tree) (user-error "%s" - (substitute-command-keys - "The kill is not a (set of) tree(s) - please use \\[yank] to yank anyway"))) + (substitute-command-keys + "The kill is not a (set of) tree(s) - please use \\[yank] to yank anyway"))) (org-with-limited-levels - (let* ((visp (not (outline-invisible-p))) + (let* ((visp (not (org-invisible-p))) (txt tree) (^re_ "\\(\\*+\\)[ \t]*") (old-level (if (string-match org-outline-regexp-bol txt) @@ -8364,22 +8740,22 @@ the inserted text when done." (org-odd-levels-only nil) beg end newend) ;; Remove the forced level indicator - (if force-level - (delete-region (point-at-bol) (point))) + (when force-level + (delete-region (point-at-bol) (point))) ;; Paste (beginning-of-line (if (bolp) 1 2)) (setq beg (point)) (and (fboundp 'org-id-paste-tracker) (org-id-paste-tracker txt)) (insert-before-markers txt) - (unless (string-match "\n\\'" txt) (insert "\n")) + (unless (string-suffix-p "\n" txt) (insert "\n")) (setq newend (point)) (org-reinstall-markers-in-region beg) (setq end (point)) (goto-char beg) (skip-chars-forward " \t\n\r") (setq beg (point)) - (if (and (outline-invisible-p) visp) - (save-excursion (outline-show-heading))) + (when (and (org-invisible-p) visp) + (save-excursion (outline-show-heading))) ;; Shift if necessary (unless (= shift 0) (save-restriction @@ -8389,15 +8765,16 @@ the inserted text when done." (setq shift (+ delta shift))) (goto-char (point-min)) (setq newend (point-max)))) - (when (or (org-called-interactively-p 'interactive) for-yank) + (when (or (called-interactively-p 'interactive) for-yank) (message "Clipboard pasted as level %d subtree" new-level)) - (if (and (not for-yank) ; in this case, org-yank will decide about folding - kill-ring - (eq org-subtree-clip (current-kill 0)) - org-subtree-clip-folded) - ;; The tree was folded before it was killed/copied - (hide-subtree)) - (and for-yank (goto-char newend))))) + (when (and (not for-yank) ; in this case, org-yank will decide about folding + kill-ring + (eq org-subtree-clip (current-kill 0)) + org-subtree-clip-folded) + ;; The tree was folded before it was killed/copied + (outline-hide-subtree)) + (and for-yank (goto-char newend)) + (and remove (setq kill-ring (cdr kill-ring)))))) (defun org-kill-is-subtree-p (&optional txt) "Check if the current kill is an outline subtree, or a set of trees. @@ -8447,15 +8824,14 @@ called immediately, to move the markers with the entries." "Check if MARKER is between BEG and END. If yes, remember the marker and the distance to BEG." (when (and (marker-buffer marker) - (equal (marker-buffer marker) (current-buffer))) - (if (and (>= marker beg) (< marker end)) - (push (cons marker (- marker beg)) org-markers-to-move)))) + (equal (marker-buffer marker) (current-buffer)) + (>= marker beg) (< marker end)) + (push (cons marker (- marker beg)) org-markers-to-move))) (defun org-reinstall-markers-in-region (beg) "Move all remembered markers to their position relative to BEG." - (mapc (lambda (x) - (move-marker (car x) (+ beg (cdr x)))) - org-markers-to-move) + (dolist (x org-markers-to-move) + (move-marker (car x) (+ beg (cdr x)))) (setq org-markers-to-move nil)) (defun org-narrow-to-subtree () @@ -8467,7 +8843,7 @@ If yes, remember the marker and the distance to BEG." (narrow-to-region (progn (org-back-to-heading t) (point)) (progn (org-end-of-subtree t t) - (if (and (org-at-heading-p) (not (eobp))) (backward-char 1)) + (when (and (org-at-heading-p) (not (eobp))) (backward-char 1)) (point))))))) (defun org-narrow-to-block () @@ -8480,10 +8856,6 @@ If yes, remember the marker and the distance to BEG." (narrow-to-region (car blockp) (cdr blockp)) (user-error "Not in a block")))) -(eval-when-compile - (defvar org-property-drawer-re)) - -(defvar org-property-start-re) ;; defined below (defun org-clone-subtree-with-time-shift (n &optional shift) "Clone the task (subtree) at point N times. The clones will be inserted as siblings. @@ -8500,6 +8872,9 @@ stamps in the subtree shifted for each clone produced. If SHIFT is nil or the empty string, time stamps will be left alone. The ID property of the original subtree is removed. +In each clone, all the CLOCK entries will be removed. This +prevents Org from considering that the clocked times overlap. + If the original subtree did contain time stamps with a repeater, the following will happen: - the repeater will be removed in each clone @@ -8510,80 +8885,86 @@ the following will happen: - the start days in the repeater in the original entry will be shifted to past the last clone. In this way you can spell out a number of instances of a repeating task, -and still retain the repeater to cover future instances of the task." +and still retain the repeater to cover future instances of the task. + +As described above, N+1 clones are produced when the original +subtree has a repeater. Setting N to 0, then, can be used to +remove the repeater from a subtree and create a shifted clone +with the original repeater." (interactive "nNumber of clones to produce: ") - (let ((shift - (or shift - (if (and (not (equal current-prefix-arg '(4))) - (save-excursion - (re-search-forward org-ts-regexp-both - (save-excursion - (org-end-of-subtree t) - (point)) t))) - (read-from-minibuffer - "Date shift per clone (e.g. +1w, empty to copy unchanged): ") - ""))) ;; No time shift - (n-no-remove -1) - (drawer-re org-drawer-regexp) - beg end template task idprop - shift-n shift-what doshift nmin nmax) - (if (not (and (integerp n) (> n 0))) - (error "Invalid number of replications %s" n)) - (if (and (setq doshift (and (stringp shift) (string-match "\\S-" shift))) - (not (string-match "\\`[ \t]*\\+?\\([0-9]+\\)\\([hdwmy]\\)[ \t]*\\'" - shift))) - (error "Invalid shift specification %s" shift)) - (when doshift - (setq shift-n (string-to-number (match-string 1 shift)) - shift-what (cdr (assoc (match-string 2 shift) - '(("d" . day) ("w" . week) - ("m" . month) ("y" . year)))))) - (if (eq shift-what 'week) (setq shift-n (* 7 shift-n) shift-what 'day)) - (setq nmin 1 nmax n) - (org-back-to-heading t) - (setq beg (point)) - (setq idprop (org-entry-get nil "ID")) - (org-end-of-subtree t t) - (or (bolp) (insert "\n")) - (setq end (point)) - (setq template (buffer-substring beg end)) - (when (and doshift - (string-match "<[^<>\n]+ [.+]?\\+[0-9]+[hdwmy][^<>\n]*>" template)) - (delete-region beg end) - (setq end beg) - (setq nmin 0 nmax (1+ nmax) n-no-remove nmax)) - (goto-char end) - (loop for n from nmin to nmax do - ;; prepare clone - (with-temp-buffer - (insert template) - (org-mode) - (goto-char (point-min)) - (org-show-subtree) - (and idprop (if org-clone-delete-id - (org-entry-delete nil "ID") - (org-id-get-create t))) - (unless (= n 0) - (while (re-search-forward "^[ \t]*CLOCK:.*$" nil t) - (kill-whole-line)) - (goto-char (point-min)) - (while (re-search-forward drawer-re nil t) - (mapc (lambda (d) - (org-remove-empty-drawer-at d (point))) - org-drawers))) - (goto-char (point-min)) - (when doshift - (while (re-search-forward org-ts-regexp-both nil t) - (org-timestamp-change (* n shift-n) shift-what)) - (unless (= n n-no-remove) - (goto-char (point-min)) - (while (re-search-forward org-ts-regexp nil t) - (save-excursion - (goto-char (match-beginning 0)) - (if (looking-at "<[^<>\n]+\\( +[.+]?\\+[0-9]+[hdwmy]\\)") - (delete-region (match-beginning 1) (match-end 1))))))) - (setq task (buffer-string))) - (insert task)) + (unless (wholenump n) (user-error "Invalid number of replications %s" n)) + (when (org-before-first-heading-p) (user-error "No subtree to clone")) + (let* ((beg (save-excursion (org-back-to-heading t) (point))) + (end-of-tree (save-excursion (org-end-of-subtree t t) (point))) + (shift + (or shift + (if (and (not (equal current-prefix-arg '(4))) + (save-excursion + (goto-char beg) + (re-search-forward org-ts-regexp-both end-of-tree t))) + (read-from-minibuffer + "Date shift per clone (e.g. +1w, empty to copy unchanged): ") + ""))) ;No time shift + (doshift + (and (org-string-nw-p shift) + (or (string-match "\\`[ \t]*\\+?\\([0-9]+\\)\\([dwmy]\\)[ \t]*\\'" + shift) + (user-error "Invalid shift specification %s" shift))))) + (goto-char end-of-tree) + (unless (bolp) (insert "\n")) + (let* ((end (point)) + (template (buffer-substring beg end)) + (shift-n (and doshift (string-to-number (match-string 1 shift)))) + (shift-what (pcase (and doshift (match-string 2 shift)) + (`nil nil) + ("d" 'day) + ("w" (setq shift-n (* 7 shift-n)) 'day) + ("m" 'month) + ("y" 'year) + (_ (error "Unsupported time unit")))) + (nmin 1) + (nmax n) + (n-no-remove -1) + (idprop (org-entry-get nil "ID"))) + (when (and doshift + (string-match-p "<[^<>\n]+ [.+]?\\+[0-9]+[hdwmy][^<>\n]*>" + template)) + (delete-region beg end) + (setq end beg) + (setq nmin 0) + (setq nmax (1+ nmax)) + (setq n-no-remove nmax)) + (goto-char end) + (cl-loop for n from nmin to nmax do + (insert + ;; Prepare clone. + (with-temp-buffer + (insert template) + (org-mode) + (goto-char (point-min)) + (org-show-subtree) + (and idprop (if org-clone-delete-id + (org-entry-delete nil "ID") + (org-id-get-create t))) + (unless (= n 0) + (while (re-search-forward org-clock-line-re nil t) + (delete-region (line-beginning-position) + (line-beginning-position 2))) + (goto-char (point-min)) + (while (re-search-forward org-drawer-regexp nil t) + (org-remove-empty-drawer-at (point)))) + (goto-char (point-min)) + (when doshift + (while (re-search-forward org-ts-regexp-both nil t) + (org-timestamp-change (* n shift-n) shift-what)) + (unless (= n n-no-remove) + (goto-char (point-min)) + (while (re-search-forward org-ts-regexp nil t) + (save-excursion + (goto-char (match-beginning 0)) + (when (looking-at "<[^<>\n]+\\( +[.+]?\\+[0-9]+[hdwmy]\\)") + (delete-region (match-beginning 1) (match-end 1))))))) + (buffer-string))))) (goto-char beg))) ;;; Outline Sorting @@ -8621,7 +9002,8 @@ hook gets called. When a region or a plain list is sorted, the cursor will be in the first entry of the sorted region/list.") (defun org-sort-entries - (&optional with-case sorting-type getkey-func compare-func property) + (&optional with-case sorting-type getkey-func compare-func property + interactive?) "Sort entries on a certain level of an outline tree. If there is an active region, the entries in the region are sorted. Else, if the cursor is before the first entry, sort the top-level items. @@ -8632,33 +9014,40 @@ a time stamp, by a property, by priority order, or by a custom function. 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 ?s ?S ?d ?D ?p ?P ?o ?O ?r ?R ?f ?F). Here is the -precise meaning of each character: +\(?n ?N ?a ?A ?t ?T ?s ?S ?d ?D ?p ?P ?o ?O ?r ?R ?f ?F ?k ?K). Here is +the precise meaning of each character: -n Numerically, by converting the beginning of the entry/item to a number. a Alphabetically, ignoring the TODO keyword and the priority, if any. -o By order of TODO keywords. -t By date/time, either the first active time stamp in the entry, or, if - none exist, by the first inactive one. -s By the scheduled date/time. -d By deadline date/time. c By creation time, which is assumed to be the first inactive time stamp at the beginning of a line. +d By deadline date/time. +k By clocking time. +n Numerically, by converting the beginning of the entry/item to a number. +o By order of TODO keywords. p By priority according to the cookie. r By the value of a property. +s By scheduled date/time. +t By date/time, either the first active time stamp in the entry, or, if + none exist, by the first inactive one. 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. +called with point at the beginning of the record. It must return a +value that is compatible with COMPARE-FUNC, the function used to +compare entries. Comparing entries ignores case by default. However, with an optional argument WITH-CASE, the sorting considers case as well. Sorting is done against the visible part of the headlines, it ignores hidden -links." - (interactive "P") +links. + +When sorting is done, call `org-after-sorting-entries-or-items-hook'. + +A non-nil value for INTERACTIVE? is used to signal that this +function is being called interactively." + (interactive (list current-prefix-arg nil nil nil nil t)) (let ((case-func (if with-case 'identity 'downcase)) (cmstr ;; The clock marker is lost when using `sort-subr', let's @@ -8677,10 +9066,10 @@ links." (setq end (region-end) what "region") (goto-char (region-beginning)) - (if (not (org-at-heading-p)) (outline-next-heading)) + (unless (org-at-heading-p) (outline-next-heading)) (setq start (point))) ((or (org-at-heading-p) - (condition-case nil (progn (org-back-to-heading) t) (error nil))) + (ignore-errors (progn (org-back-to-heading) t))) ;; we will sort the children of the current headline (org-back-to-heading) (setq start (point) @@ -8691,7 +9080,7 @@ links." (point)) what "children") (goto-char start) - (show-subtree) + (outline-show-subtree) (outline-next-heading)) (t ;; we will sort the top-level entries in this file @@ -8707,7 +9096,7 @@ links." (setq end (point-max)) (setq what "top-level") (goto-char start) - (show-all))) + (outline-show-all))) (setq beg (point)) (when (>= beg end) (goto-char start) (user-error "Nothing to sort")) @@ -8717,32 +9106,34 @@ links." re (concat "^" (regexp-quote stars) " +") re2 (concat "^" (regexp-quote (substring stars 0 -1)) "[ \t\n]") txt (buffer-substring beg end)) - (if (not (equal (substring txt -1) "\n")) (setq txt (concat txt "\n"))) - (if (and (not (equal stars "*")) (string-match re2 txt)) - (user-error "Region to sort contains a level above the first entry")) + (unless (equal (substring txt -1) "\n") (setq txt (concat txt "\n"))) + (when (and (not (equal stars "*")) (string-match re2 txt)) + (user-error "Region to sort contains a level above the first entry")) (unless sorting-type (message "Sort %s: [a]lpha [n]umeric [p]riority p[r]operty todo[o]rder [f]unc - [t]ime [s]cheduled [d]eadline [c]reated - A/N/P/R/O/F/T/S/D/C means reversed:" + [t]ime [s]cheduled [d]eadline [c]reated cloc[k]ing + A/N/P/R/O/F/T/S/D/C/K means reversed:" what) - (setq sorting-type (read-char-exclusive)) - - (unless getkey-func - (and (= (downcase sorting-type) ?f) - (setq getkey-func - (org-icompleting-read "Sort using function: " - obarray 'fboundp t nil nil)) - (setq getkey-func (intern getkey-func)))) - - (and (= (downcase sorting-type) ?r) - (not property) - (setq property - (org-icompleting-read "Property: " - (mapcar 'list (org-buffer-property-keys t)) - nil t)))) - + (setq sorting-type (read-char-exclusive))) + + (unless getkey-func + (and (= (downcase sorting-type) ?f) + (setq getkey-func + (or (and interactive? + (org-read-function + "Function for extracting keys: ")) + (error "Missing key extractor"))))) + + (and (= (downcase sorting-type) ?r) + (not property) + (setq property + (completing-read "Property: " + (mapcar #'list (org-buffer-property-keys t)) + nil t))) + + (when (member sorting-type '(?k ?K)) (org-clock-sum)) (message "Sorting entries...") (save-restriction @@ -8777,6 +9168,8 @@ links." (if (looking-at org-complex-heading-regexp) (funcall case-func (org-sort-remove-invisible (match-string 4))) nil)) + ((= dcst ?k) + (or (get-text-property (point) :org-clock-minutes) 0)) ((= dcst ?t) (let ((end (save-excursion (outline-next-heading) (point)))) (if (or (re-search-forward org-ts-regexp end t) @@ -8807,22 +9200,29 @@ links." ((= dcst ?r) (or (org-entry-get nil property) "")) ((= dcst ?o) - (if (looking-at org-complex-heading-regexp) - (- 9999 (length (member (match-string 2) - org-todo-keywords-1))))) + (when (looking-at org-complex-heading-regexp) + (let* ((m (match-string 2)) + (s (if (member m org-done-keywords) '- '+))) + (- 99 (funcall s (length (member m org-todo-keywords-1))))))) ((= dcst ?f) (if getkey-func (progn (setq tmp (funcall getkey-func)) - (if (stringp tmp) (setq tmp (funcall case-func tmp))) + (when (stringp tmp) (setq tmp (funcall case-func tmp))) tmp) (error "Invalid key function `%s'" getkey-func))) (t (error "Invalid sorting type `%c'" sorting-type)))) nil (cond ((= dcst ?a) 'string<) - ((= dcst ?f) compare-func) - ((member dcst '(?p ?t ?s ?d ?c)) '<))))) + ((= dcst ?f) + (or compare-func + (and interactive? + (org-read-function + (concat "Function for comparing keys " + "(empty for default `sort-subr' predicate): ") + 'allow-empty)))) + ((member dcst '(?p ?t ?s ?d ?c ?k)) '<))))) (run-hooks 'org-after-sorting-entries-or-items-hook) ;; Reset the clock marker if needed (when cmstr @@ -8832,60 +9232,18 @@ links." (move-marker org-clock-marker (point)))) (message "Sorting entries...done"))) -(defun org-do-sort (table what &optional with-case sorting-type) - "Sort TABLE of WHAT according to SORTING-TYPE. -The user will be prompted for the SORTING-TYPE if the call to this -function does not specify it. WHAT is only for the prompt, to indicate -what is being sorted. The sorting key will be extracted from -the car of the elements of the table. -If WITH-CASE is non-nil, the sorting will be case-sensitive." - (unless sorting-type - (message - "Sort %s: [a]lphabetic, [n]umeric, [t]ime. A/N/T means reversed:" - what) - (setq sorting-type (read-char-exclusive))) - (let ((dcst (downcase sorting-type)) - extractfun comparefun) - ;; Define the appropriate functions - (cond - ((= dcst ?n) - (setq extractfun 'string-to-number - comparefun (if (= dcst sorting-type) '< '>))) - ((= dcst ?a) - (setq extractfun (if with-case (lambda(x) (org-sort-remove-invisible x)) - (lambda(x) (downcase (org-sort-remove-invisible x)))) - comparefun (if (= dcst sorting-type) - 'string< - (lambda (a b) (and (not (string< a b)) - (not (string= a b))))))) - ((= dcst ?t) - (setq extractfun - (lambda (x) - (if (or (string-match org-ts-regexp x) - (string-match org-ts-regexp-both x)) - (float-time - (org-time-string-to-time (match-string 0 x))) - 0)) - comparefun (if (= dcst sorting-type) '< '>))) - (t (error "Invalid sorting type `%c'" sorting-type))) - - (sort (mapcar (lambda (x) (cons (funcall extractfun (car x)) (cdr x))) - table) - (lambda (a b) (funcall comparefun (car a) (car b)))))) - - ;;; The orgstruct minor mode ;; Define a minor mode which can be used in other modes in order to -;; integrate the org-mode structure editing commands. +;; integrate the Org mode structure editing commands. -;; This is really a hack, because the org-mode structure commands use +;; This is really a hack, because the Org mode structure commands use ;; keys which normally belong to the major mode. Here is how it ;; works: The minor mode defines all the keys necessary to operate the ;; structure commands, but wraps the commands into a function which ;; tests if the cursor is currently at a headline or a plain list ;; item. If that is the case, the structure command is used, -;; temporarily setting many Org-mode variables like regular +;; temporarily setting many Org mode variables like regular ;; expressions for filling etc. However, when any of those keys is ;; used at a different location, function uses `key-binding' to look ;; up if the key has an associated command in another currently active @@ -8917,10 +9275,10 @@ orgstruct(++)-mode." ;;;###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 +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)." +defined by Org mode)." nil " OrgStruct" (make-sparse-keymap) (funcall (if orgstruct-mode 'add-to-invisibility-spec @@ -8937,40 +9295,38 @@ defined by Org-mode)." "Unconditionally turn on `orgstruct-mode'." (orgstruct-mode 1)) -(defvar org-fb-vars nil) -(make-variable-buffer-local 'org-fb-vars) +(defvar-local orgstruct-is-++ nil + "Is `orgstruct-mode' in ++ version in the current-buffer?") +(defvar-local org-fb-vars nil) (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 +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)) + (dolist (v org-fb-vars) + (set (make-local-variable (car v)) + (if (eq (car-safe (cadr v)) 'quote) + (cl-cadadr v) + (nth 1 v))))) (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 'orgstruct-is-++ t)))) - -(defvar orgstruct-is-++ nil - "Is `orgstruct-mode' in ++ version in the current-buffer?") -(make-variable-buffer-local 'orgstruct-is-++) + (dolist (x org-local-vars) + (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) + (set (make-local-variable var) + (if (eq (car-safe val) 'quote) (nth 1 val) val)))) + (setq-local orgstruct-is-++ t)))) ;;;###autoload (defun turn-on-orgstruct++ () @@ -8999,6 +9355,7 @@ buffer. It will also recognize item context in multiline items." org-ctrl-c-minus org-ctrl-c-star org-cycle + org-force-cycle-archived org-forward-heading-same-level org-insert-heading org-insert-heading-respect-content @@ -9018,6 +9375,7 @@ buffer. It will also recognize item context in multiline items." org-shifttab org-shifttab org-shiftup + org-show-children org-show-subtree org-sort org-up-element @@ -9025,8 +9383,7 @@ buffer. It will also recognize item context in multiline items." outline-next-visible-heading outline-previous-visible-heading outline-promote - outline-up-heading - show-children)) + outline-up-heading)) (let ((f (or (car-safe cell) cell)) (disable-when-heading-prefix (cdr-safe cell))) (when (fboundp f) @@ -9045,15 +9402,15 @@ buffer. It will also recognize item context in multiline items." (regexp-quote (cdr rep)) (car rep) (key-description binding))))) - (pushnew binding new-bindings :test 'equal))) + (cl-pushnew binding new-bindings :test 'equal))) (dolist (binding new-bindings) (let ((key (lookup-key orgstruct-mode-map binding))) (when (or (not key) (numberp key)) - (condition-case nil - (org-defkey orgstruct-mode-map - binding - (orgstruct-make-binding f binding disable-when-heading-prefix)) - (error nil))))))))) + (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) @@ -9152,9 +9509,9 @@ definitions." ;; normalize contexts (mapcar (lambda(c) (cond ((listp (cadr c)) - (list (car c) (car c) (cadr c))) + (list (car c) (car c) (nth 1 c))) ((string= "" (cadr c)) - (list (car c) (car c) (caddr c))) + (list (car c) (car c) (nth 2 c))) (t c))) contexts)) (a alist) r s) @@ -9168,7 +9525,7 @@ definitions." (setq vrules (org-contextualize-validate-key (car c) contexts))) (mapc (lambda (vr) - (when (not (equal (car vr) (cadr vr))) + (unless (equal (car vr) (cadr vr)) (setq repl vr))) vrules) (if (not repl) (push c r) @@ -9185,39 +9542,37 @@ definitions." (delete-dups (mapcar (lambda (x) (let ((tpl (car x))) - (when (not (delq - nil - (mapcar (lambda (y) - (equal y tpl)) - s))) + (unless (delq + nil + (mapcar (lambda (y) + (equal y tpl)) + s)) x))) (reverse r)))))) (defun org-contextualize-validate-key (key contexts) "Check CONTEXTS for agenda or capture KEY." - (let (rr res) + (let (res) (dolist (r contexts) - (mapc - (lambda (rr) - (when - (and (equal key (car r)) - (if (functionp rr) (funcall rr) - (or (and (eq (car rr) 'in-file) - (buffer-file-name) - (string-match (cdr rr) (buffer-file-name))) - (and (eq (car rr) 'in-mode) - (string-match (cdr rr) (symbol-name major-mode))) - (and (eq (car rr) 'in-buffer) - (string-match (cdr rr) (buffer-name))) - (when (and (eq (car rr) 'not-in-file) - (buffer-file-name)) - (not (string-match (cdr rr) (buffer-file-name)))) - (when (eq (car rr) 'not-in-mode) - (not (string-match (cdr rr) (symbol-name major-mode)))) - (when (eq (car rr) 'not-in-buffer) - (not (string-match (cdr rr) (buffer-name))))))) - (push r res))) - (car (last r)))) + (dolist (rr (car (last r))) + (when + (and (equal key (car r)) + (if (functionp rr) (funcall rr) + (or (and (eq (car rr) 'in-file) + (buffer-file-name) + (string-match (cdr rr) (buffer-file-name))) + (and (eq (car rr) 'in-mode) + (string-match (cdr rr) (symbol-name major-mode))) + (and (eq (car rr) 'in-buffer) + (string-match (cdr rr) (buffer-name))) + (when (and (eq (car rr) 'not-in-file) + (buffer-file-name)) + (not (string-match (cdr rr) (buffer-file-name)))) + (when (eq (car rr) 'not-in-mode) + (not (string-match (cdr rr) (symbol-name major-mode)))) + (when (eq (car rr) 'not-in-buffer) + (not (string-match (cdr rr) (buffer-name))))))) + (push r res)))) (delete-dups (delq nil res)))) (defun org-context-p (&rest contexts) @@ -9235,45 +9590,47 @@ Possible values in the list of contexts are `table', `headline', and `item'." (org-in-item-p))) (goto-char pos)))) +(defconst org-unique-local-variables + '(org-element--cache + org-element--cache-objects + org-element--cache-sync-keys + org-element--cache-sync-requests + org-element--cache-sync-timer) + "List of local variables that cannot be transferred to another buffer.") + (defun org-get-local-variables () "Return a list of all local variables in an Org mode buffer." - (let (varlist) - (with-current-buffer (get-buffer-create "*Org tmp*") - (erase-buffer) - (org-mode) - (setq varlist (buffer-local-variables))) - (kill-buffer "*Org tmp*") - (delq nil - (mapcar - (lambda (x) - (setq x - (if (symbolp x) - (list x) - (list (car x) (cdr x)))) - (if (and (not (get (car x) 'org-state)) - (string-match - "^\\(org-\\|orgtbl-\\|outline-\\|comment-\\|paragraph-\\|auto-fill\\|normal-auto-fill\\|fill-paragraph\\|indent-\\)" - (symbol-name (car x)))) - x nil)) - varlist)))) + (delq nil + (mapcar + (lambda (x) + (let* ((binding (if (symbolp x) (list x) (list (car x) (cdr x)))) + (name (car binding))) + (and (not (get name 'org-state)) + (not (memq name org-unique-local-variables)) + (string-match-p + "\\`\\(org-\\|orgtbl-\\|outline-\\|comment-\\|paragraph-\\|\ +auto-fill\\|normal-auto-fill\\|fill-paragraph\\|indent-\\)" + (symbol-name name)) + binding))) + (with-temp-buffer + (org-mode) + (buffer-local-variables))))) (defun org-clone-local-variables (from-buffer &optional regexp) "Clone local variables from FROM-BUFFER. Optional argument REGEXP selects variables to clone." - (mapc - (lambda (pair) - (and (symbolp (car pair)) - (or (null regexp) - (string-match regexp (symbol-name (car pair)))) - (set (make-local-variable (car pair)) - (cdr pair)))) - (buffer-local-variables from-buffer))) + (dolist (pair (buffer-local-variables from-buffer)) + (pcase pair + (`(,name . ,value) ;ignore unbound variables + (when (and (not (memq name org-unique-local-variables)) + (or (null regexp) (string-match-p regexp (symbol-name name)))) + (set (make-local-variable name) value)))))) ;;;###autoload (defun org-run-like-in-org-mode (cmd) - "Run a command, pretending that the current buffer is in Org-mode. + "Run a command, pretending that the current buffer is in Org mode. This will temporarily bind local variables that are typically bound in -Org-mode to the values they have in Org-mode, and then interactively +Org mode to the values they have in Org mode, and then interactively call CMD." (org-load-modules-maybe) (unless org-local-vars @@ -9287,67 +9644,119 @@ call CMD." (eval `(let ,binds (call-interactively (quote ,cmd)))))) -;;;; Archiving - (defun org-get-category (&optional pos force-refresh) "Get the category applying to position POS." (save-match-data - (if force-refresh (org-refresh-category-properties)) + (when force-refresh (org-refresh-category-properties)) (let ((pos (or pos (point)))) (or (get-text-property pos 'org-category) (progn (org-refresh-category-properties) (get-text-property pos 'org-category)))))) -(defun org-refresh-category-properties () - "Refresh category text properties in the buffer." - (let ((case-fold-search t) - (inhibit-read-only t) - (def-cat (cond - ((null org-category) - (if buffer-file-name - (file-name-sans-extension - (file-name-nondirectory buffer-file-name)) - "???")) - ((symbolp org-category) (symbol-name org-category)) - (t org-category))) - beg end cat pos optionp) - (org-with-silent-modifications - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (put-text-property (point) (point-max) 'org-category def-cat) - (while (re-search-forward - "^\\(#\\+CATEGORY:\\|[ \t]*:CATEGORY:\\)\\(.*\\)" nil t) - (setq pos (match-end 0) - optionp (equal (char-after (match-beginning 0)) ?#) - cat (org-trim (match-string 2))) - (if optionp - (setq beg (point-at-bol) end (point-max)) - (org-back-to-heading t) - (setq beg (point) end (org-end-of-subtree t t))) - (put-text-property beg end 'org-category cat) - (put-text-property beg end 'org-category-position beg) - (goto-char pos))))))) +;;; Refresh properties (defun org-refresh-properties (dprop tprop) "Refresh buffer text properties. -DPROP is the drawer property and TPROP is the corresponding text -property to set." - (let ((case-fold-search t) - (inhibit-read-only t) p) +DPROP is the drawer property and TPROP is either the +corresponding text property to set, or an alist with each element +being a text property (as a symbol) and a function to apply to +the value of the drawer property." + (let* ((case-fold-search t) + (inhibit-read-only t) + (inherit? (org-property-inherit-p dprop)) + (property-re (org-re-property (concat (regexp-quote dprop) "\\+?") t)) + (global (and inherit? (org--property-global-value dprop nil)))) (org-with-silent-modifications - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while (re-search-forward (concat "^[ \t]*:" dprop ": +\\(.*\\)[ \t]*$") nil t) - (setq p (org-match-string-no-properties 1)) - (save-excursion - (org-back-to-heading t) - (put-text-property - (point-at-bol) (or (outline-next-heading) (point-max)) tprop p)))))))) + (org-with-point-at 1 + ;; Set global values (e.g., values defined through + ;; "#+PROPERTY:" keywords) to the whole buffer. + (when global (put-text-property (point-min) (point-max) tprop global)) + ;; Set local values. + (while (re-search-forward property-re nil t) + (when (org-at-property-p) + (org-refresh-property tprop (org-entry-get (point) dprop) inherit?)) + (outline-next-heading)))))) + +(defun org-refresh-property (tprop p &optional inherit) + "Refresh the buffer text property TPROP from the drawer property P. +The refresh happens only for the current headline, or the whole +sub-tree if optional argument INHERIT is non-nil." + (unless (org-before-first-heading-p) + (save-excursion + (org-back-to-heading t) + (let ((start (point)) + (end (save-excursion + (if inherit (org-end-of-subtree t t) + (or (outline-next-heading) (point-max)))))) + (if (symbolp tprop) + ;; TPROP is a text property symbol. + (put-text-property start end tprop p) + ;; TPROP is an alist with (property . function) elements. + (pcase-dolist (`(,prop . ,f) tprop) + (put-text-property start end prop (funcall f p)))))))) +(defun org-refresh-category-properties () + "Refresh category text properties in the buffer." + (let ((case-fold-search t) + (inhibit-read-only t) + (default-category + (cond ((null org-category) + (if buffer-file-name + (file-name-sans-extension + (file-name-nondirectory buffer-file-name)) + "???")) + ((symbolp org-category) (symbol-name org-category)) + (t org-category)))) + (org-with-silent-modifications + (org-with-wide-buffer + ;; Set buffer-wide category. Search last #+CATEGORY keyword. + ;; This is the default category for the buffer. If none is + ;; found, fall-back to `org-category' or buffer file name. + (put-text-property + (point-min) (point-max) + 'org-category + (catch 'buffer-category + (goto-char (point-max)) + (while (re-search-backward "^[ \t]*#\\+CATEGORY:" (point-min) t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'keyword) + (throw 'buffer-category + (org-element-property :value element))))) + default-category)) + ;; Set sub-tree specific categories. + (goto-char (point-min)) + (let ((regexp (org-re-property "CATEGORY"))) + (while (re-search-forward regexp nil t) + (let ((value (match-string-no-properties 3))) + (when (org-at-property-p) + (put-text-property + (save-excursion (org-back-to-heading t) (point)) + (save-excursion (org-end-of-subtree t t) (point)) + 'org-category + value))))))))) + +(defun org-refresh-stats-properties () + "Refresh stats text properties in the buffer." + (org-with-silent-modifications + (org-with-point-at 1 + (let ((regexp (concat org-outline-regexp-bol + ".*\\[\\([0-9]*\\)\\(?:%\\|/\\([0-9]*\\)\\)\\]"))) + (while (re-search-forward regexp nil t) + (let* ((numerator (string-to-number (match-string 1))) + (denominator (and (match-end 2) + (string-to-number (match-string 2)))) + (stats (cond ((not denominator) numerator) ;percent + ((= denominator 0) 0) + (t (/ (* numerator 100) denominator))))) + (put-text-property (point) (progn (org-end-of-subtree t t) (point)) + 'org-stats stats))))))) + +(defun org-refresh-effort-properties () + "Refresh effort properties" + (org-refresh-properties + org-effort-property + '((effort . identity) + (effort-minutes . org-duration-string-to-minutes)))) ;;;; Link Stuff @@ -9387,78 +9796,54 @@ property to set." (defvar org-store-link-plist nil "Plist with info about the most recently link created with `org-store-link'.") -(defvar org-link-protocols nil - "Link protocols added to Org-mode using `org-add-link-type'.") +(defun org-store-link-functions () + "Return a list of functions that are called to create and store a link. +The functions defined in the :store property of +`org-link-parameters'. -(defvar org-store-link-functions nil - "List of functions that are called to create and store a link. Each function will be called in turn until one returns a non-nil -value. Each function should check if it is responsible for creating -this link (for example by looking at the major mode). -If not, it must exit and return nil. -If yes, it should return a non-nil value after a calling -`org-store-link-props' with a list of properties and values. -Special properties are: +value. Each function should check if it is responsible for +creating this link (for example by looking at the major mode). +If not, it must exit and return nil. If yes, it should return +a non-nil value after calling `org-store-link-props' with a list +of properties and values. Special properties are: :type The link prefix, like \"http\". This must be given. :link The link, like \"http://www.astro.uva.nl/~dominik\". This is obligatory as well. :description Optional default description for the second pair - of brackets in an Org-mode link. The user can still change - this when inserting this link into an Org-mode buffer. + of brackets in an Org mode link. The user can still change + this when inserting this link into an Org mode buffer. In addition to these, any additional properties can be specified -and then used in capture templates.") - -(defun org-add-link-type (type &optional follow export) - "Add TYPE to the list of `org-link-types'. -Re-compute all regular expressions depending on `org-link-types' - -FOLLOW and EXPORT are two functions. - -FOLLOW should take the link path as the single argument and do whatever -is necessary to follow the link, for example find a file or display -a mail message. - -EXPORT should format the link path for export to one of the export formats. -It should be a function accepting three arguments: - - path the path of the link, the text after the prefix (like \"http:\") - desc the description of the link, if any, or a description added by - org-export-normalize-links if there is none - format the export format, a symbol like `html' or `latex' or `ascii'.. - -The function may use the FORMAT information to return different values -depending on the format. The return value will be put literally into -the exported file. If the return value is nil, this means Org should -do what it normally does with links which do not have EXPORT defined. - -Org-mode has a built-in default for exporting links. If you are happy with -this default, there is no need to define an export function for the link -type. For a simple example of an export function, see `org-bbdb.el'." - (add-to-list 'org-link-types type t) - (org-make-link-regexps) - (if (assoc type org-link-protocols) - (setcdr (assoc type org-link-protocols) (list follow export)) - (push (list type follow export) org-link-protocols))) +and then used in capture templates." + (cl-loop for link in org-link-parameters + with store-func + do (setq store-func (org-link-get-parameter (car link) :store)) + if store-func + collect store-func)) (defvar org-agenda-buffer-name) ; Defined in org-agenda.el (defvar org-id-link-to-org-use-id) ; Defined in org-id.el ;;;###autoload (defun org-store-link (arg) - "\\Store an org-link to the current location. + "Store an org-link to the current location. +\\ This link is added to `org-stored-links' and can later be inserted -into an org-buffer with \\[org-insert-link]. +into an Org buffer with `org-insert-link' (`\\[org-insert-link]'). -For some link types, a prefix arg is interpreted. -For links to Usenet articles, arg negates `org-gnus-prefer-web-links'. -For file links, arg negates `org-context-in-file-links'. +For some link types, a `\\[universal-argument]' prefix ARG is interpreted. \ +A single +`\\[universal-argument]' negates `org-context-in-file-links' for file links or +`org-gnus-prefer-web-links' for links to Usenet articles. -A double prefix arg force skipping storing functions that are not -part of Org's core. +A `\\[universal-argument] \\[universal-argument]' prefix ARG forces \ +skipping storing functions that are not +part of Org core. -A triple prefix arg force storing a link for each line in the +A `\\[universal-argument] \\[universal-argument] \\[universal-argument]' \ +prefix ARG forces storing a link for each line in the active region." (interactive "P") (org-load-modules-maybe) @@ -9473,111 +9858,111 @@ active region." (call-interactively 'org-store-link)) (move-beginning-of-line 2) (set-mark (point))))) - (org-with-limited-levels - (setq org-store-link-plist nil) - (let (link cpltxt desc description search - txt custom-id agenda-link sfuns sfunsn) - (cond + (setq org-store-link-plist nil) + (let (link cpltxt desc description search + txt custom-id agenda-link sfuns sfunsn) + (cond - ;; Store a link using an external link type - ((and (not (equal arg '(16))) - (setq sfuns - (delq - nil (mapcar (lambda (f) - (let (fs) (if (funcall f) (push f fs)))) - org-store-link-functions)) - sfunsn (mapcar (lambda (fu) (symbol-name (car fu))) sfuns)) - (or (and (cdr sfuns) - (funcall (intern - (completing-read - "Which function for creating the link? " - sfunsn nil t (car sfunsn))))) - (funcall (caar sfuns))) - (setq link (plist-get org-store-link-plist :link) - desc (or (plist-get org-store-link-plist - :description) - link)))) - - ;; Store a link from a source code buffer - ((org-src-edit-buffer-p) - (let (label gc) - (while (or (not label) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (re-search-forward - (regexp-quote (format org-coderef-label-format label)) - nil t)))) - (when label (message "Label exists already") (sit-for 2)) - (setq label (read-string "Code line label: " label))) - (end-of-line 1) - (setq link (format org-coderef-label-format label)) - (setq gc (- 79 (length link))) - (if (< (current-column) gc) (org-move-to-column gc t) (insert " ")) - (insert link) - (setq link (concat "(" label ")") desc nil))) - - ;; We are in the agenda, link to referenced location - ((equal (org-bound-and-true-p org-agenda-buffer-name) (buffer-name)) - (let ((m (or (get-text-property (point) 'org-hd-marker) - (get-text-property (point) 'org-marker)))) - (when m - (org-with-point-at m - (setq agenda-link - (if (org-called-interactively-p 'any) - (call-interactively 'org-store-link) - (org-store-link nil))))))) - - ((eq major-mode 'calendar-mode) - (let ((cd (calendar-cursor-to-date))) - (setq link - (format-time-string - (car org-time-stamp-formats) - (apply 'encode-time - (list 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd) - nil nil nil)))) - (org-store-link-props :type "calendar" :date cd))) - - ((eq major-mode 'help-mode) - (setq link (concat "help:" (save-excursion - (goto-char (point-min)) - (looking-at "^[^ ]+") - (match-string 0)))) - (org-store-link-props :type "help")) - - ((eq major-mode 'w3-mode) - (setq cpltxt (if (and (buffer-name) - (not (string-match "Untitled" (buffer-name)))) - (buffer-name) - (url-view-url t)) - link (url-view-url t)) - (org-store-link-props :type "w3" :url (url-view-url t))) - - ((eq major-mode 'image-mode) - (setq cpltxt (concat "file:" - (abbreviate-file-name buffer-file-name)) - link cpltxt) - (org-store-link-props :type "image" :file buffer-file-name)) - - ;; In dired, store a link to the file of the current line - ((eq major-mode 'dired-mode) - (let ((file (dired-get-filename nil t))) - (setq file (if file - (abbreviate-file-name - (expand-file-name (dired-get-filename nil t))) - ;; otherwise, no file so use current directory. - default-directory)) - (setq cpltxt (concat "file:" file) - link cpltxt))) - - ((setq search (run-hook-with-args-until-success - 'org-create-file-search-functions)) - (setq link (concat "file:" (abbreviate-file-name buffer-file-name) - "::" search)) - (setq cpltxt (or description link))) - - ((and (buffer-file-name (buffer-base-buffer)) (derived-mode-p 'org-mode)) + ;; Store a link using an external link type + ((and (not (equal arg '(16))) + (setq sfuns + (delq + nil (mapcar (lambda (f) + (let (fs) (if (funcall f) (push f fs)))) + (org-store-link-functions))) + sfunsn (mapcar (lambda (fu) (symbol-name (car fu))) sfuns)) + (or (and (cdr sfuns) + (funcall (intern + (completing-read + "Which function for creating the link? " + sfunsn nil t (car sfunsn))))) + (funcall (caar sfuns))) + (setq link (plist-get org-store-link-plist :link) + desc (or (plist-get org-store-link-plist + :description) + link)))) + + ;; Store a link from a source code buffer. + ((org-src-edit-buffer-p) + (let ((coderef-format (org-src-coderef-format))) + (cond ((save-excursion + (beginning-of-line) + (looking-at (org-src-coderef-regexp coderef-format))) + (setq link (format "(%s)" (match-string-no-properties 3)))) + ((called-interactively-p 'any) + (let ((label (read-string "Code line label: "))) + (end-of-line) + (setq link (format coderef-format label)) + (let ((gc (- 79 (length link)))) + (if (< (current-column) gc) + (org-move-to-column gc t) + (insert " "))) + (insert link) + (setq link (concat "(" label ")")) + (setq desc nil))) + (t (setq link nil))))) + + ;; We are in the agenda, link to referenced location + ((equal (bound-and-true-p org-agenda-buffer-name) (buffer-name)) + (let ((m (or (get-text-property (point) 'org-hd-marker) + (get-text-property (point) 'org-marker)))) + (when m + (org-with-point-at m + (setq agenda-link + (if (called-interactively-p 'any) + (call-interactively 'org-store-link) + (org-store-link nil))))))) + + ((eq major-mode 'calendar-mode) + (let ((cd (calendar-cursor-to-date))) + (setq link + (format-time-string + (car org-time-stamp-formats) + (apply 'encode-time + (list 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd) + nil nil nil)))) + (org-store-link-props :type "calendar" :date cd))) + + ((eq major-mode 'help-mode) + (setq link (concat "help:" (save-excursion + (goto-char (point-min)) + (looking-at "^[^ ]+") + (match-string 0)))) + (org-store-link-props :type "help")) + + ((eq major-mode 'w3-mode) + (setq cpltxt (if (and (buffer-name) + (not (string-match "Untitled" (buffer-name)))) + (buffer-name) + (url-view-url t)) + link (url-view-url t)) + (org-store-link-props :type "w3" :url (url-view-url t))) + + ((eq major-mode 'image-mode) + (setq cpltxt (concat "file:" + (abbreviate-file-name buffer-file-name)) + link cpltxt) + (org-store-link-props :type "image" :file buffer-file-name)) + + ;; In dired, store a link to the file of the current line + ((derived-mode-p 'dired-mode) + (let ((file (dired-get-filename nil t))) + (setq file (if file + (abbreviate-file-name + (expand-file-name (dired-get-filename nil t))) + ;; otherwise, no file so use current directory. + default-directory)) + (setq cpltxt (concat "file:" file) + link cpltxt))) + + ((setq search (run-hook-with-args-until-success + 'org-create-file-search-functions)) + (setq link (concat "file:" (abbreviate-file-name buffer-file-name) + "::" search)) + (setq cpltxt (or description link))) + + ((and (buffer-file-name (buffer-base-buffer)) (derived-mode-p 'org-mode)) + (org-with-limited-levels (setq custom-id (org-entry-get nil "CUSTOM_ID")) (cond ;; Store a link using the target at point @@ -9590,7 +9975,7 @@ active region." link cpltxt)) ((and (featurep 'org-id) (or (eq org-id-link-to-org-use-id t) - (and (org-called-interactively-p 'any) + (and (called-interactively-p 'any) (or (eq org-id-link-to-org-use-id 'create-if-interactive) (and (eq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id) @@ -9613,15 +9998,13 @@ active region." (abbreviate-file-name (buffer-file-name (buffer-base-buffer))))) ;; Add a context search string - (when (org-xor org-context-in-file-links arg) - (let* ((ee (org-element-at-point)) - (et (org-element-type ee)) - (ev (plist-get (cadr ee) :value)) - (ek (plist-get (cadr ee) :key)) - (eok (and (stringp ek) (string-match "name" ek)))) + (when (org-xor org-context-in-file-links + (equal arg '(4))) + (let* ((element (org-element-at-point)) + (name (org-element-property :name element))) (setq txt (cond ((org-at-heading-p) nil) - ((and (eq et 'keyword) eok) ev) + (name) ((org-region-active-p) (buffer-substring (region-beginning) (region-end))))) (when (or (null txt) (string-match "\\S-" txt)) @@ -9630,74 +10013,80 @@ active region." (condition-case nil (org-make-org-heading-search-string txt) (error ""))) - desc (or (and (eq et 'keyword) eok ev) + desc (or name (nth 4 (ignore-errors (org-heading-components))) "NONE"))))) - (if (string-match "::\\'" cpltxt) - (setq cpltxt (substring cpltxt 0 -2))) - (setq link cpltxt)))) - - ((buffer-file-name (buffer-base-buffer)) - ;; Just link to this file here. - (setq cpltxt (concat "file:" - (abbreviate-file-name - (buffer-file-name (buffer-base-buffer))))) - ;; Add a context string. - (when (org-xor org-context-in-file-links arg) - (setq txt (if (org-region-active-p) - (buffer-substring (region-beginning) (region-end)) - (buffer-substring (point-at-bol) (point-at-eol)))) - ;; Only use search option if there is some text. - (when (string-match "\\S-" txt) - (setq cpltxt - (concat cpltxt "::" (org-make-org-heading-search-string txt)) - desc "NONE"))) - (setq link cpltxt)) - - ((org-called-interactively-p 'interactive) - (user-error "No method for storing a link from this buffer")) - - (t (setq link nil))) - - ;; We're done setting link and desc, clean up - (if (consp link) (setq cpltxt (car link) link (cdr link))) - (setq link (or link cpltxt) - desc (or desc cpltxt)) - (cond ((equal desc "NONE") (setq desc nil)) - ((and desc (string-match org-bracket-link-analytic-regexp desc)) - (let ((d0 (match-string 3 desc)) - (p0 (match-string 5 desc))) - (setq desc - (replace-regexp-in-string - org-bracket-link-regexp - (concat (or p0 d0) - (if (equal (length (match-string 0 desc)) - (length desc)) "*" "")) desc))))) - - ;; Return the link - (if (not (and (or (org-called-interactively-p 'any) - executing-kbd-macro) - link)) - (or agenda-link (and link (org-make-link-string link desc))) - (push (list link desc) org-stored-links) - (message "Stored: %s" (or desc link)) - (when custom-id - (setq link (concat "file:" (abbreviate-file-name - (buffer-file-name)) "::#" custom-id)) - (push (list link desc) org-stored-links)) - (car org-stored-links)))))) + (when (string-match "::\\'" cpltxt) + (setq cpltxt (substring cpltxt 0 -2))) + (setq link cpltxt))))) + + ((buffer-file-name (buffer-base-buffer)) + ;; Just link to this file here. + (setq cpltxt (concat "file:" + (abbreviate-file-name + (buffer-file-name (buffer-base-buffer))))) + ;; Add a context string. + (when (org-xor org-context-in-file-links + (equal arg '(4))) + (setq txt (if (org-region-active-p) + (buffer-substring (region-beginning) (region-end)) + (buffer-substring (point-at-bol) (point-at-eol)))) + ;; Only use search option if there is some text. + (when (string-match "\\S-" txt) + (setq cpltxt + (concat cpltxt "::" (org-make-org-heading-search-string txt)) + desc "NONE"))) + (setq link cpltxt)) + + ((called-interactively-p 'interactive) + (user-error "No method for storing a link from this buffer")) + + (t (setq link nil))) + + ;; We're done setting link and desc, clean up + (when (consp link) (setq cpltxt (car link) link (cdr link))) + (setq link (or link cpltxt) + desc (or desc cpltxt)) + (cond ((not desc)) + ((equal desc "NONE") (setq desc nil)) + (t (setq desc + (replace-regexp-in-string + org-bracket-link-analytic-regexp + (lambda (m) (or (match-string 5 m) (match-string 3 m))) + desc)))) + ;; Return the link + (if (not (and (or (called-interactively-p 'any) + executing-kbd-macro) + link)) + (or agenda-link (and link (org-make-link-string link desc))) + (push (list link desc) org-stored-links) + (message "Stored: %s" (or desc link)) + (when custom-id + (setq link (concat "file:" (abbreviate-file-name + (buffer-file-name)) "::#" custom-id)) + (push (list link desc) org-stored-links)) + (car org-stored-links))))) (defun org-store-link-props (&rest plist) - "Store link properties, extract names and addresses." - (let (x adr) - (when (setq x (plist-get plist :from)) - (setq adr (mail-extract-address-components x)) - (setq plist (plist-put plist :fromname (car adr))) - (setq plist (plist-put plist :fromaddress (nth 1 adr)))) - (when (setq x (plist-get plist :to)) - (setq adr (mail-extract-address-components x)) - (setq plist (plist-put plist :toname (car adr))) - (setq plist (plist-put plist :toaddress (nth 1 adr))))) + "Store link properties, extract names, addresses and dates." + (let ((x (plist-get plist :from))) + (when x + (let ((adr (mail-extract-address-components x))) + (setq plist (plist-put plist :fromname (car adr))) + (setq plist (plist-put plist :fromaddress (nth 1 adr)))))) + (let ((x (plist-get plist :to))) + (when x + (let ((adr (mail-extract-address-components x))) + (setq plist (plist-put plist :toname (car adr))) + (setq plist (plist-put plist :toaddress (nth 1 adr)))))) + (let ((x (ignore-errors (date-to-time (plist-get plist :date))))) + (when x + (setq plist (plist-put plist :date-timestamp + (format-time-string + (org-time-stamp-format t) x))) + (setq plist (plist-put plist :date-timestamp-inactive + (format-time-string + (org-time-stamp-format t t) x))))) (let ((from (plist-get plist :from)) (to (plist-get plist :to))) (when (and from to org-from-is-user-regexp) @@ -9763,45 +10152,34 @@ according to FMT (default from `org-email-link-description-format')." (defun org-make-link-string (link &optional description) "Make a link with brackets, consisting of LINK and DESCRIPTION." - (unless (string-match "\\S-" link) - (error "Empty link")) - (when (and description - (stringp description) - (not (string-match "\\S-" description))) - (setq description nil)) - (when (stringp description) - ;; Remove brackets from the description, they are fatal. - (while (string-match "\\[" description) - (setq description (replace-match "{" t t description))) - (while (string-match "\\]" description) - (setq description (replace-match "}" t t description)))) - (when (equal link description) - ;; No description needed, it is identical - (setq description nil)) - (when (and (not description) - (not (string-match (org-image-file-name-regexp) link)) - (not (equal link (org-link-escape link)))) - (setq description (org-extract-attributes link))) - (setq link - (cond ((string-match (org-image-file-name-regexp) link) link) - ((string-match org-link-types-re link) - (concat (match-string 1 link) - (org-link-escape (substring link (match-end 1))))) - (t (org-link-escape link)))) - (concat "[[" link "]" - (if description (concat "[" description "]") "") - "]")) + (unless (org-string-nw-p link) (error "Empty link")) + (let ((uri (cond ((string-match org-link-types-re link) + (concat (match-string 1 link) + (org-link-escape (substring link (match-end 1))))) + ;; For readability, url-encode internal links only + ;; when absolutely needed (i.e, when they contain + ;; square brackets). File links however, are + ;; encoded since, e.g., spaces are significant. + ((or (file-name-absolute-p link) + (string-match-p "\\`\\.\\.?/\\|[][]" link)) + (org-link-escape link)) + (t link))) + (description + (and (org-string-nw-p description) + ;; Remove brackets from description, as they are fatal. + (replace-regexp-in-string + "[][]" (lambda (m) (if (equal "[" m) "{" "}")) + (org-trim description))))) + (format "[[%s]%s]" + uri + (if description (format "[%s]" description) "")))) (defconst org-link-escape-chars - '(?\ ?\[ ?\] ?\; ?\= ?\+) - "List of characters that should be escaped in link. + ;;%20 %5B %5D %25 + '(?\s ?\[ ?\] ?%) + "List of characters that should be escaped in a link when stored to Org. This is the list that is used for internal purposes.") -(defconst org-link-escape-chars-browser - '(?\ ?\") - "List of escapes for characters that are problematic in links. -This is the list that is used before handing over to the browser.") - (defun org-link-escape (text &optional table merge) "Return percent escaped representation of TEXT. TEXT is a string with the text to escape. @@ -9809,35 +10187,29 @@ Optional argument TABLE is a list with characters that should be escaped. When nil, `org-link-escape-chars' is used. If optional argument MERGE is set, merge TABLE into `org-link-escape-chars'." - (cond - ((and table merge) - (mapc (lambda (defchr) - (unless (member defchr table) - (setq table (cons defchr table)))) org-link-escape-chars)) - ((null table) - (setq table org-link-escape-chars))) - (mapconcat - (lambda (char) - (if (or (member char table) - (and (or (< char 32) (= char 37) (> char 126)) - org-url-hexify-p)) - (mapconcat (lambda (sequence-element) - (format "%%%.2X" sequence-element)) - (or (encode-coding-char char 'utf-8) - (error "Unable to percent escape character: %s" - (char-to-string char))) "") - (char-to-string char))) text "")) + (let ((characters-to-encode + (cond ((null table) org-link-escape-chars) + (merge (append org-link-escape-chars table)) + (t table)))) + (mapconcat + (lambda (c) + (if (or (memq c characters-to-encode) + (and org-url-hexify-p (or (< c 32) (> c 126)))) + (mapconcat (lambda (e) (format "%%%.2X" e)) + (or (encode-coding-char c 'utf-8) + (error "Unable to percent escape character: %c" c)) + "") + (char-to-string c))) + text ""))) (defun org-link-unescape (str) - "Unhex hexified Unicode strings as returned from the JavaScript function -encodeURIComponent. E.g. `%C3%B6' is the german o-Umlaut." - (unless (and (null str) (string= "" str)) - (let ((pos 0) (case-fold-search t) unhexed) - (while (setq pos (string-match "\\(%[0-9a-f][0-9a-f]\\)+" str pos)) - (setq unhexed (org-link-unescape-compound (match-string 0 str))) - (setq str (replace-match unhexed t t str)) - (setq pos (+ pos (length unhexed)))))) - str) + "Unhex hexified Unicode parts in string STR. +E.g. `%C3%B6' becomes the german o-Umlaut. This is the +reciprocal of `org-link-escape', which see." + (if (org-string-nw-p str) + (replace-regexp-in-string + "\\(%[0-9A-Za-z]\\{2\\}\\)+" #'org-link-unescape-compound str t t) + str)) (defun org-link-unescape-compound (hex) "Unhexify Unicode hex-chars. E.g. `%C3%B6' is the German o-Umlaut. @@ -9860,18 +10232,17 @@ Note: this function also decodes single byte encodings like ((>= val 192) (cons 2 192)) (t (cons 0 0))) (cons 6 128)))) - (if (>= val 192) (setq eat (car shift-xor))) + (when (>= val 192) (setq eat (car shift-xor))) (setq val (logxor val (cdr shift-xor))) (setq sum (+ (lsh sum (car shift-xor)) val)) - (if (> eat 0) (setq eat (- eat 1))) + (when (> eat 0) (setq eat (- eat 1))) (cond ((= 0 eat) ;multi byte - (setq ret (concat ret (org-char-to-string sum))) + (setq ret (concat ret (char-to-string sum))) (setq sum 0)) ((not bytes) ; single byte(s) - (setq ret (org-link-unescape-single-byte-sequence hex)))) - )) ;; end (while bytes - ret ))) + (setq ret (org-link-unescape-single-byte-sequence hex)))))) + ret))) (defun org-link-unescape-single-byte-sequence (hex) "Unhexify hex-encoded single byte character sequences." @@ -9901,28 +10272,47 @@ Note: this function also decodes single byte encodings like (defun org-link-prettify (link) "Return a human-readable representation of LINK. -The car of LINK must be a raw link the cdr of LINK must be either -a link description or nil." +The car of LINK must be a raw link. +The cdr of LINK must be either a link description or nil." (let ((desc (or (cadr link) ""))) (concat (format "%-45s" (substring desc 0 (min (length desc) 40))) "<" (car link) ">"))) ;;;###autoload (defun org-insert-link-global () - "Insert a link like Org-mode does. -This command can be called in any mode to insert a link in Org-mode syntax." + "Insert a link like Org mode does. +This command can be called in any mode to insert a link in Org syntax." (interactive) (org-load-modules-maybe) (org-run-like-in-org-mode 'org-insert-link)) -(defun org-insert-all-links (&optional keep) - "Insert all links in `org-stored-links'." +(defun org-insert-all-links (arg &optional pre post) + "Insert all links in `org-stored-links'. +When a universal prefix, do not delete the links from `org-stored-links'. +When `ARG' is a number, insert the last N link(s). +`PRE' and `POST' are optional arguments to define a string to +prepend or to append." (interactive "P") - (let ((links (copy-sequence org-stored-links)) l) - (while (setq l (if keep (pop links) (pop org-stored-links))) - (insert "- ") - (org-insert-link nil (car l) (or (cadr l) "")) - (insert "\n")))) + (let ((org-keep-stored-link-after-insertion (equal arg '(4))) + (links (copy-sequence org-stored-links)) + (pr (or pre "- ")) + (po (or post "\n")) + (cnt 1) l) + (if (null org-stored-links) + (message "No link to insert") + (while (and (or (listp arg) (>= arg cnt)) + (setq l (if (listp arg) + (pop links) + (pop org-stored-links)))) + (setq cnt (1+ cnt)) + (insert pr) + (org-insert-link nil (car l) (or (cadr l) "")) + (insert po))))) + +(defun org-insert-last-stored-link (arg) + "Insert the last link stored in `org-stored-links'." + (interactive "p") + (org-insert-all-links arg "" "\n")) (defun org-link-fontify-links-to-this-file () "Fontify links to the current file in `org-stored-links'." @@ -9946,73 +10336,73 @@ This command can be called in any mode to insert a link in Org-mode syntax." (put-text-property 0 (length l) 'face 'font-lock-comment-face l)) (delq nil (append a b))))) -(defvar org-link-links-in-this-file nil) +(defvar org--links-history nil) (defun org-insert-link (&optional complete-file link-location default-description) "Insert a link. At the prompt, enter the link. -Completion can be used to insert any of the link protocol prefixes like -http or ftp in use. +Completion can be used to insert any of the link protocol prefixes in use. The history can be used to select a link previously stored with `org-store-link'. When the empty string is entered (i.e. if you just -press RET at the prompt), the link defaults to the most recently -stored link. As SPC triggers completion in the minibuffer, you need to -use M-SPC or C-q SPC to force the insertion of a space character. +press `RET' at the prompt), the link defaults to the most recently +stored link. As `SPC' triggers completion in the minibuffer, you need to +use `M-SPC' or `C-q SPC' to force the insertion of a space character. You will also be prompted for a description, and if one is given, it will be displayed in the buffer instead of the link. -If there is already a link at point, this command will allow you to edit link -and description parts. +If there is already a link at point, this command will allow you to edit +link and description parts. -With a \\[universal-argument] prefix, prompts for a file to link to. The file name can -be selected using completion. The path to the file will be relative to the +With a `\\[universal-argument]' prefix, prompts for a file to link to. The \ +file name can be +selected using completion. The path to the file will be relative to the current directory if the file is in the current directory or a subdirectory. Otherwise, the link will be the absolute path as completed in the minibuffer \(i.e. normally ~/path/to/file). You can configure this behavior using the option `org-link-file-path-type'. -With two \\[universal-argument] prefixes, enforce an absolute path even if the file is in +With a `\\[universal-argument] \\[universal-argument]' prefix, enforce an \ +absolute path even if the file is in the current directory or below. -With three \\[universal-argument] prefixes, negate the meaning of -`org-keep-stored-link-after-insertion'. +A `\\[universal-argument] \\[universal-argument] \\[universal-argument]' \ +prefix negates `org-keep-stored-link-after-insertion'. If `org-make-link-description-function' is non-nil, this function will be called with the link target, and the result will be the default link description. -If the LINK-LOCATION parameter is non-nil, this value will be -used as the link location instead of reading one interactively. +If the LINK-LOCATION parameter is non-nil, this value will be used as +the link location instead of reading one interactively. -If the DEFAULT-DESCRIPTION parameter is non-nil, this value will -be used as the default description." +If the DEFAULT-DESCRIPTION parameter is non-nil, this value will be used +as the default description." (interactive "P") (let* ((wcf (current-window-configuration)) (origbuf (current-buffer)) - (region (if (org-region-active-p) - (buffer-substring (region-beginning) (region-end)))) + (region (when (org-region-active-p) + (buffer-substring (region-beginning) (region-end)))) (remove (and region (list (region-beginning) (region-end)))) (desc region) - tmphist ; byte-compile incorrectly complains about this (link link-location) (abbrevs org-link-abbrev-alist-local) - entry file all-prefixes auto-desc) + entry all-prefixes auto-desc) (cond - (link-location) ; specified by arg, just use it. + (link-location) ; specified by arg, just use it. ((org-in-regexp org-bracket-link-regexp 1) ;; We do have a link at point, and we are going to edit it. (setq remove (list (match-beginning 0) (match-end 0))) - (setq desc (if (match-end 3) (org-match-string-no-properties 3))) + (setq desc (when (match-end 3) (match-string-no-properties 3))) (setq link (read-string "Link: " (org-link-unescape - (org-match-string-no-properties 1))))) + (match-string-no-properties 1))))) ((or (org-in-regexp org-angle-link-re) (org-in-regexp org-plain-link-re)) ;; Convert to bracket link (setq remove (list (match-beginning 0) (match-end 0)) link (read-string "Link: " - (org-remove-angle-brackets (match-string 0))))) + (org-unbracket-string "<" ">" (match-string 0))))) ((member complete-file '((4) (16))) ;; Completing read for file names. (setq link (org-file-complete-link complete-file))) @@ -10035,92 +10425,91 @@ Use TAB to complete link prefixes, then RET for type-specific completion support (unless (pos-visible-in-window-p (point-max)) (org-fit-window-to-buffer)) (and (window-live-p cw) (select-window cw))) - ;; Fake a link history, containing the stored links. - (setq tmphist (append (mapcar 'car org-stored-links) - org-insert-link-history)) (setq all-prefixes (append (mapcar 'car abbrevs) (mapcar 'car org-link-abbrev-alist) - org-link-types)) + (org-link-types))) (unwind-protect - (progn + ;; Fake a link history, containing the stored links. + (let ((org--links-history + (append (mapcar #'car org-stored-links) + org-insert-link-history))) (setq link (org-completing-read "Link: " (append - (mapcar (lambda (x) (concat x ":")) - all-prefixes) - (mapcar 'car org-stored-links)) + (mapcar (lambda (x) (concat x ":")) all-prefixes) + (mapcar #'car org-stored-links)) nil nil nil - 'tmphist + 'org--links-history (caar org-stored-links))) - (if (not (string-match "\\S-" link)) - (user-error "No link selected")) - (mapc (lambda(l) - (when (equal link (cadr l)) (setq link (car l) auto-desc t))) - org-stored-links) - (if (or (member link all-prefixes) - (and (equal ":" (substring link -1)) - (member (substring link 0 -1) all-prefixes) - (setq link (substring link 0 -1)))) - (setq link (with-current-buffer origbuf - (org-link-try-special-completion link))))) + (unless (org-string-nw-p link) (user-error "No link selected")) + (dolist (l org-stored-links) + (when (equal link (cadr l)) + (setq link (car l)) + (setq auto-desc t))) + (when (or (member link all-prefixes) + (and (equal ":" (substring link -1)) + (member (substring link 0 -1) all-prefixes) + (setq link (substring link 0 -1)))) + (setq link (with-current-buffer origbuf + (org-link-try-special-completion link))))) (set-window-configuration wcf) (kill-buffer "*Org Links*")) (setq entry (assoc link org-stored-links)) (or entry (push link org-insert-link-history)) (setq desc (or desc (nth 1 entry))))) - (if (funcall (if (equal complete-file '(64)) 'not 'identity) - (not org-keep-stored-link-after-insertion)) - (setq org-stored-links (delq (assoc link org-stored-links) - org-stored-links))) + (when (funcall (if (equal complete-file '(64)) 'not 'identity) + (not org-keep-stored-link-after-insertion)) + (setq org-stored-links (delq (assoc link org-stored-links) + org-stored-links))) - (if (and (string-match org-plain-link-re link) - (not (string-match org-ts-regexp link))) - ;; URL-like link, normalize the use of angular brackets. - (setq link (org-remove-angle-brackets link))) + (when (and (string-match org-plain-link-re link) + (not (string-match org-ts-regexp link))) + ;; URL-like link, normalize the use of angular brackets. + (setq link (org-unbracket-string "<" ">" link))) ;; Check if we are linking to the current file with a search ;; option If yes, simplify the link by using only the search ;; option. (when (and buffer-file-name - (string-match "^file:\\(.+?\\)::\\(.+\\)" link)) - (let* ((path (match-string 1 link)) - (case-fold-search nil) - (search (match-string 2 link))) + (let ((case-fold-search nil)) + (string-match "\\`file:\\(.+?\\)::" link))) + (let ((path (match-string-no-properties 1 link)) + (search (substring-no-properties link (match-end 0)))) (save-match-data - (if (equal (file-truename buffer-file-name) (file-truename path)) - ;; We are linking to this same file, with a search option - (setq link search))))) + (when (equal (file-truename buffer-file-name) (file-truename path)) + ;; We are linking to this same file, with a search option + (setq link search))))) ;; Check if we can/should use a relative path. If yes, simplify the link - (when (string-match "^\\(file:\\|docview:\\)\\(.*\\)" link) - (let* ((type (match-string 1 link)) - (path (match-string 2 link)) - (origpath path) - (case-fold-search nil)) - (cond - ((or (eq org-link-file-path-type 'absolute) - (equal complete-file '(16))) - (setq path (abbreviate-file-name (expand-file-name path)))) - ((eq org-link-file-path-type 'noabbrev) - (setq path (expand-file-name path))) - ((eq org-link-file-path-type 'relative) - (setq path (file-relative-name path))) - (t - (save-match-data - (if (string-match (concat "^" (regexp-quote - (expand-file-name - (file-name-as-directory - default-directory)))) - (expand-file-name path)) - ;; We are linking a file with relative path name. - (setq path (substring (expand-file-name path) - (match-end 0))) - (setq path (abbreviate-file-name (expand-file-name path))))))) - (setq link (concat type path)) - (if (equal desc origpath) - (setq desc path)))) + (let ((case-fold-search nil)) + (when (string-match "\\`\\(file\\|docview\\):" link) + (let* ((type (match-string-no-properties 0 link)) + (path (substring-no-properties link (match-end 0))) + (origpath path)) + (cond + ((or (eq org-link-file-path-type 'absolute) + (equal complete-file '(16))) + (setq path (abbreviate-file-name (expand-file-name path)))) + ((eq org-link-file-path-type 'noabbrev) + (setq path (expand-file-name path))) + ((eq org-link-file-path-type 'relative) + (setq path (file-relative-name path))) + (t + (save-match-data + (if (string-match (concat "^" (regexp-quote + (expand-file-name + (file-name-as-directory + default-directory)))) + (expand-file-name path)) + ;; We are linking a file with relative path name. + (setq path (substring (expand-file-name path) + (match-end 0))) + (setq path (abbreviate-file-name (expand-file-name path))))))) + (setq link (concat type path)) + (when (equal desc origpath) + (setq desc path))))) (if org-make-link-description-function (setq desc @@ -10135,49 +10524,36 @@ Use TAB to complete link prefixes, then RET for type-specific completion support (read-string "Description: " desc))))) (unless (string-match "\\S-" desc) (setq desc nil)) - (if remove (apply 'delete-region remove)) - (insert (org-make-link-string link desc)))) + (when remove (apply 'delete-region remove)) + (insert (org-make-link-string link desc)) + ;; Redisplay so as the new link has proper invisible characters. + (sit-for 0))) (defun org-link-try-special-completion (type) "If there is completion support for link type TYPE, offer it." - (let ((fun (intern (concat "org-" type "-complete-link")))) + (let ((fun (org-link-get-parameter type :complete))) (if (functionp fun) (funcall fun) (read-string "Link (no completion support): " (concat type ":"))))) (defun org-file-complete-link (&optional arg) "Create a file link using completion." - (let (file link) - (setq file (org-iread-file-name "File: ")) - (let ((pwd (file-name-as-directory (expand-file-name "."))) - (pwd1 (file-name-as-directory (abbreviate-file-name - (expand-file-name "."))))) - (cond - ((equal arg '(16)) - (setq link (concat - "file:" - (abbreviate-file-name (expand-file-name file))))) - ((string-match (concat "^" (regexp-quote pwd1) "\\(.+\\)") file) - (setq link (concat "file:" (match-string 1 file)))) - ((string-match (concat "^" (regexp-quote pwd) "\\(.+\\)") - (expand-file-name file)) - (setq link (concat - "file:" (match-string 1 (expand-file-name file))))) - (t (setq link (concat "file:" file))))) - link)) - -(defun org-iread-file-name (&rest args) - "Read-file-name using `ido-mode' speedup if available. -ARGS are arguments that may be passed to `ido-read-file-name' or `read-file-name'. -See `read-file-name' for a description of parameters." - (org-without-partial-completion - (if (and org-completion-use-ido - (fboundp 'ido-read-file-name) - (boundp 'ido-mode) ido-mode - (listp (second args))) - (let ((ido-enter-matching-directory nil)) - (apply 'ido-read-file-name args)) - (apply 'read-file-name args)))) + (let ((file (read-file-name "File: ")) + (pwd (file-name-as-directory (expand-file-name "."))) + (pwd1 (file-name-as-directory (abbreviate-file-name + (expand-file-name "."))))) + (cond ((equal arg '(16)) + (concat "file:" + (abbreviate-file-name (expand-file-name file)))) + ((string-match + (concat "^" (regexp-quote pwd1) "\\(.+\\)") file) + (concat "file:" (match-string 1 file))) + ((string-match + (concat "^" (regexp-quote pwd) "\\(.+\\)") + (expand-file-name file)) + (concat "file:" + (match-string 1 (expand-file-name file)))) + (t (concat "file:" file))))) (defun org-completing-read (&rest args) "Completing-read with SPACE being a normal character." @@ -10186,58 +10562,9 @@ See `read-file-name' for a description of parameters." (copy-keymap minibuffer-local-completion-map))) (org-defkey minibuffer-local-completion-map " " 'self-insert-command) (org-defkey minibuffer-local-completion-map "?" 'self-insert-command) - (org-defkey minibuffer-local-completion-map (kbd "C-c !") 'org-time-stamp-inactive) - (apply 'org-icompleting-read args))) - -(defun org-completing-read-no-i (&rest args) - (let (org-completion-use-ido org-completion-use-iswitchb) - (apply 'org-completing-read args))) - -(defun org-iswitchb-completing-read (prompt choices &rest args) - "Use iswitch as a completing-read replacement to choose from choices. -PROMPT is a string to prompt with. CHOICES is a list of strings to choose -from." - (let* ((iswitchb-use-virtual-buffers nil) - (iswitchb-make-buflist-hook - (lambda () - (setq iswitchb-temp-buflist choices)))) - (iswitchb-read-buffer prompt))) - -(defun org-icompleting-read (&rest args) - "Completing-read using `ido-mode' or `iswitchb' speedups if available." - (org-without-partial-completion - (if (and org-completion-use-ido - (fboundp 'ido-completing-read) - (boundp 'ido-mode) ido-mode - (listp (second args))) - (let ((ido-enter-matching-directory nil)) - (apply 'ido-completing-read (concat (car args)) - (if (consp (car (nth 1 args))) - (mapcar 'car (nth 1 args)) - (nth 1 args)) - (cddr args))) - (if (and org-completion-use-iswitchb - (boundp 'iswitchb-mode) iswitchb-mode - (listp (second args))) - (apply 'org-iswitchb-completing-read (concat (car args)) - (if (consp (car (nth 1 args))) - (mapcar 'car (nth 1 args)) - (nth 1 args)) - (cddr args)) - (apply 'completing-read args))))) - -(defun org-extract-attributes (s) - "Extract the attributes cookie from a string and set as text property." - (let (a attr (start 0) key value) - (save-match-data - (when (string-match "{{\\([^}]+\\)}}$" s) - (setq a (match-string 1 s) s (substring s 0 (match-beginning 0))) - (while (string-match "\\([a-zA-Z]+\\)=\"\\([^\"]*\\)\"" a start) - (setq key (match-string 1 a) value (match-string 2 a) - start (match-end 0) - attr (plist-put attr (intern key) value)))) - (org-add-props s nil 'org-attr attr)) - s)) + (org-defkey minibuffer-local-completion-map (kbd "C-c !") + 'org-time-stamp-inactive) + (apply #'completing-read args))) ;;; Opening/following a link @@ -10257,8 +10584,8 @@ handle this as a special case. When the function does handle the link, it must return a non-nil value. If it decides that it is not responsible for this link, it must return -nil to indicate that that Org-mode can continue with other options -like exact and fuzzy text search.") +nil to indicate that that Org can continue with other options like +exact and fuzzy text search.") (defun org-next-link (&optional search-backward) "Move forward to the next link. @@ -10270,7 +10597,7 @@ If the link is in hidden text, expose it." (setq org-link-search-failed nil) (let* ((pos (point)) (ct (org-context)) - (a (assoc :link ct)) + (a (assq :link ct)) (srch-fun (if search-backward 're-search-backward 're-search-forward))) (cond (a (goto-char (nth (if search-backward 1 2) a))) ((looking-at org-any-link-re) @@ -10279,7 +10606,7 @@ If the link is in hidden text, expose it." (if (funcall srch-fun org-any-link-re nil t) (progn (goto-char (match-beginning 0)) - (if (outline-invisible-p) (org-show-context))) + (when (org-invisible-p) (org-show-context))) (goto-char pos) (setq org-link-search-failed t) (message "No further link found")))) @@ -10292,14 +10619,9 @@ If the link is in hidden text, expose it." (defun org-translate-link (s) "Translate a link string if a translation function has been defined." - (if (and org-link-translation-function - (fboundp org-link-translation-function) - (string-match "\\([a-zA-Z0-9]+\\):\\(.*\\)" s)) - (progn - (setq s (funcall org-link-translation-function - (match-string 1 s) (match-string 2 s))) - (concat (car s) ":" (cdr s))) - s)) + (with-temp-buffer + (insert (org-trim s)) + (org-trim (org-element-interpret-data (org-element-context))))) (defun org-translate-link-from-planner (type path) "Translate a link from Emacs Planner syntax so that Org can follow it. @@ -10319,7 +10641,7 @@ This is still an experimental function, your mileage may vary." ;; A typical message link. Planner has the id after the final slash, ;; we separate it with a hash mark (setq path (concat (match-string 1 path) "#" - (org-remove-angle-brackets (match-string 2 path)))))) + (org-unbracket-string "<" ">" (match-string 2 path)))))) (cons type path)) (defun org-find-file-at-mouse (ev) @@ -10333,28 +10655,32 @@ This is still an experimental function, your mileage may vary." See the docstring of `org-open-file' for details." (interactive "e") (mouse-set-point ev) - (if (eq major-mode 'org-agenda-mode) - (org-agenda-copy-local-variable 'org-link-abbrev-alist-local)) + (when (eq major-mode 'org-agenda-mode) + (org-agenda-copy-local-variable 'org-link-abbrev-alist-local)) (org-open-at-point)) (defvar org-window-config-before-follow-link nil "The window configuration before following a link. This is saved in case the need arises to restore it.") -(defvar org-open-link-marker (make-marker) - "Marker pointing to the location where `org-open-at-point' was called.") - ;;;###autoload (defun org-open-at-point-global () - "Follow a link like Org-mode does. -This command can be called in any mode to follow a link that has -Org-mode syntax." + "Follow a link or time-stamp like Org mode does. +This command can be called in any mode to follow an external link +or a time-stamp that has Org mode syntax. Its behavior is +undefined when called on internal links (e.g., fuzzy links). +Raise an error when there is nothing to follow. " (interactive) - (org-run-like-in-org-mode 'org-open-at-point)) + (cond ((org-in-regexp org-any-link-re) + (org-open-link-from-string (match-string-no-properties 0))) + ((or (org-in-regexp org-ts-regexp-both nil t) + (org-in-regexp org-tsr-regexp-both nil t)) + (org-follow-timestamp-link)) + (t (user-error "No link found")))) ;;;###autoload (defun org-open-link-from-string (s &optional arg reference-buffer) - "Open a link in the string S, as if it was in Org-mode." + "Open a link in the string S, as if it was in Org mode." (interactive "sLink: \nP") (let ((reference-buffer (or reference-buffer (current-buffer)))) (with-temp-buffer @@ -10375,267 +10701,240 @@ Functions in this hook must return t if they identify and follow a link at point. If they don't find anything interesting at point, they must return nil.") -(defvar org-link-search-inhibit-query nil) ;; dynamically scoped -(defvar clean-buffer-list-kill-buffer-names) ; Defined in midnight.el +(defvar org-link-search-inhibit-query nil) +(defvar clean-buffer-list-kill-buffer-names) ;Defined in midnight.el +(defun org--open-doi-link (path) + "Open a \"doi\" type link. +PATH is a the path to search for, as a string." + (browse-url (url-encode-url (concat org-doi-server-url path)))) + +(defun org--open-elisp-link (path) + "Open a \"elisp\" type link. +PATH is the sexp to evaluate, as a string." + (let ((cmd path)) + (if (or (and (org-string-nw-p + org-confirm-elisp-link-not-regexp) + (string-match-p org-confirm-elisp-link-not-regexp cmd)) + (not org-confirm-elisp-link-function) + (funcall org-confirm-elisp-link-function + (format "Execute \"%s\" as elisp? " + (org-add-props cmd nil 'face 'org-warning)))) + (message "%s => %s" cmd + (if (eq (string-to-char cmd) ?\() + (eval (read cmd)) + (call-interactively (read cmd)))) + (user-error "Abort")))) + +(defun org--open-help-link (path) + "Open a \"help\" type link. +PATH is a symbol name, as a string." + (pcase (intern path) + ((and (pred fboundp) variable) (describe-function variable)) + ((and (pred boundp) function) (describe-variable function)) + (name (user-error "Unknown function or variable: %s" name)))) + +(defun org--open-shell-link (path) + "Open a \"shell\" type link. +PATH is the command to execute, as a string." + (let ((buf (generate-new-buffer "*Org Shell Output*")) + (cmd path)) + (if (or (and (org-string-nw-p + org-confirm-shell-link-not-regexp) + (string-match + org-confirm-shell-link-not-regexp cmd)) + (not org-confirm-shell-link-function) + (funcall org-confirm-shell-link-function + (format "Execute \"%s\" in shell? " + (org-add-props cmd nil + 'face 'org-warning)))) + (progn + (message "Executing %s" cmd) + (shell-command cmd buf) + (when (featurep 'midnight) + (setq clean-buffer-list-kill-buffer-names + (cons (buffer-name buf) + clean-buffer-list-kill-buffer-names)))) + (user-error "Abort")))) + (defun org-open-at-point (&optional arg reference-buffer) - "Open link at or after point. -If there is no link at point, this function will search forward up to -the end of the current line. -Normally, files will be opened by an appropriate application. If the -optional prefix argument ARG is non-nil, Emacs will visit the file. -With a double prefix argument, try to open outside of Emacs, in the -application the system uses for this file type." - (interactive "P") - ;; if in a code block, then open the block's results - (unless (call-interactively #'org-babel-open-src-block-result) - (org-load-modules-maybe) - (move-marker org-open-link-marker (point)) - (setq org-window-config-before-follow-link (current-window-configuration)) - (org-remove-occur-highlights nil nil t) - (cond - ((and (org-at-heading-p) - (not (org-at-timestamp-p t)) - (not (org-in-regexp - (concat org-plain-link-re "\\|" - org-bracket-link-regexp "\\|" - org-angle-link-re "\\|" - "[ \t]:[^ \t\n]+:[ \t]*$"))) - (not (get-text-property (point) 'org-linked-text))) - (or (let* ((lkall (org-offer-links-in-entry (current-buffer) (point) arg)) - (lk0 (car lkall)) - (lk (if (stringp lk0) (list lk0) lk0)) - (lkend (cdr lkall))) - (mapcar (lambda(l) - (search-forward l nil lkend) - (goto-char (match-beginning 0)) - (org-open-at-point)) - lk)) - (progn (require 'org-attach) (org-attach-reveal 'if-exists)))) - ((run-hook-with-args-until-success 'org-open-at-point-functions)) - ((and (org-at-timestamp-p t) - (not (org-in-regexp org-bracket-link-regexp))) - (org-follow-timestamp-link)) - ((and (or (org-footnote-at-reference-p) (org-footnote-at-definition-p)) - (not (org-in-regexp org-any-link-re))) - (org-footnote-action)) - (t - (let (type path link line search (pos (point))) - (catch 'match - (save-excursion - (or (org-in-regexp org-plain-link-re) - (skip-chars-forward "^]\n\r")) - (when (org-in-regexp org-bracket-link-regexp 1) - (setq link (org-extract-attributes - (org-link-unescape (org-match-string-no-properties 1)))) - (while (string-match " *\n *" link) - (setq link (replace-match " " t t link))) - (setq link (org-link-expand-abbrev link)) - (cond - ((or (file-name-absolute-p link) - (string-match "^\\.\\.?/" link)) - (setq type "file" path link)) - ((string-match org-link-re-with-space3 link) - (setq type (match-string 1 link) path (match-string 2 link))) - ((string-match "^help:+\\(.+\\)" link) - (setq type "help" path (match-string 1 link))) - (t (setq type "thisfile" path link))) - (throw 'match t))) - - (when (get-text-property (point) 'org-linked-text) - (setq type "thisfile" - pos (if (get-text-property (1+ (point)) 'org-linked-text) - (1+ (point)) (point)) - path (buffer-substring - (or (previous-single-property-change pos 'org-linked-text) - (point-min)) - (or (next-single-property-change pos 'org-linked-text) - (point-max))) - ;; Ensure we will search for a <<>> link, not - ;; a simple reference like <> - path (concat "<" path)) - (throw 'match t)) + "Open link, timestamp, footnote or tags at point. - (save-excursion - (when (or (org-in-regexp org-angle-link-re) - (let ((match (org-in-regexp org-plain-link-re))) - ;; Check a plain link is not within a bracket link - (and match - (save-excursion - (save-match-data - (progn - (goto-char (car match)) - (not (org-in-regexp org-bracket-link-regexp))))))) - (let ((line_ending (save-excursion (end-of-line) (point)))) - ;; We are in a line before a plain or bracket link - (or (re-search-forward org-plain-link-re line_ending t) - (re-search-forward org-bracket-link-regexp line_ending t)))) - (setq type (match-string 1) - path (org-link-unescape (match-string 2))) - (throw 'match t))) - (save-excursion - (when (org-in-regexp (org-re "\\(:[[:alnum:]_@#%:]+\\):[ \t]*$")) - (setq type "tags" - path (match-string 1)) - (while (string-match ":" path) - (setq path (replace-match "+" t t path))) - (throw 'match t))) - (when (org-in-regexp "<\\([^><\n]+\\)>") - (setq type "tree-match" - path (match-string 1)) - (throw 'match t))) - (unless path - (user-error "No link found")) +When point is on a link, follow it. Normally, files will be +opened by an appropriate application. If the optional prefix +argument ARG is non-nil, Emacs will visit the file. With +a double prefix argument, try to open outside of Emacs, in the +application the system uses for this file type. - ;; switch back to reference buffer - ;; needed when if called in a temporary buffer through - ;; org-open-link-from-string - (with-current-buffer (or reference-buffer (current-buffer)) +When point is on a timestamp, open the agenda at the day +specified. - ;; Remove any trailing spaces in path - (if (string-match " +\\'" path) - (setq path (replace-match "" t t path))) - (if (and org-link-translation-function - (fboundp org-link-translation-function)) - ;; Check if we need to translate the link - (let ((tmp (funcall org-link-translation-function type path))) - (setq type (car tmp) path (cdr tmp)))) +When point is a footnote definition, move to the first reference +found. If it is on a reference, move to the associated +definition. - (cond +When point is on a headline, display a list of every link in the +entry, so it is possible to pick one, or all, of them. If point +is on a tag, call `org-tags-view' instead. - ((assoc type org-link-protocols) - (funcall (nth 1 (assoc type org-link-protocols)) path)) - - ((equal type "help") - (let ((f-or-v (intern path))) - (cond ((fboundp f-or-v) - (describe-function f-or-v)) - ((boundp f-or-v) - (describe-variable f-or-v)) - (t (error "Not a known function or variable"))))) - - ((equal type "mailto") - (let ((cmd (car org-link-mailto-program)) - (args (cdr org-link-mailto-program)) args1 - (address path) (subject "") a) - (if (string-match "\\(.*\\)::\\(.*\\)" path) - (setq address (match-string 1 path) - subject (org-link-escape (match-string 2 path)))) - (while args - (cond - ((not (stringp (car args))) (push (pop args) args1)) - (t (setq a (pop args)) - (if (string-match "%a" a) - (setq a (replace-match address t t a))) - (if (string-match "%s" a) - (setq a (replace-match subject t t a))) - (push a args1)))) - (apply cmd (nreverse args1)))) - - ((member type '("http" "https" "ftp" "news")) - (browse-url - (concat type ":" - (if (org-string-match-p - (concat "[[:nonascii:]" - org-link-escape-chars-browser "]") - path) - (org-link-escape path org-link-escape-chars-browser) - path)))) - - ((string= type "doi") - (browse-url - (concat org-doi-server-url - (if (org-string-match-p - (concat "[[:nonascii:]" - org-link-escape-chars-browser "]") - path) - (org-link-escape path org-link-escape-chars-browser) - path)))) - - ((member type '("message")) - (browse-url (concat type ":" path))) - - ((string= type "tags") - (org-tags-view arg path)) - - ((string= type "tree-match") - (org-occur (concat "\\[" (regexp-quote path) "\\]"))) - - ((string= type "file") - (if (string-match "::\\([0-9]+\\)\\'" path) - (setq line (string-to-number (match-string 1 path)) - path (substring path 0 (match-beginning 0))) - (if (string-match "::\\(.+\\)\\'" path) - (setq search (match-string 1 path) - path (substring path 0 (match-beginning 0))))) - (if (string-match "[*?{]" (file-name-nondirectory path)) - (dired path) - (org-open-file path arg line search))) - - ((string= type "shell") - (let ((buf (generate-new-buffer "*Org Shell Output")) - (cmd path)) - (if (or (and (not (string= org-confirm-shell-link-not-regexp "")) - (string-match org-confirm-shell-link-not-regexp cmd)) - (not org-confirm-shell-link-function) - (funcall org-confirm-shell-link-function - (format "Execute \"%s\" in shell? " - (org-add-props cmd nil - 'face 'org-warning)))) - (progn - (message "Executing %s" cmd) - (shell-command cmd buf) - (if (featurep 'midnight) - (setq clean-buffer-list-kill-buffer-names - (cons buf clean-buffer-list-kill-buffer-names)))) - (error "Abort")))) - - ((string= type "elisp") - (let ((cmd path)) - (if (or (and (not (string= org-confirm-elisp-link-not-regexp "")) - (string-match org-confirm-elisp-link-not-regexp cmd)) - (not org-confirm-elisp-link-function) - (funcall org-confirm-elisp-link-function - (format "Execute \"%s\" as elisp? " - (org-add-props cmd nil - 'face 'org-warning)))) - (message "%s => %s" cmd - (if (equal (string-to-char cmd) ?\() - (eval (read cmd)) - (call-interactively (read cmd)))) - (error "Abort")))) - - ((and (string= type "thisfile") - (or (run-hook-with-args-until-success - 'org-open-link-functions path) - (and link - (string-match "^id:" link) - (or (featurep 'org-id) (require 'org-id)) - (progn - (funcall (nth 1 (assoc "id" org-link-protocols)) - (substring path 3)) - t))))) - - ((string= type "thisfile") - (if arg - (switch-to-buffer-other-window - (org-get-buffer-for-internal-link (current-buffer))) - (org-mark-ring-push)) - (let ((cmd `(org-link-search - ,path - ,(cond ((equal arg '(4)) ''occur) - ((equal arg '(16)) ''org-occur)) - ,pos))) - (condition-case nil (let ((org-link-search-inhibit-query t)) - (eval cmd)) - (error (progn (widen) (eval cmd)))))) - - (t (browse-url-at-point))))))) - (move-marker org-open-link-marker nil) - (run-hook-with-args 'org-follow-link-hook))) +When optional argument REFERENCE-BUFFER is non-nil, it should +specify a buffer from where the link search should happen. This +is used internally by `org-open-link-from-string'. -(defsubst org-uniquify (list) - "Non-destructively remove duplicate elements from LIST." - (let ((res (copy-sequence list))) (delete-dups res))) +On top of syntactically correct links, this function will open +the link at point in comments or comment blocks and the first +link in a property drawer line." + (interactive "P") + ;; On a code block, open block's results. + (unless (call-interactively 'org-babel-open-src-block-result) + (org-load-modules-maybe) + (setq org-window-config-before-follow-link (current-window-configuration)) + (org-remove-occur-highlights nil nil t) + (unless (run-hook-with-args-until-success 'org-open-at-point-functions) + (let* ((context + ;; Only consider supported types, even if they are not + ;; the closest one. + (org-element-lineage + (org-element-context) + '(clock comment comment-block footnote-definition + footnote-reference headline inlinetask keyword link + node-property timestamp) + t)) + (type (org-element-type context)) + (value (org-element-property :value context))) + (cond + ((not context) (user-error "No link found")) + ;; Exception: open timestamps and links in properties + ;; drawers, keywords and comments. + ((memq type '(comment comment-block keyword node-property)) + (call-interactively #'org-open-at-point-global)) + ;; On a headline or an inlinetask, but not on a timestamp, + ;; a link, a footnote reference or on tags. + ((and (memq type '(headline inlinetask)) + ;; Not on tags. + (let ((case-fold-search nil)) + (save-excursion + (beginning-of-line) + (looking-at org-complex-heading-regexp)) + (or (not (match-beginning 5)) + (< (point) (match-beginning 5))))) + (let* ((data (org-offer-links-in-entry (current-buffer) (point) arg)) + (links (car data)) + (links-end (cdr data))) + (if links + (dolist (link (if (stringp links) (list links) links)) + (search-forward link nil links-end) + (goto-char (match-beginning 0)) + (org-open-at-point)) + (require 'org-attach) + (org-attach-reveal 'if-exists)))) + ;; On a clock line, make sure point is on the timestamp + ;; before opening it. + ((and (eq type 'clock) + value + (>= (point) (org-element-property :begin value)) + (<= (point) (org-element-property :end value))) + (org-follow-timestamp-link)) + ;; Do nothing on white spaces after an object. + ((>= (point) + (save-excursion + (goto-char (org-element-property :end context)) + (skip-chars-backward " \t") + (point))) + (user-error "No link found")) + ((eq type 'timestamp) (org-follow-timestamp-link)) + ;; On tags within a headline or an inlinetask. + ((and (memq type '(headline inlinetask)) + (let ((case-fold-search nil)) + (save-excursion (beginning-of-line) + (looking-at org-complex-heading-regexp)) + (and (match-beginning 5) + (>= (point) (match-beginning 5))))) + (org-tags-view arg (substring (match-string 5) 0 -1))) + ((eq type 'link) + ;; When link is located within the description of another + ;; link (e.g., an inline image), always open the parent + ;; link. + (let* ((link (let ((up (org-element-property :parent context))) + (if (eq (org-element-type up) 'link) up context))) + (type (org-element-property :type link)) + (path (org-link-unescape (org-element-property :path link)))) + ;; Switch back to REFERENCE-BUFFER needed when called in + ;; a temporary buffer through `org-open-link-from-string'. + (with-current-buffer (or reference-buffer (current-buffer)) + (cond + ((equal type "file") + (if (string-match "[*?{]" (file-name-nondirectory path)) + (dired path) + ;; Look into `org-link-parameters' in order to find + ;; a DEDICATED-FUNCTION to open file. The function + ;; will be applied on raw link instead of parsed + ;; link due to the limitation in `org-add-link-type' + ;; ("open" function called with a single argument). + ;; If no such function is found, fallback to + ;; `org-open-file'. + (let* ((option (org-element-property :search-option link)) + (app (org-element-property :application link)) + (dedicated-function + (org-link-get-parameter + (if app (concat type "+" app) type) + :follow))) + (if dedicated-function + (funcall dedicated-function + (concat path + (and option (concat "::" option)))) + (apply #'org-open-file + path + (cond (arg) + ((equal app "emacs") 'emacs) + ((equal app "sys") 'system)) + (cond ((not option) nil) + ((string-match-p "\\`[0-9]+\\'" option) + (list (string-to-number option))) + (t (list nil + (org-link-unescape option))))))))) + ((functionp (org-link-get-parameter type :follow)) + (funcall (org-link-get-parameter type :follow) path)) + ((member type '("coderef" "custom-id" "fuzzy" "radio")) + (unless (run-hook-with-args-until-success + 'org-open-link-functions path) + (if (not arg) (org-mark-ring-push) + (switch-to-buffer-other-window + (org-get-buffer-for-internal-link (current-buffer)))) + (let ((destination + (org-with-wide-buffer + (if (equal type "radio") + (org-search-radio-target + (org-element-property :path link)) + (org-link-search + (if (member type '("custom-id" "coderef")) + (org-element-property :raw-link link) + path) + ;; Prevent fuzzy links from matching + ;; themselves. + (and (equal type "fuzzy") + (+ 2 (org-element-property :begin link))))) + (point)))) + (unless (and (<= (point-min) destination) + (>= (point-max) destination)) + (widen)) + (goto-char destination)))) + (t (browse-url-at-point)))))) + ;; On a footnote reference or at a footnote definition's label. + ((or (eq type 'footnote-reference) + (and (eq type 'footnote-definition) + (save-excursion + ;; Do not validate action when point is on the + ;; spaces right after the footnote label, in + ;; order to be on par with behaviour on links. + (skip-chars-forward " \t") + (let ((begin + (org-element-property :contents-begin context))) + (if begin (< (point) begin) + (= (org-element-property :post-affiliated context) + (line-beginning-position))))))) + (org-footnote-action)) + (t (user-error "No link found"))))) + (run-hook-with-args 'org-follow-link-hook))) (defun org-offer-links-in-entry (buffer marker &optional nth zero) "Offer links in the current entry and return the selected link. @@ -10644,65 +10943,57 @@ If NTH is an integer, return the NTH link found. If ZERO is a string, check also this string for a link, and if there is one, return it." (with-current-buffer buffer - (save-excursion - (save-restriction - (widen) - (goto-char marker) - (let ((re (concat "\\(" org-bracket-link-regexp "\\)\\|" - "\\(" org-angle-link-re "\\)\\|" - "\\(" org-plain-link-re "\\)")) - (cnt ?0) - (in-emacs (if (integerp nth) nil nth)) - have-zero end links link c) - (when (and (stringp zero) (string-match org-bracket-link-regexp zero)) - (push (match-string 0 zero) links) - (setq cnt (1- cnt) have-zero t)) - (save-excursion - (org-back-to-heading t) - (setq end (save-excursion (outline-next-heading) (point))) - (while (re-search-forward re end t) - (push (match-string 0) links)) - (setq links (org-uniquify (reverse links)))) - (cond - ((null links) - (message "No links")) - ((equal (length links) 1) - (setq link (car links))) - ((and (integerp nth) (>= (length links) (if have-zero (1+ nth) nth))) - (setq link (nth (if have-zero nth (1- nth)) links))) - (t ; we have to select a link - (save-excursion - (save-window-excursion - (delete-other-windows) - (with-output-to-temp-buffer "*Select Link*" - (mapc (lambda (l) - (if (not (string-match org-bracket-link-regexp l)) - (princ (format "[%c] %s\n" (incf cnt) - (org-remove-angle-brackets l))) - (if (match-end 3) - (princ (format "[%c] %s (%s)\n" (incf cnt) - (match-string 3 l) (match-string 1 l))) - (princ (format "[%c] %s\n" (incf cnt) - (match-string 1 l)))))) - links)) - (org-fit-window-to-buffer (get-buffer-window "*Select Link*")) - (message "Select link to open, RET to open all:") - (setq c (read-char-exclusive)) - (and (get-buffer "*Select Link*") (kill-buffer "*Select Link*")))) - (when (equal c ?q) (error "Abort")) - (if (equal c ?\C-m) - (setq link links) - (setq nth (- c ?0)) - (if have-zero (setq nth (1+ nth))) - (unless (and (integerp nth) (>= (length links) nth)) - (user-error "Invalid link selection")) - (setq link (nth (1- nth) links))))) - (cons link end)))))) - -;; Add special file links that specify the way of opening - -(org-add-link-type "file+sys" 'org-open-file-with-system) -(org-add-link-type "file+emacs" 'org-open-file-with-emacs) + (org-with-wide-buffer + (goto-char marker) + (let ((cnt ?0) + have-zero end links link c) + (when (and (stringp zero) (string-match org-bracket-link-regexp zero)) + (push (match-string 0 zero) links) + (setq cnt (1- cnt) have-zero t)) + (save-excursion + (org-back-to-heading t) + (setq end (save-excursion (outline-next-heading) (point))) + (while (re-search-forward org-any-link-re end t) + (push (match-string 0) links)) + (setq links (org-uniquify (reverse links)))) + (cond + ((null links) + (message "No links")) + ((equal (length links) 1) + (setq link (car links))) + ((and (integerp nth) (>= (length links) (if have-zero (1+ nth) nth))) + (setq link (nth (if have-zero nth (1- nth)) links))) + (t ; we have to select a link + (save-excursion + (save-window-excursion + (delete-other-windows) + (with-output-to-temp-buffer "*Select Link*" + (dolist (l links) + (cond + ((not (string-match org-bracket-link-regexp l)) + (princ (format "[%c] %s\n" (cl-incf cnt) + (org-unbracket-string "<" ">" l)))) + ((match-end 3) + (princ (format "[%c] %s (%s)\n" (cl-incf cnt) + (match-string 3 l) (match-string 1 l)))) + (t (princ (format "[%c] %s\n" (cl-incf cnt) + (match-string 1 l))))))) + (org-fit-window-to-buffer (get-buffer-window "*Select Link*")) + (message "Select link to open, RET to open all:") + (setq c (read-char-exclusive)) + (and (get-buffer "*Select Link*") (kill-buffer "*Select Link*")))) + (when (equal c ?q) (user-error "Abort")) + (if (equal c ?\C-m) + (setq link links) + (setq nth (- c ?0)) + (when have-zero (setq nth (1+ nth))) + (unless (and (integerp nth) (>= (length links) nth)) + (user-error "Invalid link selection")) + (setq link (nth (1- nth) links))))) + (cons link end))))) + +;; TODO: These functions are deprecated since `org-open-at-point' +;; hard-codes behaviour for "file+emacs" and "file+sys" types. (defun org-open-file-with-system (path) "Open file at PATH using the system way of opening it." (org-open-file path 'system)) @@ -10732,8 +11023,8 @@ which see. A function in this hook may also use `setq' to set the variable `description' to provide a suggestion for the descriptive text to -be used for this link when it gets inserted into an Org-mode -buffer with \\[org-insert-link].") +be used for this link when it gets inserted into an Org buffer +with \\[org-insert-link].") (defvar org-execute-file-search-functions nil "List of functions to execute a file search triggered by a link. @@ -10757,179 +11048,201 @@ the window configuration before `org-open-at-point' was called using: (set-window-configuration org-window-config-before-follow-link)") -(defun org-link-search (s &optional type avoid-pos stealth) - "Search for a link search option. -If S is surrounded by forward slashes, it is interpreted as a -regular expression. In org-mode files, this will create an `org-occur' -sparse tree. In ordinary files, `occur' will be used to list matches. -If the current buffer is in `dired-mode', grep will be used to search -in all files. If AVOID-POS is given, ignore matches near that position. +(defun org-search-radio-target (target) + "Search a radio target matching TARGET in current buffer. +White spaces are not significant." + (let ((re (format "<<<%s>>>" + (mapconcat #'regexp-quote + (org-split-string target "[ \t\n]+") + "[ \t]+\\(?:\n[ \t]*\\)?"))) + (origin (point))) + (goto-char (point-min)) + (catch :radio-match + (while (re-search-forward re nil t) + (backward-char) + (let ((object (org-element-context))) + (when (eq (org-element-type object) 'radio-target) + (goto-char (org-element-property :begin object)) + (org-show-context 'link-search) + (throw :radio-match nil)))) + (goto-char origin) + (user-error "No match for radio target: %s" target)))) + +(defun org-link-search (s &optional avoid-pos stealth) + "Search for a search string S. + +If S starts with \"#\", it triggers a custom ID search. + +If S is enclosed within parenthesis, it initiates a coderef +search. + +If S is surrounded by forward slashes, it is interpreted as +a regular expression. In Org mode files, this will create an +`org-occur' sparse tree. In ordinary files, `occur' will be used +to list matches. If the current buffer is in `dired-mode', grep +will be used to search in all files. + +When AVOID-POS is given, ignore matches near that position. When optional argument STEALTH is non-nil, do not modify -visibility around point, thus ignoring -`org-show-hierarchy-above', `org-show-following-heading' and -`org-show-siblings' variables." - (let ((case-fold-search t) - (s0 (mapconcat 'identity (org-split-string s "[ \t\r\n]+") " ")) - (markers (concat "\\(?:" (mapconcat (lambda (x) (regexp-quote (car x))) - (append '(("") (" ") ("\t") ("\n")) - org-emphasis-alist) - "\\|") "\\)")) - (pos (point)) - (pre nil) (post nil) - words re0 re1 re2 re3 re4_ re4 re5 re2a re2a_ reall) +visibility around point, thus ignoring `org-show-context-detail' +variable. + +Search is case-insensitive and ignores white spaces. Return type +of matched result, which is either `dedicated' or `fuzzy'." + (unless (org-string-nw-p s) (error "Invalid search string \"%s\"" s)) + (let* ((case-fold-search t) + (origin (point)) + (normalized (replace-regexp-in-string "\n[ \t]*" " " s)) + (starred (eq (string-to-char normalized) ?*)) + (words (split-string (if starred (substring s 1) s))) + (s-multi-re (mapconcat #'regexp-quote words "\\(?:[ \t\n]+\\)")) + (s-single-re (mapconcat #'regexp-quote words "[ \t]+")) + type) (cond - ;; First check if there are any special search functions + ;; Check if there are any special search functions. ((run-hook-with-args-until-success 'org-execute-file-search-functions s)) - ;; Now try the builtin stuff - ((and (equal (string-to-char s0) ?#) - (> (length s0) 1) - (save-excursion - (goto-char (point-min)) - (and - (re-search-forward - (concat "^[ \t]*:CUSTOM_ID:[ \t]+" - (regexp-quote (substring s0 1)) "[ \t]*$") nil t) - (setq type 'dedicated - pos (match-beginning 0)))) - ;; There is an exact target for this - (goto-char pos) - (org-back-to-heading t))) - ((save-excursion + ((eq (string-to-char s) ?#) + ;; Look for a custom ID S if S starts with "#". + (let* ((id (substring normalized 1)) + (match (org-find-property "CUSTOM_ID" id))) + (if match (progn (goto-char match) (setf type 'dedicated)) + (error "No match for custom ID: %s" id)))) + ((string-match "\\`(\\(.*\\))\\'" normalized) + ;; Look for coderef targets if S is enclosed within parenthesis. + (let ((coderef (match-string-no-properties 1 normalized)) + (re (substring s-single-re 1 -1))) (goto-char (point-min)) - (and - (re-search-forward - (concat "<<" (regexp-quote s0) ">>") nil t) - (setq type 'dedicated - pos (match-beginning 0)))) - ;; There is an exact target for this - (goto-char pos)) - ((save-excursion - (goto-char (point-min)) - (and - (re-search-forward - (format "^[ \t]*#\\+NAME: %s" (regexp-quote s0)) nil t) - (setq type 'dedicated pos (match-beginning 0)))) - ;; Found an element with a matching #+name affiliated keyword. - (goto-char pos)) - ((and (string-match "^(\\(.*\\))$" s0) - (save-excursion + (catch :coderef-match + (while (re-search-forward re nil t) + (let ((element (org-element-at-point))) + (when (and (memq (org-element-type element) + '(example-block src-block)) + ;; Build proper regexp according to current + ;; block's label format. + (let ((label-fmt + (regexp-quote + (or (org-element-property :label-fmt element) + org-coderef-label-format)))) + (save-excursion + (beginning-of-line) + (looking-at (format ".*?\\(%s\\)[ \t]*$" + (format label-fmt coderef)))))) + (setq type 'dedicated) + (goto-char (match-beginning 1)) + (throw :coderef-match nil)))) + (goto-char origin) + (error "No match for coderef: %s" coderef)))) + ((string-match "\\`/\\(.*\\)/\\'" normalized) + ;; Look for a regular expression. + (funcall (if (derived-mode-p 'org-mode) #'org-occur #'org-do-occur) + (match-string 1 s))) + ;; From here, we handle fuzzy links. + ;; + ;; Look for targets, only if not in a headline search. + ((and (not starred) + (let ((target (format "<<%s>>" s-multi-re))) + (catch :target-match + (goto-char (point-min)) + (while (re-search-forward target nil t) + (backward-char) + (let ((context (org-element-context))) + (when (eq (org-element-type context) 'target) + (setq type 'dedicated) + (goto-char (org-element-property :begin context)) + (throw :target-match t)))) + nil)))) + ;; Look for elements named after S, only if not in a headline + ;; search. + ((and (not starred) + (let ((name (format "^[ \t]*#\\+NAME: +%s[ \t]*$" s-single-re))) + (catch :name-match + (goto-char (point-min)) + (while (re-search-forward name nil t) + (let ((element (org-element-at-point))) + (when (equal words + (split-string + (org-element-property :name element))) + (setq type 'dedicated) + (beginning-of-line) + (throw :name-match t)))) + nil)))) + ;; Regular text search. Prefer headlines in Org mode buffers. + ;; Ignore COMMENT keyword, TODO keywords, priority cookies, + ;; statistics cookies and tags. + ((and (derived-mode-p 'org-mode) + (let ((title-re + (format "%s.*\\(?:%s[ \t]\\)?.*%s" + org-outline-regexp-bol + org-comment-string + (mapconcat #'regexp-quote words ".+"))) + (cookie-re "\\[[0-9]*\\(?:%\\|/[0-9]*\\)\\]") + (comment-re (format "\\`%s[ \t]+" org-comment-string))) (goto-char (point-min)) - (and - (re-search-forward - (concat "[^[]" (regexp-quote - (format org-coderef-label-format - (match-string 1 s0)))) - nil t) - (setq type 'dedicated - pos (1+ (match-beginning 0)))))) - ;; There is a coderef target for this - (goto-char pos)) - ((string-match "^/\\(.*\\)/$" s) - ;; A regular expression - (cond - ((derived-mode-p 'org-mode) - (org-occur (match-string 1 s))) - (t (org-do-occur (match-string 1 s))))) - ((and (derived-mode-p 'org-mode) org-link-search-must-match-exact-headline) - (and (equal (string-to-char s) ?*) (setq s (substring s 1))) - (goto-char (point-min)) - (cond - ((let (case-fold-search) - (re-search-forward (format org-complex-heading-regexp-format - (regexp-quote s)) - nil t)) - ;; OK, found a match - (setq type 'dedicated) - (goto-char (match-beginning 0))) - ((and (not org-link-search-inhibit-query) - (eq org-link-search-must-match-exact-headline 'query-to-create) - (y-or-n-p "No match - create this as a new heading? ")) - (goto-char (point-max)) - (or (bolp) (newline)) - (insert "* " s "\n") - (beginning-of-line 0)) - (t - (goto-char pos) - (error "No match")))) - (t - ;; A normal search string - (when (equal (string-to-char s) ?*) - ;; Anchor on headlines, post may include tags. - (setq pre "^\\*+[ \t]+\\(?:\\sw+\\)?[ \t]*" - post (org-re "[ \t]*\\(?:[ \t]+:[[:alnum:]_@#%:+]:[ \t]*\\)?$") - s (substring s 1))) - (remove-text-properties - 0 (length s) - '(face nil mouse-face nil keymap nil fontified nil) s) - ;; Make a series of regular expressions to find a match - (setq words (org-split-string s "[ \n\r\t]+") - - re0 (concat "\\(<<" (regexp-quote s0) ">>\\)") - re2 (concat markers "\\(" (mapconcat 'downcase words "[ \t]+") - "\\)" markers) - re2a_ (concat "\\(" (mapconcat 'downcase words - "[ \t\r\n]+") "\\)[ \t\r\n]") - re2a (concat "[ \t\r\n]" re2a_) - re4_ (concat "\\(" (mapconcat 'downcase words - "[^a-zA-Z_\r\n]+") "\\)[^a-zA-Z_]") - re4 (concat "[^a-zA-Z_]" re4_) - - re1 (concat pre re2 post) - re3 (concat pre (if pre re4_ re4) post) - re5 (concat pre ".*" re4) - re2 (concat pre re2) - re2a (concat pre (if pre re2a_ re2a)) - re4 (concat pre (if pre re4_ re4)) - reall (concat "\\(" re0 "\\)\\|\\(" re1 "\\)\\|\\(" re2 - "\\)\\|\\(" re3 "\\)\\|\\(" re4 "\\)\\|\\(" - re5 "\\)")) - (cond - ((eq type 'org-occur) (org-occur reall)) - ((eq type 'occur) (org-do-occur (downcase reall) 'cleanup)) - (t (goto-char (point-min)) - (setq type 'fuzzy) - (if (or (and (org-search-not-self 1 re0 nil t) - (setq type 'dedicated)) - (org-search-not-self 1 re1 nil t) - (org-search-not-self 1 re2 nil t) - (org-search-not-self 1 re2a nil t) - (org-search-not-self 1 re3 nil t) - (org-search-not-self 1 re4 nil t) - (org-search-not-self 1 re5 nil t)) - (goto-char (match-beginning 1)) - (goto-char pos) - (error "No match")))))) - (and (derived-mode-p 'org-mode) - (not stealth) - (org-show-context 'link-search)) + (catch :found + (while (re-search-forward title-re nil t) + (when (equal words + (split-string + (replace-regexp-in-string + cookie-re "" + (replace-regexp-in-string + comment-re "" (org-get-heading t t))))) + (throw :found t))) + nil))) + (beginning-of-line) + (setq type 'dedicated)) + ;; Offer to create non-existent headline depending on + ;; `org-link-search-must-match-exact-headline'. + ((and (derived-mode-p 'org-mode) + (not org-link-search-inhibit-query) + (eq org-link-search-must-match-exact-headline 'query-to-create) + (yes-or-no-p "No match - create this as a new heading? ")) + (goto-char (point-max)) + (unless (bolp) (newline)) + (org-insert-heading nil t t) + (insert s "\n") + (beginning-of-line 0)) + ;; Only headlines are looked after. No need to process + ;; further: throw an error. + ((and (derived-mode-p 'org-mode) + (or starred org-link-search-must-match-exact-headline)) + (goto-char origin) + (error "No match for fuzzy expression: %s" normalized)) + ;; Regular text search. + ((catch :fuzzy-match + (goto-char (point-min)) + (while (re-search-forward s-multi-re nil t) + ;; Skip match if it contains AVOID-POS or it is included in + ;; a link with a description but outside the description. + (unless (or (and avoid-pos + (<= (match-beginning 0) avoid-pos) + (> (match-end 0) avoid-pos)) + (and (save-match-data + (org-in-regexp org-bracket-link-regexp)) + (match-beginning 3) + (or (> (match-beginning 3) (point)) + (<= (match-end 3) (point))) + (org-element-lineage + (save-match-data (org-element-context)) + '(link) t))) + (goto-char (match-beginning 0)) + (setq type 'fuzzy) + (throw :fuzzy-match t))) + nil)) + ;; All failed. Throw an error. + (t (goto-char origin) + (error "No match for fuzzy expression: %s" normalized))) + ;; Disclose surroundings of match, if appropriate. + (when (and (derived-mode-p 'org-mode) (not stealth)) + (org-show-context 'link-search)) type)) -(defun org-search-not-self (group &rest args) - "Execute `re-search-forward', but only accept matches that do not -enclose the position of `org-open-link-marker'." - (let ((m org-open-link-marker)) - (catch 'exit - (while (apply #'re-search-forward args) - (unless (get-text-property (match-end group) 'intangible) ; Emacs 21 - (goto-char (match-end group)) - (if (and (or (not (eq (marker-buffer m) (current-buffer))) - (> (match-beginning 0) (marker-position m)) - (< (match-end 0) (marker-position m))) - (save-match-data - (or (not (org-in-regexp - org-bracket-link-analytic-regexp 1)) - (not (match-end 4)) ; no description - (and (<= (match-beginning 4) (point)) - (>= (match-end 4) (point)))))) - (throw 'exit (point)))))))) - (defun org-get-buffer-for-internal-link (buffer) "Return a buffer to be used for displaying the link target of internal links." (cond ((not org-display-internal-link-with-indirect-buffer) buffer) - ((string-match "(Clone)$" (buffer-name buffer)) + ((string-suffix-p "(Clone)" (buffer-name buffer)) (message "Buffer is already a clone, not making another one") ;; we also do not modify visibility in this case buffer) @@ -10953,8 +11266,8 @@ to read." (goto-char (point-min)) (when (re-search-forward "match[a-z]+" nil t) (setq beg (match-end 0)) - (if (re-search-forward "^[ \t]*[0-9]+" nil t) - (setq end (1- (match-beginning 0))))) + (when (re-search-forward "^[ \t]*[0-9]+" nil t) + (setq end (1- (match-beginning 0))))) (and beg end (let ((inhibit-read-only t)) (delete-region beg end))) (goto-char (point-min)) (select-window cwin)))) @@ -10962,13 +11275,13 @@ to read." ;;; The mark ring for links jumps (defvar org-mark-ring nil - "Mark ring for positions before jumps in Org-mode.") + "Mark ring for positions before jumps in Org mode.") (defvar org-mark-ring-last-goto nil "Last position in the mark ring used to go back.") ;; Fill and close the ring (setq org-mark-ring nil org-mark-ring-last-goto nil) ;; in case file is reloaded -(loop for i from 1 to org-mark-ring-length do - (push (make-marker) org-mark-ring)) +(dotimes (_ org-mark-ring-length) + (push (make-marker) org-mark-ring)) (setcdr (nthcdr (1- org-mark-ring-length) org-mark-ring) org-mark-ring) @@ -10982,15 +11295,15 @@ to read." (or buffer (current-buffer))) (message "%s" (substitute-command-keys - "Position saved to mark ring, go back with \\[org-mark-ring-goto]."))) + "Position saved to mark ring, go back with \ +`\\[org-mark-ring-goto]'."))) (defun org-mark-ring-goto (&optional n) "Jump to the previous position in the mark ring. With prefix arg N, jump back that many stored positions. When called several times in succession, walk through the entire ring. -Org-mode commands jumping to a different position in the current file, -or to another Org-mode file, automatically push the old position -onto the ring." +Org mode commands jumping to a different position in the current file, +or to another Org file, automatically push the old position onto the ring." (interactive "p") (let (p m) (if (eq last-command this-command) @@ -10998,25 +11311,19 @@ onto the ring." (setq p org-mark-ring)) (setq org-mark-ring-last-goto p) (setq m (car p)) - (org-pop-to-buffer-same-window (marker-buffer m)) + (pop-to-buffer-same-window (marker-buffer m)) (goto-char m) - (if (or (outline-invisible-p) (org-invisible-p2)) (org-show-context 'mark-goto)))) + (when (or (org-invisible-p) (org-invisible-p2)) (org-show-context 'mark-goto)))) -(defun org-remove-angle-brackets (s) - (if (equal (substring s 0 1) "<") (setq s (substring s 1))) - (if (equal (substring s -1) ">") (setq s (substring s 0 -1))) - s) (defun org-add-angle-brackets (s) - (if (equal (substring s 0 1) "<") nil (setq s (concat "<" s))) - (if (equal (substring s -1) ">") nil (setq s (concat s ">"))) - s) -(defun org-remove-double-quotes (s) - (if (equal (substring s 0 1) "\"") (setq s (substring s 1))) - (if (equal (substring s -1) "\"") (setq s (substring s 0 -1))) + (unless (equal (substring s 0 1) "<") (setq s (concat "<" s))) + (unless (equal (substring s -1) ">") (setq s (concat s ">"))) s) ;;; Following specific links +(defvar org-agenda-buffer-tmp-name) +(defvar org-agenda-start-on-weekday) (defun org-follow-timestamp-link () "Open an agenda view for the time-stamp date/range at point." (cond @@ -11071,43 +11378,40 @@ If the file does not exist, an error is thrown." buffer-file-name (substitute-in-file-name (expand-file-name path)))) (file-apps (append org-file-apps (org-default-apps))) - (apps (org-remove-if + (apps (cl-remove-if 'org-file-apps-entry-match-against-dlink-p file-apps)) - (apps-dlink (org-remove-if-not + (apps-dlink (cl-remove-if-not 'org-file-apps-entry-match-against-dlink-p file-apps)) (remp (and (assq 'remote apps) (org-file-remote-p file))) - (dirp (if remp nil (file-directory-p file))) + (dirp (unless remp (file-directory-p file))) (file (if (and dirp org-open-directory-means-index-dot-org) (concat (file-name-as-directory file) "index.org") file)) (a-m-a-p (assq 'auto-mode apps)) (dfile (downcase file)) - ;; reconstruct the original file: link from the PATH, LINE and SEARCH args - (link (cond ((and (eq line nil) - (eq search nil)) - file) - (line - (concat file "::" (number-to-string line))) - (search - (concat file "::" search)))) + ;; Reconstruct the original link from the PATH, LINE and + ;; SEARCH args. + (link (cond (line (concat file "::" (number-to-string line))) + (search (concat file "::" search)) + (t file))) (dlink (downcase link)) (old-buffer (current-buffer)) (old-pos (point)) (old-mode major-mode) - ext cmd link-match-data) - (if (string-match "^.*\\.\\([a-zA-Z0-9]+\\.gz\\)$" dfile) - (setq ext (match-string 1 dfile)) - (if (string-match "^.*\\.\\([a-zA-Z0-9]+\\)$" dfile) - (setq ext (match-string 1 dfile)))) + (ext + (and (string-match "\\`.*?\\.\\([a-zA-Z0-9]+\\(\\.gz\\)?\\)\\'" dfile) + (match-string 1 dfile))) + cmd link-match-data) (cond ((member in-emacs '((16) system)) - (setq cmd (cdr (assoc 'system apps)))) + (setq cmd (cdr (assq 'system apps)))) (in-emacs (setq cmd 'emacs)) (t - (setq cmd (or (and remp (cdr (assoc 'remote apps))) - (and dirp (cdr (assoc 'directory apps))) - ; first, try matching against apps-dlink - ; if we get a match here, store the match data for later + (setq cmd (or (and remp (cdr (assq 'remote apps))) + (and dirp (cdr (assq 'directory apps))) + ;; First, try matching against apps-dlink if we + ;; get a match here, store the match data for + ;; later. (let ((match (assoc-default dlink apps-dlink 'string-match))) (if match @@ -11120,9 +11424,9 @@ If the file does not exist, an error is thrown." (assoc-default dfile (org-apps-regexp-alist apps a-m-a-p) 'string-match) (cdr (assoc ext apps)) - (cdr (assoc t apps)))))) + (cdr (assq t apps)))))) (when (eq cmd 'system) - (setq cmd (cdr (assoc 'system apps)))) + (setq cmd (cdr (assq 'system apps)))) (when (eq cmd 'default) (setq cmd (cdr (assoc t apps)))) (when (eq cmd 'mailcap) @@ -11133,21 +11437,20 @@ If the file does not exist, an error is thrown." (if (stringp command) (setq cmd command) (setq cmd 'emacs)))) - (if (and (not (eq cmd 'emacs)) ; Emacs has no problems with non-ex files - (not (file-exists-p file)) - (not org-open-non-existing-files)) - (user-error "No such file: %s" file)) + (when (and (not (eq cmd 'emacs)) ; Emacs has no problems with non-ex files + (not (file-exists-p file)) + (not org-open-non-existing-files)) + (user-error "No such file: %s" file)) (cond ((and (stringp cmd) (not (string-match "^\\s-*$" cmd))) ;; Remove quotes around the file name - we'll use shell-quote-argument. (while (string-match "['\"]%s['\"]" cmd) (setq cmd (replace-match "%s" t t cmd))) - (while (string-match "%s" cmd) - (setq cmd (replace-match - (save-match-data - (shell-quote-argument - (convert-standard-filename file))) - t t cmd))) + (setq cmd (replace-regexp-in-string + "%s" + (shell-quote-argument (convert-standard-filename file)) + cmd + nil t)) ;; Replace "%1", "%2" etc. in command with group matches from regex (save-match-data @@ -11169,17 +11472,33 @@ If the file does not exist, an error is thrown." (eq cmd 'emacs)) (funcall (cdr (assq 'file org-link-frame-setup)) file) (widen) - (if line (org-goto-line line) - (if search (org-link-search search)))) + (cond (line (org-goto-line line) + (when (derived-mode-p 'org-mode) (org-reveal))) + (search (org-link-search search)))) + ((functionp cmd) + (save-match-data + (set-match-data link-match-data) + (condition-case nil + (funcall cmd file link) + ;; FIXME: Remove this check when most default installations + ;; of Emacs have at least Org 9.0. + ((debug wrong-number-of-arguments wrong-type-argument + invalid-function) + (user-error "Please see Org News for version 9.0 about \ +`org-file-apps'--Lisp error: %S" cmd))))) ((consp cmd) - (let ((file (convert-standard-filename file))) - (save-match-data - (set-match-data link-match-data) - (eval cmd)))) + ;; FIXME: Remove this check when most default installations of + ;; Emacs have at least Org 9.0. + ;; Heads-up instead of silently fall back to + ;; `org-link-frame-setup' for an old usage of `org-file-apps' + ;; with sexp instead of a function for `cmd'. + (user-error "Please see Org News for version 9.0 about \ +`org-file-apps'--Error: Deprecated usage of %S" cmd)) (t (funcall (cdr (assq 'file org-link-frame-setup)) file))) - (and (derived-mode-p 'org-mode) (eq old-mode 'org-mode) - (or (not (equal old-buffer (current-buffer))) - (not (equal old-pos (point)))) + (and (derived-mode-p 'org-mode) + (eq old-mode 'org-mode) + (or (not (eq old-buffer (current-buffer))) + (not (eq old-pos (point)))) (org-mark-ring-push old-pos old-buffer)))) (defun org-file-apps-entry-match-against-dlink-p (entry) @@ -11220,16 +11539,15 @@ be opened in Emacs." (append (delq nil (mapcar (lambda (x) - (if (not (stringp (car x))) - nil + (unless (not (stringp (car x))) (if (string-match "\\W" (car x)) x (cons (concat "\\." (car x) "\\'") (cdr x))))) list)) - (if add-auto-mode - (mapcar (lambda (x) (cons (car x) 'emacs)) auto-mode-alist)))) + (when add-auto-mode + (mapcar (lambda (x) (cons (car x) 'emacs)) auto-mode-alist)))) -(defvar ange-ftp-name-format) ; to silence the XEmacs compiler. +(defvar ange-ftp-name-format) (defun org-file-remote-p (file) "Test whether FILE specifies a location on a remote system. Return non-nil if the location is indeed remote. @@ -11262,8 +11580,8 @@ on the system \"/user@host:\"." ((not (listp org-reverse-note-order)) nil) (t (catch 'exit (dolist (entry org-reverse-note-order) - (if (string-match (car entry) buffer-file-name) - (throw 'exit (cdr entry)))))))) + (when (string-match (car entry) buffer-file-name) + (throw 'exit (cdr entry)))))))) (defvar org-refile-target-table nil "The list of refile targets, created by `org-refile'.") @@ -11288,7 +11606,7 @@ on the system \"/user@host:\"." (defun org-refile-cache-clear () "Clear the refile cache and disable all the markers." - (mapc (lambda (m) (move-marker m nil)) org-refile-markers) + (dolist (m org-refile-markers) (move-marker m nil)) (setq org-refile-markers nil) (setq org-refile-cache nil) (message "Refile cache has been cleared")) @@ -11323,17 +11641,23 @@ on the system \"/user@host:\"." org-refile-cache)))) (and set (org-refile-cache-check-set set) set))))) -(defun org-refile-get-targets (&optional default-buffer excluded-entries) +(defvar org-outline-path-cache nil + "Alist between buffer positions and outline paths. +It value is an alist (POSITION . PATH) where POSITION is the +buffer position at the beginning of an entry and PATH is a list +of strings describing the outline path for that entry, in reverse +order.") + +(defun org-refile-get-targets (&optional default-buffer) "Produce a table with refile targets." (let ((case-fold-search nil) ;; otherwise org confuses "TODO" as a kw and "Todo" as a word (entries (or org-refile-targets '((nil . (:level . 1))))) - targets tgs txt re files desc descre fast-path-p level pos0) + targets tgs files desc descre) (message "Getting targets...") (with-current-buffer (or default-buffer (current-buffer)) (dolist (entry entries) (setq files (car entry) desc (cdr entry)) - (setq fast-path-p nil) (cond ((null files) (setq files (list (current-buffer)))) ((eq files 'org-agenda-files) @@ -11342,7 +11666,7 @@ on the system \"/user@host:\"." (setq files (funcall files))) ((and (symbolp files) (boundp files)) (setq files (symbol-value files)))) - (if (stringp files) (setq files (list files))) + (when (stringp files) (setq files (list files))) (cond ((eq (car desc) :tag) (setq descre (concat "^\\*+[ \t]+.*?:" (regexp-quote (cdr desc)) ":"))) @@ -11357,7 +11681,6 @@ on the system \"/user@host:\"." (cdr desc))) "\\}[ \t]"))) ((eq (car desc) :maxlevel) - (setq fast-path-p t) (setq descre (concat "^\\*\\{1," (number-to-string (if org-odd-levels-only (1- (* 2 (cdr desc))) @@ -11365,99 +11688,113 @@ on the system \"/user@host:\"." "\\}[ \t]"))) (t (error "Bad refiling target description %s" desc))) (dolist (f files) - (with-current-buffer - (if (bufferp f) f (org-get-agenda-file-buffer f)) + (with-current-buffer (if (bufferp f) f (org-get-agenda-file-buffer f)) (or (setq tgs (org-refile-cache-get (buffer-file-name) descre)) (progn - (if (bufferp f) (setq f (buffer-file-name - (buffer-base-buffer f)))) + (when (bufferp f) + (setq f (buffer-file-name (buffer-base-buffer f)))) (setq f (and f (expand-file-name f))) - (if (eq org-refile-use-outline-path 'file) - (push (list (file-name-nondirectory f) f nil nil) tgs)) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while (re-search-forward descre nil t) - (goto-char (setq pos0 (point-at-bol))) - (catch 'next - (when org-refile-target-verify-function - (save-match-data - (or (funcall org-refile-target-verify-function) - (throw 'next t)))) - (when (and (looking-at org-complex-heading-regexp) - (not (member (match-string 4) excluded-entries)) - (match-string 4)) - (setq level (org-reduced-level - (- (match-end 1) (match-beginning 1))) - txt (org-link-display-format (match-string 4)) - txt (replace-regexp-in-string "\\( *[[0-9]+/?[0-9]*%?]\\)+$" "" txt) - re (format org-complex-heading-regexp-format - (regexp-quote (match-string 4)))) - (when org-refile-use-outline-path - (setq txt (mapconcat - 'org-protect-slash - (append - (if (eq org-refile-use-outline-path - 'file) - (list (file-name-nondirectory - (buffer-file-name - (buffer-base-buffer)))) - (if (eq org-refile-use-outline-path - 'full-file-path) - (list (buffer-file-name - (buffer-base-buffer))))) - (org-get-outline-path fast-path-p - level txt) - (list txt)) - "/"))) - (push (list txt f re (org-refile-marker (point))) - tgs))) - (when (= (point) pos0) - ;; verification function has not moved point - (goto-char (point-at-eol)))))))) + (when (eq org-refile-use-outline-path 'file) + (push (list (file-name-nondirectory f) f nil nil) tgs)) + (org-with-wide-buffer + (goto-char (point-min)) + (setq org-outline-path-cache nil) + (while (re-search-forward descre nil t) + (beginning-of-line) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp)) + (let ((begin (point)) + (heading (match-string-no-properties 4))) + (unless (or (and + org-refile-target-verify-function + (not + (funcall org-refile-target-verify-function))) + (not heading)) + (let ((re (format org-complex-heading-regexp-format + (regexp-quote heading))) + (target + (if (not org-refile-use-outline-path) heading + (mapconcat + #'org-protect-slash + (append + (pcase org-refile-use-outline-path + (`file (list (file-name-nondirectory + (buffer-file-name + (buffer-base-buffer))))) + (`full-file-path + (list (buffer-file-name + (buffer-base-buffer)))) + (_ nil)) + (org-get-outline-path t t)) + "/")))) + (push (list target f re (org-refile-marker (point))) + tgs))) + (when (= (point) begin) + ;; Verification function has not moved point. + (end-of-line))))))) (when org-refile-use-cache (org-refile-cache-put tgs (buffer-file-name) descre)) (setq targets (append tgs targets)))))) (message "Getting targets...done") - (nreverse targets))) + (delete-dups (nreverse targets)))) (defun org-protect-slash (s) - (while (string-match "/" s) - (setq s (replace-match "\\" t t s))) - s) - -(defvar org-olpa (make-vector 20 nil)) - -(defun org-get-outline-path (&optional fastp level heading) - "Return the outline path to the current entry, as a list. - -The parameters FASTP, LEVEL, and HEADING are for use by a scanner -routine which makes outline path derivations for an entire file, -avoiding backtracing. Refile target collection makes use of that." - (if fastp - (progn - (if (> level 19) - (error "Outline path failure, more than 19 levels")) - (loop for i from level upto 19 do - (aset org-olpa i nil)) - (prog1 - (delq nil (append org-olpa nil)) - (aset org-olpa level heading))) - (let (rtn case-fold-search) - (save-excursion - (save-restriction - (widen) - (while (org-up-heading-safe) - (when (looking-at org-complex-heading-regexp) - (push (org-trim - (replace-regexp-in-string - ;; Remove statistical/checkboxes cookies - "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" "" - (org-match-string-no-properties 4))) - rtn))) - rtn))))) + (replace-regexp-in-string "/" "\\/" s nil t)) + +(defun org--get-outline-path-1 (&optional use-cache) + "Return outline path to current headline. + +Outline path is a list of strings, in reverse order. When +optional argument USE-CACHE is non-nil, make use of a cache. See +`org-get-outline-path' for details. + +Assume buffer is widened and point is on a headline." + (or (and use-cache (cdr (assq (point) org-outline-path-cache))) + (let ((p (point)) + (heading (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp) + (if (not (match-end 4)) "" + ;; Remove statistics cookies. + (org-trim + (org-link-display-format + (replace-regexp-in-string + "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" "" + (match-string-no-properties 4)))))))) + (if (org-up-heading-safe) + (let ((path (cons heading (org--get-outline-path-1 use-cache)))) + (when use-cache + (push (cons p path) org-outline-path-cache)) + path) + ;; This is a new root node. Since we assume we are moving + ;; forward, we can drop previous cache so as to limit number + ;; of associations there. + (let ((path (list heading))) + (when use-cache (setq org-outline-path-cache (list (cons p path)))) + path))))) + +(defun org-get-outline-path (&optional with-self use-cache) + "Return the outline path to the current entry. + +An outline path is a list of ancestors for current headline, as +a list of strings. Statistics cookies are removed and links are +replaced with their description, if any, or their path otherwise. + +When optional argument WITH-SELF is non-nil, the path also +includes the current headline. + +When optional argument USE-CACHE is non-nil, cache outline paths +between calls to this function so as to avoid backtracking. This +argument is useful when planning to find more than one outline +path in the same document. In that case, there are two +conditions to satisfy: + - `org-outline-path-cache' is set to nil before starting the + process; + - outline paths are computed by increasing buffer positions." + (org-with-wide-buffer + (and (or (and with-self (org-back-to-heading t)) + (org-up-heading-safe)) + (reverse (org--get-outline-path-1 use-cache))))) (defun org-format-outline-path (path &optional width prefix separator) "Format the outline path PATH for display. @@ -11467,38 +11804,28 @@ such as the file name. SEPARATOR is inserted between the different parts of the path, the default is \"/\"." (setq width (or width 79)) - (if prefix (setq width (- width (length prefix)))) - (if (not path) - (or prefix "") - (let* ((nsteps (length path)) - (total-width (+ nsteps (apply '+ (mapcar 'length path)))) - (maxwidth (if (<= total-width width) - 10000 ;; everything fits - ;; we need to shorten the level headings - (/ (- width nsteps) nsteps))) - (org-odd-levels-only nil) - (n 0) - (total (1+ (length prefix)))) - (setq maxwidth (max maxwidth 10)) - (concat prefix - (if prefix (or separator "/")) - (mapconcat - (lambda (h) - (setq n (1+ n)) - (if (and (= n nsteps) (< maxwidth 10000)) - (setq maxwidth (- total-width total))) - (if (< (length h) maxwidth) - (progn (setq total (+ total (length h) 1)) h) - (setq h (substring h 0 (- maxwidth 2)) - total (+ total maxwidth 1)) - (if (string-match "[ \t]+\\'" h) - (setq h (substring h 0 (match-beginning 0)))) - (setq h (concat h ".."))) - (org-add-props h nil 'face - (nth (% (1- n) org-n-level-faces) - org-level-faces)) - h) - path (or separator "/")))))) + (setq path (delq nil path)) + (unless (> width 0) + (user-error "Argument `width' must be positive")) + (setq separator (or separator "/")) + (let* ((org-odd-levels-only nil) + (fpath (concat + prefix (and prefix path separator) + (mapconcat + (lambda (s) (replace-regexp-in-string "[ \t]+\\'" "" s)) + (cl-loop for head in path + for n from 0 + collect (org-add-props + head nil 'face + (nth (% n org-n-level-faces) org-level-faces))) + separator)))) + (when (> (length fpath) width) + (if (< width 7) + ;; It's unlikely that `width' will be this small, but don't + ;; waste characters by adding ".." if it is. + (setq fpath (substring fpath 0 width)) + (setf (substring fpath (- width 2)) ".."))) + fpath)) (defun org-display-outline-path (&optional file current separator just-return-string) "Display the current outline path in the echo area. @@ -11513,10 +11840,10 @@ If JUST-RETURN-STRING is non-nil, return a string, don't display a message." (bfn (buffer-file-name (buffer-base-buffer))) (path (and (derived-mode-p 'org-mode) (org-get-outline-path))) res) - (if current (setq path (append path - (save-excursion - (org-back-to-heading t) - (if (looking-at org-complex-heading-regexp) + (when current (setq path (append path + (save-excursion + (org-back-to-heading t) + (when (looking-at org-complex-heading-regexp) (list (match-string 4))))))) (setq res (org-format-outline-path @@ -11546,25 +11873,27 @@ the *old* location.") (let ((org-refile-keep t)) (funcall 'org-refile nil nil nil "Copy"))) -(defun org-refile (&optional goto default-buffer rfloc msg) +(defun org-refile (&optional arg default-buffer rfloc msg) "Move the entry or entries at point to another heading. + The list of target headings is compiled using the information in `org-refile-targets', which see. -At the target location, the entry is filed as a subitem of the target -heading. Depending on `org-reverse-note-order', the new subitem will -either be the first or the last subitem. +At the target location, the entry is filed as a subitem of the +target heading. Depending on `org-reverse-note-order', the new +subitem will either be the first or the last subitem. -If there is an active region, all entries in that region will be moved. -However, the region must fulfill the requirement that the first heading -is the first one sets the top-level of the moved text - at most siblings -below it are allowed. +If there is an active region, all entries in that region will be +refiled. However, the region must fulfill the requirement that +the first heading sets the top-level of the moved text. -With prefix arg GOTO, the command will only visit the target location +With a `\\[universal-argument]' ARG, the command will only visit the target \ +location and not actually move anything. -With a double prefix arg \\[universal-argument] \\[universal-argument], \ -go to the location where the last refiling operation has put the subtree. +With a prefix `\\[universal-argument] \\[universal-argument]', go to the \ +location where the last +refiling operation has put the subtree. With a numeric prefix argument of `2', refile to the running clock. @@ -11578,26 +11907,23 @@ RFLOC can be a refile location obtained in a different way. MSG is a string to replace \"Refile\" in the default prompt with another verb. E.g. `org-copy' sets this parameter to \"Copy\". -See also `org-refile-use-outline-path' and `org-completion-use-ido'. +See also `org-refile-use-outline-path'. -If you are using target caching (see `org-refile-use-cache'), -you have to clear the target cache in order to find new targets. -This can be done with a 0 prefix (`C-0 C-c C-w') or a triple +If you are using target caching (see `org-refile-use-cache'), you +have to clear the target cache in order to find new targets. +This can be done with a `0' prefix (`C-0 C-c C-w') or a triple prefix argument (`C-u C-u C-u C-c C-w')." - (interactive "P") - (if (member goto '(0 (64))) + (if (member arg '(0 (64))) (org-refile-cache-clear) (let* ((actionmsg (cond (msg msg) - ((equal goto 3) "Refile (and keep)") + ((equal arg 3) "Refile (and keep)") (t "Refile"))) - (cbuf (current-buffer)) (regionp (org-region-active-p)) (region-start (and regionp (region-beginning))) (region-end (and regionp (region-end))) - (filename (buffer-file-name (buffer-base-buffer cbuf))) - (org-refile-keep (if (equal goto 3) t org-refile-keep)) - pos it nbuf file re level reversed) + (org-refile-keep (if (equal arg 3) t org-refile-keep)) + pos it nbuf file level reversed) (setq last-command nil) (when regionp (goto-char region-start) @@ -11610,10 +11936,10 @@ prefix argument (`C-u C-u C-u C-c C-w')." (org-toggle-heading) (setq region-end (+ (- (point-at-eol) s) region-end))))) (user-error "The region is not a (sequence of) subtree(s)"))) - (if (equal goto '(16)) + (if (equal arg '(16)) (org-refile-goto-last-stored) (when (or - (and (equal goto 2) + (and (equal arg 2) org-clock-hd-marker (marker-buffer org-clock-hd-marker) (prog1 (setq it (list (or org-clock-heading "running clock") @@ -11621,43 +11947,44 @@ prefix argument (`C-u C-u C-u C-c C-w')." (marker-buffer org-clock-hd-marker)) "" (marker-position org-clock-hd-marker))) - (setq goto nil))) - (setq it (or rfloc - (let (heading-text) - (save-excursion - (unless (and goto (listp goto)) - (org-back-to-heading t) - (setq heading-text - (nth 4 (org-heading-components)))) - - (org-refile-get-location - (cond ((and goto (listp goto)) "Goto") - (regionp (concat actionmsg " region to")) - (t (concat actionmsg " subtree \"" - heading-text "\" to"))) - default-buffer - (and (not (equal '(4) goto)) - org-refile-allow-creating-parent-nodes) - goto)))))) + (setq arg nil))) + (setq it + (or rfloc + (let (heading-text) + (save-excursion + (unless (and arg (listp arg)) + (org-back-to-heading t) + (setq heading-text + (replace-regexp-in-string + org-bracket-link-regexp + "\\3" + (or (nth 4 (org-heading-components)) + "")))) + (org-refile-get-location + (cond ((and arg (listp arg)) "Goto") + (regionp (concat actionmsg " region to")) + (t (concat actionmsg " subtree \"" + heading-text "\" to"))) + default-buffer + (and (not (equal '(4) arg)) + org-refile-allow-creating-parent-nodes))))))) (setq file (nth 1 it) - re (nth 2 it) pos (nth 3 it)) - (if (and (not goto) - pos - (equal (buffer-file-name) file) - (if regionp - (and (>= pos region-start) - (<= pos region-end)) - (and (>= pos (point)) - (< pos (save-excursion - (org-end-of-subtree t t)))))) - (error "Cannot refile to position inside the tree or region")) - + (when (and (not arg) + pos + (equal (buffer-file-name) file) + (if regionp + (and (>= pos region-start) + (<= pos region-end)) + (and (>= pos (point)) + (< pos (save-excursion + (org-end-of-subtree t t)))))) + (error "Cannot refile to position inside the tree or region")) (setq nbuf (or (find-buffer-visiting file) (find-file-noselect file))) - (if (and goto (not (equal goto 3))) + (if (and arg (not (equal arg 3))) (progn - (org-pop-to-buffer-same-window nbuf) + (pop-to-buffer-same-window nbuf) (goto-char pos) (org-show-context 'org-goto)) (if regionp @@ -11668,50 +11995,48 @@ prefix argument (`C-u C-u C-u C-c C-w')." (with-current-buffer (setq nbuf (or (find-buffer-visiting file) (find-file-noselect file))) (setq reversed (org-notes-order-reversed-p)) - (save-excursion - (save-restriction - (widen) - (if pos - (progn - (goto-char pos) - (looking-at org-outline-regexp) - (setq level (org-get-valid-level (funcall outline-level) 1)) - (goto-char - (if reversed - (or (outline-next-heading) (point-max)) - (or (save-excursion (org-get-next-sibling)) - (org-end-of-subtree t t) - (point-max))))) - (setq level 1) - (if (not reversed) - (goto-char (point-max)) - (goto-char (point-min)) - (or (outline-next-heading) (goto-char (point-max))))) - (if (not (bolp)) (newline)) - (org-paste-subtree level) - (when org-log-refile - (org-add-log-setup 'refile nil nil 'findpos org-log-refile) - (unless (eq org-log-refile 'note) - (save-excursion (org-add-log-note)))) - (and org-auto-align-tags - (let ((org-loop-over-headlines-in-active-region nil)) - (org-set-tags nil t))) - (let ((bookmark-name (plist-get org-bookmark-names-plist - :last-refile))) - (when bookmark-name - (with-demoted-errors - (bookmark-set bookmark-name)))) - ;; If we are refiling for capture, make sure that the - ;; last-capture pointers point here - (when (org-bound-and-true-p org-refile-for-capture) - (let ((bookmark-name (plist-get org-bookmark-names-plist - :last-capture-marker))) - (when bookmark-name - (with-demoted-errors - (bookmark-set bookmark-name)))) - (move-marker org-capture-last-stored-marker (point))) - (if (fboundp 'deactivate-mark) (deactivate-mark)) - (run-hooks 'org-after-refile-insert-hook)))) + (org-with-wide-buffer + (if pos + (progn + (goto-char pos) + (looking-at org-outline-regexp) + (setq level (org-get-valid-level (funcall outline-level) 1)) + (goto-char + (if reversed + (or (outline-next-heading) (point-max)) + (or (save-excursion (org-get-next-sibling)) + (org-end-of-subtree t t) + (point-max))))) + (setq level 1) + (if (not reversed) + (goto-char (point-max)) + (goto-char (point-min)) + (or (outline-next-heading) (goto-char (point-max))))) + (unless (bolp) (newline)) + (org-paste-subtree level nil nil t) + (when org-log-refile + (org-add-log-setup 'refile nil nil org-log-refile) + (unless (eq org-log-refile 'note) + (save-excursion (org-add-log-note)))) + (and org-auto-align-tags + (let ((org-loop-over-headlines-in-active-region nil)) + (org-set-tags nil t))) + (let ((bookmark-name (plist-get org-bookmark-names-plist + :last-refile))) + (when bookmark-name + (with-demoted-errors + (bookmark-set bookmark-name)))) + ;; If we are refiling for capture, make sure that the + ;; last-capture pointers point here + (when (bound-and-true-p org-capture-is-refiling) + (let ((bookmark-name (plist-get org-bookmark-names-plist + :last-capture-marker))) + (when bookmark-name + (with-demoted-errors + (bookmark-set bookmark-name)))) + (move-marker org-capture-last-stored-marker (point))) + (when (fboundp 'deactivate-mark) (deactivate-mark)) + (run-hooks 'org-after-refile-insert-hook))) (unless org-refile-keep (if regionp (delete-region (point) (+ (point) (- region-end region-start))) @@ -11726,7 +12051,7 @@ prefix argument (`C-u C-u C-u C-c C-w')." (defun org-refile-goto-last-stored () "Go to the location where the last refile was stored." (interactive) - (bookmark-jump "org-refile-last-stored") + (bookmark-jump (plist-get org-bookmark-names-plist :last-refile)) (message "This is the location of the last refile")) (defun org-refile--get-location (refloc tbl) @@ -11740,35 +12065,22 @@ Also check `org-refile-target-table'." (list (replace-regexp-in-string "/$" "" refloc) (replace-regexp-in-string "\\([^/]\\)$" "\\1/" refloc)))))) -(defun org-refile-get-location (&optional prompt default-buffer new-nodes - no-exclude) +(defun org-refile-get-location (&optional prompt default-buffer new-nodes) "Prompt the user for a refile location, using PROMPT. PROMPT should not be suffixed with a colon and a space, because this function appends the default value from -`org-refile-history' automatically, if that is not empty. -When NO-EXCLUDE is set, do not exclude headlines in the current subtree, -this is used for the GOTO interface." +`org-refile-history' automatically, if that is not empty." (let ((org-refile-targets org-refile-targets) - (org-refile-use-outline-path org-refile-use-outline-path) - excluded-entries) - (when (and (derived-mode-p 'org-mode) - (not org-refile-use-cache) - (not no-exclude)) - (org-map-tree - (lambda() - (setq excluded-entries - (append excluded-entries (list (org-get-heading t t))))))) - (setq org-refile-target-table - (org-refile-get-targets default-buffer excluded-entries))) + (org-refile-use-outline-path org-refile-use-outline-path)) + (setq org-refile-target-table (org-refile-get-targets default-buffer))) (unless org-refile-target-table (user-error "No refile targets")) (let* ((cbuf (current-buffer)) - (partial-completion-mode nil) (cfn (buffer-file-name (buffer-base-buffer cbuf))) (cfunc (if (and org-refile-use-outline-path org-outline-path-complete-in-steps) - 'org-olpath-completing-read - 'org-icompleting-read)) + #'org-olpath-completing-read + #'completing-read)) (extra (if org-refile-use-outline-path "/" "")) (cbnex (concat (buffer-name) extra)) (filename (and cfn (expand-file-name cfn))) @@ -11803,8 +12115,8 @@ this is used for the GOTO interface." (cons (car pa) (if (assoc (car org-refile-history) tbl) org-refile-history (cdr org-refile-history)))) - (if (equal (car org-refile-history) (nth 1 org-refile-history)) - (pop org-refile-history))) + (when (equal (car org-refile-history) (nth 1 org-refile-history)) + (pop org-refile-history))) pa) (if (string-match "\\`\\(.*\\)/\\([^/]+\\)\\'" answ) (progn @@ -11827,20 +12139,18 @@ this is used for the GOTO interface." (pos (nth 3 refile-pointer)) buffer) (if (and (not (markerp pos)) (not file)) - (user-error "Please save the buffer to a file before refiling") + (user-error "Please indicate a target file in the refile path") (when (org-string-nw-p re) (setq buffer (if (markerp pos) (marker-buffer pos) (or (find-buffer-visiting file) (find-file-noselect file)))) (with-current-buffer buffer - (save-excursion - (save-restriction - (widen) - (goto-char pos) - (beginning-of-line 1) - (unless (org-looking-at-p re) - (user-error "Invalid refile position, please clear the cache with `C-0 C-c C-w' before refiling"))))))))) + (org-with-wide-buffer + (goto-char pos) + (beginning-of-line 1) + (unless (looking-at-p re) + (user-error "Invalid refile position, please clear the cache with `C-0 C-c C-w' before refiling")))))))) (defun org-refile-new-child (parent-target child) "Use refile target PARENT-TARGET to add new CHILD below it." @@ -11851,53 +12161,43 @@ this is used for the GOTO interface." level) (with-current-buffer (or (find-buffer-visiting file) (find-file-noselect file)) - (save-excursion - (save-restriction - (widen) - (if pos - (goto-char pos) - (goto-char (point-max)) - (if (not (bolp)) (newline))) - (when (looking-at org-outline-regexp) - (setq level (funcall outline-level)) - (org-end-of-subtree t t)) - (org-back-over-empty-lines) - (insert "\n" (make-string - (if pos (org-get-valid-level level 1) 1) ?*) - " " child "\n") - (beginning-of-line 0) - (list (concat (car parent-target) "/" child) file "" (point))))))) + (org-with-wide-buffer + (if pos + (goto-char pos) + (goto-char (point-max)) + (unless (bolp) (newline))) + (when (looking-at org-outline-regexp) + (setq level (funcall outline-level)) + (org-end-of-subtree t t)) + (org-back-over-empty-lines) + (insert "\n" (make-string + (if pos (org-get-valid-level level 1) 1) ?*) + " " child "\n") + (beginning-of-line 0) + (list (concat (car parent-target) "/" child) file "" (point)))))) (defun org-olpath-completing-read (prompt collection &rest args) "Read an outline path like a file name." - (let ((thetable collection) - (org-completion-use-ido nil) ; does not work with ido. - (org-completion-use-iswitchb nil)) ; or iswitchb - (apply - 'org-icompleting-read prompt - (lambda (string predicate &optional flag) - (let (rtn r f (l (length string))) - (cond - ((eq flag nil) - ;; try completion - (try-completion string thetable)) - ((eq flag t) - ;; all-completions - (setq rtn (all-completions string thetable predicate)) - (mapcar - (lambda (x) - (setq r (substring x l)) - (if (string-match " ([^)]*)$" x) - (setq f (match-string 0 x)) - (setq f "")) - (if (string-match "/" r) - (concat string (substring r 0 (match-end 0)) f) - x)) - rtn)) - ((eq flag 'lambda) - ;; exact match? - (assoc string thetable))))) - args))) + (let ((thetable collection)) + (apply #'completing-read + prompt + (lambda (string predicate &optional flag) + (cond + ((eq flag nil) (try-completion string thetable)) + ((eq flag t) + (let ((l (length string))) + (mapcar (lambda (x) + (let ((r (substring x l)) + (f (if (string-match " ([^)]*)$" x) + (match-string 0 x) + ""))) + (if (string-match "/" r) + (concat string (substring r 0 (match-end 0)) f) + x))) + (all-completions string thetable predicate)))) + ;; Exact match? + ((eq flag 'lambda) (assoc string thetable)))) + args))) ;;;; Dynamic blocks @@ -11910,19 +12210,12 @@ If not found, stay at current position and return nil." (setq pos (and (re-search-forward (concat "^[ \t]*#\\+\\(?:BEGIN\\|begin\\):[ \t]+" name "\\>") nil t) (match-beginning 0)))) - (if pos (goto-char pos)) + (when pos (goto-char pos)) pos)) -(defconst org-dblock-start-re - "^[ \t]*#\\+\\(?:BEGIN\\|begin\\):[ \t]+\\(\\S-+\\)\\([ \t]+\\(.*\\)\\)?" - "Matches the start line of a dynamic block, with parameters.") - -(defconst org-dblock-end-re "^[ \t]*#\\+\\(?:END\\|end\\)\\([: \t\r\n]\\|$\\)" - "Matches the end of a dynamic block.") - (defun org-create-dblock (plist) "Create a dynamic block section, with parameters taken from PLIST. -PLIST must contain a :name entry which is used as name of the block." +PLIST must contain a :name entry which is used as the name of the block." (when (string-match "\\S-" (buffer-substring (point-at-bol) (point-at-eol))) (end-of-line 1) (newline)) @@ -12042,13 +12335,14 @@ This function can be used in a hook." ;;;; Completion +(declare-function org-export-backend-options "ox" (cl-x) t) (defun org-get-export-keywords () "Return a list of all currently understood export keywords. Export keywords include options, block names, attributes and keywords relative to each registered export back-end." (let (keywords) (dolist (backend - (org-bound-and-true-p org-export--registered-backends) + (bound-and-true-p org-export-registered-backends) (delq nil keywords)) ;; Back-end name (for keywords, like #+LATEX:) (push (upcase (symbol-name (org-export-backend-name backend))) keywords) @@ -12064,27 +12358,24 @@ keywords relative to each registered export back-end." "TITLE:" "TODO:" "TYP_TODO:" "SELECT_TAGS:" "EXCLUDE_TAGS:")) (defcustom org-structure-template-alist - '(("s" "#+BEGIN_SRC ?\n\n#+END_SRC" "\n\n") - ("e" "#+BEGIN_EXAMPLE\n?\n#+END_EXAMPLE" "\n?\n") - ("q" "#+BEGIN_QUOTE\n?\n#+END_QUOTE" "\n?\n") - ("v" "#+BEGIN_VERSE\n?\n#+END_VERSE" "\n?\n") - ("V" "#+BEGIN_VERBATIM\n?\n#+END_VERBATIM" "\n?\n") - ("c" "#+BEGIN_CENTER\n?\n#+END_CENTER" "
\n?\n
") - ("l" "#+BEGIN_LaTeX\n?\n#+END_LaTeX" - "\n?\n") - ("L" "#+LaTeX: " "?") - ("h" "#+BEGIN_HTML\n?\n#+END_HTML" - "\n?\n") - ("H" "#+HTML: " "?") - ("a" "#+BEGIN_ASCII\n?\n#+END_ASCII" "") - ("A" "#+ASCII: " "") - ("i" "#+INDEX: ?" "#+INDEX: ?") - ("I" "#+INCLUDE: %file ?" - "")) + '(("s" "#+BEGIN_SRC ?\n\n#+END_SRC") + ("e" "#+BEGIN_EXAMPLE\n?\n#+END_EXAMPLE") + ("q" "#+BEGIN_QUOTE\n?\n#+END_QUOTE") + ("v" "#+BEGIN_VERSE\n?\n#+END_VERSE") + ("V" "#+BEGIN_VERBATIM\n?\n#+END_VERBATIM") + ("c" "#+BEGIN_CENTER\n?\n#+END_CENTER") + ("l" "#+BEGIN_EXPORT latex\n?\n#+END_EXPORT") + ("L" "#+LaTeX: ") + ("h" "#+BEGIN_EXPORT html\n?\n#+END_EXPORT") + ("H" "#+HTML: ") + ("a" "#+BEGIN_EXPORT ascii\n?\n#+END_EXPORT") + ("A" "#+ASCII: ") + ("i" "#+INDEX: ?") + ("I" "#+INCLUDE: %file ?")) "Structure completion elements. This is a list of abbreviation keys and values. The value gets inserted if you type `<' followed by the key and then press the completion key, -usually `M-TAB'. %file will be replaced by a file name after prompting +usually `TAB'. %file will be replaced by a file name after prompting for the file using completion. The cursor will be placed at the position of the `?' in the template. There are two templates for each key, the first uses the original Org syntax, @@ -12095,8 +12386,9 @@ variable `org-mtags-prefer-muse-templates'." :type '(repeat (list (string :tag "Key") - (string :tag "Template") - (string :tag "Muse Template")))) + (string :tag "Template"))) + :version "26.1" + :package-version '(Org . "8.3")) (defun org-try-structure-completion () "Try to complete a structure template before point. @@ -12113,29 +12405,28 @@ expands them." (defun org-complete-expand-structure-template (start cell) "Expand a structure template." - (let* ((musep (org-bound-and-true-p org-mtags-prefer-muse-templates)) - (rpl (nth (if musep 2 1) cell)) - (ind "")) + (let ((rpl (nth 1 cell)) + (ind "")) (delete-region start (point)) - (when (string-match "\\`#\\+" rpl) + (when (string-match "\\`[ \t]*#\\+" rpl) (cond ((bolp)) ((not (string-match "\\S-" (buffer-substring (point-at-bol) (point)))) (setq ind (buffer-substring (point-at-bol) (point)))) (t (newline)))) (setq start (point)) - (if (string-match "%file" rpl) - (setq rpl (replace-match - (concat - "\"" - (save-match-data - (abbreviate-file-name (read-file-name "Include file: "))) - "\"") - t t rpl))) + (when (string-match "%file" rpl) + (setq rpl (replace-match + (concat + "\"" + (save-match-data + (abbreviate-file-name (read-file-name "Include file: "))) + "\"") + t t rpl))) (setq rpl (mapconcat 'identity (split-string rpl "\n") (concat "\n" ind))) (insert rpl) - (if (re-search-backward "\\?" start t) (delete-char 1)))) + (when (re-search-backward "\\?" start t) (delete-char 1)))) ;;;; TODO, DEADLINE, Comments @@ -12144,17 +12435,18 @@ expands them." (interactive) (save-excursion (org-back-to-heading) - (let (case-fold-search) - (cond - ((looking-at (format org-heading-keyword-regexp-format - org-comment-string)) - (goto-char (match-end 1)) - (looking-at (concat " +" org-comment-string)) - (replace-match "" t t) - (when (eolp) (insert " "))) - ((looking-at org-outline-regexp) - (goto-char (match-end 0)) - (insert org-comment-string " ")))))) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp)) + (goto-char (or (match-end 3) (match-end 2) (match-end 1))) + (skip-chars-forward " \t") + (unless (memq (char-before) '(?\s ?\t)) (insert " ")) + (if (org-in-commented-heading-p t) + (delete-region (point) + (progn (search-forward " " (line-end-position) 'move) + (skip-chars-forward " \t") + (point))) + (insert org-comment-string) + (unless (eolp) (insert " "))))) (defvar org-last-todo-state-is-todo nil "This is non-nil when the last TODO state change led to a TODO state. @@ -12193,43 +12485,65 @@ nil or a string to be used for the todo mark." ) (interactive "P") (if (eq major-mode 'org-agenda-mode) (apply 'org-agenda-todo-yesterday arg) - (let* ((hour (third (decode-time - (org-current-time)))) + (let* ((org-use-effective-time t) + (hour (nth 2 (decode-time (org-current-time)))) (org-extend-today-until (1+ hour))) (org-todo arg)))) (defvar org-block-entry-blocking "" "First entry preventing the TODO state change.") +(defun org-cancel-repeater () + "Cancel a repeater by setting its numeric value to zero." + (interactive) + (save-excursion + (org-back-to-heading t) + (let ((bound1 (point)) + (bound0 (save-excursion (outline-next-heading) (point)))) + (when (and (re-search-forward + (concat "\\(" org-scheduled-time-regexp "\\)\\|\\(" + org-deadline-time-regexp "\\)\\|\\(" + org-ts-regexp "\\)") + bound0 t) + (re-search-backward "[ \t]+\\(?:[.+]\\)?\\+\\([0-9]+\\)[hdwmy]" + bound1 t)) + (replace-match "0" t nil nil 1))))) + +(defvar org-state) +(defvar org-blocked-by-checkboxes) (defun org-todo (&optional arg) "Change the TODO state of an item. + The state of an item is given by a keyword at the start of the heading, like *** TODO Write paper *** DONE Call mom The different keywords are specified in the variable `org-todo-keywords'. -By default the available states are \"TODO\" and \"DONE\". -So for this example: when the item starts with TODO, it is changed to DONE. +By default the available states are \"TODO\" and \"DONE\". So, for this +example: when the item starts with TODO, it is changed to DONE. When it starts with DONE, the DONE is removed. And when neither TODO nor DONE are present, add TODO at the beginning of the heading. -With \\[universal-argument] prefix arg, use completion to determine the new \ +With `\\[universal-argument]' prefix ARG, use completion to determine the new \ state. -With numeric prefix arg, switch to that state. -With a double \\[universal-argument] prefix, switch to the next set of TODO \ +With numeric prefix ARG, switch to that state. +With a `\\[universal-argument] \\[universal-argument]' prefix, switch to the \ +next set of TODO \ keywords (nextset). -With a triple \\[universal-argument] prefix, circumvent any state blocking. +With a `\\[universal-argument] \\[universal-argument] \\[universal-argument]' \ +prefix, circumvent any state blocking. With a numeric prefix arg of 0, inhibit note taking for the change. - -For calling through lisp, arg is also interpreted in the following way: -`none' -> empty state -\"\" (empty string) -> switch to empty state -`done' -> switch to DONE -`nextset' -> switch to the next set of keywords -`previousset' -> switch to the previous set of keywords -\"WAITING\" -> switch to the specified keyword, but only if it - really is a member of `org-todo-keywords'." +With a numeric prefix arg of -1, cancel repeater to allow marking as DONE. + +When called through ELisp, arg is also interpreted in the following way: +`none' -> empty state +\"\" -> switch to empty state +`done' -> switch to DONE +`nextset' -> switch to the next set of keywords +`previousset' -> switch to the previous set of keywords +\"WAITING\" -> switch to the specified keyword, but only if it + really is a member of `org-todo-keywords'." (interactive "P") (if (and (org-region-active-p) org-loop-over-headlines-in-active-region) (let ((cl (if (eq org-loop-over-headlines-in-active-region 'start-level) @@ -12238,8 +12552,9 @@ For calling through lisp, arg is also interpreted in the following way: (org-map-entries `(org-todo ,arg) org-loop-over-headlines-in-active-region - cl (if (outline-invisible-p) (org-end-of-subtree nil t)))) - (if (equal arg '(16)) (setq arg 'nextset)) + cl (when (org-invisible-p) (org-end-of-subtree nil t)))) + (when (equal arg '(16)) (setq arg 'nextset)) + (when (equal arg -1) (org-cancel-repeater) (setq arg nil)) (let ((org-blocker-hook org-blocker-hook) commentp case-fold-search) @@ -12252,10 +12567,10 @@ For calling through lisp, arg is also interpreted in the following way: (save-excursion (catch 'exit (org-back-to-heading t) - (when (looking-at (concat "^\\*+ " org-comment-string)) + (when (org-in-commented-heading-p t) (org-toggle-comment) (setq commentp t)) - (if (looking-at org-outline-regexp) (goto-char (1- (match-end 0)))) + (when (looking-at org-outline-regexp) (goto-char (1- (match-end 0)))) (or (looking-at (concat " +" org-todo-regexp "\\( +\\|[ \t]*$\\)")) (looking-at "\\(?: *\\|[ \t]*$\\)")) (let* ((match-data (match-data)) @@ -12285,31 +12600,30 @@ For calling through lisp, arg is also interpreted in the following way: (and (not arg) org-use-fast-todo-selection (not (eq org-use-fast-todo-selection 'prefix))))) - ;; Use fast selection + ;; Use fast selection. (org-fast-todo-selection)) ((and (equal arg '(4)) (or (not org-use-fast-todo-selection) (not org-todo-key-trigger))) - ;; Read a state with completion - (org-icompleting-read - "State: " (mapcar 'list org-todo-keywords-1) + ;; Read a state with completion. + (completing-read + "State: " (mapcar #'list org-todo-keywords-1) nil t)) ((eq arg 'right) (if this (if tail (car tail) nil) (car org-todo-keywords-1))) ((eq arg 'left) - (if (equal member org-todo-keywords-1) - nil + (unless (equal member org-todo-keywords-1) (if this (nth (- (length org-todo-keywords-1) (length tail) 2) org-todo-keywords-1) (org-last org-todo-keywords-1)))) ((and (eq org-use-fast-todo-selection t) (equal arg '(4)) - (setq arg nil))) ; hack to fall back to cycling + (setq arg nil))) ;hack to fall back to cycling (arg - ;; user or caller requests a specific state + ;; User or caller requests a specific state. (cond ((equal arg "") nil) ((eq arg 'none) nil) @@ -12327,8 +12641,8 @@ For calling through lisp, arg is also interpreted in the following way: ((nth (1- (prefix-numeric-value arg)) org-todo-keywords-1)))) ((null member) (or head (car org-todo-keywords-1))) - ((equal this final-done-word) nil) ;; -> make empty - ((null tail) nil) ;; -> first entry + ((equal this final-done-word) nil) ;-> make empty + ((null tail) nil) ;-> first entry ((memq interpret '(type priority)) (if (eq this-command last-command) (car tail) @@ -12346,24 +12660,30 @@ For calling through lisp, arg is also interpreted in the following way: :position startpos)) dolog now-done-p) (when org-blocker-hook - (setq org-last-todo-state-is-todo - (not (member this org-done-keywords))) - (unless (save-excursion - (save-match-data - (org-with-wide-buffer - (run-hook-with-args-until-failure - 'org-blocker-hook change-plist)))) - (if (org-called-interactively-p 'interactive) - (user-error "TODO state change from %s to %s blocked (by \"%s\")" - this org-state org-block-entry-blocking) - ;; fail silently - (message "TODO state change from %s to %s blocked (by \"%s\")" - this org-state org-block-entry-blocking) - (throw 'exit nil)))) + (let (org-blocked-by-checkboxes block-reason) + (setq org-last-todo-state-is-todo + (not (member this org-done-keywords))) + (unless (save-excursion + (save-match-data + (org-with-wide-buffer + (run-hook-with-args-until-failure + 'org-blocker-hook change-plist)))) + (setq block-reason (if org-blocked-by-checkboxes + "contained checkboxes" + (format "\"%s\"" org-block-entry-blocking))) + (if (called-interactively-p 'interactive) + (user-error "TODO state change from %s to %s blocked (by %s)" + this org-state block-reason) + ;; Fail silently. + (message "TODO state change from %s to %s blocked (by %s)" + this org-state block-reason) + (throw 'exit nil))))) (store-match-data match-data) (replace-match next t t) - (unless (pos-visible-in-window-p hl-pos) - (message "TODO state changed to %s" (org-trim next))) + (cond ((equal this org-state) + (message "TODO state was already %s" (org-trim next))) + ((pos-visible-in-window-p hl-pos) + (message "TODO state changed to %s" (org-trim next)))) (unless head (setq head (org-get-todo-sequence-head org-state) ass (assoc head org-todo-kwd-alist) @@ -12384,11 +12704,11 @@ For calling through lisp, arg is also interpreted in the following way: (when (and (or org-todo-log-states org-log-done) (not (eq org-inhibit-logging t)) (not (memq arg '(nextset previousset)))) - ;; we need to look at recording a time and note + ;; We need to look at recording a time and note. (setq dolog (or (nth 1 (assoc org-state org-todo-log-states)) (nth 2 (assoc this org-todo-log-states)))) - (if (and (eq dolog 'note) (eq org-inhibit-logging 'note)) - (setq dolog 'time)) + (when (and (eq dolog 'note) (eq org-inhibit-logging 'note)) + (setq dolog 'time)) (when (or (and (not org-state) (not org-closed-keep-when-no-todo)) (and org-state (member org-state org-not-done-keywords) @@ -12397,21 +12717,21 @@ For calling through lisp, arg is also interpreted in the following way: ;; If there was a CLOSED time stamp, get rid of it. (org-add-planning-info nil nil 'closed)) (when (and now-done-p org-log-done) - ;; It is now done, and it was not done before + ;; It is now done, and it was not done before. (org-add-planning-info 'closed (org-current-effective-time)) - (if (and (not dolog) (eq 'note org-log-done)) - (org-add-log-setup 'done org-state this 'findpos 'note))) + (when (and (not dolog) (eq 'note org-log-done)) + (org-add-log-setup 'done org-state this 'note))) (when (and org-state dolog) - ;; This is a non-nil state, and we need to log it - (org-add-log-setup 'state org-state this 'findpos dolog))) - ;; Fixup tag positioning + ;; This is a non-nil state, and we need to log it. + (org-add-log-setup 'state org-state this dolog))) + ;; Fixup tag positioning. (org-todo-trigger-tag-changes org-state) (and org-auto-align-tags (not org-setting-tags) (org-set-tags nil t)) (when org-provide-todo-statistics (org-update-parent-todo-statistics)) (run-hooks 'org-after-todo-state-change-hook) - (if (and arg (not (member org-state org-done-keywords))) - (setq head (org-get-todo-sequence-head org-state))) + (when (and arg (not (member org-state org-done-keywords))) + (setq head (org-get-todo-sequence-head org-state))) (put-text-property (point-at-bol) (point-at-eol) 'org-todo-head head) ;; Do we need to trigger a repeat? (when now-done-p @@ -12421,15 +12741,14 @@ For calling through lisp, arg is also interpreted in the following way: (setq org-agenda-headline-snapshot-before-repeat (org-get-heading)))) (org-auto-repeat-maybe org-state)) - ;; Fixup cursor location if close to the keyword - (if (and (outline-on-heading-p) - (not (bolp)) - (save-excursion (beginning-of-line 1) - (looking-at org-todo-line-regexp)) - (< (point) (+ 2 (or (match-end 2) (match-end 1))))) - (progn - (goto-char (or (match-end 2) (match-end 1))) - (and (looking-at " ") (just-one-space)))) + ;; Fixup cursor location if close to the keyword. + (when (and (outline-on-heading-p) + (not (bolp)) + (save-excursion (beginning-of-line 1) + (looking-at org-todo-line-regexp)) + (< (point) (+ 2 (or (match-end 2) (match-end 1))))) + (goto-char (or (match-end 2) (match-end 1))) + (and (looking-at " ") (just-one-space))) (when org-trigger-hook (save-excursion (run-hook-with-args 'org-trigger-hook change-plist))) @@ -12471,10 +12790,10 @@ changes. Such blocking occurs when: (> child-level this-level)) ;; this todo has children, check whether they are all ;; completed - (if (and (not (org-entry-is-done-p)) - (org-entry-is-todo-p)) - (progn (setq org-block-entry-blocking (org-get-heading)) - (throw 'dont-block nil))) + (when (and (not (org-entry-is-done-p)) + (org-entry-is-todo-p)) + (setq org-block-entry-blocking (org-get-heading)) + (throw 'dont-block nil)) (outline-next-heading) (setq child-level (funcall outline-level)))))) ;; Otherwise, if the task's parent has the :ORDERED: property, and @@ -12482,8 +12801,9 @@ changes. Such blocking occurs when: (save-excursion (org-back-to-heading t) (let* ((pos (point)) - (parent-pos (and (org-up-heading-safe) (point)))) - (if (not parent-pos) (throw 'dont-block t)) ; no parent + (parent-pos (and (org-up-heading-safe) (point))) + (case-fold-search nil)) + (unless parent-pos (throw 'dont-block t)) ; no parent (when (and (org-not-nil (org-entry-get (point) "ORDERED")) (forward-line 1) (re-search-forward org-not-done-heading-regexp pos t)) @@ -12492,11 +12812,11 @@ changes. Such blocking occurs when: ;; Search further up the hierarchy, to see if an ancestor is blocked (while t (goto-char parent-pos) - (if (not (looking-at org-not-done-heading-regexp)) - (throw 'dont-block t)) ; do not block, parent is not a TODO + (unless (looking-at org-not-done-heading-regexp) + (throw 'dont-block t)) ; do not block, parent is not a TODO (setq pos (point)) (setq parent-pos (and (org-up-heading-safe) (point))) - (if (not parent-pos) (throw 'dont-block t)) ; no parent + (unless parent-pos (throw 'dont-block t)) ; no parent (when (and (org-not-nil (org-entry-get (point) "ORDERED")) (forward-line 1) (re-search-forward org-not-done-heading-regexp pos t) @@ -12533,14 +12853,13 @@ See variable `org-track-ordered-property-with-tag'." (org-back-to-heading) (if (org-entry-get nil "ORDERED") (progn - (org-delete-property "ORDERED" "PROPERTIES") + (org-delete-property "ORDERED") (and tag (org-toggle-tag tag 'off)) (message "Subtasks can be completed in arbitrary order")) (org-entry-put nil "ORDERED" "t") (and tag (org-toggle-tag tag 'on)) (message "Subtasks must be completed in sequence"))))) -(defvar org-blocked-by-checkboxes) ; dynamically scoped (defun org-block-todo-from-checkboxes (change-plist) "Block turning an entry into a TODO, using checkboxes. This checks whether the current task should be blocked from state @@ -12564,32 +12883,32 @@ changes because there are unchecked boxes in this entry." (outline-next-heading) (setq end (point)) (goto-char beg) - (if (org-list-search-forward - (concat (org-item-beginning-re) - "\\(?:\\[@\\(?:start:\\)?\\([0-9]+\\|[A-Za-z]\\)\\][ \t]*\\)?" - "\\[[- ]\\]") - end t) - (progn - (if (boundp 'org-blocked-by-checkboxes) - (setq org-blocked-by-checkboxes t)) - (throw 'dont-block nil))))) + (when (org-list-search-forward + (concat (org-item-beginning-re) + "\\(?:\\[@\\(?:start:\\)?\\([0-9]+\\|[A-Za-z]\\)\\][ \t]*\\)?" + "\\[[- ]\\]") + end t) + (when (boundp 'org-blocked-by-checkboxes) + (setq org-blocked-by-checkboxes t)) + (throw 'dont-block nil)))) t))) ; do not block (defun org-entry-blocked-p () - "Is the current entry blocked?" - (org-with-silent-modifications - (if (org-entry-get nil "NOBLOCKING") - nil ;; Never block this entry - (not (run-hook-with-args-until-failure - 'org-blocker-hook - (list :type 'todo-state-change - :position (point) - :from 'todo - :to 'done)))))) + "Non-nil if entry at point is blocked." + (and (not (org-entry-get nil "NOBLOCKING")) + (member (org-entry-get nil "TODO") org-not-done-keywords) + (not (run-hook-with-args-until-failure + 'org-blocker-hook + (list :type 'todo-state-change + :position (point) + :from 'todo + :to 'done))))) (defun org-update-statistics-cookies (all) "Update the statistics cookie, either from TODO or from checkboxes. -This should be called with the cursor in a line with a statistics cookie." +This should be called with the cursor in a line with a statistics +cookie. When called with a \\[universal-argument] prefix, update +all statistics cookies in the buffer." (interactive "P") (if all (progn @@ -12605,7 +12924,7 @@ This should be called with the cursor in a line with a statistics cookie." (setq l1 (org-outline-level)) (setq end (save-excursion (outline-next-heading) - (if (org-at-heading-p) (setq l2 (org-outline-level))) + (when (org-at-heading-p) (setq l2 (org-outline-level))) (point))) (if (and (save-excursion (re-search-forward @@ -12642,7 +12961,7 @@ statistics everywhere." (box-re "\\(\\(\\[[0-9]*%\\]\\)\\|\\(\\[[0-9]*/[0-9]*\\]\\)\\)") level ltoggle l1 new ndel (cnt-all 0) (cnt-done 0) is-percent kwd - checkbox-beg ov ovs ove cookie-present) + checkbox-beg cookie-present) (catch 'exit (save-excursion (beginning-of-line 1) @@ -12677,14 +12996,31 @@ statistics everywhere." (setq kwd (and (or recursive (= l1 ltoggle)) (match-string 2))) (if (or (eq org-provide-todo-statistics 'all-headlines) + (and (eq org-provide-todo-statistics t) + (or (member kwd org-done-keywords))) (and (listp org-provide-todo-statistics) + (stringp (car org-provide-todo-statistics)) (or (member kwd org-provide-todo-statistics) - (member kwd org-done-keywords)))) + (member kwd org-done-keywords))) + (and (listp org-provide-todo-statistics) + (listp (car org-provide-todo-statistics)) + (or (member kwd (car org-provide-todo-statistics)) + (and (member kwd org-done-keywords) + (member kwd (cadr org-provide-todo-statistics)))))) (setq cnt-all (1+ cnt-all)) - (if (eq org-provide-todo-statistics t) - (and kwd (setq cnt-all (1+ cnt-all))))) - (and (member kwd org-done-keywords) - (setq cnt-done (1+ cnt-done))) + (and (eq org-provide-todo-statistics t) + kwd + (setq cnt-all (1+ cnt-all)))) + (when (or (and (member org-provide-todo-statistics '(t all-headlines)) + (member kwd org-done-keywords)) + (and (listp org-provide-todo-statistics) + (listp (car org-provide-todo-statistics)) + (member kwd org-done-keywords) + (member kwd (cadr org-provide-todo-statistics))) + (and (listp org-provide-todo-statistics) + (stringp (car org-provide-todo-statistics)) + (member kwd org-done-keywords))) + (setq cnt-done (1+ cnt-done))) (outline-next-heading))) (setq new (if is-percent @@ -12692,15 +13028,10 @@ statistics everywhere." (max 1 cnt-all))) (format "[%d/%d]" cnt-done cnt-all)) ndel (- (match-end 0) checkbox-beg)) - ;; handle overlays when updating cookie from column view - (when (setq ov (car (overlays-at checkbox-beg))) - (setq ovs (overlay-start ov) ove (overlay-end ov)) - (delete-overlay ov)) (goto-char checkbox-beg) (insert new) (delete-region (point) (+ (point) ndel)) - (when org-auto-align-tags (org-fix-tags-on-the-fly)) - (when ov (move-overlay ov ovs ove))) + (when org-auto-align-tags (org-fix-tags-on-the-fly))) (when cookie-present (run-hook-with-args 'org-after-todo-statistics-hook cnt-done (- cnt-all cnt-done)))))) @@ -12736,9 +13067,9 @@ This hook runs even if there is no statistics cookie present, in which case (when (and (stringp state) (> (length state) 0)) (setq changes (append changes (cdr (assoc state l))))) (when (member state org-not-done-keywords) - (setq changes (append changes (cdr (assoc 'todo l))))) + (setq changes (append changes (cdr (assq 'todo l))))) (when (member state org-done-keywords) - (setq changes (append changes (cdr (assoc 'done l))))) + (setq changes (append changes (cdr (assq 'done l))))) (dolist (c changes) (org-toggle-tag (car c) (if (cdr c) 'on 'off))))) @@ -12749,7 +13080,7 @@ This hook runs even if there is no statistics cookie present, in which case org-log-repeat nil org-todo-log-states nil) (dolist (w (org-split-string value)) - (let* (a) + (let (a) (cond ((setq a (assoc w org-startup-options)) (and (member (nth 1 a) '(org-log-done org-log-repeat)) @@ -12786,7 +13117,7 @@ Returns the new TODO keyword, or nil if no state change should occur." (expert nil) (fwidth (+ maxlen 3 1 3)) (ncol (/ (- (window-width) 4) fwidth)) - tg cnt c tbl + tg cnt e c tbl groups ingroup) (save-excursion (save-window-excursion @@ -12794,13 +13125,13 @@ Returns the new TODO keyword, or nil if no state change should occur." (set-buffer (get-buffer-create " *Org todo*")) (org-switch-to-buffer-other-window (get-buffer-create " *Org todo*"))) (erase-buffer) - (org-set-local 'org-done-keywords done-keywords) + (setq-local org-done-keywords done-keywords) (setq tbl fulltable cnt 0) - (dolist (e tbl) + (while (setq e (pop tbl)) (cond ((equal e '(:startgroup)) (push '() groups) (setq ingroup t) - (when (not (= cnt 0)) + (unless (= cnt 0) (setq cnt 0) (insert "\n")) (insert "{ ")) @@ -12808,7 +13139,7 @@ Returns the new TODO keyword, or nil if no state change should occur." (setq ingroup nil cnt 0) (insert "}\n")) ((equal e '(:newline)) - (when (not (= cnt 0)) + (unless (= cnt 0) (setq cnt 0) (insert "\n") (setq e (car tbl)) @@ -12817,19 +13148,19 @@ Returns the new TODO keyword, or nil if no state change should occur." (setq tbl (cdr tbl))))) (t (setq tg (car e) c (cdr e)) - (if ingroup (push tg (car groups))) + (when ingroup (push tg (car groups))) (setq tg (org-add-props tg nil 'face (org-get-todo-face tg))) - (if (and (= cnt 0) (not ingroup)) (insert " ")) + (when (and (= cnt 0) (not ingroup)) (insert " ")) (insert "[" c "] " tg (make-string (- fwidth 4 (length tg)) ?\ )) (when (= (setq cnt (1+ cnt)) ncol) (insert "\n") - (if ingroup (insert " ")) + (when ingroup (insert " ")) (setq cnt 0))))) (insert "\n") (goto-char (point-min)) - (if (not expert) (org-fit-window-to-buffer)) + (unless expert (org-fit-window-to-buffer)) (message "[a-z..]:Set [SPC]:clear") (setq c (let ((inhibit-quit t)) (read-char-exclusive))) (cond @@ -12851,12 +13182,19 @@ Returns the new TODO keyword, or nil if no state change should occur." "Return the TODO keyword of the current subtree." (save-excursion (org-back-to-heading t) - (and (looking-at org-todo-line-regexp) + (and (let ((case-fold-search nil)) (looking-at org-todo-line-regexp)) (match-end 2) (match-string 2)))) (defun org-at-date-range-p (&optional inactive-ok) - "Is the cursor inside a date range?" + "Non-nil if point is inside a date range. + +When optional argument INACTIVE-OK is non-nil, also consider +inactive time ranges. + +When this function returns a non-nil value, match data is set +according to `org-tr-regexp-both' or `org-tr-regexp', depending +on INACTIVE-OK." (interactive) (save-excursion (catch 'exit @@ -12888,14 +13226,15 @@ Returns the new TODO keyword, or nil if no state change should occur." (defvar org-last-inserted-timestamp) (defvar org-log-post-message) (defvar org-log-note-purpose) -(defvar org-log-note-how) +(defvar org-log-note-how nil) (defvar org-log-note-extra) (defun org-auto-repeat-maybe (done-word) - "Check if the current headline contains a repeated deadline/schedule. + "Check if the current headline contains a repeated time-stamp. + If yes, set TODO state back to what it was and change the base date of repeating deadline/scheduled time stamps to new date. + This function is run automatically after each state change to a DONE state." - ;; last-state is dynamically scoped into this function (let* ((repeat (org-get-repeat)) (aa (assoc org-last-state org-todo-kwd-alist)) (interpret (nth 1 aa)) @@ -12903,73 +13242,108 @@ This function is run automatically after each state change to a DONE state." (whata '(("h" . hour) ("d" . day) ("m" . month) ("y" . year))) (msg "Entry repeats: ") (org-log-done nil) - (org-todo-log-states nil) - re type n what ts time to-state) - (when repeat - (if (eq org-log-repeat t) (setq org-log-repeat 'state)) - (setq to-state (or (org-entry-get nil "REPEAT_TO_STATE") - org-todo-repeat-to-state)) - (unless (and to-state (member to-state org-todo-keywords-1)) - (setq to-state (if (eq interpret 'type) org-last-state head))) - (org-todo to-state) + (org-todo-log-states nil)) + (when (and repeat (not (zerop (string-to-number (substring repeat 1))))) + (when (eq org-log-repeat t) (setq org-log-repeat 'state)) + (let ((to-state (or (org-entry-get nil "REPEAT_TO_STATE" 'selective) + org-todo-repeat-to-state))) + (org-todo (cond ((and to-state (member to-state org-todo-keywords-1)) + to-state) + ((eq interpret 'type) org-last-state) + (head) + (t 'none)))) (when (or org-log-repeat (org-entry-get nil "CLOCK")) (org-entry-put nil "LAST_REPEAT" (format-time-string (org-time-stamp-format t t)))) (when org-log-repeat (if (or (memq 'org-add-log-note (default-value 'post-command-hook)) (memq 'org-add-log-note post-command-hook)) - ;; OK, we are already setup for some record - (if (eq org-log-repeat 'note) - ;; make sure we take a note, not only a time stamp - (setq org-log-note-how 'note)) - ;; Set up for taking a record - (org-add-log-setup 'state (or done-word (car org-done-keywords)) + ;; We are already setup for some record. + (when (eq org-log-repeat 'note) + ;; Make sure we take a note, not only a time stamp. + (setq org-log-note-how 'note)) + ;; Set up for taking a record. + (org-add-log-setup 'state + (or done-word (car org-done-keywords)) org-last-state - 'findpos org-log-repeat))) + org-log-repeat))) (org-back-to-heading t) (org-add-planning-info nil nil 'closed) - (setq re (concat "\\(" org-scheduled-time-regexp "\\)\\|\\(" - org-deadline-time-regexp "\\)\\|\\(" - org-ts-regexp "\\)")) - (while (re-search-forward - re (save-excursion (outline-next-heading) (point)) t) - (setq type (if (match-end 1) org-scheduled-string - (if (match-end 3) org-deadline-string "Plain:")) - ts (match-string (if (match-end 2) 2 (if (match-end 4) 4 0)))) - (when (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)" ts) - (setq n (string-to-number (match-string 2 ts)) - what (match-string 3 ts)) - (if (equal what "w") (setq n (* n 7) what "d")) - (if (and (equal what "h") (not (string-match "[0-9]\\{1,2\\}:[0-9]\\{2\\}" ts))) - (user-error "Cannot repeat in Repeat in %d hour(s) because no hour has been set" n)) - ;; Preparation, see if we need to modify the start date for the change - (when (match-end 1) - (setq time (save-match-data (org-time-string-to-time ts))) + (let ((end (save-excursion (outline-next-heading) (point))) + (planning-re (regexp-opt + (list org-scheduled-string org-deadline-string)))) + (while (re-search-forward org-ts-regexp end t) + (let* ((ts (match-string 0)) + (planning? (org-at-planning-p)) + (type (if (not planning?) "Plain:" + (save-excursion + (re-search-backward + planning-re (line-beginning-position) t) + (match-string 0))))) (cond - ((equal (match-string 1 ts) ".") - ;; Shift starting date to today - (org-timestamp-change - (- (org-today) (time-to-days time)) - 'day)) - ((equal (match-string 1 ts) "+") - (let ((nshiftmax 10) (nshift 0)) - (while (or (= nshift 0) - (<= (time-to-days time) - (time-to-days (current-time)))) - (when (= (incf nshift) nshiftmax) - (or (y-or-n-p (message "%d repeater intervals were not enough to shift date past today. Continue? " nshift)) - (error "Abort"))) - (org-timestamp-change n (cdr (assoc what whata))) - (org-at-timestamp-p t) - (setq ts (match-string 1)) - (setq time (save-match-data (org-time-string-to-time ts))))) - (org-timestamp-change (- n) (cdr (assoc what whata))) - ;; rematch, so that we have everything in place for the real shift - (org-at-timestamp-p t) - (setq ts (match-string 1)) - (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)" ts)))) - (save-excursion (org-timestamp-change n (cdr (assoc what whata)) nil t)) - (setq msg (concat msg type " " org-last-changed-timestamp " ")))) + ;; Ignore fake time-stamps (e.g., within comments). + ((and (not planning?) + (not (org-at-property-p)) + (not (eq 'timestamp + (org-element-type (save-excursion + (backward-char) + (org-element-context))))))) + ;; Time-stamps without a repeater are usually skipped. + ;; However, a SCHEDULED time-stamp without one is + ;; removed, as it is considered as no longer relevant. + ((not (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)" ts)) + (when (equal type org-scheduled-string) + (org-remove-timestamp-with-keyword type))) + (t + (let ((n (string-to-number (match-string 2 ts))) + (what (match-string 3 ts))) + (when (equal what "w") (setq n (* n 7) what "d")) + (when (and (equal what "h") + (not (string-match-p "[0-9]\\{1,2\\}:[0-9]\\{2\\}" + ts))) + (user-error + "Cannot repeat in Repeat in %d hour(s) because no hour \ +has been set" + n)) + ;; Preparation, see if we need to modify the start + ;; date for the change. + (when (match-end 1) + (let ((time (save-match-data (org-time-string-to-time ts)))) + (cond + ((equal (match-string 1 ts) ".") + ;; Shift starting date to today + (org-timestamp-change + (- (org-today) (time-to-days time)) + 'day)) + ((equal (match-string 1 ts) "+") + (let ((nshiftmax 10) + (nshift 0)) + (while (or (= nshift 0) + (not (time-less-p (current-time) time))) + (when (= (cl-incf nshift) nshiftmax) + (or (y-or-n-p + (format "%d repeater intervals were not \ +enough to shift date past today. Continue? " + nshift)) + (user-error "Abort"))) + (org-timestamp-change n (cdr (assoc what whata))) + (org-at-timestamp-p t) + (setq ts (match-string 1)) + (setq time + (save-match-data + (org-time-string-to-time ts))))) + (org-timestamp-change (- n) (cdr (assoc what whata))) + ;; Rematch, so that we have everything in place + ;; for the real shift. + (org-at-timestamp-p t) + (setq ts (match-string 1)) + (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)" + ts))))) + (save-excursion + (org-timestamp-change n (cdr (assoc what whata)) nil t)) + (setq msg + (concat + msg type " " org-last-changed-timestamp " ")))))))) (setq org-log-post-message msg) (message "%s" msg)))) @@ -12977,7 +13351,7 @@ This function is run automatically after each state change to a DONE state." "Make a compact tree which shows all headlines marked with TODO. The tree will show the lines where the regexp matches, and all higher headlines above the match. -With a \\[universal-argument] prefix, prompt for a regexp to match. +With a `\\[universal-argument]' prefix, prompt for a regexp to match. With a numeric prefix N, construct a sparse tree for the Nth element of `org-todo-keywords-1'." (interactive "P") @@ -12985,8 +13359,9 @@ of `org-todo-keywords-1'." (kwd-re (cond ((null arg) org-not-done-regexp) ((equal arg '(4)) - (let ((kwd (org-icompleting-read "Keyword (or KWD1|KWD2|...): " - (mapcar 'list org-todo-keywords-1)))) + (let ((kwd + (completing-read "Keyword (or KWD1|KWD2|...): " + (mapcar #'list org-todo-keywords-1)))) (concat "\\(" (mapconcat 'identity (org-split-string kwd "|") "\\|") "\\)\\>"))) @@ -12997,75 +13372,99 @@ of `org-todo-keywords-1'." (message "%d TODO entries found" (org-occur (concat "^" org-outline-regexp " *" kwd-re ))))) +(defun org--deadline-or-schedule (arg type time) + "Insert DEADLINE or SCHEDULE information in current entry. +TYPE is either `deadline' or `scheduled'. See `org-deadline' or +`org-schedule' for information about ARG and TIME arguments." + (let* ((deadline? (eq type 'deadline)) + (keyword (if deadline? org-deadline-string org-scheduled-string)) + (log (if deadline? org-log-redeadline org-log-reschedule)) + (old-date (org-entry-get nil (if deadline? "DEADLINE" "SCHEDULED"))) + (old-date-time (and old-date (org-time-string-to-time old-date))) + ;; Save repeater cookie from either TIME or current scheduled + ;; time stamp. We are going to insert it back at the end of + ;; the process. + (repeater (or (and (org-string-nw-p time) + ;; We use `org-repeat-re' because we need + ;; to tell the difference between a real + ;; repeater and a time delta, e.g. "+2d". + (string-match org-repeat-re time) + (match-string 1 time)) + (and (org-string-nw-p old-date) + (string-match "\\([.+-]+[0-9]+[hdwmy]\ +\\(?:[/ ][-+]?[0-9]+[hdwmy]\\)?\\)" + old-date) + (match-string 1 old-date))))) + (pcase arg + (`(4) + (when (and old-date log) + (org-add-log-setup (if deadline? 'deldeadline 'delschedule) + nil old-date log)) + (org-remove-timestamp-with-keyword keyword) + (message (if deadline? "Item no longer has a deadline." + "Item is no longer scheduled."))) + (`(16) + (save-excursion + (org-back-to-heading t) + (let ((regexp (if deadline? org-deadline-time-regexp + org-scheduled-time-regexp))) + (if (not (re-search-forward regexp (line-end-position 2) t)) + (user-error (if deadline? "No deadline information to update" + "No scheduled information to update")) + (let* ((rpl0 (match-string 1)) + (rpl (replace-regexp-in-string " -[0-9]+[hdwmy]" "" rpl0)) + (msg (if deadline? "Warn starting from" "Delay until"))) + (replace-match + (concat keyword + " <" rpl + (format " -%dd" + (abs (- (time-to-days + (save-match-data + (org-read-date + nil t nil msg old-date-time))) + (time-to-days old-date-time)))) + ">") t t)))))) + (_ + (org-add-planning-info type time 'closed) + (when (and old-date + log + (not (equal old-date org-last-inserted-timestamp))) + (org-add-log-setup (if deadline? 'redeadline 'reschedule) + org-last-inserted-timestamp + old-date + log)) + (when repeater + (save-excursion + (org-back-to-heading t) + (when (re-search-forward + (concat keyword " " org-last-inserted-timestamp) + (line-end-position 2) + t) + (goto-char (1- (match-end 0))) + (insert " " repeater) + (setq org-last-inserted-timestamp + (concat (substring org-last-inserted-timestamp 0 -1) + " " repeater + (substring org-last-inserted-timestamp -1)))))) + (message (if deadline? "Deadline on %s" "Scheduled to %s") + org-last-inserted-timestamp))))) + (defun org-deadline (arg &optional time) "Insert the \"DEADLINE:\" string with a timestamp to make a deadline. With one universal prefix argument, remove any deadline from the item. With two universal prefix arguments, prompt for a warning delay. With argument TIME, set the deadline at the corresponding date. TIME -can either be an Org date like \"2011-07-24\" or a delta like \"+2d\"." - (interactive "P") - (if (and (org-region-active-p) org-loop-over-headlines-in-active-region) - (let ((cl (if (eq org-loop-over-headlines-in-active-region 'start-level) - 'region-start-level 'region)) - org-loop-over-headlines-in-active-region) - (org-map-entries - `(org-deadline ',arg ,time) - org-loop-over-headlines-in-active-region - cl (if (outline-invisible-p) (org-end-of-subtree nil t)))) - (let* ((old-date (org-entry-get nil "DEADLINE")) - (old-date-time (if old-date (org-time-string-to-time old-date))) - (repeater (and old-date - (string-match - "\\([.+-]+[0-9]+[hdwmy]\\(?:[/ ][-+]?[0-9]+[hdwmy]\\)?\\) ?" - old-date) - (match-string 1 old-date)))) - (cond - ((equal arg '(4)) - (when (and old-date org-log-redeadline) - (org-add-log-setup 'deldeadline nil old-date 'findpos - org-log-redeadline)) - (org-remove-timestamp-with-keyword org-deadline-string) - (message "Item no longer has a deadline.")) - ((equal arg '(16)) - (save-excursion - (org-back-to-heading t) - (if (re-search-forward - org-deadline-time-regexp - (save-excursion (outline-next-heading) (point)) t) - (let* ((rpl0 (match-string 1)) - (rpl (replace-regexp-in-string " -[0-9]+[hdwmy]" "" rpl0))) - (replace-match - (concat org-deadline-string - " <" rpl - (format " -%dd" - (abs - (- (time-to-days - (save-match-data - (org-read-date nil t nil "Warn starting from" old-date-time))) - (time-to-days old-date-time)))) - ">") t t)) - (user-error "No deadline information to update")))) - (t - (org-add-planning-info 'deadline time 'closed) - (when (and old-date org-log-redeadline - (not (equal old-date - (substring org-last-inserted-timestamp 1 -1)))) - (org-add-log-setup 'redeadline nil old-date 'findpos - org-log-redeadline)) - (when repeater - (save-excursion - (org-back-to-heading t) - (when (re-search-forward (concat org-deadline-string " " - org-last-inserted-timestamp) - (save-excursion - (outline-next-heading) (point)) t) - (goto-char (1- (match-end 0))) - (insert " " repeater) - (setq org-last-inserted-timestamp - (concat (substring org-last-inserted-timestamp 0 -1) - " " repeater - (substring org-last-inserted-timestamp -1)))))) - (message "Deadline on %s" org-last-inserted-timestamp)))))) +can either be an Org date like \"2011-07-24\" or a delta like \"+2d\"." + (interactive "P") + (if (and (org-region-active-p) org-loop-over-headlines-in-active-region) + (org-map-entries + (lambda () (org--deadline-or-schedule arg 'deadline time)) + nil + (if (eq org-loop-over-headlines-in-active-region 'start-level) + 'region-start-level + 'region) + (lambda () (when (org-invisible-p) (org-end-of-subtree nil t)))) + (org--deadline-or-schedule arg 'deadline time))) (defun org-schedule (arg &optional time) "Insert the SCHEDULED: string with a timestamp to schedule a TODO item. @@ -13075,68 +13474,14 @@ With argument TIME, scheduled at the corresponding date. TIME can either be an Org date like \"2011-07-24\" or a delta like \"+2d\"." (interactive "P") (if (and (org-region-active-p) org-loop-over-headlines-in-active-region) - (let ((cl (if (eq org-loop-over-headlines-in-active-region 'start-level) - 'region-start-level 'region)) - org-loop-over-headlines-in-active-region) - (org-map-entries - `(org-schedule ',arg ,time) - org-loop-over-headlines-in-active-region - cl (if (outline-invisible-p) (org-end-of-subtree nil t)))) - (let* ((old-date (org-entry-get nil "SCHEDULED")) - (old-date-time (if old-date (org-time-string-to-time old-date))) - (repeater (and old-date - (string-match - "\\([.+-]+[0-9]+[hdwmy]\\(?:[/ ][-+]?[0-9]+[hdwmy]\\)?\\) ?" - old-date) - (match-string 1 old-date)))) - (cond - ((equal arg '(4)) - (progn - (when (and old-date org-log-reschedule) - (org-add-log-setup 'delschedule nil old-date 'findpos - org-log-reschedule)) - (org-remove-timestamp-with-keyword org-scheduled-string) - (message "Item is no longer scheduled."))) - ((equal arg '(16)) - (save-excursion - (org-back-to-heading t) - (if (re-search-forward - org-scheduled-time-regexp - (save-excursion (outline-next-heading) (point)) t) - (let* ((rpl0 (match-string 1)) - (rpl (replace-regexp-in-string " -[0-9]+[hdwmy]" "" rpl0))) - (replace-match - (concat org-scheduled-string - " <" rpl - (format " -%dd" - (abs - (- (time-to-days - (save-match-data - (org-read-date nil t nil "Delay until" old-date-time))) - (time-to-days old-date-time)))) - ">") t t)) - (user-error "No scheduled information to update")))) - (t - (org-add-planning-info 'scheduled time 'closed) - (when (and old-date org-log-reschedule - (not (equal old-date - (substring org-last-inserted-timestamp 1 -1)))) - (org-add-log-setup 'reschedule nil old-date 'findpos - org-log-reschedule)) - (when repeater - (save-excursion - (org-back-to-heading t) - (when (re-search-forward (concat org-scheduled-string " " - org-last-inserted-timestamp) - (save-excursion - (outline-next-heading) (point)) t) - (goto-char (1- (match-end 0))) - (insert " " repeater) - (setq org-last-inserted-timestamp - (concat (substring org-last-inserted-timestamp 0 -1) - " " repeater - (substring org-last-inserted-timestamp -1)))))) - (message "Scheduled to %s" org-last-inserted-timestamp)))))) + (org-map-entries + (lambda () (org--deadline-or-schedule arg 'scheduled time)) + nil + (if (eq org-loop-over-headlines-in-active-region 'start-level) + 'region-start-level + 'region) + (lambda () (when (org-invisible-p) (org-end-of-subtree nil t)))) + (org--deadline-or-schedule arg 'scheduled time))) (defun org-get-scheduled-time (pom &optional inherit) "Get the scheduled time as a time tuple, of a format suitable @@ -13167,24 +13512,36 @@ nil." (if (and (string-match "\\S-" (buffer-substring (point-at-bol) (point))) (equal (char-before) ?\ )) (backward-delete-char 1) - (if (string-match "^[ \t]*$" (buffer-substring - (point-at-bol) (point-at-eol))) - (delete-region (point-at-bol) - (min (point-max) (1+ (point-at-eol)))))))))) + (when (string-match "^[ \t]*$" (buffer-substring + (point-at-bol) (point-at-eol))) + (delete-region (point-at-bol) + (min (point-max) (1+ (point-at-eol)))))))))) (defvar org-time-was-given) ; dynamically scoped parameter (defvar org-end-time-was-given) ; dynamically scoped parameter -(defun org-add-planning-info (what &optional time &rest remove) - "Insert new timestamp with keyword in the line directly after the headline. -WHAT indicates what kind of time stamp to add. TIME indicates the time to use. -If non is given, the user is prompted for a date. -REMOVE indicates what kind of entries to remove. An old WHAT entry will also -be removed." - (interactive) - (let (org-time-was-given org-end-time-was-given ts - end default-time default-input) +(defun org-at-planning-p () + "Non-nil when point is on a planning info line." + ;; This is as accurate and faster than `org-element-at-point' since + ;; planning info location is fixed in the section. + (org-with-wide-buffer + (beginning-of-line) + (and (looking-at-p org-planning-line-re) + (eq (point) + (ignore-errors + (if (and (featurep 'org-inlinetask) (org-inlinetask-in-task-p)) + (org-back-to-heading t) + (org-with-limited-levels (org-back-to-heading t))) + (line-beginning-position 2)))))) +(defun org-add-planning-info (what &optional time &rest remove) + "Insert new timestamp with keyword in the planning line. +WHAT indicates what kind of time stamp to add. It is a symbol +among `closed', `deadline', `scheduled' and nil. TIME indicates +the time to use. If none is given, the user is prompted for +a date. REMOVE indicates what kind of entries to remove. An old +WHAT entry will also be removed." + (let (org-time-was-given org-end-time-was-given default-time default-input) (catch 'exit (when (and (memq what '(scheduled deadline)) (or (not time) @@ -13193,108 +13550,98 @@ be removed." ;; Try to get a default date/time from existing timestamp (save-excursion (org-back-to-heading t) - (setq end (save-excursion (outline-next-heading) (point))) - (when (re-search-forward (if (eq what 'scheduled) - org-scheduled-time-regexp - org-deadline-time-regexp) - end t) - (setq ts (match-string 1) - default-time - (apply 'encode-time (org-parse-time-string ts)) - default-input (and ts (org-get-compact-tod ts)))))) + (let ((end (save-excursion (outline-next-heading) (point))) ts) + (when (re-search-forward (if (eq what 'scheduled) + org-scheduled-time-regexp + org-deadline-time-regexp) + end t) + (setq ts (match-string 1) + default-time (apply 'encode-time (org-parse-time-string ts)) + default-input (and ts (org-get-compact-tod ts))))))) (when what (setq time (if (stringp time) - ;; This is a string (relative or absolute), set proper date - (apply 'encode-time + ;; This is a string (relative or absolute), set + ;; proper date. + (apply #'encode-time (org-read-date-analyze time default-time (decode-time default-time))) ;; If necessary, get the time from the user (or time (org-read-date nil 'to-time nil nil default-time default-input))))) - (when (and org-insert-labeled-timestamps-at-point - (member what '(scheduled deadline))) - (insert - (if (eq what 'scheduled) org-scheduled-string org-deadline-string) " ") - (org-insert-time-stamp time org-time-was-given - nil nil nil (list org-end-time-was-given)) - (setq what nil)) - (save-excursion - (save-restriction - (let (col list elt ts buffer-invisibility-spec) - (org-back-to-heading t) - (looking-at (concat org-outline-regexp "\\( *\\)[^\r\n]*")) - (goto-char (match-end 1)) - (setq col (current-column)) - (goto-char (match-end 0)) - (if (eobp) (insert "\n") (forward-char 1)) - (when (and (not what) - (not (looking-at - (concat "[ \t]*" - org-keyword-time-not-clock-regexp)))) - ;; Nothing to add, nothing to remove...... :-) - (throw 'exit nil)) - (if (and (not (looking-at org-outline-regexp)) - (looking-at (concat "[^\r\n]*?" org-keyword-time-regexp - "[^\r\n]*")) - (not (equal (match-string 1) org-clock-string))) - (narrow-to-region (match-beginning 0) (match-end 0)) - (insert-before-markers "\n") - (backward-char 1) - (narrow-to-region (point) (point)) - (and org-adapt-indentation (org-indent-to-column col))) - ;; Check if we have to remove something. - (setq list (cons what remove)) - (while list - (setq elt (pop list)) - (when (or (and (eq elt 'scheduled) - (re-search-forward org-scheduled-time-regexp nil t)) - (and (eq elt 'deadline) - (re-search-forward org-deadline-time-regexp nil t)) - (and (eq elt 'closed) - (re-search-forward org-closed-time-regexp nil t))) - (replace-match "") - (if (looking-at "--+<[^>]+>") (replace-match "")))) - (and (looking-at "[ \t]+") (replace-match "")) - (and org-adapt-indentation (bolp) (org-indent-to-column col)) - (when what - (insert - (if (not (or (bolp) (eq (char-before) ?\ ))) " " "") - (cond ((eq what 'scheduled) org-scheduled-string) - ((eq what 'deadline) org-deadline-string) - ((eq what 'closed) org-closed-string)) - " ") - (setq ts (org-insert-time-stamp - time - (or org-time-was-given - (and (eq what 'closed) org-log-done-with-time)) - (eq what 'closed) - nil nil (list org-end-time-was-given))) - (insert - (if (not (or (bolp) (eq (char-before) ?\ ) - (memq (char-after) '(32 10)) - (eobp))) " " "")) - (end-of-line 1)) - (goto-char (point-min)) - (widen) - (if (and (looking-at "[ \t]*\n") - (equal (char-before) ?\n)) - (delete-region (1- (point)) (point-at-eol))) - ts)))))) - -(defvar org-log-note-marker (make-marker)) + (org-with-wide-buffer + (org-back-to-heading t) + (forward-line) + (unless (bolp) (insert "\n")) + (cond ((looking-at-p org-planning-line-re) + ;; Move to current indentation. + (skip-chars-forward " \t") + ;; Check if we have to remove something. + (dolist (type (if what (cons what remove) remove)) + (save-excursion + (when (re-search-forward + (cl-case type + (closed org-closed-time-regexp) + (deadline org-deadline-time-regexp) + (scheduled org-scheduled-time-regexp) + (otherwise + (error "Invalid planning type: %s" type))) + (line-end-position) t) + ;; Delete until next keyword or end of line. + (delete-region + (match-beginning 0) + (if (re-search-forward org-keyword-time-not-clock-regexp + (line-end-position) + t) + (match-beginning 0) + (line-end-position)))))) + ;; If there is nothing more to add and no more keyword + ;; is left, remove the line completely. + (if (and (looking-at-p "[ \t]*$") (not what)) + (delete-region (line-beginning-position) + (line-beginning-position 2)) + ;; If we removed last keyword, do not leave trailing + ;; white space at the end of line. + (let ((p (point))) + (save-excursion + (end-of-line) + (unless (= (skip-chars-backward " \t" p) 0) + (delete-region (point) (line-end-position))))))) + ((not what) (throw 'exit nil)) ; Nothing to do. + (t (insert-before-markers "\n") + (backward-char 1) + (when org-adapt-indentation + (indent-to-column (1+ (org-outline-level)))))) + (when what + ;; Insert planning keyword. + (insert (cl-case what + (closed org-closed-string) + (deadline org-deadline-string) + (scheduled org-scheduled-string) + (otherwise (error "Invalid planning type: %s" what))) + " ") + ;; Insert associated timestamp. + (let ((ts (org-insert-time-stamp + time + (or org-time-was-given + (and (eq what 'closed) org-log-done-with-time)) + (eq what 'closed) + nil nil (list org-end-time-was-given)))) + (unless (eolp) (insert " ")) + ts)))))) + +(defvar org-log-note-marker (make-marker) + "Marker pointing at the entry where the note is to be inserted.") (defvar org-log-note-purpose nil) (defvar org-log-note-state nil) (defvar org-log-note-previous-state nil) -(defvar org-log-note-how nil) (defvar org-log-note-extra nil) (defvar org-log-note-window-configuration nil) (defvar org-log-note-return-to (make-marker)) (defvar org-log-note-effective-time nil "Remembered current time so that dynamically scoped -`org-extend-today-until' affects tha timestamps in state change -log") +`org-extend-today-until' affects timestamps in state change log") (defvar org-log-post-message nil "Message to be displayed after a log note has been stored. @@ -13304,85 +13651,92 @@ The auto-repeater uses this.") "Add a note to the current entry. This is done in the same way as adding a state change note." (interactive) - (org-add-log-setup 'note nil nil 'findpos nil)) + (org-add-log-setup 'note)) -(defvar org-property-end-re) -(defun org-add-log-setup (&optional purpose state prev-state - findpos how extra) +(defun org-log-beginning (&optional create) + "Return expected start of log notes in current entry. +When optional argument CREATE is non-nil, the function creates +a drawer to store notes, if necessary. Returned position ignores +narrowing." + (org-with-wide-buffer + (let ((drawer (org-log-into-drawer))) + (cond + (drawer + (org-end-of-meta-data) + (let ((regexp (concat "^[ \t]*:" (regexp-quote drawer) ":[ \t]*$")) + (end (if (org-at-heading-p) (point) + (save-excursion (outline-next-heading) (point)))) + (case-fold-search t)) + (catch 'exit + ;; Try to find existing drawer. + (while (re-search-forward regexp end t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'drawer) + (let ((cend (org-element-property :contents-end element))) + (when (and (not org-log-states-order-reversed) cend) + (goto-char cend))) + (throw 'exit nil)))) + ;; No drawer found. Create one, if permitted. + (when create + (unless (bolp) (insert "\n")) + (let ((beg (point))) + (insert ":" drawer ":\n:END:\n") + (org-indent-region beg (point))) + (end-of-line -1))))) + (t + (org-end-of-meta-data org-log-state-notes-insert-after-drawers) + (skip-chars-forward " \t\n") + (beginning-of-line) + (unless org-log-states-order-reversed + (org-skip-over-state-notes) + (skip-chars-backward " \t\n") + (forward-line))))) + (if (bolp) (point) (line-beginning-position 2)))) + +(defun org-add-log-setup (&optional purpose state prev-state how extra) "Set up the post command hook to take a note. If this is about to TODO state change, the new state is expected in STATE. -When FINDPOS is non-nil, find the correct position for the note in -the current entry. If not, assume that it can be inserted at point. HOW is an indicator what kind of note should be created. EXTRA is additional text that will be inserted into the notes buffer." - (let* ((org-log-into-drawer (org-log-into-drawer)) - (drawer (cond ((stringp org-log-into-drawer) - org-log-into-drawer) - (org-log-into-drawer "LOGBOOK")))) - (save-restriction - (save-excursion - (when findpos - (org-back-to-heading t) - (narrow-to-region (point) (save-excursion - (outline-next-heading) (point))) - (looking-at (concat org-outline-regexp "\\( *\\)[^\r\n]*" - "\\(\n[^\r\n]*?" org-keyword-time-not-clock-regexp - "[^\r\n]*\\)?")) - (goto-char (match-end 0)) - (cond - (drawer - (if (re-search-forward (concat "^[ \t]*:" drawer ":[ \t]*$") - nil t) - (progn - (goto-char (match-end 0)) - (or org-log-states-order-reversed - (and (re-search-forward org-property-end-re nil t) - (goto-char (1- (match-beginning 0)))))) - (insert "\n:" drawer ":\n:END:") - (beginning-of-line 0) - (org-indent-line) - (beginning-of-line 2) - (org-indent-line) - (end-of-line 0))) - ((and org-log-state-notes-insert-after-drawers - (save-excursion - (forward-line) (looking-at org-drawer-regexp))) - (forward-line) - (while (looking-at org-drawer-regexp) - (goto-char (match-end 0)) - (re-search-forward org-property-end-re (point-max) t) - (forward-line)) - (forward-line -1))) - (unless org-log-states-order-reversed - (and (= (char-after) ?\n) (forward-char 1)) - (org-skip-over-state-notes) - (skip-chars-backward " \t\n\r"))) - (move-marker org-log-note-marker (point)) - (setq org-log-note-purpose purpose - org-log-note-state state - org-log-note-previous-state prev-state - org-log-note-how how - org-log-note-extra extra - org-log-note-effective-time (org-current-effective-time)) - (add-hook 'post-command-hook 'org-add-log-note 'append))))) + (move-marker org-log-note-marker (point)) + (setq org-log-note-purpose purpose + org-log-note-state state + org-log-note-previous-state prev-state + org-log-note-how how + org-log-note-extra extra + org-log-note-effective-time (org-current-effective-time)) + (add-hook 'post-command-hook 'org-add-log-note 'append)) (defun org-skip-over-state-notes () "Skip past the list of State notes in an entry." - (if (looking-at "\n[ \t]*- State") (forward-char 1)) (when (ignore-errors (goto-char (org-in-item-p))) (let* ((struct (org-list-struct)) - (prevs (org-list-prevs-alist struct))) - (while (looking-at "[ \t]*- State") + (prevs (org-list-prevs-alist struct)) + (regexp + (concat "[ \t]*- +" + (replace-regexp-in-string + " +" " +" + (org-replace-escapes + (regexp-quote (cdr (assq 'state org-log-note-headings))) + `(("%d" . ,org-ts-regexp-inactive) + ("%D" . ,org-ts-regexp) + ("%s" . "\"\\S-+\"") + ("%S" . "\"\\S-+\"") + ("%t" . ,org-ts-regexp-inactive) + ("%T" . ,org-ts-regexp) + ("%u" . ".*?") + ("%U" . ".*?"))))))) + (while (looking-at-p regexp) (goto-char (or (org-list-get-next-item (point) struct prevs) (org-list-get-item-end (point) struct))))))) -(defun org-add-log-note (&optional purpose) - "Pop up a window for taking a note, and add this note later at point." +(defun org-add-log-note (&optional _purpose) + "Pop up a window for taking a note, and add this note later." (remove-hook 'post-command-hook 'org-add-log-note) (setq org-log-note-window-configuration (current-window-configuration)) (delete-other-windows) (move-marker org-log-note-return-to (point)) - (org-pop-to-buffer-same-window (marker-buffer org-log-note-marker)) + (pop-to-buffer-same-window (marker-buffer org-log-note-marker)) (goto-char org-log-note-marker) (org-switch-to-buffer-other-window "*Org Note*") (erase-buffer) @@ -13411,23 +13765,23 @@ EXTRA is additional text that will be inserted into the notes buffer." ((eq org-log-note-purpose 'note) "this entry") (t (error "This should not happen"))))) - (if org-log-note-extra (insert org-log-note-extra)) - (org-set-local 'org-finish-function 'org-store-log-note) + (when org-log-note-extra (insert org-log-note-extra)) + (setq-local org-finish-function 'org-store-log-note) (run-hooks 'org-log-buffer-setup-hook))) (defvar org-note-abort nil) ; dynamically scoped (defun org-store-log-note () "Finish taking a log note, and insert it to where it belongs." - (let ((txt (buffer-string))) - (kill-buffer (current-buffer)) - (let ((note (cdr (assq org-log-note-purpose org-log-note-headings))) - lines ind bul) + (let ((txt (prog1 (buffer-string) + (kill-buffer))) + (note (cdr (assq org-log-note-purpose org-log-note-headings))) + lines) (while (string-match "\\`# .*\n[ \t\n]*" txt) (setq txt (replace-match "" t t txt))) - (if (string-match "\\s-+\\'" txt) - (setq txt (replace-match "" t t txt))) + (when (string-match "\\s-+\\'" txt) + (setq txt (replace-match "" t t txt))) (setq lines (org-split-string txt "\n")) - (when (and note (string-match "\\S-" note)) + (when (org-string-nw-p note) (setq note (org-replace-escapes note @@ -13445,74 +13799,83 @@ EXTRA is additional text that will be inserted into the notes buffer." (cons "%D" (format-time-string (org-time-stamp-format nil nil) org-log-note-effective-time)) - (cons "%s" (if org-log-note-state - (concat "\"" org-log-note-state "\"") - "")) - (cons "%S" (if org-log-note-previous-state - (concat "\"" org-log-note-previous-state "\"") - "\"\""))))) - (if lines (setq note (concat note " \\\\"))) + (cons "%s" (cond + ((not org-log-note-state) "") + ((string-match-p org-ts-regexp + org-log-note-state) + (format "\"[%s]\"" + (substring org-log-note-state 1 -1))) + (t (format "\"%s\"" org-log-note-state)))) + (cons "%S" + (cond + ((not org-log-note-previous-state) "") + ((string-match-p org-ts-regexp + org-log-note-previous-state) + (format "\"[%s]\"" + (substring + org-log-note-previous-state 1 -1))) + (t (format "\"%s\"" + org-log-note-previous-state))))))) + (when lines (setq note (concat note " \\\\"))) (push note lines)) - (when (or current-prefix-arg org-note-abort) - (when org-log-into-drawer - (org-remove-empty-drawer-at - (if (stringp org-log-into-drawer) org-log-into-drawer "LOGBOOK") - org-log-note-marker)) - (setq lines nil)) - (when lines + (when (and lines (not (or current-prefix-arg org-note-abort))) (with-current-buffer (marker-buffer org-log-note-marker) - (save-excursion - (goto-char org-log-note-marker) - (move-marker org-log-note-marker nil) - (end-of-line 1) - (if (not (bolp)) (let ((inhibit-read-only t)) (insert "\n"))) - (setq ind (save-excursion - (if (ignore-errors (goto-char (org-in-item-p))) - (let ((struct (org-list-struct))) - (org-list-get-ind - (org-list-get-top-point struct) struct)) - (skip-chars-backward " \r\t\n") - (cond - ((and (org-at-heading-p) - org-adapt-indentation) - (1+ (org-current-level))) - ((org-at-heading-p) 0) - (t (org-get-indentation)))))) - (setq bul (org-list-bullet-string "-")) - (org-indent-line-to ind) - (insert bul (pop lines)) - (let ((ind-body (+ (length bul) ind))) - (while lines - (insert "\n") - (org-indent-line-to ind-body) - (insert (pop lines)))) - (message "Note stored") - (org-back-to-heading t) - (org-cycle-hide-drawers 'children)) + (org-with-wide-buffer + ;; Find location for the new note. + (goto-char org-log-note-marker) + (set-marker org-log-note-marker nil) + ;; Note associated to a clock is to be located right after + ;; the clock. Do not move point. + (unless (eq org-log-note-purpose 'clock-out) + (goto-char (org-log-beginning t))) + ;; Make sure point is at the beginning of an empty line. + (cond ((not (bolp)) (let ((inhibit-read-only t)) (insert "\n"))) + ((looking-at "[ \t]*\\S-") (save-excursion (insert "\n")))) + ;; In an existing list, add a new item at the top level. + ;; Otherwise, indent line like a regular one. + (let ((itemp (org-in-item-p))) + (if itemp + (indent-line-to + (let ((struct (save-excursion + (goto-char itemp) (org-list-struct)))) + (org-list-get-ind (org-list-get-top-point struct) struct))) + (org-indent-line))) + (insert (org-list-bullet-string "-") (pop lines)) + (let ((ind (org-list-item-body-column (line-beginning-position)))) + (dolist (line lines) + (insert "\n") + (indent-line-to ind) + (insert line))) + (message "Note stored") + (org-back-to-heading t) + (org-cycle-hide-drawers 'children)) ;; Fix `buffer-undo-list' when `org-store-log-note' is called ;; from within `org-add-log-note' because `buffer-undo-list' ;; is then modified outside of `org-with-remote-undo'. (when (eq this-command 'org-agenda-todo) - (setcdr buffer-undo-list (cddr buffer-undo-list))))))) - ;; Don't add undo information when called from `org-agenda-todo' + (setcdr buffer-undo-list (cddr buffer-undo-list)))))) + ;; Don't add undo information when called from `org-agenda-todo'. (let ((buffer-undo-list (eq this-command 'org-agenda-todo))) (set-window-configuration org-log-note-window-configuration) (with-current-buffer (marker-buffer org-log-note-return-to) (goto-char org-log-note-return-to)) (move-marker org-log-note-return-to nil) - (and org-log-post-message (message "%s" org-log-post-message)))) + (when org-log-post-message (message "%s" org-log-post-message)))) -(defun org-remove-empty-drawer-at (drawer pos) - "Remove an empty drawer DRAWER at position POS. +(defun org-remove-empty-drawer-at (pos) + "Remove an empty drawer at position POS. POS may also be a marker." (with-current-buffer (if (markerp pos) (marker-buffer pos) (current-buffer)) - (save-excursion - (save-restriction - (widen) - (goto-char pos) - (if (org-in-regexp - (concat "^[ \t]*:" drawer ":[ \t]*\n[ \t]*:END:[ \t]*\n?") 2) - (replace-match "")))))) + (org-with-wide-buffer + (goto-char pos) + (let ((drawer (org-element-at-point))) + (when (and (memq (org-element-type drawer) '(drawer property-drawer)) + (not (org-element-property :contents-begin drawer))) + (delete-region (org-element-property :begin drawer) + (progn (goto-char (org-element-property :end drawer)) + (skip-chars-backward " \r\t\n") + (forward-line) + (point)))))))) (defvar org-ts-type nil) (defun org-sparse-tree (&optional arg type) @@ -13533,47 +13896,45 @@ D Show deadlines and scheduled items between a date range." (interactive "P") (setq type (or type org-sparse-tree-default-date-type)) (setq org-ts-type type) - (message "Sparse tree: [/]regexp [t]odo [T]odo-kwd [m]atch [p]roperty - [d]eadlines [b]efore-date [a]fter-date [D]ates range - [c]ycle through date types: %s" - (case type + (message "Sparse tree: [r]egexp [t]odo [T]odo-kwd [m]atch [p]roperty + \[d]eadlines [b]efore-date [a]fter-date [D]ates range + \[c]ycle through date types: %s" + (cl-case type (all "all timestamps") (scheduled "only scheduled") (deadline "only deadline") (active "only active timestamps") (inactive "only inactive timestamps") - (scheduled-or-deadline "scheduled/deadline") (closed "with a closed time-stamp") (otherwise "scheduled/deadline"))) (let ((answer (read-char-exclusive))) - (case answer + (cl-case answer (?c (org-sparse-tree arg - (cadr (memq type '(scheduled-or-deadline all scheduled deadline active - inactive closed))))) - (?d (call-interactively #'org-check-deadlines)) - (?b (call-interactively #'org-check-before-date)) - (?a (call-interactively #'org-check-after-date)) - (?D (call-interactively #'org-check-dates-range)) - (?t (call-interactively #'org-show-todo-tree)) + (cadr + (memq type '(nil all scheduled deadline active inactive closed))))) + (?d (call-interactively 'org-check-deadlines)) + (?b (call-interactively 'org-check-before-date)) + (?a (call-interactively 'org-check-after-date)) + (?D (call-interactively 'org-check-dates-range)) + (?t (call-interactively 'org-show-todo-tree)) (?T (org-show-todo-tree '(4))) - (?m (call-interactively #'org-match-sparse-tree)) + (?m (call-interactively 'org-match-sparse-tree)) ((?p ?P) - (let* ((kwd (org-icompleting-read + (let* ((kwd (completing-read "Property: " (mapcar #'list (org-buffer-property-keys)))) - (value (org-icompleting-read + (value (completing-read "Value: " (mapcar #'list (org-property-values kwd))))) (unless (string-match "\\`{.*}\\'" value) (setq value (concat "\"" value "\""))) (org-match-sparse-tree arg (concat kwd "=" value)))) - ((?r ?R ?/) (call-interactively #'org-occur)) + ((?r ?R ?/) (call-interactively 'org-occur)) (otherwise (user-error "No such sparse tree command \"%c\"" answer))))) -(defvar org-occur-highlights nil +(defvar-local org-occur-highlights nil "List of overlays used for occur matches.") -(make-variable-buffer-local 'org-occur-highlights) -(defvar org-occur-parameters nil +(defvar-local org-occur-parameters nil "Parameters of the active org-occur calls. This is a list, each call to org-occur pushes as cons cell, containing the regular expression and the callback, onto the list. @@ -13583,18 +13944,21 @@ will only contain one set of parameters. When the highlights are removed (for example with `C-c C-c', or with the next edit (depending on `org-remove-highlights-with-change'), this variable is emptied as well.") -(make-variable-buffer-local 'org-occur-parameters) (defun org-occur (regexp &optional keep-previous callback) "Make a compact tree which shows all matches of REGEXP. -The tree will show the lines where the regexp matches, and all higher -headlines above the match. It will also show the heading after the match, -to make sure editing the matching entry is easy. -If KEEP-PREVIOUS is non-nil, highlighting and exposing done by a previous -call to `org-occur' will be kept, to allow stacking of calls to this -command. -If CALLBACK is non-nil, it is a function which is called to confirm -that the match should indeed be shown." + +The tree will show the lines where the regexp matches, and any other context +defined in `org-show-context-detail', which see. + +When optional argument KEEP-PREVIOUS is non-nil, highlighting and exposing +done by a previous call to `org-occur' will be kept, to allow stacking of +calls to this command. + +Optional argument CALLBACK can be a function of no argument. In this case, +it is called with point at the end of the match, match data being set +accordingly. Current match is shown only if the return value is non-nil. +The function must neither move point nor alter narrowing." (interactive "sRegexp: \nP") (when (equal regexp "") (user-error "Regexp cannot be empty")) @@ -13604,32 +13968,35 @@ that the match should indeed be shown." (let ((cnt 0)) (save-excursion (goto-char (point-min)) - (if (or (not keep-previous) ; do not want to keep - (not org-occur-highlights)) ; no previous matches - ;; hide everything - (org-overview)) - (while (re-search-forward regexp nil t) - (when (or (not callback) - (save-match-data (funcall callback))) - (setq cnt (1+ cnt)) - (when org-highlight-sparse-tree-matches - (org-highlight-new-match (match-beginning 0) (match-end 0))) - (org-show-context 'occur-tree)))) + (when (or (not keep-previous) ; do not want to keep + (not org-occur-highlights)) ; no previous matches + ;; hide everything + (org-overview)) + (let ((case-fold-search (if (eq org-occur-case-fold-search 'smart) + (isearch-no-upper-case-p regexp t) + org-occur-case-fold-search))) + (while (re-search-forward regexp nil t) + (when (or (not callback) + (save-match-data (funcall callback))) + (setq cnt (1+ cnt)) + (when org-highlight-sparse-tree-matches + (org-highlight-new-match (match-beginning 0) (match-end 0))) + (org-show-context 'occur-tree))))) (when org-remove-highlights-with-change - (org-add-hook 'before-change-functions 'org-remove-occur-highlights - nil 'local)) + (add-hook 'before-change-functions 'org-remove-occur-highlights + nil 'local)) (unless org-sparse-tree-open-archived-trees (org-hide-archived-subtrees (point-min) (point-max))) (run-hooks 'org-occur-hook) - (if (org-called-interactively-p 'interactive) - (message "%d match(es) for regexp %s" cnt regexp)) + (when (called-interactively-p 'interactive) + (message "%d match(es) for regexp %s" cnt regexp)) cnt)) -(defun org-occur-next-match (&optional n reset) +(defun org-occur-next-match (&optional n _reset) "Function for `next-error-function' to find sparse tree matches. N is the number of matches to move, when negative move backwards. -RESET is entirely ignored - this function always goes back to the -starting point when no match is found." +This function always goes back to the starting point when no +match is found." (let* ((limit (if (< n 0) (point-min) (point-max))) (search-func (if (< n 0) 'previous-single-char-property-change @@ -13641,7 +14008,7 @@ starting point when no match is found." (while (setq p1 (funcall search-func (point) 'org-type)) (when (equal p1 limit) (goto-char pos) - (error "No more matches")) + (user-error "No more matches")) (when (equal (get-char-property p1 'org-type) 'org-occur) (setq n (1- n)) (when (= n 0) @@ -13649,65 +14016,75 @@ starting point when no match is found." (throw 'exit (point)))) (goto-char p1)) (goto-char p1) - (error "No more matches")))) + (user-error "No more matches")))) (defun org-show-context (&optional key) "Make sure point and context are visible. -How much context is shown depends upon the variables -`org-show-hierarchy-above', `org-show-following-heading', -`org-show-entry-below' and `org-show-siblings'." - (let ((heading-p (org-at-heading-p t)) - (hierarchy-p (org-get-alist-option org-show-hierarchy-above key)) - (following-p (org-get-alist-option org-show-following-heading key)) - (entry-p (org-get-alist-option org-show-entry-below key)) - (siblings-p (org-get-alist-option org-show-siblings key))) - ;; Show heading or entry text - (if (and heading-p (not entry-p)) - (org-flag-heading nil) ; only show the heading - (and (or entry-p (outline-invisible-p) (org-invisible-p2)) - (org-show-hidden-entry))) ; show entire entry - (when following-p - ;; Show next sibling, or heading below text - (save-excursion - (and (if heading-p (org-goto-sibling) (outline-next-heading)) - (org-flag-heading nil)))) - (when siblings-p (org-show-siblings)) - (when hierarchy-p - ;; show all higher headings, possibly with siblings - (save-excursion - (while (and (condition-case nil - (progn (org-up-heading-all 1) t) - (error nil)) - (not (bobp))) - (org-flag-heading nil) - (when siblings-p (org-show-siblings))))))) +Optional argument KEY, when non-nil, is a symbol. See +`org-show-context-detail' for allowed values and how much is to +be shown." + (org-show-set-visibility + (cond ((symbolp org-show-context-detail) org-show-context-detail) + ((cdr (assq key org-show-context-detail))) + (t (cdr (assq 'default org-show-context-detail)))))) + +(defun org-show-set-visibility (detail) + "Set visibility around point according to DETAIL. +DETAIL is either nil, `minimal', `local', `ancestors', `lineage', +`tree', `canonical' or t. See `org-show-context-detail' for more +information." + ;; Show current heading and possibly its entry, following headline + ;; or all children. + (if (and (org-at-heading-p) (not (eq detail 'local))) + (org-flag-heading nil) + (org-show-entry) + ;; If point is hidden within a drawer or a block, make sure to + ;; expose it. + (dolist (o (overlays-at (point))) + (when (memq (overlay-get o 'invisible) '(org-hide-block outline)) + (delete-overlay o))) + (unless (org-before-first-heading-p) + (org-with-limited-levels + (cl-case detail + ((tree canonical t) (org-show-children)) + ((nil minimal ancestors)) + (t (save-excursion + (outline-next-heading) + (org-flag-heading nil))))))) + ;; Show all siblings. + (when (eq detail 'lineage) (org-show-siblings)) + ;; Show ancestors, possibly with their children. + (when (memq detail '(ancestors lineage tree canonical t)) + (save-excursion + (while (org-up-heading-safe) + (org-flag-heading nil) + (when (memq detail '(canonical t)) (org-show-entry)) + (when (memq detail '(tree canonical t)) (org-show-children)))))) (defvar org-reveal-start-hook nil "Hook run before revealing a location.") (defun org-reveal (&optional siblings) "Show current entry, hierarchy above it, and the following headline. -This can be used to show a consistent set of context around locations -exposed with `org-show-hierarchy-above' or `org-show-following-heading' -not t for the search context. + +This can be used to show a consistent set of context around +locations exposed with `org-show-context'. With optional argument SIBLINGS, on each level of the hierarchy all siblings are shown. This repairs the tree structure to what it would look like when opened with hierarchical calls to `org-cycle'. -With double optional argument \\[universal-argument] \\[universal-argument], \ -go to the parent and show the -entire tree." + +With a \\[universal-argument] \\[universal-argument] prefix, \ +go to the parent and show the entire tree." (interactive "P") (run-hooks 'org-reveal-start-hook) - (let ((org-show-hierarchy-above t) - (org-show-following-heading t) - (org-show-siblings (if siblings t org-show-siblings))) - (org-show-context nil)) - (when (equal siblings '(16)) - (save-excursion - (when (org-up-heading-safe) - (org-show-subtree) - (run-hook-with-args 'org-cycle-hook 'subtree))))) + (cond ((equal siblings '(4)) (org-show-set-visibility 'canonical)) + ((equal siblings '(16)) + (save-excursion + (when (org-up-heading-safe) + (org-show-subtree) + (run-hook-with-args 'org-cycle-hook 'subtree)))) + (t (org-show-set-visibility 'lineage)))) (defun org-highlight-new-match (beg end) "Highlight from BEG to END and mark the highlight is an occur headline." @@ -13716,13 +14093,13 @@ entire tree." (overlay-put ov 'org-type 'org-occur) (push ov org-occur-highlights))) -(defun org-remove-occur-highlights (&optional beg end noremove) +(defun org-remove-occur-highlights (&optional _beg _end noremove) "Remove the occur highlights from the buffer. BEG and END are ignored. If NOREMOVE is nil, remove this function from the `before-change-functions' in the current buffer." (interactive) (unless org-inhibit-highlight-removal - (mapc 'delete-overlay org-occur-highlights) + (mapc #'delete-overlay org-occur-highlights) (setq org-occur-highlights nil) (setq org-occur-parameters nil) (unless noremove @@ -13746,89 +14123,88 @@ from the `before-change-functions' in the current buffer." (interactive) (org-priority 'down)) -(defun org-priority (&optional action show) +(defun org-priority (&optional action _show) "Change the priority of an item. ACTION can be `set', `up', `down', or a character." (interactive "P") (if (equal action '(4)) (org-show-priority) - (unless org-enable-priority-commands - (user-error "Priority commands are disabled")) - (setq action (or action 'set)) - (let (current new news have remove) - (save-excursion - (org-back-to-heading t) - (if (looking-at org-priority-regexp) + (unless org-enable-priority-commands + (user-error "Priority commands are disabled")) + (setq action (or action 'set)) + (let (current new news have remove) + (save-excursion + (org-back-to-heading t) + (when (looking-at org-priority-regexp) (setq current (string-to-char (match-string 2)) have t)) - (cond - ((eq action 'remove) - (setq remove t new ?\ )) - ((or (eq action 'set) - (if (featurep 'xemacs) (characterp action) (integerp action))) - (if (not (eq action 'set)) - (setq new action) - (message "Priority %c-%c, SPC to remove: " - org-highest-priority org-lowest-priority) - (save-match-data - (setq new (read-char-exclusive)))) - (if (and (= (upcase org-highest-priority) org-highest-priority) - (= (upcase org-lowest-priority) org-lowest-priority)) + (cond + ((eq action 'remove) + (setq remove t new ?\ )) + ((or (eq action 'set) + (integerp action)) + (if (not (eq action 'set)) + (setq new action) + (message "Priority %c-%c, SPC to remove: " + org-highest-priority org-lowest-priority) + (save-match-data + (setq new (read-char-exclusive)))) + (when (and (= (upcase org-highest-priority) org-highest-priority) + (= (upcase org-lowest-priority) org-lowest-priority)) (setq new (upcase new))) - (cond ((equal new ?\ ) (setq remove t)) - ((or (< (upcase new) org-highest-priority) (> (upcase new) org-lowest-priority)) - (user-error "Priority must be between `%c' and `%c'" - org-highest-priority org-lowest-priority)))) - ((eq action 'up) - (setq new (if have - (1- current) ; normal cycling - ;; last priority was empty - (if (eq last-command this-command) - org-lowest-priority ; wrap around empty to lowest - ;; default - (if org-priority-start-cycle-with-default - org-default-priority - (1- org-default-priority)))))) - ((eq action 'down) - (setq new (if have - (1+ current) ; normal cycling - ;; last priority was empty - (if (eq last-command this-command) - org-highest-priority ; wrap around empty to highest - ;; default - (if org-priority-start-cycle-with-default - org-default-priority - (1+ org-default-priority)))))) - (t (user-error "Invalid action"))) - (if (or (< (upcase new) org-highest-priority) - (> (upcase new) org-lowest-priority)) + (cond ((equal new ?\ ) (setq remove t)) + ((or (< (upcase new) org-highest-priority) (> (upcase new) org-lowest-priority)) + (user-error "Priority must be between `%c' and `%c'" + org-highest-priority org-lowest-priority)))) + ((eq action 'up) + (setq new (if have + (1- current) ; normal cycling + ;; last priority was empty + (if (eq last-command this-command) + org-lowest-priority ; wrap around empty to lowest + ;; default + (if org-priority-start-cycle-with-default + org-default-priority + (1- org-default-priority)))))) + ((eq action 'down) + (setq new (if have + (1+ current) ; normal cycling + ;; last priority was empty + (if (eq last-command this-command) + org-highest-priority ; wrap around empty to highest + ;; default + (if org-priority-start-cycle-with-default + org-default-priority + (1+ org-default-priority)))))) + (t (user-error "Invalid action"))) + (when (or (< (upcase new) org-highest-priority) + (> (upcase new) org-lowest-priority)) (if (and (memq action '(up down)) (not have) (not (eq last-command this-command))) - ;; `new' is from default priority + ;; `new' is from default priority (error "The default can not be set, see `org-default-priority' why") - ;; normal cycling: `new' is beyond highest/lowest priority - ;; and is wrapped around to the empty priority + ;; normal cycling: `new' is beyond highest/lowest priority + ;; and is wrapped around to the empty priority (setq remove t))) - (setq news (format "%c" new)) - (if have + (setq news (format "%c" new)) + (if have + (if remove + (replace-match "" t t nil 1) + (replace-match news t t nil 2)) (if remove - (replace-match "" t t nil 1) - (replace-match news t t nil 2)) - (if remove - (user-error "No priority cookie found in line") - (let ((case-fold-search nil)) - (looking-at org-todo-line-regexp)) - (if (match-end 2) - (progn - (goto-char (match-end 2)) - (insert " [#" news "]")) - (goto-char (match-beginning 3)) - (insert "[#" news "] ")))) - (org-preserve-lc (org-set-tags nil 'align))) - (if remove - (message "Priority removed") - (message "Priority of current item set to %s" news))))) + (user-error "No priority cookie found in line") + (let ((case-fold-search nil)) (looking-at org-todo-line-regexp)) + (if (match-end 2) + (progn + (goto-char (match-end 2)) + (insert " [#" news "]")) + (goto-char (match-beginning 3)) + (insert "[#" news "] ")))) + (org-set-tags nil 'align)) + (if remove + (message "Priority removed") + (message "Priority of current item set to %s" news))))) (defun org-show-priority () "Show the priority of the current item. @@ -13863,6 +14239,7 @@ Can be set by the action argument to `org-scan-tags' and `org-map-entries'.") (defvar org-scanner-tags nil "The current tag list while the tags scanner is running.") + (defvar org-trust-scanner-tags nil "Should `org-get-tags-at' use the tags for the scanner. This is for internal dynamical scoping only. @@ -13874,6 +14251,8 @@ obtain a list of properties. Building the tags list for each entry in such a file becomes an N^2 operation - but with this variable set, it scales as N.") +(defvar org--matcher-tags-todo-only nil) + (defun org-scan-tags (action matcher todo-only &optional start-level) "Scan headline tags with inheritance and produce output ACTION. @@ -13882,11 +14261,14 @@ or `agenda' to produce an entry list for an agenda view. It can also be a Lisp form or a function that should be called at each matched headline, in this case the return value is a list of all return values from these calls. -MATCHER is a Lisp form to be evaluated, testing if a given set of tags -qualifies a headline for inclusion. When TODO-ONLY is non-nil, -only lines with a not-done TODO keyword are included in the output. -This should be the same variable that was scoped into -and set by `org-make-tags-matcher' when it constructed MATCHER. +MATCHER is a function accepting three arguments, returning +a non-nil value whenever a given set of tags qualifies a headline +for inclusion. See `org-make-tags-matcher' for more information. +As a special case, it can also be set to t (respectively nil) in +order to match all (respectively none) headline. + +When TODO-ONLY is non-nil, only lines with a not-done TODO +keyword are included in the output. START-LEVEL can be a string with asterisks, reducing the scope to headlines matching this string." @@ -13897,8 +14279,8 @@ headlines matching this string." (concat "\\*\\{" (number-to-string start-level) "\\} ") org-outline-regexp) " *\\(\\<\\(" - (mapconcat 'regexp-quote org-todo-keywords-1 "\\|") - (org-re "\\)\\>\\)? *\\(.*?\\)\\(:[[:alnum:]_@#%:]+:\\)?[ \t]*$"))) + (mapconcat #'regexp-quote org-todo-keywords-1 "\\|") + "\\)\\>\\)? *\\(.*?\\)\\(:[[:alnum:]_@#%:]+:\\)?[ \t]*$")) (props (list 'face 'default 'done-face 'org-agenda-done 'undone-face 'default @@ -13915,8 +14297,9 @@ headlines matching this string." lspos tags tags-list (tags-alist (list (cons 0 org-file-tags))) (llast 0) rtn rtn1 level category i txt - todo marker entry priority) - (when (not (or (member action '(agenda sparse-tree)) (functionp action))) + todo marker entry priority + ts-date ts-date-type ts-date-pair) + (unless (or (member action '(agenda sparse-tree)) (functionp action)) (setq action (list 'lambda nil action))) (save-excursion (goto-char (point-min)) @@ -13927,11 +14310,17 @@ headlines matching this string." (re-search-forward re nil t)) (setq org-map-continue-from nil) (catch :skip - (setq todo (if (match-end 1) (org-match-string-no-properties 2)) - tags (if (match-end 4) (org-match-string-no-properties 4))) + (setq todo + ;; TODO: is the 1-2 difference a bug? + (when (match-end 1) (match-string-no-properties 2)) + tags (when (match-end 4) (match-string-no-properties 4))) (goto-char (setq lspos (match-beginning 0))) (setq level (org-reduced-level (org-outline-level)) category (org-get-category)) + (when (eq action 'agenda) + (setq ts-date-pair (org-agenda-entry-get-agenda-timestamp (point)) + ts-date (car ts-date-pair) + ts-date-type (cdr ts-date-pair))) (setq i llast llast level) ;; remove tag lists from same and sublevels (while (>= i level) @@ -13958,18 +14347,20 @@ headlines matching this string." (when (and tags org-use-tag-inheritance (or (not (eq t org-use-tag-inheritance)) org-tags-exclude-from-inheritance)) - ;; selective inheritance, remove uninherited ones + ;; Selective inheritance, remove uninherited ones. (setcdr (car tags-alist) (org-remove-uninherited-tags (cdar tags-alist)))) (when (and ;; eval matcher only when the todo condition is OK (and (or (not todo-only) (member todo org-not-done-keywords)) - (let ((case-fold-search t) (org-trust-scanner-tags t)) - (eval matcher))) + (if (functionp matcher) + (let ((case-fold-search t) (org-trust-scanner-tags t)) + (funcall matcher todo tags-list level)) + matcher)) - ;; Call the skipper, but return t if it does not skip, - ;; so that the `and' form continues evaluating + ;; Call the skipper, but return t if it does not + ;; skip, so that the `and' form continues evaluating. (progn (unless (eq action 'sparse-tree) (org-agenda-skip)) t) @@ -13995,7 +14386,8 @@ headlines matching this string." (if (eq org-tags-match-list-sublevels 'indented) (make-string (1- level) ?.) "") (org-get-heading)) - level category + (make-string level ?\s) + category tags-list) priority (org-get-priority txt)) (goto-char lspos) @@ -14003,7 +14395,9 @@ headlines matching this string." (org-add-props txt props 'org-marker marker 'org-hd-marker marker 'org-category category 'todo-state todo - 'priority priority 'type "tagsmatch") + 'ts-date ts-date + 'priority priority + 'type (concat "tagsmatch" ts-date-type)) (push txt rtn)) ((functionp action) (setq org-map-continue-from nil) @@ -14048,13 +14442,19 @@ headlines matching this string." (defun org-match-sparse-tree (&optional todo-only match) "Create a sparse tree according to tags string MATCH. -MATCH can contain positive and negative selection of tags, like -\"+WORK+URGENT-WITHBOSS\". -If optional argument TODO-ONLY is non-nil, only select lines that are -also TODO lines." + +MATCH is a string with match syntax. It can contain a selection +of tags (\"+work+urgent-boss\"), properties (\"LEVEL>3\"), and +TODO keywords (\"TODO=\\\"WAITING\\\"\") or a combination of +those. See the manual for details. + +If optional argument TODO-ONLY is non-nil, only select lines that +are also TODO tasks." (interactive "P") (org-agenda-prepare-buffers (list (current-buffer))) - (org-scan-tags 'sparse-tree (cdr (org-make-tags-matcher match)) todo-only)) + (let ((org--matcher-tags-todo-only todo-only)) + (org-scan-tags 'sparse-tree (cdr (org-make-tags-matcher match)) + org--matcher-tags-todo-only))) (defalias 'org-tags-sparse-tree 'org-match-sparse-tree) @@ -14062,15 +14462,17 @@ also TODO lines." (defun org-cached-entry-get (pom property) (if (or (eq t org-use-property-inheritance) (and (stringp org-use-property-inheritance) - (string-match org-use-property-inheritance property)) + (let ((case-fold-search t)) + (string-match-p org-use-property-inheritance property))) (and (listp org-use-property-inheritance) - (member property org-use-property-inheritance))) - ;; Caching is not possible, check it directly + (member-ignore-case property org-use-property-inheritance))) + ;; Caching is not possible, check it directly. (org-entry-get pom property 'inherit) - ;; Get all properties, so that we can do complicated checks easily - (cdr (assoc property (or org-cached-props - (setq org-cached-props - (org-entry-properties pom))))))) + ;; Get all properties, so we can do complicated checks easily. + (cdr (assoc-string property + (or org-cached-props + (setq org-cached-props (org-entry-properties pom))) + t)))) (defun org-global-tags-completion-table (&optional files) "Return the list of all tags in all agenda buffer/files. @@ -14079,186 +14481,173 @@ instead of the agenda files." (save-excursion (org-uniquify (delq nil - (apply 'append + (apply #'append (mapcar (lambda (file) (set-buffer (find-file-noselect file)) - (append (org-get-buffer-tags) - (mapcar (lambda (x) (if (stringp (car-safe x)) - (list (car-safe x)) nil)) - org-tag-alist))) - (if (and files (car files)) - files + (mapcar (lambda (x) + (and (stringp (car-safe x)) + (list (car-safe x)))) + (or org-current-tag-alist (org-get-buffer-tags)))) + (if (car-safe files) files (org-agenda-files)))))))) (defun org-make-tags-matcher (match) "Create the TAGS/TODO matcher form for the selection string MATCH. -The variable `todo-only' is scoped dynamically into this function. -It will be set to t if the matcher restricts matching to TODO entries, -otherwise will not be touched. - -Returns a cons of the selection string MATCH and the constructed -lisp form implementing the matcher. The matcher is to be evaluated -at an Org entry, with point on the headline, and returns t if the -entry matches the selection string MATCH. The returned lisp form -references two variables with information about the entry, which -must be bound around the form's evaluation: todo, the TODO keyword -at the entry (or nil of none); and tags-list, the list of all tags -at the entry including inherited ones. Additionally, the category -of the entry (if any) must be specified as the text property -'org-category on the headline. - -See also `org-scan-tags'. -" - (declare (special todo-only)) - (unless (boundp 'todo-only) - (error "`org-make-tags-matcher' expects todo-only to be scoped in")) +Returns a cons of the selection string MATCH and a function +implementing the matcher. + +The matcher is to be called at an Org entry, with point on the +headline, and returns non-nil if the entry matches the selection +string MATCH. It must be called with three arguments: the TODO +keyword at the entry (or nil if none), the list of all tags at +the entry including inherited ones and the reduced level of the +headline. Additionally, the category of the entry, if any, must +be specified as the text property `org-category' on the headline. + +This function sets the variable `org--matcher-tags-todo-only' to +a non-nil value if the matcher restricts matching to TODO +entries, otherwise it is not touched. + +See also `org-scan-tags'." (unless match ;; Get a new match request, with completion against the global - ;; tags table and the local tags in current buffer + ;; tags table and the local tags in current buffer. (let ((org-last-tags-completion-table (org-uniquify (delq nil (append (org-get-buffer-tags) (org-global-tags-completion-table)))))) - (setq match (org-completing-read-no-i - "Match: " 'org-tags-completion-function nil nil nil - 'org-tags-history)))) + (setq match + (completing-read + "Match: " + 'org-tags-completion-function nil nil nil 'org-tags-history)))) - ;; Parse the string and create a lisp form (let ((match0 match) - (re (org-re "^&?\\([-+:]\\)?\\({[^}]+}\\|LEVEL\\([<=>]\\{1,2\\}\\)\\([0-9]+\\)\\|\\(\\(?:[[:alnum:]_]+\\(?:\\\\-\\)*\\)+\\)\\([<>=]\\{1,2\\}\\)\\({[^}]+}\\|\"[^\"]*\"\\|-?[.0-9]+\\(?:[eE][-+]?[0-9]+\\)?\\)\\|[[:alnum:]_@#%]+\\)")) - minus tag mm - tagsmatch todomatch tagsmatcher todomatcher kwd matcher - orterms orlist re-p str-p level-p level-op time-p - prop-p pn pv po gv rest (start 0) (ss 0)) - ;; Expand group tags + (re "^&?\\([-+:]\\)?\\({[^}]+}\\|LEVEL\\([<=>]\\{1,2\\}\\)\\([0-9]+\\)\\|\\(\\(?:[[:alnum:]_]+\\(?:\\\\-\\)*\\)+\\)\\([<>=]\\{1,2\\}\\)\\({[^}]+}\\|\"[^\"]*\"\\|-?[.0-9]+\\(?:[eE][-+]?[0-9]+\\)?\\)\\|[[:alnum:]_@#%]+\\)") + (start 0) + tagsmatch todomatch tagsmatcher todomatcher) + + ;; Expand group tags. (setq match (org-tags-expand match)) ;; Check if there is a TODO part of this match, which would be the - ;; part after a "/". TO make sure that this slash is not part of - ;; a property value to be matched against, we also check that there - ;; is no " after that slash. - ;; First, find the last slash - (while (string-match "/+" match ss) - (setq start (match-beginning 0) ss (match-end 0))) + ;; part after a "/". To make sure that this slash is not part of + ;; a property value to be matched against, we also check that + ;; there is no / after that slash. First, find the last slash. + (let ((s 0)) + (while (string-match "/+" match s) + (setq start (match-beginning 0)) + (setq s (match-end 0)))) (if (and (string-match "/+" match start) - (not (save-match-data (string-match "\"" match start)))) - ;; match contains also a todo-matching request + (not (string-match-p "\"" match start))) + ;; Match contains also a TODO-matching request. (progn - (setq tagsmatch (substring match 0 (match-beginning 0)) - todomatch (substring match (match-end 0))) - (if (string-match "^!" todomatch) - (setq todo-only t todomatch (substring todomatch 1))) - (if (string-match "^\\s-*$" todomatch) - (setq todomatch nil))) - ;; only matching tags - (setq tagsmatch match todomatch nil)) - - ;; Make the tags matcher - (if (or (not tagsmatch) (not (string-match "\\S-" tagsmatch))) - (setq tagsmatcher t) - (setq orterms (org-split-string tagsmatch "|") orlist nil) - (dolist (term orterms) - (while (and (equal (substring term -1) "\\") orterms) - (setq term (concat term "|" (pop orterms)))) ; repair bad split - (while (string-match re term) - (setq rest (substring term (match-end 0)) - minus (and (match-end 1) - (equal (match-string 1 term) "-")) - tag (save-match-data (replace-regexp-in-string - "\\\\-" "-" - (match-string 2 term))) - re-p (equal (string-to-char tag) ?{) - level-p (match-end 4) - prop-p (match-end 5) - mm (cond - (re-p `(org-match-any-p ,(substring tag 1 -1) tags-list)) - (level-p - (setq level-op (org-op-to-function (match-string 3 term))) - `(,level-op level ,(string-to-number - (match-string 4 term)))) - (prop-p - (setq pn (match-string 5 term) - po (match-string 6 term) - pv (match-string 7 term) - re-p (equal (string-to-char pv) ?{) - str-p (equal (string-to-char pv) ?\") - time-p (save-match-data - (string-match "^\"[[<].*[]>]\"$" pv)) - pv (if (or re-p str-p) (substring pv 1 -1) pv)) - (if time-p (setq pv (org-matcher-time pv))) - (setq po (org-op-to-function po (if time-p 'time str-p))) - (cond - ((equal pn "CATEGORY") - (setq gv '(get-text-property (point) 'org-category))) - ((equal pn "TODO") - (setq gv 'todo)) - (t - (setq gv `(org-cached-entry-get nil ,pn)))) - (if re-p - (if (eq po 'org<>) - `(not (string-match ,pv (or ,gv ""))) - `(string-match ,pv (or ,gv ""))) - (if str-p - `(,po (or ,gv "") ,pv) - `(,po (string-to-number (or ,gv "")) - ,(string-to-number pv) )))) - (t `(member ,tag tags-list))) - mm (if minus (list 'not mm) mm) - term rest) - (push mm tagsmatcher)) - (push (if (> (length tagsmatcher) 1) - (cons 'and tagsmatcher) - (car tagsmatcher)) - orlist) - (setq tagsmatcher nil)) - (setq tagsmatcher (if (> (length orlist) 1) (cons 'or orlist) (car orlist))) - (setq tagsmatcher - (list 'progn '(setq org-cached-props nil) tagsmatcher))) - ;; Make the todo matcher - (if (or (not todomatch) (not (string-match "\\S-" todomatch))) - (setq todomatcher t) - (setq orterms (org-split-string todomatch "|") orlist nil) - (dolist (term orterms) - (while (string-match re term) - (setq minus (and (match-end 1) - (equal (match-string 1 term) "-")) - kwd (match-string 2 term) - re-p (equal (string-to-char kwd) ?{) - term (substring term (match-end 0)) - mm (if re-p - `(string-match ,(substring kwd 1 -1) todo) - (list 'equal 'todo kwd)) - mm (if minus (list 'not mm) mm)) - (push mm todomatcher)) - (push (if (> (length todomatcher) 1) - (cons 'and todomatcher) - (car todomatcher)) - orlist) - (setq todomatcher nil)) - (setq todomatcher (if (> (length orlist) 1) - (cons 'or orlist) (car orlist)))) - - ;; Return the string and lisp forms of the matcher - (setq matcher (if todomatcher - (list 'and tagsmatcher todomatcher) - tagsmatcher)) - (when todo-only - (setq matcher (list 'and '(member todo org-not-done-keywords) - matcher))) - (cons match0 matcher))) - -(defun org-tags-expand (match &optional single-as-list downcased) + (setq tagsmatch (substring match 0 (match-beginning 0))) + (setq todomatch (substring match (match-end 0))) + (when (string-prefix-p "!" todomatch) + (setq org--matcher-tags-todo-only t) + (setq todomatch (substring todomatch 1))) + (when (string-match "\\`\\s-*\\'" todomatch) + (setq todomatch nil))) + ;; Only matching tags. + (setq tagsmatch match) + (setq todomatch nil)) + + ;; Make the tags matcher. + (when (org-string-nw-p tagsmatch) + (let ((orlist nil) + (orterms (org-split-string tagsmatch "|")) + term) + (while (setq term (pop orterms)) + (while (and (equal (substring term -1) "\\") orterms) + (setq term (concat term "|" (pop orterms)))) ;repair bad split. + (while (string-match re term) + (let* ((rest (substring term (match-end 0))) + (minus (and (match-end 1) + (equal (match-string 1 term) "-"))) + (tag (save-match-data + (replace-regexp-in-string + "\\\\-" "-" (match-string 2 term)))) + (regexp (eq (string-to-char tag) ?{)) + (levelp (match-end 4)) + (propp (match-end 5)) + (mm + (cond + (regexp `(org-match-any-p ,(substring tag 1 -1) tags-list)) + (levelp + `(,(org-op-to-function (match-string 3 term)) + level + ,(string-to-number (match-string 4 term)))) + (propp + (let* ((gv (pcase (upcase (match-string 5 term)) + ("CATEGORY" + '(get-text-property (point) 'org-category)) + ("TODO" 'todo) + (p `(org-cached-entry-get nil ,p)))) + (pv (match-string 7 term)) + (regexp (eq (string-to-char pv) ?{)) + (strp (eq (string-to-char pv) ?\")) + (timep (string-match-p "^\"[[<].*[]>]\"$" pv)) + (po (org-op-to-function (match-string 6 term) + (if timep 'time strp)))) + (setq pv (if (or regexp strp) (substring pv 1 -1) pv)) + (when timep (setq pv (org-matcher-time pv))) + (cond ((and regexp (eq po 'org<>)) + `(not (string-match ,pv (or ,gv "")))) + (regexp `(string-match ,pv (or ,gv ""))) + (strp `(,po (or ,gv "") ,pv)) + (t + `(,po + (string-to-number (or ,gv "")) + ,(string-to-number pv)))))) + (t `(member ,tag tags-list))))) + (push (if minus `(not ,mm) mm) tagsmatcher) + (setq term rest))) + (push `(and ,@tagsmatcher) orlist) + (setq tagsmatcher nil)) + (setq tagsmatcher `(progn (setq org-cached-props nil) (or ,@orlist))))) + + ;; Make the TODO matcher. + (when (org-string-nw-p todomatch) + (let ((orlist nil)) + (dolist (term (org-split-string todomatch "|")) + (while (string-match re term) + (let* ((minus (and (match-end 1) + (equal (match-string 1 term) "-"))) + (kwd (match-string 2 term)) + (regexp (eq (string-to-char kwd) ?{)) + (mm (if regexp `(string-match ,(substring kwd 1 -1) todo) + `(equal todo ,kwd)))) + (push (if minus `(not ,mm) mm) todomatcher)) + (setq term (substring term (match-end 0)))) + (push (if (> (length todomatcher) 1) + (cons 'and todomatcher) + (car todomatcher)) + orlist) + (setq todomatcher nil)) + (setq todomatcher (cons 'or orlist)))) + + ;; Return the string and function of the matcher. If no + ;; tags-specific or todo-specific matcher exists, match + ;; everything. + (let ((matcher (if (and tagsmatcher todomatcher) + `(and ,tagsmatcher ,todomatcher) + (or tagsmatcher todomatcher t)))) + (when org--matcher-tags-todo-only + (setq matcher `(and (member todo org-not-done-keywords) ,matcher))) + (cons match0 `(lambda (todo tags-list level) ,matcher))))) + +(defun org-tags-expand (match &optional single-as-list downcased tags-already-expanded) "Expand group tags in MATCH. This replaces every group tag in MATCH with a regexp tag search. For example, a group tag \"Work\" defined as { Work : Lab Conf } will be replaced like this: - Work => {\\(?:Work\\|Lab\\|Conf\\)} - +Work => +{\\(?:Work\\|Lab\\|Conf\\)} - -Work => -{\\(?:Work\\|Lab\\|Conf\\)} + Work => {\\<\\(?:Work\\|Lab\\|Conf\\)\\>} + +Work => +{\\<\\(?:Work\\|Lab\\|Conf\\)\\>} + -Work => -{\\<\\(?:Work\\|Lab\\|Conf\\)\\>} Replacing by a regexp preserves the structure of the match. E.g., this expansion @@ -14268,6 +14657,12 @@ E.g., this expansion will match anything tagged with \"Lab\" and \"Home\", or tagged with \"Conf\" and \"Home\" or tagged with \"Work\" and \"home\". +A group tag in MATCH can contain regular expressions of its own. +For example, a group tag \"Proj\" defined as { Proj : {P@.+} } +will be replaced like this: + + Proj => {\\<\\(?:Proj\\)\\>\\|P@.+} + When the optional argument SINGLE-AS-LIST is non-nil, MATCH is assumed to be a single group tag, and the function will return the list of tags in this group. @@ -14276,34 +14671,113 @@ When DOWNCASE is non-nil, expand downcased TAGS." (if org-group-tags (let* ((case-fold-search t) (stable org-mode-syntax-table) - (tal (or org-tag-groups-alist-for-agenda - org-tag-groups-alist)) - (tal (if downcased - (mapcar (lambda(tg) (mapcar 'downcase tg)) tal) tal)) - (tml (mapcar 'car tal)) - (rtnmatch match) rpl) - ;; @ and _ are allowed as word-components in tags + (taggroups (or org-tag-groups-alist-for-agenda org-tag-groups-alist)) + (taggroups (if downcased + (mapcar (lambda (tg) (mapcar #'downcase tg)) + taggroups) + taggroups)) + (taggroups-keys (mapcar #'car taggroups)) + (return-match (if downcased (downcase match) match)) + (count 0) + (work-already-expanded tags-already-expanded) + regexps-in-match tags-in-group regexp-in-group regexp-in-group-escaped) + ;; @ and _ are allowed as word-components in tags. (modify-syntax-entry ?@ "w" stable) (modify-syntax-entry ?_ "w" stable) - (while (and tml + ;; Temporarily replace regexp-expressions in the match-expression. + (while (string-match "{.+?}" return-match) + (cl-incf count) + (push (match-string 0 return-match) regexps-in-match) + (setq return-match (replace-match (format "<%d>" count) t nil return-match))) + (while (and taggroups-keys (with-syntax-table stable (string-match (concat "\\(?1:[+-]?\\)\\(?2:\\<" - (regexp-opt tml) "\\>\\)") - rtnmatch))) - (let* ((dir (match-string 1 rtnmatch)) - (tag (match-string 2 rtnmatch)) + (regexp-opt taggroups-keys) "\\>\\)") + return-match))) + (let* ((dir (match-string 1 return-match)) + (tag (match-string 2 return-match)) (tag (if downcased (downcase tag) tag))) - (setq tml (delete tag tml)) - (when (not (get-text-property 0 'grouptag (match-string 2 rtnmatch))) - (setq rpl (append (org-uniquify rpl) (assoc tag tal))) - (setq rpl (concat dir "{\\<" (regexp-opt rpl) "\\>}")) - (if (stringp rpl) (org-add-props rpl '(grouptag t))) - (setq rtnmatch (replace-match rpl t t rtnmatch))))) + (unless (or (get-text-property 0 'grouptag (match-string 2 return-match)) + (member tag work-already-expanded)) + (setq tags-in-group (assoc tag taggroups)) + (push tag work-already-expanded) + ;; Recursively expand each tag in the group, if the tag hasn't + ;; already been expanded. Restore the match-data after all recursive calls. + (save-match-data + (let (tags-expanded) + (dolist (x (cdr tags-in-group)) + (if (and (member x taggroups-keys) + (not (member x work-already-expanded))) + (setq tags-expanded + (delete-dups + (append + (org-tags-expand x t downcased + work-already-expanded) + tags-expanded))) + (setq tags-expanded + (append (list x) tags-expanded))) + (setq work-already-expanded + (delete-dups + (append tags-expanded + work-already-expanded)))) + (setq tags-in-group + (delete-dups (cons (car tags-in-group) + tags-expanded))))) + ;; Filter tag-regexps from tags. + (setq regexp-in-group-escaped + (delq nil (mapcar (lambda (x) + (if (stringp x) + (and (equal "{" (substring x 0 1)) + (equal "}" (substring x -1)) + x) + x)) + tags-in-group)) + regexp-in-group + (mapcar (lambda (x) + (substring x 1 -1)) + regexp-in-group-escaped) + tags-in-group + (delq nil (mapcar (lambda (x) + (if (stringp x) + (and (not (equal "{" (substring x 0 1))) + (not (equal "}" (substring x -1))) + x) + x)) + tags-in-group))) + ;; If single-as-list, do no more in the while-loop. + (if (not single-as-list) + (progn + (when regexp-in-group + (setq regexp-in-group + (concat "\\|" + (mapconcat 'identity regexp-in-group + "\\|")))) + (setq tags-in-group + (concat dir + "{\\<" + (regexp-opt tags-in-group) + "\\>" + regexp-in-group + "}")) + (when (stringp tags-in-group) + (org-add-props tags-in-group '(grouptag t))) + (setq return-match + (replace-match tags-in-group t t return-match))) + (setq tags-in-group + (append regexp-in-group-escaped tags-in-group)))) + (setq taggroups-keys (delete tag taggroups-keys)))) + ;; Add the regular expressions back into the match-expression again. + (while regexps-in-match + (setq return-match (replace-regexp-in-string (format "<%d>" count) + (pop regexps-in-match) + return-match t t)) + (cl-decf count)) (if single-as-list - (or (reverse rpl) (list rtnmatch)) - rtnmatch)) - (if single-as-list (list (if downcased (downcase match) match)) + (if tags-in-group tags-in-group (list return-match)) + return-match)) + (if single-as-list + (list (if downcased (downcase match) match)) match))) (defun org-op-to-function (op &optional stringp) @@ -14371,7 +14845,7 @@ epoch to the beginning of today (00:00)." (defvar org-add-colon-after-tag-completion nil) ;; dynamically scoped param (defvar org-tags-overlay (make-overlay 1 1)) -(org-detach-overlay org-tags-overlay) +(delete-overlay org-tags-overlay) (defun org-get-local-tags-at (&optional pos) "Get a list of tags defined in the current headline." @@ -14405,10 +14879,9 @@ ignore inherited ones." (org-back-to-heading t) (while (not (equal lastpos (point))) (setq lastpos (point)) - (when (looking-at - (org-re "[^\r\n]+?:\\([[:alnum:]_@#%:]+\\):[ \t]*$")) + (when (looking-at ".+?:\\([[:alnum:]_@#%:]+\\):[ \t]*$") (setq ltags (org-split-string - (org-match-string-no-properties 1) ":")) + (match-string-no-properties 1) ":")) (when parent (setq ltags (mapcar 'org-add-prop-inherited ltags))) (setq tags (append @@ -14417,7 +14890,7 @@ ignore inherited ones." ltags) tags))) (or org-use-tag-inheritance (throw 'done t)) - (if local (throw 'done t)) + (when local (throw 'done t)) (or (org-up-heading-safe) (error nil)) (setq parent t))) (error nil))))) @@ -14439,7 +14912,7 @@ If ONOFF is `on' or `off', don't toggle but set to this state." (let (res current) (save-excursion (org-back-to-heading t) - (if (re-search-forward (org-re "[ \t]:\\([[:alnum:]_@#%:]+\\):[ \t]*$") + (if (re-search-forward "[ \t]:\\([[:alnum:]_@#%:]+\\):[ \t]*$" (point-at-eol) t) (progn (setq current (match-string 1)) @@ -14465,29 +14938,24 @@ If ONOFF is `on' or `off', don't toggle but set to this state." (run-hooks 'org-after-tags-change-hook)) res)) -(defun org-align-tags-here (to-col) - ;; Assumes that this is a headline - "Align tags on the current headline to TO-COL." - (let ((pos (point)) (col (current-column)) ncol tags-l p) - (beginning-of-line 1) - (if (and (looking-at (org-re ".*?\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$")) - (< pos (match-beginning 2))) - (progn - (setq tags-l (- (match-end 2) (match-beginning 2))) - (goto-char (match-beginning 1)) - (insert " ") - (delete-region (point) (1+ (match-beginning 2))) - (setq ncol (max (current-column) - (1+ col) - (if (> to-col 0) - to-col - (- (abs to-col) tags-l)))) - (setq p (point)) - (insert (make-string (- ncol (current-column)) ?\ )) - (setq ncol (current-column)) - (when indent-tabs-mode (tabify p (point-at-eol))) - (org-move-to-column (min ncol col))) - (goto-char pos)))) +(defun org--align-tags-here (to-col) + "Align tags on the current headline to TO-COL. +Assume point is on a headline." + (let ((pos (point))) + (beginning-of-line) + (if (or (not (looking-at ".*?\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$")) + (>= pos (match-beginning 2))) + ;; No tags or point within tags: do not align. + (goto-char pos) + (goto-char (match-beginning 1)) + (let ((shift (max (- (if (>= to-col 0) to-col + (- (abs to-col) (string-width (match-string 2)))) + (current-column)) + 1))) + (replace-match (make-string shift ?\s) nil nil nil 1) + ;; Preserve initial position, if possible. In any case, stop + ;; before tags. + (when (< pos (point)) (goto-char pos)))))) (defun org-set-tags-command (&optional arg just-align) "Call the set-tags command for the current entry." @@ -14517,7 +14985,8 @@ If DATA is nil or the empty string, any tags will be removed." (when data (save-excursion (org-back-to-heading t) - (when (looking-at org-complex-heading-regexp) + (when (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp)) (if (match-end 5) (progn (goto-char (match-beginning 5)) @@ -14528,11 +14997,11 @@ If DATA is nil or the empty string, any tags will be removed." (insert " " data) (org-set-tags nil 'align))) (beginning-of-line 1) - (if (looking-at ".*?\\([ \t]+\\)$") - (delete-region (match-beginning 1) (match-end 1)))))) + (when (looking-at ".*?\\([ \t]+\\)$") + (delete-region (match-beginning 1) (match-end 1)))))) (defun org-align-all-tags () - "Align the tags i all headings." + "Align the tags in all headings." (interactive) (save-excursion (or (ignore-errors (org-back-to-heading t)) @@ -14549,106 +15018,124 @@ When JUST-ALIGN is non-nil, only align tags." (interactive "P") (if (and (org-region-active-p) org-loop-over-headlines-in-active-region) (let ((cl (if (eq org-loop-over-headlines-in-active-region 'start-level) - 'region-start-level 'region)) - org-loop-over-headlines-in-active-region) - (org-map-entries - ;; We don't use ARG and JUST-ALIGN here because these args - ;; are not useful when looping over headlines. - `(org-set-tags) - org-loop-over-headlines-in-active-region - cl (if (outline-invisible-p) (org-end-of-subtree nil t)))) - (let* ((re org-outline-regexp-bol) - (current (unless arg (org-get-tags-string))) - (col (current-column)) - (org-setting-tags t) - table current-tags inherited-tags ; computed below when needed - tags p0 c0 c1 rpl di tc level) + 'region-start-level + 'region)) + org-loop-over-headlines-in-active-region) + (org-map-entries + ;; We don't use ARG and JUST-ALIGN here because these args + ;; are not useful when looping over headlines. + #'org-set-tags + org-loop-over-headlines-in-active-region + cl + '(when (org-invisible-p) (org-end-of-subtree nil t)))) + (let ((org-setting-tags t)) (if arg - (save-excursion - (goto-char (point-min)) - (let ((buffer-invisibility-spec (org-inhibit-invisibility))) - (while (re-search-forward re nil t) - (org-set-tags nil t) - (end-of-line 1))) - (message "All tags realigned to column %d" org-tags-column)) - (if just-align - (setq tags current) - ;; Get a new set of tags from the user - (save-excursion - (setq table (append org-tag-persistent-alist - (or org-tag-alist (org-get-buffer-tags)) - (and - org-complete-tags-always-offer-all-agenda-tags - (org-global-tags-completion-table - (org-agenda-files)))) - org-last-tags-completion-table table - current-tags (org-split-string current ":") - inherited-tags (nreverse - (nthcdr (length current-tags) - (nreverse (org-get-tags-at)))) - tags - (if (or (eq t org-use-fast-tag-selection) - (and org-use-fast-tag-selection - (delq nil (mapcar 'cdr table)))) - (org-fast-tag-selection - current-tags inherited-tags table - (if org-fast-tag-selection-include-todo - org-todo-key-alist)) - (let ((org-add-colon-after-tag-completion (< 1 (length table)))) - (org-trim - (org-icompleting-read "Tags: " - 'org-tags-completion-function - nil nil current 'org-tags-history)))))) - (while (string-match "[-+&]+" tags) - ;; No boolean logic, just a list - (setq tags (replace-match ":" t t tags)))) - - (setq tags (replace-regexp-in-string "[,]" ":" tags)) - - (if org-tags-sort-function - (setq tags (mapconcat 'identity - (sort (org-split-string - tags (org-re "[^[:alnum:]_@#%]+")) - org-tags-sort-function) ":"))) - - (if (string-match "\\`[\t ]*\\'" tags) - (setq tags "") - (unless (string-match ":$" tags) (setq tags (concat tags ":"))) - (unless (string-match "^:" tags) (setq tags (concat ":" tags)))) - - ;; Insert new tags at the correct column - (beginning-of-line 1) - (setq level (or (and (looking-at org-outline-regexp) - (- (match-end 0) (point) 1)) - 1)) - (cond - ((and (equal current "") (equal tags ""))) - ((re-search-forward - (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$") - (point-at-eol) t) - (if (equal tags "") - (setq rpl "") - (goto-char (match-beginning 0)) - (setq c0 (current-column) - ;; compute offset for the case of org-indent-mode active - di (if (org-bound-and-true-p org-indent-mode) - (* (1- org-indent-indentation-per-level) (1- level)) - 0) - p0 (if (equal (char-before) ?*) (1+ (point)) (point)) - tc (+ org-tags-column (if (> org-tags-column 0) (- di) di)) - c1 (max (1+ c0) (if (> tc 0) tc (- (- tc) (length tags)))) - rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags))) - (replace-match rpl t t) - (and (not (featurep 'xemacs)) c0 indent-tabs-mode (tabify p0 (point))) - tags) - (t (error "Tags alignment failed"))) - (org-move-to-column col) - (unless just-align - (run-hooks 'org-after-tags-change-hook)))))) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward org-outline-regexp-bol nil t) + (org-set-tags nil t) + (end-of-line)) + (message "All tags realigned to column %d" org-tags-column)) + (let* ((current (org-get-tags-string)) + (tags + (if just-align current + ;; Get a new set of tags from the user. + (save-excursion + (let* ((seen) + (table + (setq + org-last-tags-completion-table + ;; Uniquify tags in alists, yet preserve + ;; structure (i.e., keywords). + (delq nil + (mapcar + (lambda (pair) + (let ((head (car pair))) + (cond ((symbolp head) pair) + ((member head seen) nil) + (t (push head seen) + pair)))) + (append + (or org-current-tag-alist + (org-get-buffer-tags)) + (and + org-complete-tags-always-offer-all-agenda-tags + (org-global-tags-completion-table + (org-agenda-files)))))))) + (current-tags (org-split-string current ":")) + (inherited-tags + (nreverse (nthcdr (length current-tags) + (nreverse (org-get-tags-at)))))) + (replace-regexp-in-string + "\\([-+&]+\\|,\\)" + ":" + (if (or (eq t org-use-fast-tag-selection) + (and org-use-fast-tag-selection + (delq nil (mapcar #'cdr table)))) + (org-fast-tag-selection + current-tags inherited-tags table + (and org-fast-tag-selection-include-todo + org-todo-key-alist)) + (let ((org-add-colon-after-tag-completion + (< 1 (length table)))) + (org-trim + (completing-read + "Tags: " + #'org-tags-completion-function + nil nil current 'org-tags-history)))))))))) + + (when org-tags-sort-function + (setq tags + (mapconcat + #'identity + (sort (org-split-string tags "[^[:alnum:]_@#%]+") + org-tags-sort-function) + ":"))) + + (if (or (string= ":" tags) + (string= "::" tags)) + (setq tags "")) + (if (not (org-string-nw-p tags)) (setq tags "") + (unless (string-suffix-p ":" tags) (setq tags (concat tags ":"))) + (unless (string-prefix-p ":" tags) (setq tags (concat ":" tags)))) + + ;; Insert new tags at the correct column. + (unless (equal current tags) + (save-excursion + (beginning-of-line) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp)) + ;; Remove current tags, if any. + (when (match-end 5) (replace-match "" nil nil nil 5)) + ;; Insert new tags, if any. Otherwise, remove trailing + ;; white spaces. + (end-of-line) + (if (not (equal tags "")) + ;; When text is being inserted on an invisible + ;; region boundary, it can be inadvertently sucked + ;; into invisibility. + (outline-flag-region (point) (progn (insert " " tags) (point)) nil) + (skip-chars-backward " \t") + (delete-region (point) (line-end-position))))) + ;; Align tags, if any. Fix tags column if `org-indent-mode' + ;; is on. + (unless (equal tags "") + (let* ((level (save-excursion + (beginning-of-line) + (skip-chars-forward "\\*"))) + (offset (if (bound-and-true-p org-indent-mode) + (* (1- org-indent-indentation-per-level) + (1- level)) + 0)) + (tags-column + (+ org-tags-column + (if (> org-tags-column 0) (- offset) offset)))) + (org--align-tags-here tags-column)))) + (unless just-align (run-hooks 'org-after-tags-change-hook)))))) (defun org-change-tag-in-region (beg end tag off) "Add or remove TAG for each entry in the region. -This works in the agenda, and also in an org-mode buffer." +This works in the agenda, and also in an Org buffer." (interactive (list (region-beginning) (region-end) (let ((org-last-tags-completion-table @@ -14657,37 +15144,37 @@ This works in the agenda, and also in an org-mode buffer." (delq nil (append (org-get-buffer-tags) (org-global-tags-completion-table)))) (org-global-tags-completion-table)))) - (org-icompleting-read + (completing-read "Tag: " 'org-tags-completion-function nil nil nil 'org-tags-history)) (progn (message "[s]et or [r]emove? ") (equal (read-char-exclusive) ?r)))) - (if (fboundp 'deactivate-mark) (deactivate-mark)) + (when (fboundp 'deactivate-mark) (deactivate-mark)) (let ((agendap (equal major-mode 'org-agenda-mode)) l1 l2 m buf pos newhead (cnt 0)) (goto-char end) (setq l2 (1- (org-current-line))) (goto-char beg) (setq l1 (org-current-line)) - (loop for l from l1 to l2 do - (org-goto-line l) - (setq m (get-text-property (point) 'org-hd-marker)) - (when (or (and (derived-mode-p 'org-mode) (org-at-heading-p)) - (and agendap m)) - (setq buf (if agendap (marker-buffer m) (current-buffer)) - pos (if agendap m (point))) - (with-current-buffer buf - (save-excursion - (save-restriction - (goto-char pos) - (setq cnt (1+ cnt)) - (org-toggle-tag tag (if off 'off 'on)) - (setq newhead (org-get-heading))))) - (and agendap (org-agenda-change-all-lines newhead m)))) + (cl-loop for l from l1 to l2 do + (org-goto-line l) + (setq m (get-text-property (point) 'org-hd-marker)) + (when (or (and (derived-mode-p 'org-mode) (org-at-heading-p)) + (and agendap m)) + (setq buf (if agendap (marker-buffer m) (current-buffer)) + pos (if agendap m (point))) + (with-current-buffer buf + (save-excursion + (save-restriction + (goto-char pos) + (setq cnt (1+ cnt)) + (org-toggle-tag tag (if off 'off 'on)) + (setq newhead (org-get-heading))))) + (and agendap (org-agenda-change-all-lines newhead m)))) (message "Tag :%s: %s in %d headings" tag (if off "removed" "set") cnt))) -(defun org-tags-completion-function (string predicate &optional flag) +(defun org-tags-completion-function (string _predicate &optional flag) (let (s1 s2 rtn (ctable org-last-tags-completion-table) (confirm (lambda (x) (stringp (car x))))) (if (string-match "^\\(.*[-+:&,|]\\)\\([^-+:&,|]*\\)$" string) @@ -14698,12 +15185,12 @@ This works in the agenda, and also in an org-mode buffer." ((eq flag nil) ;; try completion (setq rtn (try-completion s2 ctable confirm)) - (if (stringp rtn) - (setq rtn - (concat s1 s2 (substring rtn (length s2)) - (if (and org-add-colon-after-tag-completion - (assoc rtn ctable)) - ":" "")))) + (when (stringp rtn) + (setq rtn + (concat s1 s2 (substring rtn (length s2)) + (if (and org-add-colon-after-tag-completion + (assoc rtn ctable)) + ":" "")))) rtn) ((eq flag t) ;; all-completions @@ -14722,8 +15209,8 @@ Also insert END." (defun org-fast-tag-show-exit (flag) (save-excursion (org-goto-line 3) - (if (re-search-forward "[ \t]+Next change exits" (point-at-eol) t) - (replace-match "")) + (when (re-search-forward "[ \t]+Next change exits" (point-at-eol) t) + (replace-match "")) (when flag (end-of-line 1) (org-move-to-column (- (window-width) 19) t) @@ -14732,11 +15219,8 @@ Also insert END." (defun org-set-current-tags-overlay (current prefix) "Add an overlay to CURRENT tag with PREFIX." (let ((s (concat ":" (mapconcat 'identity current ":") ":"))) - (if (featurep 'xemacs) - (org-overlay-display org-tags-overlay (concat prefix s) - 'secondary-selection) - (put-text-property 0 (length s) 'face '(secondary-selection org-tag) s) - (org-overlay-display org-tags-overlay (concat prefix s))))) + (put-text-property 0 (length s) 'face '(secondary-selection org-tag) s) + (org-overlay-display org-tags-overlay (concat prefix s)))) (defvar org-last-tag-selection-key nil) (defun org-fast-tag-selection (current inherited table &optional todo-table) @@ -14759,15 +15243,14 @@ Returns the new tags string, or nil to not change the current settings." (ncol (/ (- (window-width) 4) fwidth)) (i-face 'org-done) (c-face 'org-todo) - tg cnt c char c1 c2 ntable tbl rtn + tg cnt e c char c1 c2 ntable tbl rtn ov-start ov-end ov-prefix (exit-after-next org-fast-tag-selection-single-key) (done-keywords org-done-keywords) - groups ingroup) + groups ingroup intaggroup) (save-excursion (beginning-of-line 1) - (if (looking-at - (org-re ".*[ \t]\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$")) + (if (looking-at ".*[ \t]\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$") (setq ov-start (match-beginning 1) ov-end (match-end 1) ov-prefix "") @@ -14788,32 +15271,41 @@ Returns the new tags string, or nil to not change the current settings." (set-window-buffer (split-window-vertically) (get-buffer-create " *Org tags*")) (org-switch-to-buffer-other-window " *Org tags*")) (erase-buffer) - (org-set-local 'org-done-keywords done-keywords) + (setq-local org-done-keywords done-keywords) (org-fast-tag-insert "Inherited" inherited i-face "\n") (org-fast-tag-insert "Current" current c-face "\n\n") (org-fast-tag-show-exit exit-after-next) (org-set-current-tags-overlay current ov-prefix) (setq tbl fulltable char ?a cnt 0) - (dolist (e tbl) + (while (setq e (pop tbl)) (cond - ((equal (car e) :startgroup) + ((eq (car e) :startgroup) (push '() groups) (setq ingroup t) - (when (not (= cnt 0)) + (unless (zerop cnt) (setq cnt 0) (insert "\n")) (insert (if (cdr e) (format "%s: " (cdr e)) "") "{ ")) - ((equal (car e) :endgroup) + ((eq (car e) :endgroup) (setq ingroup nil cnt 0) (insert "}" (if (cdr e) (format " (%s) " (cdr e)) "") "\n")) + ((eq (car e) :startgrouptag) + (setq intaggroup t) + (unless (zerop cnt) + (setq cnt 0) + (insert "\n")) + (insert "[ ")) + ((eq (car e) :endgrouptag) + (setq intaggroup nil cnt 0) + (insert "]\n")) ((equal e '(:newline)) - (when (not (= cnt 0)) + (unless (zerop cnt) (setq cnt 0) (insert "\n") (setq e (car tbl)) (while (equal (car tbl) '(:newline)) (insert "\n") (setq tbl (cdr tbl))))) - ((equal e '(:grouptags)) nil) + ((equal e '(:grouptags)) (insert " : ")) (t (setq tg (copy-sequence (car e)) c2 nil) (if (cdr e) @@ -14827,27 +15319,27 @@ Returns the new tags string, or nil to not change the current settings." (setq char (1+ char))) (setq c2 c1)) (setq c (or c2 char))) - (if ingroup (push tg (car groups))) + (when ingroup (push tg (car groups))) (setq tg (org-add-props tg nil 'face (cond ((not (assoc tg table)) (org-get-todo-face tg)) ((member tg current) c-face) ((member tg inherited) i-face)))) - (if (equal (caar tbl) :grouptags) - (org-add-props tg nil 'face 'org-tag-group)) - (if (and (= cnt 0) (not ingroup)) (insert " ")) + (when (equal (caar tbl) :grouptags) + (org-add-props tg nil 'face 'org-tag-group)) + (when (and (zerop cnt) (not ingroup) (not intaggroup)) (insert " ")) (insert "[" c "] " tg (make-string (- fwidth 4 (length tg)) ?\ )) (push (cons tg c) ntable) - (when (= (setq cnt (1+ cnt)) ncol) + (when (= (cl-incf cnt) ncol) (insert "\n") - (if ingroup (insert " ")) + (when (or ingroup intaggroup) (insert " ")) (setq cnt 0))))) (setq ntable (nreverse ntable)) (insert "\n") (goto-char (point-min)) - (if (not expert) (org-fit-window-to-buffer)) + (unless expert (org-fit-window-to-buffer)) (setq rtn (catch 'exit (while t @@ -14873,53 +15365,51 @@ Returns the new tags string, or nil to not change the current settings." (org-fit-window-to-buffer))) ((or (= c ?\C-g) (and (= c ?q) (not (rassoc c ntable)))) - (org-detach-overlay org-tags-overlay) + (delete-overlay org-tags-overlay) (setq quit-flag t)) ((= c ?\ ) (setq current nil) - (if exit-after-next (setq exit-after-next 'now))) + (when exit-after-next (setq exit-after-next 'now))) ((= c ?\t) (condition-case nil - (setq tg (org-icompleting-read + (setq tg (completing-read "Tag: " (or buffer-tags (with-current-buffer buf - (org-get-buffer-tags))))) + (setq buffer-tags + (org-get-buffer-tags)))))) (quit (setq tg ""))) (when (string-match "\\S-" tg) - (add-to-list 'buffer-tags (list tg)) + (cl-pushnew (list tg) buffer-tags :test #'equal) (if (member tg current) (setq current (delete tg current)) (push tg current))) - (if exit-after-next (setq exit-after-next 'now))) + (when exit-after-next (setq exit-after-next 'now))) ((setq e (rassoc c todo-table) tg (car e)) (with-current-buffer buf (save-excursion (org-todo tg))) - (if exit-after-next (setq exit-after-next 'now))) + (when exit-after-next (setq exit-after-next 'now))) ((setq e (rassoc c ntable) tg (car e)) (if (member tg current) (setq current (delete tg current)) - (loop for g in groups do - (if (member tg g) - (mapc (lambda (x) - (setq current (delete x current))) - g))) + (cl-loop for g in groups do + (when (member tg g) + (dolist (x g) (setq current (delete x current))))) (push tg current)) - (if exit-after-next (setq exit-after-next 'now)))) + (when exit-after-next (setq exit-after-next 'now)))) ;; Create a sorted list (setq current (sort current (lambda (a b) (assoc b (cdr (memq (assoc a ntable) ntable)))))) - (if (eq exit-after-next 'now) (throw 'exit t)) + (when (eq exit-after-next 'now) (throw 'exit t)) (goto-char (point-min)) (beginning-of-line 2) (delete-region (point) (point-at-eol)) (org-fast-tag-insert "Current" current c-face) (org-set-current-tags-overlay current ov-prefix) - (while (re-search-forward - (org-re "\\[.\\] \\([[:alnum:]_@#%]+\\)") nil t) + (while (re-search-forward "\\[.\\] \\([[:alnum:]_@#%]+\\)" nil t) (setq tg (match-string 1)) (add-text-properties (match-beginning 1) (match-end 1) @@ -14929,7 +15419,7 @@ Returns the new tags string, or nil to not change the current settings." ((member tg inherited) i-face) (t (get-text-property (match-beginning 1) 'face)))))) (goto-char (point-min))))) - (org-detach-overlay org-tags-overlay) + (delete-overlay org-tags-overlay) (if rtn (mapconcat 'identity current ":") nil)))) @@ -14940,8 +15430,8 @@ Returns the new tags string, or nil to not change the current settings." (user-error "Not on a heading")) (save-excursion (beginning-of-line 1) - (if (looking-at (org-re ".*[ \t]\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$")) - (org-match-string-no-properties 1) + (if (looking-at ".*[ \t]\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$") + (match-string-no-properties 1) ""))) (defun org-get-tags () @@ -14950,19 +15440,20 @@ Returns the new tags string, or nil to not change the current settings." (defun org-get-buffer-tags () "Get a table of all tags used in the buffer, for completion." - (let (tags) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward - (org-re "[ \t]:\\([[:alnum:]_@#%:]+\\):[ \t\r\n]") nil t) - (when (equal (char-after (point-at-bol 0)) ?*) - (mapc (lambda (x) (add-to-list 'tags x)) - (org-split-string (org-match-string-no-properties 1) ":"))))) - (mapc (lambda (s) (add-to-list 'tags s)) org-file-tags) - (mapcar 'list tags))) + (org-with-wide-buffer + (goto-char (point-min)) + (let ((tag-re (concat org-outline-regexp-bol + "\\(?:.*?[ \t]\\)?:\\([[:alnum:]_@#%:]+\\):[ \t]*$")) + tags) + (while (re-search-forward tag-re nil t) + (dolist (tag (org-split-string (match-string-no-properties 1) ":")) + (push tag tags))) + (mapcar #'list (append org-file-tags (org-uniquify tags)))))) ;;;; The mapping API +(defvar org-agenda-skip-comment-trees) +(defvar org-agenda-skip-function) (defun org-map-entries (func &optional match scope &rest skip) "Call FUNC at each headline selected by MATCH in SCOPE. @@ -15032,13 +15523,12 @@ a *different* entry, you cannot use these techniques." (car (org-delete-all '(comment archive) skip))) (org-tags-match-list-sublevels t) (start-level (eq scope 'region-start-level)) - matcher file res + matcher res org-todo-keywords-for-agenda org-done-keywords-for-agenda org-todo-keyword-alist-for-agenda - org-drawers-for-agenda org-tag-alist-for-agenda - todo-only) + org--matcher-tags-todo-only) (cond ((eq match t) (setq matcher t)) @@ -15071,7 +15561,9 @@ a *different* entry, you cannot use these techniques." (progn (org-agenda-prepare-buffers (and buffer-file-name (list buffer-file-name))) - (setq res (org-scan-tags func matcher todo-only start-level))) + (setq res + (org-scan-tags + func matcher org--matcher-tags-todo-only start-level))) ;; Get the right scope (cond ((and scope (listp scope) (symbolp (car scope))) @@ -15088,22 +15580,21 @@ a *different* entry, you cannot use these techniques." (org-agenda-prepare-buffers scope) (dolist (file scope) (with-current-buffer (org-find-base-buffer-visiting file) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (setq res (append res (org-scan-tags func matcher todo-only)))))))))) + (org-with-wide-buffer + (goto-char (point-min)) + (setq res + (append + res + (org-scan-tags + func matcher org--matcher-tags-todo-only))))))))) res))) -;;;; Properties - -;;; Setting and retrieving properties +;;; Properties API (defconst org-special-properties - '("TODO" "TAGS" "ALLTAGS" "DEADLINE" "SCHEDULED" "CLOCK" "CLOSED" "PRIORITY" - "TIMESTAMP" "TIMESTAMP_IA" "BLOCKED" "FILE" "CLOCKSUM" "CLOCKSUM_T") - "The special properties valid in Org-mode. - + '("ALLTAGS" "BLOCKED" "CLOCKSUM" "CLOCKSUM_T" "CLOSED" "DEADLINE" "FILE" + "ITEM" "PRIORITY" "SCHEDULED" "TAGS" "TIMESTAMP" "TIMESTAMP_IA" "TODO") + "The special properties valid in Org mode. These are properties that are not defined in the property drawer, but in some other way.") @@ -15112,59 +15603,86 @@ but in some other way.") "LOCATION" "LOGGING" "COLUMNS" "VISIBILITY" "TABLE_EXPORT_FORMAT" "TABLE_EXPORT_FILE" "EXPORT_OPTIONS" "EXPORT_TEXT" "EXPORT_FILE_NAME" - "EXPORT_TITLE" "EXPORT_AUTHOR" "EXPORT_DATE" + "EXPORT_TITLE" "EXPORT_AUTHOR" "EXPORT_DATE" "UNNUMBERED" "ORDERED" "NOBLOCKING" "COOKIE_DATA" "LOG_INTO_DRAWER" "REPEAT_TO_STATE" "CLOCK_MODELINE_TOTAL" "STYLE" "HTML_CONTAINER_CLASS") - "Some properties that are used by Org-mode for various purposes. + "Some properties that are used by Org mode for various purposes. Being in this list makes sure that they are offered for completion.") -(defconst org-property-start-re "^[ \t]*:PROPERTIES:[ \t]*$" - "Regular expression matching the first line of a property drawer.") - -(defconst org-property-end-re "^[ \t]*:END:[ \t]*$" - "Regular expression matching the last line of a property drawer.") - -(defconst org-clock-drawer-start-re "^[ \t]*:CLOCK:[ \t]*$" - "Regular expression matching the first line of a property drawer.") - -(defconst org-clock-drawer-end-re "^[ \t]*:END:[ \t]*$" - "Regular expression matching the first line of a property drawer.") - -(defconst org-property-drawer-re - (concat "\\(" org-property-start-re "\\)[^\000]*\\(" - org-property-end-re "\\)\n?") - "Matches an entire property drawer.") +(defun org--valid-property-p (property) + "Non nil when string PROPERTY is a valid property name." + (not + (or (equal property "") + (string-match-p "\\s-" property)))) + +(defun org--update-property-plist (key val props) + "Associate KEY to VAL in alist PROPS. +Modifications are made by side-effect. Return new alist." + (let* ((appending (string= (substring key -1) "+")) + (key (if appending (substring key 0 -1) key)) + (old (assoc-string key props t))) + (if (not old) (cons (cons key val) props) + (setcdr old (if appending (concat (cdr old) " " val) val)) + props))) + +(defun org-get-property-block (&optional beg force) + "Return the (beg . end) range of the body of the property drawer. +BEG is the beginning of the current subtree, or of the part +before the first headline. If it is not given, it will be found. +If the drawer does not exist, create it if FORCE is non-nil, or +return nil." + (org-with-wide-buffer + (when beg (goto-char beg)) + (unless (org-before-first-heading-p) + (let ((beg (cond (beg) + ((or (not (featurep 'org-inlinetask)) + (org-inlinetask-in-task-p)) + (org-back-to-heading t)) + (t (org-with-limited-levels (org-back-to-heading t)))))) + (forward-line) + (when (looking-at-p org-planning-line-re) (forward-line)) + (cond ((looking-at org-property-drawer-re) + (forward-line) + (cons (point) (progn (goto-char (match-end 0)) + (line-beginning-position)))) + (force + (goto-char beg) + (org-insert-property-drawer) + (let ((pos (save-excursion (search-forward ":END:") + (line-beginning-position)))) + (cons pos pos)))))))) -(defconst org-clock-drawer-re - (concat "\\(" org-clock-drawer-start-re "\\)[^\000]*\\(" - org-property-end-re "\\)\n?") - "Matches an entire clock drawer.") +(defun org-at-property-p () + "Non-nil when point is inside a property drawer. +See `org-property-re' for match data, if applicable." + (save-excursion + (beginning-of-line) + (and (looking-at org-property-re) + (let ((property-drawer (save-match-data (org-get-property-block)))) + (and property-drawer + (>= (point) (car property-drawer)) + (< (point) (cdr property-drawer))))))) (defun org-property-action () "Do an action on properties." (interactive) - (let (c) - (org-at-property-p) - (message "Property Action: [s]et [d]elete [D]elete globally [c]ompute") - (setq c (read-char-exclusive)) - (cond - ((equal c ?s) - (call-interactively 'org-set-property)) - ((equal c ?d) - (call-interactively 'org-delete-property)) - ((equal c ?D) - (call-interactively 'org-delete-property-globally)) - ((equal c ?c) - (call-interactively 'org-compute-property-at-point)) - (t (user-error "No such property action %c" c))))) + (unless (org-at-property-p) (user-error "Not at a property")) + (message "Property Action: [s]et [d]elete [D]elete globally [c]ompute") + (let ((c (read-char-exclusive))) + (cl-case c + (?s (call-interactively #'org-set-property)) + (?d (call-interactively #'org-delete-property)) + (?D (call-interactively #'org-delete-property-globally)) + (?c (call-interactively #'org-compute-property-at-point)) + (otherwise (user-error "No such property action %c" c))))) (defun org-inc-effort () "Increment the value of the effort property in the current entry." (interactive) (org-set-effort nil t)) -(defvar org-clock-effort) ;; Defined in org-clock.el -(defvar org-clock-current-task) ;; Defined in org-clock.el +(defvar org-clock-effort) ; Defined in org-clock.el. +(defvar org-clock-current-task) ; Defined in org-clock.el. (defun org-set-effort (&optional value increment) "Set the effort property of the current entry. With numerical prefix arg, use the nth allowed value, 0 stands for the @@ -15172,7 +15690,7 @@ With numerical prefix arg, use the nth allowed value, 0 stands for the When INCREMENT is non-nil, set the property to the next allowed value." (interactive "P") - (if (equal value 0) (setq value 10)) + (when (equal value 0) (setq value 10)) (let* ((completion-ignore-case t) (prop org-effort-property) (cur (org-entry-get nil prop)) @@ -15186,7 +15704,7 @@ When INCREMENT is non-nil, set the property to the next allowed value." (or (car (nth (1- value) allowed)) (car (org-last allowed)))) ((and allowed increment) - (or (caadr (member (list cur) allowed)) + (or (cl-caadr (member (list cur) allowed)) (user-error "Allowed effort values are not set"))) (allowed (message "Select 1-9,0, [RET%s]: %s" @@ -15196,231 +15714,295 @@ When INCREMENT is non-nil, set the property to the next allowed value." (if (equal rpl ?\r) cur (setq rpl (- rpl ?0)) - (if (equal rpl 0) (setq rpl 10)) + (when (equal rpl 0) (setq rpl 10)) (if (and (> rpl 0) (<= rpl (length allowed))) (car (nth (1- rpl) allowed)) (org-completing-read "Effort: " allowed nil)))) (t - (let (org-completion-use-ido org-completion-use-iswitchb) - (org-completing-read - (concat "Effort " (if (and cur (string-match "\\S-" cur)) - (concat "[" cur "]") "") - ": ") - existing nil nil "" nil cur)))))) + (org-completing-read + (concat "Effort" (and cur (string-match "\\S-" cur) + (concat " [" cur "]")) + ": ") + existing nil nil "" nil cur))))) (unless (equal (org-entry-get nil prop) val) (org-entry-put nil prop val)) - (save-excursion - (org-back-to-heading t) - (put-text-property (point-at-bol) (point-at-eol) 'org-effort val)) - (when (string= heading org-clock-current-task) - (setq org-clock-effort (get-text-property (point-at-bol) 'org-effort)) + (org-refresh-property + '((effort . identity) + (effort-minutes . org-duration-string-to-minutes)) + val) + (when (equal heading (bound-and-true-p org-clock-current-task)) + (setq org-clock-effort (get-text-property (point-at-bol) 'effort)) (org-clock-update-mode-line)) (message "%s is now %s" prop val))) -(defun org-at-property-p () - "Is cursor inside a property drawer?" - (save-excursion - (when (equal 'node-property (car (org-element-at-point))) - (beginning-of-line 1) - (looking-at org-property-re)))) +(defun org-entry-properties (&optional pom which) + "Get all properties of the current entry. + +When POM is a buffer position, get all properties from the entry +there instead. + +This includes the TODO keyword, the tags, time strings for +deadline, scheduled, and clocking, and any additional properties +defined in the entry. -(defun org-get-property-block (&optional beg end force) - "Return the (beg . end) range of the body of the property drawer. -BEG and END are the beginning and end of the current subtree, or of -the part before the first headline. If they are not given, they will -be found. If the drawer does not exist and FORCE is non-nil, create -the drawer." - (catch 'exit - (save-excursion - (let* ((beg (or beg (and (org-before-first-heading-p) (point-min)) - (progn (org-back-to-heading t) (point)))) - (end (or end (and (not (outline-next-heading)) (point-max)) - (point)))) - (goto-char beg) - (if (re-search-forward org-property-start-re end t) - (setq beg (1+ (match-end 0))) - (if force - (save-excursion - (org-insert-property-drawer) - (setq end (progn (outline-next-heading) (point)))) - (throw 'exit nil)) - (goto-char beg) - (if (re-search-forward org-property-start-re end t) - (setq beg (1+ (match-end 0))))) - (if (re-search-forward org-property-end-re end t) - (setq end (match-beginning 0)) - (or force (throw 'exit nil)) - (goto-char beg) - (setq end beg) - (org-indent-line) - (insert ":END:\n")) - (cons beg end))))) - -(defun org-entry-properties (&optional pom which specific) - "Get all properties of the entry at point-or-marker POM. -This includes the TODO keyword, the tags, time strings for deadline, -scheduled, and clocking, and any additional properties defined in the -entry. The return value is an alist, keys may occur multiple times -if the property key was used several times. -POM may also be nil, in which case the current entry is used. If WHICH is nil or `all', get all properties. If WHICH is -`special' or `standard', only get that subclass. If WHICH -is a string only get exactly this property. SPECIFIC can be a string, the -specific property we are interested in. Specifying it can speed -things up because then unnecessary parsing is avoided." - (setq which (or which 'all)) - (org-with-wide-buffer - (org-with-point-at pom - (let ((clockstr (substring org-clock-string 0 -1)) - (excluded '("TODO" "TAGS" "ALLTAGS" "PRIORITY" "BLOCKED")) - (case-fold-search nil) - beg end range props sum-props key key1 value string clocksum clocksumt) - (when (and (derived-mode-p 'org-mode) - (ignore-errors (org-back-to-heading t))) - (setq beg (point)) - (setq sum-props (get-text-property (point) 'org-summaries)) - (setq clocksum (get-text-property (point) :org-clock-minutes) - clocksumt (get-text-property (point) :org-clock-minutes-today)) - (outline-next-heading) - (setq end (point)) - (when (memq which '(all special)) - ;; Get the special properties, like TODO and tags - (goto-char beg) - (when (and (or (not specific) (string= specific "TODO")) - (looking-at org-todo-line-regexp) (match-end 2)) - (push (cons "TODO" (org-match-string-no-properties 2)) props)) - (when (and (or (not specific) (string= specific "PRIORITY")) - (looking-at org-priority-regexp)) - (push (cons "PRIORITY" (org-match-string-no-properties 2)) props)) - (when (or (not specific) (string= specific "FILE")) - (push (cons "FILE" buffer-file-name) props)) - (when (and (or (not specific) (string= specific "TAGS")) - (setq value (org-get-tags-string)) - (string-match "\\S-" value)) - (push (cons "TAGS" value) props)) - (when (and (or (not specific) (string= specific "ALLTAGS")) - (setq value (org-get-tags-at))) - (push (cons "ALLTAGS" (concat ":" (mapconcat 'identity value ":") - ":")) - props)) - (when (or (not specific) (string= specific "BLOCKED")) - (push (cons "BLOCKED" (if (org-entry-blocked-p) "t" "")) props)) - (when (or (not specific) - (member specific - '("SCHEDULED" "DEADLINE" "CLOCK" "CLOSED" - "TIMESTAMP" "TIMESTAMP_IA"))) - (catch 'match - (while (and (re-search-forward org-maybe-keyword-time-regexp end t) - (not (text-property-any 0 (length (match-string 0)) - 'face 'font-lock-comment-face - (match-string 0)))) - (setq key (if (match-end 1) - (substring (org-match-string-no-properties 1) - 0 -1)) - string (if (equal key clockstr) - (org-trim - (buffer-substring-no-properties - (match-beginning 3) (goto-char - (point-at-eol)))) - (substring (org-match-string-no-properties 3) - 1 -1))) - ;; Get the correct property name from the key. This is - ;; necessary if the user has configured time keywords. - (setq key1 (concat key ":")) - (cond - ((not key) - (setq key - (if (= (char-after (match-beginning 3)) ?\[) - "TIMESTAMP_IA" "TIMESTAMP"))) - ((equal key1 org-scheduled-string) (setq key "SCHEDULED")) - ((equal key1 org-deadline-string) (setq key "DEADLINE")) - ((equal key1 org-closed-string) (setq key "CLOSED")) - ((equal key1 org-clock-string) (setq key "CLOCK"))) - (if (and specific (equal key specific) (not (equal key "CLOCK"))) - (progn - (push (cons key string) props) - ;; no need to search further if match is found - (throw 'match t)) - (when (or (equal key "CLOCK") (not (assoc key props))) - (push (cons key string) props))))))) - - (when (memq which '(all standard)) - ;; Get the standard properties, like :PROP: ... - (setq range (org-get-property-block beg end)) - (when range - (goto-char (car range)) - (while (re-search-forward org-property-re - (cdr range) t) - (setq key (org-match-string-no-properties 2) - value (org-trim (or (org-match-string-no-properties 3) ""))) - (unless (member key excluded) - (push (cons key (or value "")) props))))) - (if clocksum - (push (cons "CLOCKSUM" - (org-columns-number-to-string (/ (float clocksum) 60.) - 'add_times)) - props)) - (if clocksumt - (push (cons "CLOCKSUM_T" - (org-columns-number-to-string (/ (float clocksumt) 60.) - 'add_times)) - props)) - (unless (assoc "CATEGORY" props) - (push (cons "CATEGORY" (org-get-category)) props)) - (append sum-props (nreverse props))))))) +`special' or `standard', only get that subclass. If WHICH is +a string, only get that property. + +Return value is an alist. Keys are properties, as upcased +strings." + (org-with-point-at pom + (when (and (derived-mode-p 'org-mode) + (ignore-errors (org-back-to-heading t))) + (catch 'exit + (let* ((beg (point)) + (specific (and (stringp which) (upcase which))) + (which (cond ((not specific) which) + ((member specific org-special-properties) 'special) + (t 'standard))) + props) + ;; Get the special properties, like TODO and TAGS. + (when (memq which '(nil all special)) + (when (or (not specific) (string= specific "CLOCKSUM")) + (let ((clocksum (get-text-property (point) :org-clock-minutes))) + (when clocksum + (push (cons "CLOCKSUM" + (org-minutes-to-clocksum-string clocksum)) + props))) + (when specific (throw 'exit props))) + (when (or (not specific) (string= specific "CLOCKSUM_T")) + (let ((clocksumt (get-text-property (point) + :org-clock-minutes-today))) + (when clocksumt + (push (cons "CLOCKSUM_T" + (org-minutes-to-clocksum-string clocksumt)) + props))) + (when specific (throw 'exit props))) + (when (or (not specific) (string= specific "ITEM")) + (let ((case-fold-search nil)) + (when (looking-at org-complex-heading-regexp) + (push (cons "ITEM" + (let ((title (match-string-no-properties 4))) + (if (org-string-nw-p title) + (org-remove-tabs title) + ""))) + props))) + (when specific (throw 'exit props))) + (when (or (not specific) (string= specific "TODO")) + (let ((case-fold-search nil)) + (when (and (looking-at org-todo-line-regexp) (match-end 2)) + (push (cons "TODO" (match-string-no-properties 2)) props))) + (when specific (throw 'exit props))) + (when (or (not specific) (string= specific "PRIORITY")) + (push (cons "PRIORITY" + (if (looking-at org-priority-regexp) + (match-string-no-properties 2) + (char-to-string org-default-priority))) + props) + (when specific (throw 'exit props))) + (when (or (not specific) (string= specific "FILE")) + (push (cons "FILE" (buffer-file-name (buffer-base-buffer))) + props) + (when specific (throw 'exit props))) + (when (or (not specific) (string= specific "TAGS")) + (let ((value (org-string-nw-p (org-get-tags-string)))) + (when value (push (cons "TAGS" value) props))) + (when specific (throw 'exit props))) + (when (or (not specific) (string= specific "ALLTAGS")) + (let ((value (org-get-tags-at))) + (when value + (push (cons "ALLTAGS" + (format ":%s:" (mapconcat #'identity value ":"))) + props))) + (when specific (throw 'exit props))) + (when (or (not specific) (string= specific "BLOCKED")) + (push (cons "BLOCKED" (if (org-entry-blocked-p) "t" "")) props) + (when specific (throw 'exit props))) + (when (or (not specific) + (member specific '("CLOSED" "DEADLINE" "SCHEDULED"))) + (forward-line) + (when (looking-at-p org-planning-line-re) + (end-of-line) + (let ((bol (line-beginning-position)) + ;; Backward compatibility: time keywords used to + ;; be configurable (before 8.3). Make sure we + ;; get the correct keyword. + (key-assoc `(("CLOSED" . ,org-closed-string) + ("DEADLINE" . ,org-deadline-string) + ("SCHEDULED" . ,org-scheduled-string)))) + (dolist (pair (if specific (list (assoc specific key-assoc)) + key-assoc)) + (save-excursion + (when (search-backward (cdr pair) bol t) + (goto-char (match-end 0)) + (skip-chars-forward " \t") + (and (looking-at org-ts-regexp-both) + (push (cons (car pair) + (match-string-no-properties 0)) + props))))))) + (when specific (throw 'exit props))) + (when (or (not specific) + (member specific '("TIMESTAMP" "TIMESTAMP_IA"))) + (let ((find-ts + (lambda (end ts) + ;; Fix next time-stamp before END. TS is the + ;; list of time-stamps found so far. + (let ((ts ts) + (regexp (cond + ((string= specific "TIMESTAMP") + org-ts-regexp) + ((string= specific "TIMESTAMP_IA") + org-ts-regexp-inactive) + ((assoc "TIMESTAMP_IA" ts) + org-ts-regexp) + ((assoc "TIMESTAMP" ts) + org-ts-regexp-inactive) + (t org-ts-regexp-both)))) + (catch 'next + (while (re-search-forward regexp end t) + (backward-char) + (let ((object (org-element-context))) + ;; Accept to match timestamps in node + ;; properties, too. + (when (memq (org-element-type object) + '(node-property timestamp)) + (let ((type + (org-element-property :type object))) + (cond + ((and (memq type '(active active-range)) + (not (equal specific "TIMESTAMP_IA"))) + (unless (assoc "TIMESTAMP" ts) + (push (cons "TIMESTAMP" + (org-element-property + :raw-value object)) + ts) + (when specific (throw 'exit ts)))) + ((and (memq type '(inactive inactive-range)) + (not (string= specific "TIMESTAMP"))) + (unless (assoc "TIMESTAMP_IA" ts) + (push (cons "TIMESTAMP_IA" + (org-element-property + :raw-value object)) + ts) + (when specific (throw 'exit ts)))))) + ;; Both timestamp types are found, + ;; move to next part. + (when (= (length ts) 2) (throw 'next ts))))) + ts))))) + (goto-char beg) + ;; First look for timestamps within headline. + (let ((ts (funcall find-ts (line-end-position) nil))) + (if (= (length ts) 2) (setq props (nconc ts props)) + ;; Then find timestamps in the section, skipping + ;; planning line. + (let ((end (save-excursion (outline-next-heading)))) + (forward-line) + (when (looking-at-p org-planning-line-re) (forward-line)) + (setq props (nconc (funcall find-ts end ts) props)))))))) + ;; Get the standard properties, like :PROP:. + (when (memq which '(nil all standard)) + ;; If we are looking after a specific property, delegate + ;; to `org-entry-get', which is faster. However, make an + ;; exception for "CATEGORY", since it can be also set + ;; through keywords (i.e. #+CATEGORY). + (if (and specific (not (equal specific "CATEGORY"))) + (let ((value (org-entry-get beg specific nil t))) + (throw 'exit (and value (list (cons specific value))))) + (let ((range (org-get-property-block beg))) + (when range + (let ((end (cdr range)) seen-base) + (goto-char (car range)) + ;; Unlike to `org--update-property-plist', we + ;; handle the case where base values is found + ;; after its extension. We also forbid standard + ;; properties to be named as special properties. + (while (re-search-forward org-property-re end t) + (let* ((key (upcase (match-string-no-properties 2))) + (extendp (string-match-p "\\+\\'" key)) + (key-base (if extendp (substring key 0 -1) key)) + (value (match-string-no-properties 3))) + (cond + ((member-ignore-case key-base org-special-properties)) + (extendp + (setq props + (org--update-property-plist key value props))) + ((member key seen-base)) + (t (push key seen-base) + (let ((p (assoc-string key props t))) + (if p (setcdr p (concat value " " (cdr p))) + (push (cons key value) props)))))))))))) + (unless (assoc "CATEGORY" props) + (push (cons "CATEGORY" (org-get-category beg)) props) + (when (string= specific "CATEGORY") (throw 'exit props))) + ;; Return value. + props))))) + +(defun org--property-local-values (property literal-nil) + "Return value for PROPERTY in current entry. +Value is a list whose car is the base value for PROPERTY and cdr +a list of accumulated values. Return nil if neither is found in +the entry. Also return nil when PROPERTY is set to \"nil\", +unless LITERAL-NIL is non-nil." + (let ((range (org-get-property-block))) + (when range + (goto-char (car range)) + (let* ((case-fold-search t) + (end (cdr range)) + (value + ;; Base value. + (save-excursion + (let ((v (and (re-search-forward + (org-re-property property nil t) end t) + (match-string-no-properties 3)))) + (list (if literal-nil v (org-not-nil v))))))) + ;; Find additional values. + (let* ((property+ (org-re-property (concat property "+") nil t))) + (while (re-search-forward property+ end t) + (push (match-string-no-properties 3) value))) + ;; Return final values. + (and (not (equal value '(nil))) (nreverse value)))))) + +(defun org--property-global-value (property literal-nil) + "Return value for PROPERTY in current buffer. +Return value is a string. Return nil if property is not set +globally. Also return nil when PROPERTY is set to \"nil\", +unless LITERAL-NIL is non-nil." + (let ((global + (cdr (or (assoc-string property org-file-properties t) + (assoc-string property org-global-properties t) + (assoc-string property org-global-properties-fixed t))))) + (if literal-nil global (org-not-nil global)))) (defun org-entry-get (pom property &optional inherit literal-nil) "Get value of PROPERTY for entry or content at point-or-marker POM. -If INHERIT is non-nil and the entry does not have the property, -then also check higher levels of the hierarchy. -If INHERIT is the symbol `selective', use inheritance only if the setting -in `org-use-property-inheritance' selects PROPERTY for inheritance. -If the property is present but empty, the return value is the empty string. -If the property is not present at all, nil is returned. - -Return the value as a string. -If LITERAL-NIL is set, return the string value \"nil\" as a string, -do not interpret it as the list atom nil. This is used for inheritance -when a \"nil\" value can supersede a non-nil value higher up the hierarchy." +If INHERIT is non-nil and the entry does not have the property, +then also check higher levels of the hierarchy. If INHERIT is +the symbol `selective', use inheritance only if the setting in +`org-use-property-inheritance' selects PROPERTY for inheritance. + +If the property is present but empty, the return value is the +empty string. If the property is not present at all, nil is +returned. In any other case, return the value as a string. +Search is case-insensitive. + +If LITERAL-NIL is set, return the string value \"nil\" as +a string, do not interpret it as the list atom nil. This is used +for inheritance when a \"nil\" value can supersede a non-nil +value higher up the hierarchy." (org-with-point-at pom - (if (and inherit (if (eq inherit 'selective) - (org-property-inherit-p property) - t)) - (org-entry-get-with-inheritance property literal-nil) - (if (member property org-special-properties) - ;; We need a special property. Use `org-entry-properties' - ;; to retrieve it, but specify the wanted property - (cdr (assoc property (org-entry-properties nil 'special property))) - (org-with-wide-buffer - (let ((range (org-get-property-block))) - (when (and range (not (eq (car range) (cdr range))) - (save-excursion - (goto-char (car range)) - (re-search-forward - (concat (org-re-property property) "\\|" - (org-re-property (concat property "+"))) - (cdr range) t))) - (let* ((props - (list (or (assoc property org-file-properties) - (assoc property org-global-properties) - (assoc property org-global-properties-fixed)))) - (ap (lambda (key) - (when (re-search-forward - (org-re-property key) (cdr range) t) - (setq props - (org-update-property-plist - key - (if (match-end 3) - (org-match-string-no-properties 3) "") - props))))) - val) - (goto-char (car range)) - (funcall ap property) - (goto-char (car range)) - (while (funcall ap (concat property "+"))) - (setq val (cdr (assoc property props))) - (when val (if literal-nil val (org-not-nil val))))))))))) + (cond + ((member-ignore-case property (cons "CATEGORY" org-special-properties)) + ;; We need a special property. Use `org-entry-properties' to + ;; retrieve it, but specify the wanted property. + (cdr (assoc-string property (org-entry-properties nil property)))) + ((and inherit + (or (not (eq inherit 'selective)) (org-property-inherit-p property))) + (org-entry-get-with-inheritance property literal-nil)) + (t + (let* ((local (org--property-local-values property literal-nil)) + (value (and local (mapconcat #'identity (delq nil local) " ")))) + (if literal-nil value (org-not-nil value))))))) (defun org-property-or-variable-value (var &optional inherit) "Check if there is a property fixing the value of VAR. @@ -15430,26 +16012,26 @@ If yes, return this value. If not, return the current value of the variable." (read prop) (symbol-value var)))) -(defun org-entry-delete (pom property &optional delete-empty-drawer) - "Delete the property PROPERTY from entry at point-or-marker POM. -When optional argument DELETE-EMPTY-DRAWER is a string, it defines -an empty drawer to delete." +(defun org-entry-delete (pom property) + "Delete PROPERTY from entry at point-or-marker POM. +Accumulated properties, i.e. PROPERTY+, are also removed. Return +non-nil when a property was removed." (org-with-point-at pom - (if (member property org-special-properties) - nil ; cannot delete these properties. - (let ((range (org-get-property-block))) - (if (and range - (goto-char (car range)) - (re-search-forward - (org-re-property property nil t) - (cdr range) t)) - (progn - (delete-region (match-beginning 0) (1+ (point-at-eol))) - (and delete-empty-drawer - (org-remove-empty-drawer-at - delete-empty-drawer (car range))) - t) - nil))))) + (pcase (org-get-property-block) + (`(,begin . ,origin) + (let* ((end (copy-marker origin)) + (re (org-re-property + (concat (regexp-quote property) "\\+?") t t))) + (goto-char begin) + (while (re-search-forward re end t) + (delete-region (match-beginning 0) (line-beginning-position 2))) + ;; If drawer is empty, remove it altogether. + (when (= begin end) + (delete-region (line-beginning-position 0) + (line-beginning-position 2))) + ;; Return non-nil if some property was removed. + (prog1 (/= end origin) (set-marker end nil)))) + (_ nil)))) ;; Multi-values properties are properties that contain multiple values ;; These values are assumed to be single words, separated by whitespace. @@ -15526,24 +16108,29 @@ If the value found is \"nil\", return nil to show that the property should be considered as undefined (this is the meaning of nil here). However, if LITERAL-NIL is set, return the string value \"nil\" instead." (move-marker org-entry-property-inherited-from nil) - (let (tmp) - (save-excursion - (save-restriction - (widen) - (catch 'ex - (while t - (when (setq tmp (org-entry-get nil property nil literal-nil)) - (or (ignore-errors (org-back-to-heading t)) - (goto-char (point-min))) - (move-marker org-entry-property-inherited-from (point)) - (throw 'ex tmp)) - (or (ignore-errors (org-up-heading-safe)) - (throw 'ex nil)))))) - (setq tmp (or tmp - (cdr (assoc property org-file-properties)) - (cdr (assoc property org-global-properties)) - (cdr (assoc property org-global-properties-fixed)))) - (if literal-nil tmp (org-not-nil tmp)))) + (org-with-wide-buffer + (let (value) + (catch 'exit + (while t + (let ((v (org--property-local-values property literal-nil))) + (when v + (setq value + (concat (mapconcat #'identity (delq nil v) " ") + (and value " ") + value))) + (cond + ((car v) + (org-back-to-heading t) + (move-marker org-entry-property-inherited-from (point)) + (throw 'exit nil)) + ((org-up-heading-safe)) + (t + (let ((global (org--property-global-value property literal-nil))) + (cond ((not global)) + (value (setq value (concat global " " value))) + (t (setq value global)))) + (throw 'exit nil)))))) + (if literal-nil value (org-not-nil value))))) (defvar org-property-changed-functions nil "Hook called when the value of a property has changed. @@ -15552,177 +16139,188 @@ and the new value.") (defun org-entry-put (pom property value) "Set PROPERTY to VALUE for entry at point-or-marker POM. -If the value is nil, it is converted to the empty string. -If it is not a string, an error is raised." + +If the value is nil, it is converted to the empty string. If it +is not a string, an error is raised. Also raise an error on +invalid property names. + +PROPERTY can be any regular property (see +`org-special-properties'). It can also be \"TODO\", +\"PRIORITY\", \"SCHEDULED\" and \"DEADLINE\". + +For the last two properties, VALUE may have any of the special +values \"earlier\" and \"later\". The function then increases or +decreases scheduled or deadline date by one day." (cond ((null value) (setq value "")) - ((not (stringp value)) - (error "Properties values should be strings."))) + ((not (stringp value)) (error "Properties values should be strings")) + ((not (org--valid-property-p property)) + (user-error "Invalid property name: \"%s\"" property))) (org-with-point-at pom - (org-back-to-heading t) - (let ((beg (point)) (end (save-excursion (outline-next-heading) (point))) - range) + (if (or (not (featurep 'org-inlinetask)) (org-inlinetask-in-task-p)) + (org-back-to-heading t) + (org-with-limited-levels (org-back-to-heading t))) + (let ((beg (point))) (cond ((equal property "TODO") - (when (and (string-match "\\S-" value) - (not (member value org-todo-keywords-1))) - (user-error "\"%s\" is not a valid TODO state" value)) - (if (or (not value) - (not (string-match "\\S-" value))) - (setq value 'none)) + (cond ((not (org-string-nw-p value)) (setq value 'none)) + ((not (member value org-todo-keywords-1)) + (user-error "\"%s\" is not a valid TODO state" value))) (org-todo value) (org-set-tags nil 'align)) ((equal property "PRIORITY") - (org-priority (if (and value (string-match "\\S-" value)) - (string-to-char value) ?\ )) + (org-priority (if (org-string-nw-p value) (string-to-char value) ?\s)) (org-set-tags nil 'align)) - ((equal property "CLOCKSUM") - (if (not (re-search-forward - (concat org-clock-string ".*\\]--\\(\\[[^]]+\\]\\)") nil t)) - (error "Cannot find a clock log") - (goto-char (- (match-end 1) 2)) - (cond - ((eq value 'earlier) (org-timestamp-down)) - ((eq value 'later) (org-timestamp-up))) - (org-clock-sum-current-item))) ((equal property "SCHEDULED") - (if (re-search-forward org-scheduled-time-regexp end t) - (cond - ((eq value 'earlier) (org-timestamp-change -1 'day)) - ((eq value 'later) (org-timestamp-change 1 'day)) - (t (call-interactively 'org-schedule))) - (call-interactively 'org-schedule))) + (forward-line) + (if (and (looking-at-p org-planning-line-re) + (re-search-forward + org-scheduled-time-regexp (line-end-position) t)) + (cond ((string= value "earlier") (org-timestamp-change -1 'day)) + ((string= value "later") (org-timestamp-change 1 'day)) + ((string= value "") (org-schedule '(4))) + (t (org-schedule nil value))) + (if (member value '("earlier" "later" "")) + (call-interactively #'org-schedule) + (org-schedule nil value)))) ((equal property "DEADLINE") - (if (re-search-forward org-deadline-time-regexp end t) - (cond - ((eq value 'earlier) (org-timestamp-change -1 'day)) - ((eq value 'later) (org-timestamp-change 1 'day)) - (t (call-interactively 'org-deadline))) - (call-interactively 'org-deadline))) + (forward-line) + (if (and (looking-at-p org-planning-line-re) + (re-search-forward + org-deadline-time-regexp (line-end-position) t)) + (cond ((string= value "earlier") (org-timestamp-change -1 'day)) + ((string= value "later") (org-timestamp-change 1 'day)) + ((string= value "") (org-deadline '(4))) + (t (org-deadline nil value))) + (if (member value '("earlier" "later" "")) + (call-interactively #'org-deadline) + (org-deadline nil value)))) ((member property org-special-properties) - (error "The %s property can not yet be set with `org-entry-put'" - property)) - (t ; a non-special property - (let ((buffer-invisibility-spec (org-inhibit-invisibility))) ; Emacs 21 - (setq range (org-get-property-block beg end 'force)) + (error "The %s property cannot be set with `org-entry-put'" property)) + (t + (let* ((range (org-get-property-block beg 'force)) + (end (cdr range)) + (case-fold-search t)) (goto-char (car range)) - (if (re-search-forward - (org-re-property property nil t) (cdr range) t) - (progn - (delete-region (match-beginning 0) (match-end 0)) - (goto-char (match-beginning 0))) - (goto-char (cdr range)) + (if (re-search-forward (org-re-property property nil t) end t) + (progn (delete-region (match-beginning 0) (match-end 0)) + (goto-char (match-beginning 0))) + (goto-char end) (insert "\n") - (backward-char 1) - (org-indent-line)) + (backward-char)) (insert ":" property ":") - (and value (insert " " value)) + (when value (insert " " value)) (org-indent-line))))) (run-hook-with-args 'org-property-changed-functions property value))) -(defun org-buffer-property-keys (&optional include-specials include-defaults include-columns) +(defun org-buffer-property-keys + (&optional specials defaults columns ignore-malformed) "Get all property keys in the current buffer. -With INCLUDE-SPECIALS, also list the special properties that reflect things -like tags and TODO state. -With INCLUDE-DEFAULTS, also include properties that has special meaning -internally: ARCHIVE, CATEGORY, SUMMARY, DESCRIPTION, LOCATION, and LOGGING -and others. -With INCLUDE-COLUMNS, also include property names given in COLUMN -formats in the current buffer." - (let (rtn range cfmt s p) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while (re-search-forward org-property-start-re nil t) - (setq range (org-get-property-block)) - (goto-char (car range)) - (while (re-search-forward org-property-re - (cdr range) t) - (add-to-list 'rtn (org-match-string-no-properties 2))) - (outline-next-heading)))) - (when include-specials - (setq rtn (append org-special-properties rtn))) +When SPECIALS is non-nil, also list the special properties that +reflect things like tags and TODO state. - (when include-defaults - (mapc (lambda (x) (add-to-list 'rtn x)) org-default-properties) - (add-to-list 'rtn org-effort-property)) +When DEFAULTS is non-nil, also include properties that has +special meaning internally: ARCHIVE, CATEGORY, SUMMARY, +DESCRIPTION, LOCATION, and LOGGING and others. - (when include-columns - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (while (re-search-forward - "^\\(#\\+COLUMNS:\\|[ \t]*:COLUMNS:\\)[ \t]*\\(.*\\)" - nil t) - (setq cfmt (match-string 2) s 0) - (while (string-match (org-re "%[0-9]*\\([-[:alnum:]_]+\\)") - cfmt s) - (setq s (match-end 0) - p (match-string 1 cfmt)) - (unless (or (equal p "ITEM") - (member p org-special-properties)) - (add-to-list 'rtn (match-string 1 cfmt)))))))) - - (sort rtn (lambda (a b) (string< (upcase a) (upcase b)))))) +When COLUMNS in non-nil, also include property names given in +COLUMN formats in the current buffer. + +When IGNORE-MALFORMED is non-nil, malformed drawer repair will not be +automatically performed, such drawers will be silently ignored." + (let ((case-fold-search t) + (props (append + (and specials org-special-properties) + (and defaults (cons org-effort-property org-default-properties)) + nil))) + (org-with-wide-buffer + (goto-char (point-min)) + (while (re-search-forward org-property-start-re nil t) + (let ((range (org-get-property-block))) + (catch 'skip + (unless range + (when (and (not ignore-malformed) + (not (org-before-first-heading-p)) + (y-or-n-p (format "Malformed drawer at %d, repair?" + (line-beginning-position)))) + (org-get-property-block nil t)) + (throw 'skip nil)) + (goto-char (car range)) + (let ((begin (car range)) + (end (cdr range))) + ;; Make sure that found property block is not located + ;; before current point, as it would generate an infloop. + ;; It can happen, for example, in the following + ;; situation: + ;; + ;; * Headline + ;; :PROPERTIES: + ;; ... + ;; :END: + ;; *************** Inlinetask + ;; #+BEGIN_EXAMPLE + ;; :PROPERTIES: + ;; #+END_EXAMPLE + ;; + (if (< begin (point)) (throw 'skip nil) (goto-char begin)) + (while (< (point) end) + (let ((p (progn (looking-at org-property-re) + (match-string-no-properties 2)))) + ;; Only add true property name, not extension symbol. + (push (if (not (string-match-p "\\+\\'" p)) p + (substring p 0 -1)) + props)) + (forward-line)))) + (outline-next-heading))) + (when columns + (goto-char (point-min)) + (while (re-search-forward "^[ \t]*\\(?:#\\+\\|:\\)COLUMNS:" nil t) + (let ((element (org-element-at-point))) + (when (memq (org-element-type element) '(keyword node-property)) + (let ((value (org-element-property :value element)) + (start 0)) + (while (string-match "%[0-9]*\\(\\S-+\\)" value start) + (setq start (match-end 0)) + (let ((p (match-string-no-properties 1 value))) + (unless (member-ignore-case p org-special-properties) + (push p props)))))))))) + (sort (delete-dups props) (lambda (a b) (string< (upcase a) (upcase b)))))) (defun org-property-values (key) - "Return a list of all values of property KEY in the current buffer." - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (let ((re (org-re-property key)) - values) - (while (re-search-forward re nil t) - (add-to-list 'values (org-trim (match-string 3)))) - (delete "" values))))) + "List all non-nil values of property KEY in current buffer." + (org-with-wide-buffer + (goto-char (point-min)) + (let ((case-fold-search t) + (re (org-re-property key)) + values) + (while (re-search-forward re nil t) + (push (org-entry-get (point) key) values)) + (delete-dups values)))) (defun org-insert-property-drawer () "Insert a property drawer into the current entry." - (org-back-to-heading t) - (looking-at org-outline-regexp) - (let ((indent (if org-adapt-indentation - (- (match-end 0) (match-beginning 0)) - 0)) - (beg (point)) - (re (concat "^[ \t]*" org-keyword-time-regexp)) - end hiddenp) - (outline-next-heading) - (setq end (point)) - (goto-char beg) - (while (re-search-forward re end t)) - (setq hiddenp (outline-invisible-p)) - (end-of-line 1) - (and (equal (char-after) ?\n) (forward-char 1)) - (while (looking-at "^[ \t]*\\(:CLOCK:\\|:LOGBOOK:\\|CLOCK:\\|:END:\\)") - (if (member (match-string 1) '("CLOCK:" ":END:")) - ;; just skip this line - (beginning-of-line 2) - ;; Drawer start, find the end - (re-search-forward "^\\*+ \\|^[ \t]*:END:" nil t) - (beginning-of-line 1))) - (org-skip-over-state-notes) - (skip-chars-backward " \t\n\r") - (if (and (eq (char-before) ?*) (not (eq (char-after) ?\n))) - (forward-char 1)) - (goto-char (point-at-eol)) - (let ((inhibit-read-only t)) (insert "\n:PROPERTIES:\n:END:")) - (beginning-of-line 0) - (org-indent-to-column indent) - (beginning-of-line 2) - (org-indent-to-column indent) - (beginning-of-line 0) - (if hiddenp - (save-excursion - (org-back-to-heading t) - (hide-entry)) - (org-flag-drawer t)))) + (org-with-wide-buffer + (if (or (not (featurep 'org-inlinetask)) (org-inlinetask-in-task-p)) + (org-back-to-heading t) + (org-with-limited-levels (org-back-to-heading t))) + (forward-line) + (when (looking-at-p org-planning-line-re) (forward-line)) + (unless (looking-at-p org-property-drawer-re) + ;; Make sure we start editing a line from current entry, not from + ;; next one. It prevents extending text properties or overlays + ;; belonging to the latter. + (when (bolp) (backward-char)) + (let ((begin (1+ (point))) + (inhibit-read-only t)) + (insert "\n:PROPERTIES:\n:END:") + (when (eobp) (insert "\n")) + (org-indent-region begin (point)))))) (defun org-insert-drawer (&optional arg drawer) "Insert a drawer at point. +When optional argument ARG is non-nil, insert a property drawer. + Optional argument DRAWER, when non-nil, is a string representing drawer's name. Otherwise, the user is prompted for a name. @@ -15731,23 +16329,14 @@ instead. Point is left between drawer's boundaries." (interactive "P") - (let* ((logbook (if (stringp org-log-into-drawer) org-log-into-drawer - "LOGBOOK")) - ;; SYSTEM-DRAWERS is a list of drawer names that are used - ;; internally by Org. They are meant to be inserted - ;; automatically. - (system-drawers `("CLOCK" ,logbook "PROPERTIES")) - ;; Remove system drawers from list. Note: For some reason, - ;; `org-completing-read' ignores the predicate while - ;; `completing-read' handles it fine. - (drawer (if arg "PROPERTIES" - (or drawer - (completing-read - "Drawer: " org-drawers - (lambda (d) (not (member d system-drawers)))))))) + (let* ((drawer (if arg "PROPERTIES" + (or drawer (read-from-minibuffer "Drawer: "))))) (cond ;; With C-u, fall back on `org-insert-property-drawer' (arg (org-insert-property-drawer)) + ;; Check validity of suggested drawer's name. + ((not (string-match-p org-drawer-regexp (format ":%s:" drawer))) + (user-error "Invalid drawer name")) ;; With an active region, insert a drawer at point. ((not (org-region-active-p)) (progn @@ -15813,38 +16402,25 @@ This is computed according to `org-property-set-functions-alist'." (funcall set-function prompt allowed nil (not (get-text-property 0 'org-unrestricted (caar allowed)))) - (let (org-completion-use-ido org-completion-use-iswitchb) - (funcall set-function prompt - (mapcar 'list (org-property-values property)) - nil nil "" nil cur))))) + (funcall set-function prompt + (mapcar 'list (org-property-values property)) + nil nil "" nil cur)))) (org-trim val))) (defvar org-last-set-property nil) (defvar org-last-set-property-value nil) (defun org-read-property-name () "Read a property name." - (let* ((completion-ignore-case t) - (keys (org-buffer-property-keys nil t t)) - (default-prop (or (save-excursion - (save-match-data - (beginning-of-line) - (and (looking-at "^\\s-*:\\([^:\n]+\\):") - (null (string= (match-string 1) "END")) - (match-string 1)))) - org-last-set-property)) - (property (org-icompleting-read - (concat "Property" - (if default-prop (concat " [" default-prop "]") "") - ": ") - (mapcar 'list keys) - nil nil nil nil - default-prop))) - (if (member property keys) - property - (or (cdr (assoc (downcase property) - (mapcar (lambda (x) (cons (downcase x) x)) - keys))) - property)))) + (let ((completion-ignore-case t) + (default-prop (or (and (org-at-property-p) + (match-string-no-properties 2)) + org-last-set-property))) + (org-completing-read + (concat "Property" + (if default-prop (concat " [" default-prop "]") "") + ": ") + (mapcar #'list (org-buffer-property-keys nil t t)) + nil nil nil nil default-prop))) (defun org-set-property-and-value (use-last) "Allow to set [PROPERTY]: [value] direction from prompt. @@ -15865,26 +16441,52 @@ When use-default, don't even ask, just use the last (defun org-set-property (property value) "In the current entry, set PROPERTY to VALUE. + When called interactively, this will prompt for a property name, offering completion on existing and default properties. And then it will prompt for a value, offering completion either on allowed values (via an inherited xxx_ALL property) or on existing values in other instances of this property -in the current file." +in the current file. + +Throw an error when trying to set a property with an invalid name." (interactive (list nil nil)) - (let* ((property (or property (org-read-property-name))) - (value (or value (org-read-property-value property))) - (fn (cdr (assoc property org-properties-postprocess-alist)))) - (setq org-last-set-property property) - (setq org-last-set-property-value (concat property ": " value)) - ;; Possibly postprocess the inserted value: - (when fn (setq value (funcall fn value))) - (unless (equal (org-entry-get nil property) value) - (org-entry-put nil property value)))) - -(defun org-delete-property (property &optional delete-empty-drawer) - "In the current entry, delete PROPERTY. -When optional argument DELETE-EMPTY-DRAWER is a string, it defines -an empty drawer to delete." + (let ((property (or property (org-read-property-name)))) + ;; `org-entry-put' also makes the following check, but this one + ;; avoids polluting `org-last-set-property' and + ;; `org-last-set-property-value' needlessly. + (unless (org--valid-property-p property) + (user-error "Invalid property name: \"%s\"" property)) + (let ((value (or value (org-read-property-value property))) + (fn (cdr (assoc-string property org-properties-postprocess-alist t)))) + (setq org-last-set-property property) + (setq org-last-set-property-value (concat property ": " value)) + ;; Possibly postprocess the inserted value: + (when fn (setq value (funcall fn value))) + (unless (equal (org-entry-get nil property) value) + (org-entry-put nil property value))))) + +(defun org-find-property (property &optional value) + "Find first entry in buffer that sets PROPERTY. + +When optional argument VALUE is non-nil, only consider an entry +if it contains PROPERTY set to this value. If PROPERTY should be +explicitly set to nil, use string \"nil\" for VALUE. + +Return position where the entry begins, or nil if there is no +such entry. If narrowing is in effect, only search the visible +part of the buffer." + (save-excursion + (goto-char (point-min)) + (let ((case-fold-search t) + (re (org-re-property property nil (not value) value))) + (catch 'exit + (while (re-search-forward re nil t) + (when (if value (org-at-property-p) + (org-entry-get (point) property nil t)) + (throw 'exit (progn (org-back-to-heading t) (point))))))))) + +(defun org-delete-property (property) + "In the current entry, delete PROPERTY." (interactive (let* ((completion-ignore-case t) (cat (org-entry-get (point) "CATEGORY")) @@ -15892,33 +16494,30 @@ an empty drawer to delete." (props (if cat props0 (delete `("CATEGORY" . ,(org-get-category)) props0))) (prop (if (< 1 (length props)) - (org-icompleting-read "Property: " props nil t) + (completing-read "Property: " props nil t) (caar props)))) (list prop))) (if (not property) (message "No property to delete in this entry") - (org-entry-delete nil property delete-empty-drawer) + (org-entry-delete nil property) (message "Property \"%s\" deleted" property))) (defun org-delete-property-globally (property) - "Remove PROPERTY globally, from all entries." + "Remove PROPERTY globally, from all entries. +This function ignores narrowing, if any." (interactive (let* ((completion-ignore-case t) - (prop (org-icompleting-read + (prop (completing-read "Globally remove property: " - (mapcar 'list (org-buffer-property-keys))))) + (mapcar #'list (org-buffer-property-keys))))) (list prop))) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (let ((cnt 0)) - (while (re-search-forward - (org-re-property property) - nil t) - (setq cnt (1+ cnt)) - (delete-region (match-beginning 0) (1+ (point-at-eol)))) - (message "Property \"%s\" removed from %d entries" property cnt))))) + (org-with-wide-buffer + (goto-char (point-min)) + (let ((count 0) + (re (org-re-property (concat (regexp-quote property) "\\+?") t t))) + (while (re-search-forward re nil t) + (when (org-entry-delete (point) property) (cl-incf count))) + (message "Property \"%s\" removed from %d entries" property count)))) (defvar org-columns-current-fmt-compiled) ; defined in org-colview.el @@ -15929,9 +16528,9 @@ then applies it to the property in the column format's scope." (interactive) (unless (org-at-property-p) (user-error "Not at a property")) - (let ((prop (org-match-string-no-properties 2))) + (let ((prop (match-string-no-properties 2))) (org-columns-get-format-and-top-level) - (unless (nth 3 (assoc prop org-columns-current-fmt-compiled)) + (unless (nth 3 (assoc-string prop org-columns-current-fmt-compiled t)) (user-error "No operator defined for property %s" prop)) (org-columns-compute prop))) @@ -15958,6 +16557,7 @@ completion." (while (>= n org-highest-priority) (push (char-to-string n) vals) (setq n (1- n))))) + ((equal property "CATEGORY")) ((member property org-special-properties)) ((setq vals (run-hook-with-args-until-success 'org-property-allowed-value-functions property))) @@ -15976,7 +16576,7 @@ completion." (org-add-props (car vals) '(org-unrestricted t))) (if table (mapcar 'list vals) vals))) -(defun org-property-previous-allowed-value (&optional previous) +(defun org-property-previous-allowed-value (&optional _previous) "Switch to the next allowed value for this property." (interactive) (org-property-next-allowed-value t)) @@ -15996,21 +16596,22 @@ completion." nval) (unless allowed (user-error "Allowed values for this property have not been defined")) - (if previous (setq allowed (reverse allowed))) - (if (member value allowed) - (setq nval (car (cdr (member value allowed))))) + (when previous (setq allowed (reverse allowed))) + (when (member value allowed) + (setq nval (car (cdr (member value allowed))))) (setq nval (or nval (car allowed))) - (if (equal nval value) - (user-error "Only one allowed value for this property")) + (when (equal nval value) + (user-error "Only one allowed value for this property")) (org-at-property-p) (replace-match (concat " :" key ": " nval) t t) (org-indent-line) (beginning-of-line 1) (skip-chars-forward " \t") (when (equal prop org-effort-property) - (save-excursion - (org-back-to-heading t) - (put-text-property (point-at-bol) (point-at-eol) 'org-effort nval)) + (org-refresh-property + '((effort . identity) + (effort-minutes . org-duration-string-to-minutes)) + nval) (when (string= org-clock-current-task heading) (setq org-clock-effort nval) (org-clock-update-mode-line))) @@ -16035,31 +16636,28 @@ only headings." (level 1) (lmin 1) (lmax 1) - limit re end found pos heading cnt flevel) + end found flevel) (unless buffer (error "File not found :%s" file)) (with-current-buffer buffer - (save-excursion - (save-restriction - (widen) - (setq limit (point-max)) - (goto-char (point-min)) - (dolist (heading path) - (setq re (format org-complex-heading-regexp-format - (regexp-quote heading))) - (setq cnt 0 pos (point)) - (while (re-search-forward re end t) - (setq level (- (match-end 1) (match-beginning 1))) - (if (and (>= level lmin) (<= level lmax)) - (setq found (match-beginning 0) flevel level cnt (1+ cnt)))) - (when (= cnt 0) (error "Heading not found on level %d: %s" - lmax heading)) - (when (> cnt 1) (error "Heading not unique on level %d: %s" - lmax heading)) - (goto-char found) - (setq lmin (1+ flevel) lmax (+ lmin (if org-odd-levels-only 1 0))) - (setq end (save-excursion (org-end-of-subtree t t)))) - (when (org-at-heading-p) - (point-marker))))))) + (org-with-wide-buffer + (goto-char (point-min)) + (dolist (heading path) + (let ((re (format org-complex-heading-regexp-format + (regexp-quote heading))) + (cnt 0)) + (while (re-search-forward re end t) + (setq level (- (match-end 1) (match-beginning 1))) + (when (and (>= level lmin) (<= level lmax)) + (setq found (match-beginning 0) flevel level cnt (1+ cnt)))) + (when (= cnt 0) + (error "Heading not found on level %d: %s" lmax heading)) + (when (> cnt 1) + (error "Heading not unique on level %d: %s" lmax heading)) + (goto-char found) + (setq lmin (1+ flevel) lmax (+ lmin (if org-odd-levels-only 1 0))) + (setq end (save-excursion (org-end-of-subtree t t))))) + (when (org-at-heading-p) + (point-marker)))))) (defun org-find-exact-headline-in-buffer (heading &optional buffer pos-only) "Find node HEADING in BUFFER. @@ -16069,24 +16667,22 @@ If POS-ONLY is set, return just the position instead of a marker. The heading text must match exact, but it may have a TODO keyword, a priority cookie and tags in the standard locations." (with-current-buffer (or buffer (current-buffer)) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (let (case-fold-search) - (if (re-search-forward - (format org-complex-heading-regexp-format - (regexp-quote heading)) nil t) - (if pos-only - (match-beginning 0) - (move-marker (make-marker) (match-beginning 0))))))))) + (org-with-wide-buffer + (goto-char (point-min)) + (let (case-fold-search) + (when (re-search-forward + (format org-complex-heading-regexp-format + (regexp-quote heading)) nil t) + (if pos-only + (match-beginning 0) + (move-marker (make-marker) (match-beginning 0)))))))) (defun org-find-exact-heading-in-directory (heading &optional dir) "Find Org node headline HEADING in all .org files in directory DIR. When the target headline is found, return a marker to this location." (let ((files (directory-files (or dir default-directory) - nil "\\`[^.#].*\\.org\\'")) - file visiting m buffer) + t "\\`[^.#].*\\.org\\'")) + visiting m buffer) (catch 'found (dolist (file files) (message "trying %s" file) @@ -16105,19 +16701,10 @@ Return the position where this entry starts, or nil if there is no such entry." (interactive "sID: ") (let ((id (cond ((stringp ident) ident) - ((symbol-name ident) (symbol-name ident)) + ((symbolp ident) (symbol-name ident)) ((numberp ident) (number-to-string ident)) - (t (error "IDENT %s must be a string, symbol or number" ident)))) - (case-fold-search nil)) - (save-excursion - (save-restriction - (widen) - (goto-char (point-min)) - (when (re-search-forward - (concat "^[ \t]*:ID:[ \t]+" (regexp-quote id) "[ \t]*$") - nil t) - (org-back-to-heading t) - (point)))))) + (t (error "IDENT %s must be a string, symbol or number" ident))))) + (org-with-wide-buffer (org-find-property "ID" id)))) ;;;; Timestamps @@ -16128,17 +16715,16 @@ Return the position where this entry starts, or nil if there is no such entry." (defun org-time-stamp (arg &optional inactive) "Prompt for a date/time and insert a time stamp. + If the user specifies a time like HH:MM or if this command is called with at least one prefix argument, the time stamp contains -the date and the time. Otherwise, only the date is be included. +the date and the time. Otherwise, only the date is included. -All parts of a date not specified by the user is filled in from -the current date/time. So if you just press return without -typing anything, the time stamp will represent the current -date/time. +All parts of a date not specified by the user are filled in from +the timestamp at point, if any, or the current date/time +otherwise. -If there is already a timestamp at the cursor, it will be -modified. +If there is already a timestamp at the cursor, it is replaced. With two universal prefix arguments, insert an active timestamp with the current time without prompting the user. @@ -16146,57 +16732,56 @@ with the current time without prompting the user. When called from lisp, the timestamp is inactive if INACTIVE is non-nil." (interactive "P") - (let* ((ts nil) - (default-time - ;; Default time is either today, or, when entering a range, - ;; the range start. - (if (or (and (org-at-timestamp-p t) (setq ts (match-string 0))) - (save-excursion - (re-search-backward - (concat org-ts-regexp "--?-?\\=") ; 1-3 minuses - (- (point) 20) t))) - (apply 'encode-time (org-parse-time-string (match-string 1))) - (current-time))) - (default-input (and ts (org-get-compact-tod ts))) - (repeater (save-excursion - (save-match-data - (beginning-of-line) - (when (re-search-forward - "\\([.+-]+[0-9]+[hdwmy] ?\\)+" ;;\\(?:[/ ][-+]?[0-9]+[hdwmy]\\)?\\) ?" - (save-excursion (progn (end-of-line) (point))) t) - (match-string 0))))) - org-time-was-given org-end-time-was-given time) + (let* ((ts (cond + ((org-at-date-range-p t) + (match-string (if (< (point) (- (match-beginning 2) 2)) 1 2))) + ((org-at-timestamp-p t) (match-string 0)))) + ;; Default time is either the timestamp at point or today. + ;; When entering a range, only the range start is considered. + (default-time (if (not ts) (current-time) + (apply #'encode-time (org-parse-time-string ts)))) + (default-input (and ts (org-get-compact-tod ts))) + (repeater (and ts + (string-match "\\([.+-]+[0-9]+[hdwmy] ?\\)+" ts) + (match-string 0 ts))) + org-time-was-given + org-end-time-was-given + (time + (and (if (equal arg '(16)) (current-time) + ;; Preserve `this-command' and `last-command'. + (let ((this-command this-command) + (last-command last-command)) + (org-read-date + arg 'totime nil nil default-time default-input + inactive)))))) (cond - ((and (org-at-timestamp-p t) - (memq last-command '(org-time-stamp org-time-stamp-inactive)) - (memq this-command '(org-time-stamp org-time-stamp-inactive))) + ((and ts + (memq last-command '(org-time-stamp org-time-stamp-inactive)) + (memq this-command '(org-time-stamp org-time-stamp-inactive))) (insert "--") - (setq time (let ((this-command this-command)) - (org-read-date arg 'totime nil nil - default-time default-input inactive))) (org-insert-time-stamp time (or org-time-was-given arg) inactive)) - ((org-at-timestamp-p t) - (setq time (let ((this-command this-command)) - (org-read-date arg 'totime nil nil default-time default-input inactive))) - (when (org-at-timestamp-p t) ; just to get the match data - ; (setq inactive (eq (char-after (match-beginning 0)) ?\[)) - (replace-match "") + (ts + ;; Make sure we're on a timestamp. When in the middle of a date + ;; range, move arbitrarily to range end. + (unless (org-at-timestamp-p t) + (skip-chars-forward "-") + (org-at-timestamp-p t)) + (replace-match "") + (setq org-last-changed-timestamp + (org-insert-time-stamp + time (or org-time-was-given arg) + inactive nil nil (list org-end-time-was-given))) + (when repeater + (backward-char) + (insert " " repeater) (setq org-last-changed-timestamp - (org-insert-time-stamp - time (or org-time-was-given arg) - inactive nil nil (list org-end-time-was-given))) - (when repeater (goto-char (1- (point))) (insert " " repeater) - (setq org-last-changed-timestamp - (concat (substring org-last-inserted-timestamp 0 -1) - " " repeater ">")))) + (concat (substring org-last-inserted-timestamp 0 -1) + " " repeater ">"))) (message "Timestamp updated")) - ((equal arg '(16)) - (org-insert-time-stamp (current-time) t inactive)) - (t - (setq time (let ((this-command this-command)) - (org-read-date arg 'totime nil nil default-time default-input inactive))) - (org-insert-time-stamp time (or org-time-was-given arg) inactive - nil nil (list org-end-time-was-given)))))) + ((equal arg '(16)) (org-insert-time-stamp time t inactive)) + (t (org-insert-time-stamp + time (or org-time-was-given arg) inactive nil nil + (list org-end-time-was-given)))))) ;; FIXME: can we use this for something else, like computing time differences? (defun org-get-compact-tod (s) @@ -16211,7 +16796,7 @@ non-nil." (if (not t2) t1 (setq dh (- h2 h1) dm (- m2 m1)) - (if (< dm 0) (setq dm (+ dm 60) dh (1- dh))) + (when (< dm 0) (setq dm (+ dm 60) dh (1- dh))) (concat t1 "+" (number-to-string dh) (and (/= 0 dm) (format ":%02d" dm))))))) @@ -16226,7 +16811,7 @@ So these are more for recording a certain time/date." (defvar org-date-ovl (make-overlay 1 1)) (overlay-put org-date-ovl 'face 'org-date-selected) -(org-detach-overlay org-date-ovl) +(delete-overlay org-date-ovl) (defvar org-ans1) ; dynamically scoped parameter (defvar org-ans2) ; dynamically scoped parameter @@ -16243,13 +16828,14 @@ So these are more for recording a certain time/date." (defvar org-read-date-inactive) (defvar org-read-date-minibuffer-local-map - (let* ((org-replace-disputed-keys nil) - (map (make-sparse-keymap))) + (let* ((map (make-sparse-keymap))) (set-keymap-parent map minibuffer-local-map) (org-defkey map (kbd ".") (lambda () (interactive) ;; Are we at the beginning of the prompt? - (if (looking-back "^[^:]+: ") + (if (looking-back "^[^:]+: " + (let ((inhibit-field-text-motion t)) + (line-beginning-position))) (org-eval-in-calendar '(calendar-goto-today)) (insert ".")))) (org-defkey map (kbd "C-.") @@ -16316,7 +16902,8 @@ So these are more for recording a certain time/date." (defvar org-defdecode) (defvar org-with-time) -(defun org-read-date (&optional org-with-time to-time from-string prompt +(defvar calendar-setup) ; Dynamically scoped. +(defun org-read-date (&optional with-time to-time from-string prompt default-time default-input inactive) "Read a date, possibly a time, and make things smooth for the user. The prompt will suggest to enter an ISO date, but you can also enter anything @@ -16360,8 +16947,8 @@ If you don't like the calendar, turn it off with With optional argument TO-TIME, the date will immediately be converted to an internal time. -With an optional argument ORG-WITH-TIME, the prompt will suggest to -also insert a time. Note that when ORG-WITH-TIME is not set, you can +With an optional argument WITH-TIME, the prompt will suggest to +also insert a time. Note that when WITH-TIME is not set, you can still enter a time, and this function will inform the calling routine about this change. The calling routine may then choose to change the format used to insert the time stamp into the buffer to include the time. @@ -16370,75 +16957,90 @@ the user. PROMPT can overwrite the default prompt. DEFAULT-TIME is the time/date that is used for everything that is not specified by the user." (require 'parse-time) - (let* ((org-time-stamp-rounding-minutes - (if (equal org-with-time '(16)) '(0 0) org-time-stamp-rounding-minutes)) + (let* ((org-with-time with-time) + (org-time-stamp-rounding-minutes + (if (equal org-with-time '(16)) + '(0 0) + org-time-stamp-rounding-minutes)) (org-dcst org-display-custom-times) (ct (org-current-time)) (org-def (or org-overriding-default-time default-time ct)) (org-defdecode (decode-time org-def)) - (dummy (progn - (when (< (nth 2 org-defdecode) org-extend-today-until) - (setcar (nthcdr 2 org-defdecode) -1) - (setcar (nthcdr 1 org-defdecode) 59) - (setq org-def (apply 'encode-time org-defdecode) - org-defdecode (decode-time org-def))))) - (mouse-autoselect-window nil) ; Don't let the mouse jump - (calendar-frame-setup nil) - (calendar-setup nil) + (cur-frame (selected-frame)) + (mouse-autoselect-window nil) ; Don't let the mouse jump + (calendar-setup + (and (eq calendar-setup 'calendar-only) 'calendar-only)) (calendar-move-hook nil) (calendar-view-diary-initially-flag nil) (calendar-view-holidays-initially-flag nil) - (timestr (format-time-string - (if org-with-time "%Y-%m-%d %H:%M" "%Y-%m-%d") org-def)) - (prompt (concat (if prompt (concat prompt " ") "") - (format "Date+time [%s]: " timestr))) - ans (org-ans0 "") org-ans1 org-ans2 final) - - (cond - (from-string (setq ans from-string)) - (org-read-date-popup-calendar - (save-excursion - (save-window-excursion - (calendar) - (org-eval-in-calendar '(setq cursor-type nil) t) - (unwind-protect - (progn - (calendar-forward-day (- (time-to-days org-def) - (calendar-absolute-from-gregorian - (calendar-current-date)))) - (org-eval-in-calendar nil t) - (let* ((old-map (current-local-map)) - (map (copy-keymap calendar-mode-map)) - (minibuffer-local-map - (copy-keymap org-read-date-minibuffer-local-map))) - (org-defkey map (kbd "RET") 'org-calendar-select) - (org-defkey map [mouse-1] 'org-calendar-select-mouse) - (org-defkey map [mouse-2] 'org-calendar-select-mouse) - (unwind-protect - (progn - (use-local-map map) - (setq org-read-date-inactive inactive) - (add-hook 'post-command-hook 'org-read-date-display) - (setq org-ans0 (read-string prompt default-input - 'org-read-date-history nil)) - ;; org-ans0: from prompt - ;; org-ans1: from mouse click - ;; org-ans2: from calendar motion - (setq ans (concat org-ans0 " " (or org-ans1 org-ans2)))) - (remove-hook 'post-command-hook 'org-read-date-display) - (use-local-map old-map) - (when org-read-date-overlay - (delete-overlay org-read-date-overlay) - (setq org-read-date-overlay nil))))) - (bury-buffer "*Calendar*"))))) - - (t ; Naked prompt only - (unwind-protect - (setq ans (read-string prompt default-input - 'org-read-date-history timestr)) - (when org-read-date-overlay - (delete-overlay org-read-date-overlay) - (setq org-read-date-overlay nil))))) + ans (org-ans0 "") org-ans1 org-ans2 final cal-frame) + ;; Rationalize `org-def' and `org-defdecode', if required. + (when (< (nth 2 org-defdecode) org-extend-today-until) + (setf (nth 2 org-defdecode) -1) + (setf (nth 1 org-defdecode) 59) + (setq org-def (apply #'encode-time org-defdecode)) + (setq org-defdecode (decode-time org-def))) + (let* ((timestr (format-time-string + (if org-with-time "%Y-%m-%d %H:%M" "%Y-%m-%d") + org-def)) + (prompt (concat (if prompt (concat prompt " ") "") + (format "Date+time [%s]: " timestr)))) + (cond + (from-string (setq ans from-string)) + (org-read-date-popup-calendar + (save-excursion + (save-window-excursion + (calendar) + (when (eq calendar-setup 'calendar-only) + (setq cal-frame + (window-frame (get-buffer-window "*Calendar*" 'visible))) + (select-frame cal-frame)) + (org-eval-in-calendar '(setq cursor-type nil) t) + (unwind-protect + (progn + (calendar-forward-day (- (time-to-days org-def) + (calendar-absolute-from-gregorian + (calendar-current-date)))) + (org-eval-in-calendar nil t) + (let* ((old-map (current-local-map)) + (map (copy-keymap calendar-mode-map)) + (minibuffer-local-map + (copy-keymap org-read-date-minibuffer-local-map))) + (org-defkey map (kbd "RET") 'org-calendar-select) + (org-defkey map [mouse-1] 'org-calendar-select-mouse) + (org-defkey map [mouse-2] 'org-calendar-select-mouse) + (unwind-protect + (progn + (use-local-map map) + (setq org-read-date-inactive inactive) + (add-hook 'post-command-hook 'org-read-date-display) + (setq org-ans0 + (read-string prompt + default-input + 'org-read-date-history + nil)) + ;; org-ans0: from prompt + ;; org-ans1: from mouse click + ;; org-ans2: from calendar motion + (setq ans + (concat org-ans0 " " (or org-ans1 org-ans2)))) + (remove-hook 'post-command-hook 'org-read-date-display) + (use-local-map old-map) + (when org-read-date-overlay + (delete-overlay org-read-date-overlay) + (setq org-read-date-overlay nil))))) + (bury-buffer "*Calendar*") + (when cal-frame + (delete-frame cal-frame) + (select-frame-set-input-focus cur-frame)))))) + + (t ; Naked prompt only + (unwind-protect + (setq ans (read-string prompt default-input + 'org-read-date-history timestr)) + (when org-read-date-overlay + (delete-overlay org-read-date-overlay) + (setq org-read-date-overlay nil)))))) (setq final (org-read-date-analyze ans org-def org-defdecode)) @@ -16499,13 +17101,18 @@ user." (make-overlay (1- (point-at-eol)) (point-at-eol))) (org-overlay-display org-read-date-overlay txt 'secondary-selection))))) -(defun org-read-date-analyze (ans org-def org-defdecode) +(defun org-read-date-analyze (ans def defdecode) "Analyze the combined answer of the date prompt." ;; FIXME: cleanup and comment - (let ((nowdecode (decode-time)) + ;; Pass `current-time' result to `decode-time' (instead of calling + ;; without arguments) so that only `current-time' has to be + ;; overriden in tests. + (let ((org-def def) + (org-defdecode defdecode) + (nowdecode (decode-time (current-time))) delta deltan deltaw deltadef year month day hour minute second wday pm h2 m2 tl wday1 - iso-year iso-weekday iso-week iso-year iso-date futurep kill-year) + iso-year iso-weekday iso-week iso-date futurep kill-year) (setq org-read-date-analyze-futurep nil org-read-date-analyze-forced-year nil) (when (string-match "\\`[ \t]*\\.[ \t]*\\'" ans) @@ -16521,11 +17128,11 @@ user." ;; info and postpone interpreting it until the rest of the parsing ;; is done. (when (string-match "\\<\\(?:\\([0-9]+\\)-\\)?[wW]\\([0-9]\\{1,2\\}\\)\\(?:-\\([0-6]\\)\\)?\\([ \t]\\|$\\)" ans) - (setq iso-year (if (match-end 1) - (org-small-year-to-year - (string-to-number (match-string 1 ans)))) - iso-weekday (if (match-end 3) - (string-to-number (match-string 3 ans))) + (setq iso-year (when (match-end 1) + (org-small-year-to-year + (string-to-number (match-string 1 ans)))) + iso-weekday (when (match-end 3) + (string-to-number (match-string 3 ans))) iso-week (string-to-number (match-string 2 ans))) (setq ans (replace-match "" t t ans))) @@ -16538,7 +17145,7 @@ user." (string-to-number (format-time-string "%Y")))) month (string-to-number (match-string 3 ans)) day (string-to-number (match-string 4 ans))) - (if (< year 100) (setq year (+ 2000 year))) + (setq year (org-small-year-to-year year)) (setq ans (replace-match (format "%04d-%02d-%02d\\5" year month day) t nil ans))) @@ -16562,26 +17169,26 @@ user." (string-to-number (format-time-string "%Y")))) month (string-to-number (match-string 1 ans)) day (string-to-number (match-string 2 ans))) - (if (< year 100) (setq year (+ 2000 year))) + (setq year (org-small-year-to-year year)) (setq ans (replace-match (format "%04d-%02d-%02d\\5" year month day) t nil ans))) ;; Help matching am/pm times, because `parse-time-string' does not do that. ;; If there is a time with am/pm, and *no* time without it, we convert ;; so that matching will be successful. - (loop for i from 1 to 2 do ; twice, for end time as well - (when (and (not (string-match "\\(\\`\\|[^+]\\)[012]?[0-9]:[0-9][0-9]\\([ \t\n]\\|$\\)" ans)) - (string-match "\\([012]?[0-9]\\)\\(:\\([0-5][0-9]\\)\\)?\\(am\\|AM\\|pm\\|PM\\)\\>" ans)) - (setq hour (string-to-number (match-string 1 ans)) - minute (if (match-end 3) - (string-to-number (match-string 3 ans)) - 0) - pm (equal ?p - (string-to-char (downcase (match-string 4 ans))))) - (if (and (= hour 12) (not pm)) - (setq hour 0) - (if (and pm (< hour 12)) (setq hour (+ 12 hour)))) - (setq ans (replace-match (format "%02d:%02d" hour minute) - t t ans)))) + (cl-loop for i from 1 to 2 do ; twice, for end time as well + (when (and (not (string-match "\\(\\`\\|[^+]\\)[012]?[0-9]:[0-9][0-9]\\([ \t\n]\\|$\\)" ans)) + (string-match "\\([012]?[0-9]\\)\\(:\\([0-5][0-9]\\)\\)?\\(am\\|AM\\|pm\\|PM\\)\\>" ans)) + (setq hour (string-to-number (match-string 1 ans)) + minute (if (match-end 3) + (string-to-number (match-string 3 ans)) + 0) + pm (equal ?p + (string-to-char (downcase (match-string 4 ans))))) + (if (and (= hour 12) (not pm)) + (setq hour 0) + (when (and pm (< hour 12)) (setq hour (+ 12 hour)))) + (setq ans (replace-match (format "%02d:%02d" hour minute) + t t ans)))) ;; Check if a time range is given as a duration (when (string-match "\\([012]?[0-9]\\):\\([0-6][0-9]\\)\\+\\([012]?[0-9]\\)\\(:\\([0-5][0-9]\\)\\)?" ans) @@ -16590,7 +17197,7 @@ user." minute (string-to-number (match-string 2 ans)) m2 (+ minute (if (match-end 5) (string-to-number (match-string 5 ans))0))) - (if (>= m2 60) (setq h2 (1+ h2) m2 (- m2 60))) + (when (>= m2 60) (setq h2 (1+ h2) m2 (- m2 60))) (setq ans (replace-match (format "%02d:%02d-%02d:%02d" hour minute h2 m2) t t ans))) @@ -16605,16 +17212,35 @@ user." (setq tl (parse-time-string ans) day (or (nth 3 tl) (nth 3 org-defdecode)) - month (or (nth 4 tl) - (if (and org-read-date-prefer-future - (nth 3 tl) (< (nth 3 tl) (nth 3 nowdecode))) - (prog1 (1+ (nth 4 nowdecode)) (setq futurep t)) - (nth 4 org-defdecode))) - year (or (and (not kill-year) (nth 5 tl)) - (if (and org-read-date-prefer-future - (nth 4 tl) (< (nth 4 tl) (nth 4 nowdecode))) - (prog1 (1+ (nth 5 nowdecode)) (setq futurep t)) - (nth 5 org-defdecode))) + month + (cond ((nth 4 tl)) + ((not org-read-date-prefer-future) (nth 4 org-defdecode)) + ;; Day was specified. Make sure DAY+MONTH + ;; combination happens in the future. + ((nth 3 tl) + (setq futurep t) + (if (< day (nth 3 nowdecode)) (1+ (nth 4 nowdecode)) + (nth 4 nowdecode))) + (t (nth 4 org-defdecode))) + year + (cond ((and (not kill-year) (nth 5 tl))) + ((not org-read-date-prefer-future) (nth 5 org-defdecode)) + ;; Month was guessed in the future and is at least + ;; equal to NOWDECODE's. Fix year accordingly. + (futurep + (if (or (> month (nth 4 nowdecode)) + (>= day (nth 3 nowdecode))) + (nth 5 nowdecode) + (1+ (nth 5 nowdecode)))) + ;; Month was specified. Make sure MONTH+YEAR + ;; combination happens in the future. + ((nth 4 tl) + (setq futurep t) + (cond ((> month (nth 4 nowdecode)) (nth 5 nowdecode)) + ((< month (nth 4 nowdecode)) (1+ (nth 5 nowdecode))) + ((< day (nth 3 nowdecode)) (1+ (nth 5 nowdecode))) + (t (nth 5 nowdecode)))) + (t (nth 5 org-defdecode))) hour (or (nth 2 tl) (nth 2 org-defdecode)) minute (or (nth 1 tl) (nth 1 org-defdecode)) second (or (nth 0 tl) 0) @@ -16643,7 +17269,7 @@ user." day (or iso-weekday wday 1) wday nil ; to make sure that the trigger below does not match iso-date (calendar-gregorian-from-absolute - (calendar-absolute-from-iso + (calendar-iso-to-absolute (list iso-week day year)))) ; FIXME: Should we also push ISO weeks into the future? ; (when (and org-read-date-prefer-future @@ -16652,7 +17278,7 @@ user." ; (time-to-days (current-time)))) ; (setq year (1+ year) ; iso-date (calendar-gregorian-from-absolute - ; (calendar-absolute-from-iso + ; (calendar-iso-to-absolute ; (list iso-week day year))))) (setq month (car iso-date) year (nth 2 iso-date) @@ -16660,7 +17286,10 @@ user." (deltan (setq futurep nil) (unless deltadef - (let ((now (decode-time))) + ;; Pass `current-time' result to `decode-time' (instead of + ;; calling without arguments) so that only `current-time' has + ;; to be overriden in tests. + (let ((now (decode-time (current-time)))) (setq day (nth 3 now) month (nth 4 now) year (nth 5 now)))) (cond ((member deltaw '("d" "")) (setq day (+ day deltan))) ((equal deltaw "w") (setq day (+ day (* 7 deltan)))) @@ -16672,17 +17301,17 @@ user." (setq wday1 (nth 6 (decode-time (encode-time 0 0 0 day month year)))) (unless (equal wday wday1) (setq day (+ day (% (- wday wday1 -7) 7)))))) - (if (and (boundp 'org-time-was-given) - (nth 2 tl)) - (setq org-time-was-given t)) - (if (< year 100) (setq year (+ 2000 year))) + (when (and (boundp 'org-time-was-given) + (nth 2 tl)) + (setq org-time-was-given t)) + (when (< year 100) (setq year (+ 2000 year))) ;; Check of the date is representable (if org-read-date-force-compatible-dates (progn - (if (< year 1970) - (setq year 1970 org-read-date-analyze-forced-year t)) - (if (> year 2037) - (setq year 2037 org-read-date-analyze-forced-year t))) + (when (< year 1970) + (setq year 1970 org-read-date-analyze-forced-year t)) + (when (> year 2037) + (setq year 2037 org-read-date-analyze-forced-year t))) (condition-case nil (ignore (encode-time second minute hour day month year)) (error @@ -16722,12 +17351,11 @@ DEF-FLAG is t when a double ++ or -- indicates shift relative to (if wday1 (progn (setq delta (mod (+ 7 (- wday1 wday)) 7)) - (if (= delta 0) (setq delta 7)) - (if (= dir ?-) - (progn - (setq delta (- delta 7)) - (if (= delta 0) (setq delta -7)))) - (if (> n 1) (setq delta (+ delta (* (1- n) (if (= dir ?-) -7 7))))) + (when (= delta 0) (setq delta 7)) + (when (= dir ?-) + (setq delta (- delta 7)) + (when (= delta 0) (setq delta -7))) + (when (> n 1) (setq delta (+ delta (* (1- n) (if (= dir ?-) -7 7))))) (list delta "d" rel)) (list (* n (if (= dir ?-) -1 1)) what rel))))) @@ -16736,23 +17364,14 @@ DEF-FLAG is t when a double ++ or -- indicates shift relative to The internal representation needed by the calendar is (month day year). This is a wrapper to handle the brain-dead convention in calendar that user function argument order change dependent on argument order." - (if (boundp 'calendar-date-style) - (cond - ((eq calendar-date-style 'american) - (list arg1 arg2 arg3)) - ((eq calendar-date-style 'european) - (list arg2 arg1 arg3)) - ((eq calendar-date-style 'iso) - (list arg2 arg3 arg1))) - (org-no-warnings ;; european-calendar-style is obsolete as of version 23.1 - (if (org-bound-and-true-p european-calendar-style) - (list arg2 arg1 arg3) - (list arg1 arg2 arg3))))) + (pcase calendar-date-style + (`american (list arg1 arg2 arg3)) + (`european (list arg2 arg1 arg3)) + (`iso (list arg2 arg3 arg1)))) (defun org-eval-in-calendar (form &optional keepdate) "Eval FORM in the calendar window and return to current window. -When KEEPDATE is non-nil, update `org-ans2' from the cursor date, -otherwise stick to the current value of `org-ans2'." +Unless KEEPDATE is non-nil, update `org-ans2' to the cursor date." (let ((sf (selected-frame)) (sw (selected-window))) (select-window (get-buffer-window "*Calendar*" t)) @@ -16763,7 +17382,7 @@ otherwise stick to the current value of `org-ans2'." (setq org-ans2 (format-time-string "%Y-%m-%d" time)))) (move-overlay org-date-ovl (1- (point)) (1+ (point)) (current-buffer)) (select-window sw) - (org-select-frame-set-input-focus sf))) + (select-frame-set-input-focus sf))) (defun org-calendar-select () "Return to `org-read-date' with the date currently selected. @@ -16773,10 +17392,11 @@ This is used by `org-read-date' in a temporary keymap for the calendar buffer." (let* ((date (calendar-cursor-to-date)) (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))) (setq org-ans1 (format-time-string "%Y-%m-%d" time))) - (if (active-minibuffer-window) (exit-minibuffer)))) + (when (active-minibuffer-window) (exit-minibuffer)))) (defun org-insert-time-stamp (time &optional with-hm inactive pre post extra) "Insert a date stamp for the date given by the internal TIME. +See `format-time-string' for the format of TIME. WITH-HM means use the stamp format that includes the time of the day. INACTIVE means use square brackets instead of angular ones, so that the stamp will not contribute to the agenda. @@ -16785,7 +17405,7 @@ stamp. The command returns the inserted time stamp." (let ((fmt (funcall (if with-hm 'cdr 'car) org-time-stamp-formats)) stamp) - (if inactive (setq fmt (concat "[" (substring fmt 1 -1) "]"))) + (when inactive (setq fmt (concat "[" (substring fmt 1 -1) "]"))) (insert-before-markers (or pre "")) (when (listp extra) (setq extra (car extra)) @@ -16808,14 +17428,12 @@ The command returns the inserted time stamp." (unless org-display-custom-times (let ((p (point-min)) (bmp (buffer-modified-p))) (while (setq p (next-single-property-change p 'display)) - (if (and (get-text-property p 'display) - (eq (get-text-property p 'face) 'org-date)) - (remove-text-properties - p (setq p (next-single-property-change p 'display)) - '(display t)))) + (when (and (get-text-property p 'display) + (eq (get-text-property p 'face) 'org-date)) + (remove-text-properties + p (setq p (next-single-property-change p 'display)) + '(display t)))) (set-buffer-modified-p bmp))) - (if (featurep 'xemacs) - (remove-text-properties (point-min) (point-max) '(end-glyph t))) (org-restart-font-lock) (setq org-table-may-need-update t) (if org-display-custom-times @@ -16828,8 +17446,8 @@ The command returns the inserted time stamp." t1 w1 with-hm tf time str w2 (off 0)) (save-match-data (setq t1 (org-parse-time-string ts t)) - (if (string-match "\\(-[0-9]+:[0-9]+\\)?\\( [.+]?\\+[0-9]+[hdwmy]\\(/[0-9]+[hdwmy]\\)?\\)?\\'" ts) - (setq off (- (match-end 0) (match-beginning 0))))) + (when (string-match "\\(-[0-9]+:[0-9]+\\)?\\( [.+]?\\+[0-9]+[hdwmy]\\(/[0-9]+[hdwmy]\\)?\\)?\\'" ts) + (setq off (- (match-end 0) (match-beginning 0))))) (setq end (- end off)) (setq w1 (- end beg) with-hm (and (nth 1 t1) (nth 2 t1)) @@ -16840,41 +17458,10 @@ The command returns the inserted time stamp." (substring tf 1 -1) (apply 'encode-time time)) nil 'mouse-face 'highlight) w2 (length str)) - (if (not (= w2 w1)) - (add-text-properties (1+ beg) (+ 2 beg) - (list 'org-dwidth t 'org-dwidth-n (- w1 w2)))) - (if (featurep 'xemacs) - (progn - (put-text-property beg end 'invisible t) - (put-text-property beg end 'end-glyph (make-glyph str))) - (put-text-property beg end 'display str)))) - -(defun org-translate-time (string) - "Translate all timestamps in STRING to custom format. -But do this only if the variable `org-display-custom-times' is set." - (when org-display-custom-times - (save-match-data - (let* ((start 0) - (re org-ts-regexp-both) - t1 with-hm inactive tf time str beg end) - (while (setq start (string-match re string start)) - (setq beg (match-beginning 0) - end (match-end 0) - t1 (save-match-data - (org-parse-time-string (substring string beg end) t)) - with-hm (and (nth 1 t1) (nth 2 t1)) - inactive (equal (substring string beg (1+ beg)) "[") - tf (funcall (if with-hm 'cdr 'car) - org-time-stamp-custom-formats) - time (org-fix-decoded-time t1) - str (format-time-string - (concat - (if inactive "[" "<") (substring tf 1 -1) - (if inactive "]" ">")) - (apply 'encode-time time)) - string (replace-match str t t string) - start (+ start (length str))))))) - string) + (unless (= w2 w1) + (add-text-properties (1+ beg) (+ 2 beg) + (list 'org-dwidth t 'org-dwidth-n (- w1 w2)))) + (put-text-property beg end 'display str))) (defun org-fix-decoded-time (time) "Set 0 instead of nil for the first 6 elements of time. @@ -16882,19 +17469,17 @@ Don't touch the rest." (let ((n 0)) (mapcar (lambda (x) (if (< (setq n (1+ n)) 7) (or x 0) x)) time))) -(define-obsolete-function-alias 'org-days-to-time 'org-time-stamp-to-now "24.4") - (defun org-time-stamp-to-now (timestamp-string &optional seconds) "Difference between TIMESTAMP-STRING and now in days. If SECONDS is non-nil, return the difference in seconds." - (let ((fdiff (if seconds 'float-time 'time-to-days))) + (let ((fdiff (if seconds #'float-time #'time-to-days))) (- (funcall fdiff (org-time-string-to-time timestamp-string)) (funcall fdiff (current-time))))) -(defun org-deadline-close (timestamp-string &optional ndays) +(defun org-deadline-close-p (timestamp-string &optional ndays) "Is the time in TIMESTAMP-STRING close to the current date?" (setq ndays (or ndays (org-get-wdays timestamp-string))) - (and (< (org-time-stamp-to-now timestamp-string) ndays) + (and (<= (org-time-stamp-to-now timestamp-string) ndays) (not (org-entry-is-done-p)))) (defun org-get-wdays (ts &optional delay zero-delay) @@ -16930,14 +17515,15 @@ This is used by `org-read-date' in a temporary keymap for the calendar buffer." (let* ((date (calendar-cursor-to-date)) (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))) (setq org-ans1 (format-time-string "%Y-%m-%d" time))) - (if (active-minibuffer-window) (exit-minibuffer)))) + (when (active-minibuffer-window) (exit-minibuffer)))) (defun org-check-deadlines (ndays) "Check if there are any deadlines due or past due. A deadline is considered due if it happens within `org-deadline-warning-days' days from today's date. If the deadline appears in an entry marked DONE, -it is not shown. The prefix arg NDAYS can be used to test that many -days. If the prefix is a raw \\[universal-argument] prefix, all deadlines are shown." +it is not shown. A numeric prefix argument NDAYS can be used to test that +many days. If the prefix is a raw `\\[universal-argument]', all deadlines \ +are shown." (interactive "P") (let* ((org-warn-days (cond @@ -16947,8 +17533,7 @@ days. If the prefix is a raw \\[universal-argument] prefix, all deadlines are s (case-fold-search nil) (regexp (concat "\\<" org-deadline-string " *<\\([^>]+\\)>")) (callback - (lambda () (org-deadline-close (match-string 1) org-warn-days)))) - + (lambda () (org-deadline-close-p (match-string 1) org-warn-days)))) (message "%d deadlines past-due or due within %d days" (org-occur regexp nil callback) org-warn-days))) @@ -16966,39 +17551,61 @@ Allowed values for TYPE are: When TYPE is nil, fall back on returning a regexp that matches both scheduled and deadline timestamps." - (cond ((eq type 'all) "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\(?: +[^]+0-9>\r\n -]+\\)?\\(?: +[0-9]\\{1,2\\}:[0-9]\\{2\\}\\)?\\)") - ((eq type 'active) org-ts-regexp) - ((eq type 'inactive) "\\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^\r\n>]*?\\)\\]") - ((eq type 'scheduled) (concat "\\<" org-scheduled-string " *<\\([^>]+\\)>")) - ((eq type 'deadline) (concat "\\<" org-deadline-string " *<\\([^>]+\\)>")) - ((eq type 'closed) (concat org-closed-string " \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} ?[^\r\n>]*?\\)\\]")) - ((eq type 'scheduled-or-deadline) - (concat "\\<\\(?:" org-deadline-string "\\|" org-scheduled-string "\\) *<\\([^>]+\\)>")))) - -(defun org-check-before-date (date) - "Check if there are deadlines or scheduled entries before DATE." + (cl-case type + (all org-ts-regexp-both) + (active org-ts-regexp) + (inactive org-ts-regexp-inactive) + (scheduled org-scheduled-time-regexp) + (deadline org-deadline-time-regexp) + (closed org-closed-time-regexp) + (otherwise + (concat "\\<" + (regexp-opt (list org-deadline-string org-scheduled-string)) + " *<\\([^>]+\\)>")))) + +(defun org-check-before-date (d) + "Check if there are deadlines or scheduled entries before date D." (interactive (list (org-read-date))) - (let ((case-fold-search nil) - (regexp (org-re-timestamp org-ts-type)) - (callback - (lambda () (time-less-p - (org-time-string-to-time (match-string 1)) - (org-time-string-to-time date))))) + (let* ((case-fold-search nil) + (regexp (org-re-timestamp org-ts-type)) + (ts-type org-ts-type) + (callback + (lambda () + (let ((match (match-string 1))) + (and (if (memq ts-type '(active inactive all)) + (eq (org-element-type (save-excursion + (backward-char) + (org-element-context))) + 'timestamp) + (org-at-planning-p)) + (time-less-p + (org-time-string-to-time match) + (org-time-string-to-time d))))))) (message "%d entries before %s" - (org-occur regexp nil callback) date))) + (org-occur regexp nil callback) + d))) -(defun org-check-after-date (date) - "Check if there are deadlines or scheduled entries after DATE." +(defun org-check-after-date (d) + "Check if there are deadlines or scheduled entries after date D." (interactive (list (org-read-date))) - (let ((case-fold-search nil) - (regexp (org-re-timestamp org-ts-type)) - (callback - (lambda () (not - (time-less-p - (org-time-string-to-time (match-string 1)) - (org-time-string-to-time date)))))) + (let* ((case-fold-search nil) + (regexp (org-re-timestamp org-ts-type)) + (ts-type org-ts-type) + (callback + (lambda () + (let ((match (match-string 1))) + (and (if (memq ts-type '(active inactive all)) + (eq (org-element-type (save-excursion + (backward-char) + (org-element-context))) + 'timestamp) + (org-at-planning-p)) + (not (time-less-p + (org-time-string-to-time match) + (org-time-string-to-time d)))))))) (message "%d entries after %s" - (org-occur regexp nil callback) date))) + (org-occur regexp nil callback) + d))) (defun org-check-dates-range (start-date end-date) "Check for deadlines/scheduled entries between START-DATE and END-DATE." @@ -17007,15 +17614,22 @@ both scheduled and deadline timestamps." (let ((case-fold-search nil) (regexp (org-re-timestamp org-ts-type)) (callback - (lambda () - (let ((match (match-string 1))) - (and - (not (time-less-p - (org-time-string-to-time match) - (org-time-string-to-time start-date))) - (time-less-p - (org-time-string-to-time match) - (org-time-string-to-time end-date))))))) + (let ((type org-ts-type)) + (lambda () + (let ((match (match-string 1))) + (and + (if (memq type '(active inactive all)) + (eq (org-element-type (save-excursion + (backward-char) + (org-element-context))) + 'timestamp) + (org-at-planning-p)) + (not (time-less-p + (org-time-string-to-time match) + (org-time-string-to-time start-date))) + (time-less-p + (org-time-string-to-time match) + (org-time-string-to-time end-date)))))))) (message "%d entries between %s and %s" (org-occur regexp nil callback) start-date end-date))) @@ -17034,8 +17648,8 @@ days in order to avoid rounding problems." (unless (org-at-date-range-p t) (goto-char (point-at-bol)) (re-search-forward org-tr-regexp-both (point-at-eol) t)) - (if (not (org-at-date-range-p t)) - (user-error "Not at a time-stamp range, and none found in current line"))) + (unless (org-at-date-range-p t) + (user-error "Not at a time-stamp range, and none found in current line"))) (let* ((ts1 (match-string 1)) (ts2 (match-string 2)) (havetime (or (> (length ts1) 15) (> (length ts2) 15))) @@ -17073,27 +17687,31 @@ days in order to avoid rounding problems." (setq align t) (and (looking-at " *|") (goto-char (match-end 0)))) (goto-char match-end)) - (if (looking-at - "\\( *-? *[0-9]+y\\)?\\( *[0-9]+d\\)? *[0-9][0-9]:[0-9][0-9]") - (replace-match "")) - (if negative (insert " -")) + (when (looking-at + "\\( *-? *[0-9]+y\\)?\\( *[0-9]+d\\)? *[0-9][0-9]:[0-9][0-9]") + (replace-match "")) + (when negative (insert " -")) (if (> y 0) (insert " " (format (if havetime fy fy1) y d h m)) (if (> d 0) (insert " " (format (if havetime fd fd1) d h m)) (insert " " (format fh h m)))) - (if align (org-table-align)) + (when align (org-table-align)) (message "Time difference inserted"))))) (defun org-make-tdiff-string (y d h m) (let ((fmt "") (l nil)) - (if (> y 0) (setq fmt (concat fmt "%d year" (if (> y 1) "s" "") " ") - l (push y l))) - (if (> d 0) (setq fmt (concat fmt "%d day" (if (> d 1) "s" "") " ") - l (push d l))) - (if (> h 0) (setq fmt (concat fmt "%d hour" (if (> h 1) "s" "") " ") - l (push h l))) - (if (> m 0) (setq fmt (concat fmt "%d minute" (if (> m 1) "s" "") " ") - l (push m l))) + (when (> y 0) + (setq fmt (concat fmt "%d year" (if (> y 1) "s" "") " ")) + (push y l)) + (when (> d 0) + (setq fmt (concat fmt "%d day" (if (> d 1) "s" "") " ")) + (push d l)) + (when (> h 0) + (setq fmt (concat fmt "%d hour" (if (> h 1) "s" "") " ")) + (push h l)) + (when (> m 0) + (setq fmt (concat fmt "%d minute" (if (> m 1) "s" "") " ")) + (push m l)) (apply 'format fmt (nreverse l)))) (defun org-time-string-to-time (s &optional buffer pos) @@ -17110,28 +17728,40 @@ days in order to avoid rounding problems." "Convert a timestamp string to a number of seconds." (float-time (org-time-string-to-time s))) -(defun org-time-string-to-absolute (s &optional daynr prefer show-all buffer pos) - "Convert a time stamp to an absolute day number. -If there is a specifier for a cyclic time stamp, get the closest -date to DAYNR. -PREFER and SHOW-ALL are passed through to `org-closest-date'. -The variable `date' is bound by the calendar when this is called." +(org-define-error 'org-diary-sexp-no-match "Unable to match diary sexp") + +(defun org-time-string-to-absolute (s &optional daynr prefer buffer pos) + "Convert time stamp S to an absolute day number. + +If DAYNR in non-nil, and there is a specifier for a cyclic time +stamp, get the closest date to DAYNR. If PREFER is +`past' (respectively `future') return a date past (respectively +after) or equal to DAYNR. + +POS is the location of time stamp S, as a buffer position in +BUFFER. + +Diary sexp timestamps are matched against DAYNR, when non-nil. +If matching fails or DAYNR is nil, `org-diary-sexp-no-match' is +signalled." (cond - ((and daynr (string-match "\\`%%\\((.*)\\)" s)) - (if (org-diary-sexp-entry (match-string 1 s) "" date) + ((string-match "\\`%%\\((.*)\\)" s) + ;; Sexp timestamp: try to match DAYNR, if available, since we're + ;; only able to match individual dates. If it fails, raise an + ;; error. + (if (and daynr + (org-diary-sexp-entry + (match-string 1 s) "" (calendar-gregorian-from-absolute daynr))) daynr - (+ daynr 1000))) - ((and daynr (string-match "\\+[0-9]+[hdwmy]" s)) - (org-closest-date s (if (and (boundp 'daynr) (integerp daynr)) daynr - (time-to-days (current-time))) (match-string 0 s) - prefer show-all)) + (signal 'org-diary-sexp-no-match (list s)))) + (daynr (org-closest-date s daynr prefer)) (t (time-to-days (condition-case errdata - (apply 'encode-time (org-parse-time-string s)) + (apply #'encode-time (org-parse-time-string s)) (error (error "Bad timestamp `%s'%s\nError was: %s" - s (if (not (and buffer pos)) - "" - (format-message " at %d in buffer `%s'" pos buffer)) + s + (if (not (and buffer pos)) "" + (format-message " at %d in buffer `%s'" pos buffer)) (cdr errdata)))))))) (defun org-days-to-iso-week (days) @@ -17141,43 +17771,46 @@ The variable `date' is bound by the calendar when this is called." (defun org-small-year-to-year (year) "Convert 2-digit years into 4-digit years. -38-99 are mapped into 1938-1999. 1-37 are mapped into 2001-2037. -The year 2000 cannot be abbreviated. Any year larger than 99 -is returned unchanged." - (if (< year 38) - (setq year (+ 2000 year)) - (if (< year 100) - (setq year (+ 1900 year)))) - year) +YEAR is expanded into one of the 30 next years, if possible, or +into a past one. Any year larger than 99 is returned unchanged." + (if (>= year 100) year + (let* ((current (string-to-number (format-time-string "%Y" (current-time)))) + (century (/ current 100)) + (offset (- year (% current 100)))) + (cond ((> offset 30) (+ (* (1- century) 100) year)) + ((> offset -70) (+ (* century 100) year)) + (t (+ (* (1+ century) 100) year)))))) (defun org-time-from-absolute (d) "Return the time corresponding to date D. D may be an absolute day number, or a calendar-type list (month day year)." - (if (numberp d) (setq d (calendar-gregorian-from-absolute d))) + (when (numberp d) (setq d (calendar-gregorian-from-absolute d))) (encode-time 0 0 0 (nth 1 d) (car d) (nth 2 d))) +(defvar org-agenda-current-date) (defun org-calendar-holiday () - "List of holidays, for Diary display in Org-mode." + "List of holidays, for Diary display in Org mode." (require 'holidays) - (let ((hl (funcall - (if (fboundp 'calendar-check-holidays) - 'calendar-check-holidays 'check-calendar-holidays) date))) - (if hl (mapconcat 'identity hl "; ")))) + (let ((hl (calendar-check-holidays org-agenda-current-date))) + (and hl (mapconcat #'identity hl "; ")))) -(defun org-diary-sexp-entry (sexp entry date) - "Process a SEXP diary ENTRY for DATE." +(defun org-diary-sexp-entry (sexp entry d) + "Process a SEXP diary ENTRY for date D." (require 'diary-lib) - (let ((result (if calendar-debug-sexp - (let ((stack-trace-on-error t)) - (eval (car (read-from-string sexp)))) - (condition-case nil - (eval (car (read-from-string sexp))) - (error - (beep) - (message "Bad sexp at line %d in %s: %s" - (org-current-line) - (buffer-file-name) sexp) - (sleep-for 2)))))) + ;; `org-anniversary' and alike expect ENTRY and DATE to be bound + ;; dynamically. + (let* ((sexp `(let ((entry ,entry) + (date ',d)) + ,(car (read-from-string sexp)))) + (result (if calendar-debug-sexp (eval sexp) + (condition-case nil + (eval sexp) + (error + (beep) + (message "Bad sexp at line %d in %s: %s" + (org-current-line) + (buffer-file-name) sexp) + (sleep-for 2)))))) (cond ((stringp result) (split-string result "; ")) ((and (consp result) (not (consp (cdr result))) @@ -17189,9 +17822,7 @@ D may be an absolute day number, or a calendar-type list (month day year)." (defun org-diary-to-ical-string (frombuf) "Get iCalendar entries from diary entries in buffer FROMBUF. This uses the icalendar.el library." - (let* ((tmpdir (if (featurep 'xemacs) - (temp-directory) - temporary-file-directory)) + (let* ((tmpdir temporary-file-directory) (tmpfile (make-temp-name (expand-file-name "orgics" tmpdir))) buf rtn b e) @@ -17200,125 +17831,146 @@ This uses the icalendar.el library." (setq buf (find-buffer-visiting tmpfile)) (set-buffer buf) (goto-char (point-min)) - (if (re-search-forward "^BEGIN:VEVENT" nil t) - (setq b (match-beginning 0))) + (when (re-search-forward "^BEGIN:VEVENT" nil t) + (setq b (match-beginning 0))) (goto-char (point-max)) - (if (re-search-backward "^END:VEVENT" nil t) - (setq e (match-end 0))) + (when (re-search-backward "^END:VEVENT" nil t) + (setq e (match-end 0))) (setq rtn (if (and b e) (concat (buffer-substring b e) "\n") ""))) (kill-buffer buf) (delete-file tmpfile) rtn)) -(defun org-closest-date (start current change prefer show-all) - "Find the date closest to CURRENT that is consistent with START and CHANGE. -When PREFER is `past', return a date that is either CURRENT or past. -When PREFER is `future', return a date that is either CURRENT or future. -When SHOW-ALL is nil, only return the current occurrence of a time stamp." - ;; Make the proper lists from the dates - (catch 'exit - (let ((a1 '(("h" . hour) - ("d" . day) - ("w" . week) - ("m" . month) - ("y" . year))) - (shour (nth 2 (org-parse-time-string start))) - dn dw sday cday n1 n2 n0 - d m y y1 y2 date1 date2 nmonths nm ny m2) - - (setq start (org-date-to-gregorian start) - current (org-date-to-gregorian - (if show-all - current - (time-to-days (current-time)))) - sday (calendar-absolute-from-gregorian start) - cday (calendar-absolute-from-gregorian current)) - - (if (<= cday sday) (throw 'exit sday)) - - (if (string-match "\\(\\+[0-9]+\\)\\([hdwmy]\\)" change) - (setq dn (string-to-number (match-string 1 change)) - dw (cdr (assoc (match-string 2 change) a1))) - (user-error "Invalid change specifier: %s" change)) - (if (eq dw 'week) (setq dw 'day dn (* 7 dn))) - (cond - ((eq dw 'hour) - (let ((missing-hours - (mod (+ (- (* 24 (- cday sday)) shour) org-extend-today-until) - dn))) - (setq n1 (if (zerop missing-hours) cday - (- cday (1+ (floor (/ missing-hours 24))))) - n2 (+ cday (floor (/ (- dn missing-hours) 24)))))) - ((eq dw 'day) - (setq n1 (+ sday (* dn (floor (/ (- cday sday) dn)))) - n2 (+ n1 dn))) - ((eq dw 'year) - (setq d (nth 1 start) m (car start) y1 (nth 2 start) y2 (nth 2 current)) - (setq y1 (+ (* (floor (/ (- y2 y1) dn)) dn) y1)) - (setq date1 (list m d y1) - n1 (calendar-absolute-from-gregorian date1) - date2 (list m d (+ y1 (* (if (< n1 cday) 1 -1) dn))) - n2 (calendar-absolute-from-gregorian date2))) - ((eq dw 'month) - ;; approx number of month between the two dates - (setq nmonths (floor (/ (- cday sday) 30.436875))) - ;; How often does dn fit in there? - (setq d (nth 1 start) m (car start) y (nth 2 start) - nm (* dn (max 0 (1- (floor (/ nmonths dn))))) - m (+ m nm) - ny (floor (/ m 12)) - y (+ y ny) - m (- m (* ny 12))) - (while (> m 12) (setq m (- m 12) y (1+ y))) - (setq n1 (calendar-absolute-from-gregorian (list m d y))) - (setq m2 (+ m dn) y2 y) - (if (> m2 12) (setq y2 (1+ y2) m2 (- m2 12))) - (setq n2 (calendar-absolute-from-gregorian (list m2 d y2))) - (while (<= n2 cday) - (setq n1 n2 m m2 y y2) - (setq m2 (+ m dn) y2 y) - (if (> m2 12) (setq y2 (1+ y2) m2 (- m2 12))) - (setq n2 (calendar-absolute-from-gregorian (list m2 d y2)))))) - ;; Make sure n1 is the earlier date - (setq n0 n1 n1 (min n1 n2) n2 (max n0 n2)) - (if show-all - (cond - ((eq prefer 'past) (if (= cday n2) n2 n1)) - ((eq prefer 'future) (if (= cday n1) n1 n2)) - (t (if (> (abs (- cday n1)) (abs (- cday n2))) n2 n1))) - (cond - ((eq prefer 'past) (if (= cday n2) n2 n1)) - ((eq prefer 'future) (if (= cday n1) n1 n2)) - (t (if (= cday n1) n1 n2))))))) - -(defun org-date-to-gregorian (date) - "Turn any specification of DATE into a Gregorian date for the calendar." - (cond ((integerp date) (calendar-gregorian-from-absolute date)) - ((and (listp date) (= (length date) 3)) date) - ((stringp date) - (setq date (org-parse-time-string date)) - (list (nth 4 date) (nth 3 date) (nth 5 date))) - ((listp date) - (list (nth 4 date) (nth 3 date) (nth 5 date))))) - -(defun org-parse-time-string (s &optional nodefault) - "Parse the standard Org-mode time string. +(defun org-closest-date (start current prefer) + "Return closest date to CURRENT starting from START. + +CURRENT and START are both time stamps. + +When PREFER is `past', return a date that is either CURRENT or +past. When PREFER is `future', return a date that is either +CURRENT or future. + +Only time stamps with a repeater are modified. Any other time +stamp stay unchanged. In any case, return value is an absolute +day number." + (if (not (string-match "\\+\\([0-9]+\\)\\([hdwmy]\\)" start)) + ;; No repeater. Do not shift time stamp. + (time-to-days (apply #'encode-time (org-parse-time-string start))) + (let ((value (string-to-number (match-string 1 start))) + (type (match-string 2 start))) + (if (= 0 value) + ;; Repeater with a 0-value is considered as void. + (time-to-days (apply #'encode-time (org-parse-time-string start))) + (let* ((base (org-date-to-gregorian start)) + (target (org-date-to-gregorian current)) + (sday (calendar-absolute-from-gregorian base)) + (cday (calendar-absolute-from-gregorian target)) + n1 n2) + ;; If START is already past CURRENT, just return START. + (if (<= cday sday) sday + ;; Compute closest date before (N1) and closest date past + ;; (N2) CURRENT. + (pcase type + ("h" + (let ((missing-hours + (mod (+ (- (* 24 (- cday sday)) + (nth 2 (org-parse-time-string start))) + org-extend-today-until) + value))) + (setf n1 (if (= missing-hours 0) cday + (- cday (1+ (/ missing-hours 24))))) + (setf n2 (+ cday (/ (- value missing-hours) 24))))) + ((or "d" "w") + (let ((value (if (equal type "w") (* 7 value) value))) + (setf n1 (+ sday (* value (/ (- cday sday) value)))) + (setf n2 (+ n1 value)))) + ("m" + (let* ((add-months + (lambda (d n) + ;; Add N months to gregorian date D, i.e., + ;; a list (MONTH DAY YEAR). Return a valid + ;; gregorian date. + (let ((m (+ (nth 0 d) n))) + (list (mod m 12) + (nth 1 d) + (+ (/ m 12) (nth 2 d)))))) + (months ; Complete months to TARGET. + (* (/ (+ (* 12 (- (nth 2 target) (nth 2 base))) + (- (nth 0 target) (nth 0 base)) + ;; If START's day is greater than + ;; TARGET's, remove incomplete month. + (if (> (nth 1 target) (nth 1 base)) 0 -1)) + value) + value)) + (before (funcall add-months base months))) + (setf n1 (calendar-absolute-from-gregorian before)) + (setf n2 + (calendar-absolute-from-gregorian + (funcall add-months before value))))) + (_ + (let* ((d (nth 1 base)) + (m (nth 0 base)) + (y (nth 2 base)) + (years ; Complete years to TARGET. + (* (/ (- (nth 2 target) + y + ;; If START's month and day are + ;; greater than TARGET's, remove + ;; incomplete year. + (if (or (> (nth 0 target) m) + (and (= (nth 0 target) m) + (> (nth 1 target) d))) + 0 + 1)) + value) + value)) + (before (list m d (+ y years)))) + (setf n1 (calendar-absolute-from-gregorian before)) + (setf n2 (calendar-absolute-from-gregorian + (list m d (+ (nth 2 before) value))))))) + ;; Handle PREFER parameter, if any. + (cond + ((eq prefer 'past) (if (= cday n2) n2 n1)) + ((eq prefer 'future) (if (= cday n1) n1 n2)) + (t (if (> (abs (- cday n1)) (abs (- cday n2))) n2 n1))))))))) + +(defun org-date-to-gregorian (d) + "Turn any specification of date D into a Gregorian date for the calendar." + (cond ((integerp d) (calendar-gregorian-from-absolute d)) + ((and (listp d) (= (length d) 3)) d) + ((stringp d) + (let ((d (org-parse-time-string d))) + (list (nth 4 d) (nth 3 d) (nth 5 d)))) + ((listp d) (list (nth 4 d) (nth 3 d) (nth 5 d))))) + +(defun org-parse-time-string (s &optional nodefault zone) + "Parse the standard Org time string. + This should be a lot faster than the normal `parse-time-string'. -If time is not given, defaults to 0:00. However, with optional NODEFAULT, -hour and minute fields will be nil if not given." + +If time is not given, defaults to 0:00. However, with optional +NODEFAULT, hour and minute fields will be nil if not given. + +The optional ZONE is omitted or nil for Emacs local time, t for +Universal Time, ‘wall’ for system wall clock time, or a string as +in the TZ environment variable." (cond ((string-match org-ts-regexp0 s) (list 0 - (if (or (match-beginning 8) (not nodefault)) - (string-to-number (or (match-string 8 s) "0"))) - (if (or (match-beginning 7) (not nodefault)) - (string-to-number (or (match-string 7 s) "0"))) + (when (or (match-beginning 8) (not nodefault)) + (string-to-number (or (match-string 8 s) "0"))) + (when (or (match-beginning 7) (not nodefault)) + (string-to-number (or (match-string 7 s) "0"))) (string-to-number (match-string 4 s)) (string-to-number (match-string 3 s)) (string-to-number (match-string 2 s)) - nil nil nil)) + nil nil zone)) ((string-match "^<[^>]+>$" s) + ;; FIXME: `decode-time' needs to be called with ZONE as its + ;; second argument. However, this requires at least Emacs + ;; 25.1. We can do it when we switch to this version as our + ;; minimal requirement. (decode-time (seconds-to-time (org-matcher-time s)))) - (t (error "Not a standard Org-mode time string: %s" s)))) + (t (error "Not a standard Org time string: %s" s)))) (defun org-timestamp-up (&optional arg) "Increase the date item at the cursor by one. @@ -17355,14 +18007,21 @@ With prefix ARG, change that many days." (org-timestamp-change (- (prefix-numeric-value arg)) 'day) 'updown)) (defun org-at-timestamp-p (&optional inactive-ok) - "Determine if the cursor is in or at a timestamp." + "Non-nil if point is inside a timestamp. + +When optional argument INACTIVE-OK is non-nil, also consider +inactive timestamps. + +When this function returns a non-nil value, match data is set +according to `org-ts-regexp3' or `org-ts-regexp2', depending on +INACTIVE-OK." (interactive) (let* ((tsr (if inactive-ok org-ts-regexp3 org-ts-regexp2)) (pos (point)) (ans (or (looking-at tsr) (save-excursion (skip-chars-backward "^[<\n\r\t") - (if (> (point) (point-min)) (backward-char 1)) + (when (> (point) (point-min)) (backward-char 1)) (and (looking-at tsr) (> (- (match-end 0) pos) -1)))))) (and ans @@ -17403,8 +18062,8 @@ With prefix ARG, change that many days." (defun org-at-clock-log-p nil "Is the cursor on the clock log line?" (save-excursion - (move-beginning-of-line 1) - (looking-at "^[ \t]*CLOCK:"))) + (beginning-of-line) + (looking-at org-clock-line-re))) (defvar org-clock-history) ; defined in org-clock.el (defvar org-clock-adjust-closest nil) ; defined in org-clock.el @@ -17420,19 +18079,19 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like \"--2d\"." org-ts-what extra rem ts time time0 fixnext clrgx) - (if (not (org-at-timestamp-p t)) - (user-error "Not at a timestamp")) + (unless (org-at-timestamp-p t) + (user-error "Not at a timestamp")) (if (and (not what) (eq org-ts-what 'bracket)) (org-toggle-timestamp-type) ;; Point isn't on brackets. Remember the part of the time-stamp ;; the point was in. Indeed, size of time-stamps may change, ;; but point must be kept in the same category nonetheless. (setq origin-cat org-ts-what) - (if (and (not what) (not (eq org-ts-what 'day)) - org-display-custom-times - (get-text-property (point) 'display) - (not (get-text-property (1- (point)) 'display))) - (setq org-ts-what 'day)) + (when (and (not what) (not (eq org-ts-what 'day)) + org-display-custom-times + (get-text-property (point) 'display) + (not (get-text-property (1- (point)) 'display))) + (setq org-ts-what 'day)) (setq org-ts-what (or what org-ts-what) inactive (= (char-after (match-beginning 0)) ?\[) ts (match-string 0)) @@ -17441,26 +18100,28 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like \"--2d\"." "\\(\\(-[012][0-9]:[0-5][0-9]\\)?\\( +[.+]?-?[-+][0-9]+[hdwmy]\\(/[0-9]+[hdwmy]\\)?\\)*\\)[]>]" ts) (setq extra (match-string 1 ts)) - (if suppress-tmp-delay - (setq extra (replace-regexp-in-string " --[0-9]+[hdwmy]" "" extra)))) - (if (string-match "^.\\{10\\}.*?[0-9]+:[0-9][0-9]" ts) - (setq with-hm t)) + (when suppress-tmp-delay + (setq extra (replace-regexp-in-string " --[0-9]+[hdwmy]" "" extra)))) + (when (string-match "^.\\{10\\}.*?[0-9]+:[0-9][0-9]" ts) + (setq with-hm t)) (setq time0 (org-parse-time-string ts)) (when (and updown (eq org-ts-what 'minute) (not current-prefix-arg)) ;; This looks like s-up and s-down. Change by one rounding step. (setq n (* dm (cond ((> n 0) 1) ((< n 0) -1) (t 0)))) - (when (not (= 0 (setq rem (% (nth 1 time0) dm)))) + (unless (= 0 (setq rem (% (nth 1 time0) dm))) (setcar (cdr time0) (+ (nth 1 time0) (if (> n 0) (- rem) (- dm rem)))))) (setq time - (encode-time (or (car time0) 0) - (+ (if (eq org-ts-what 'minute) n 0) (nth 1 time0)) - (+ (if (eq org-ts-what 'hour) n 0) (nth 2 time0)) - (+ (if (eq org-ts-what 'day) n 0) (nth 3 time0)) - (+ (if (eq org-ts-what 'month) n 0) (nth 4 time0)) - (+ (if (eq org-ts-what 'year) n 0) (nth 5 time0)))) + (apply #'encode-time + (or (car time0) 0) + (+ (if (eq org-ts-what 'minute) n 0) (nth 1 time0)) + (+ (if (eq org-ts-what 'hour) n 0) (nth 2 time0)) + (+ (if (eq org-ts-what 'day) n 0) (nth 3 time0)) + (+ (if (eq org-ts-what 'month) n 0) (nth 4 time0)) + (+ (if (eq org-ts-what 'year) n 0) (nth 5 time0)) + (nthcdr 6 time0))) (when (and (member org-ts-what '(hour minute)) extra (string-match "-\\([012][0-9]\\):\\([0-5][0-9]\\)" extra)) @@ -17470,15 +18131,15 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like \"--2d\"." n dm))) (when (integerp org-ts-what) (setq extra (org-modify-ts-extra extra org-ts-what n dm))) - (if (eq what 'calendar) - (let ((cal-date (org-get-date-from-calendar))) - (setcar (nthcdr 4 time0) (nth 0 cal-date)) ; month - (setcar (nthcdr 3 time0) (nth 1 cal-date)) ; day - (setcar (nthcdr 5 time0) (nth 2 cal-date)) ; year - (setcar time0 (or (car time0) 0)) - (setcar (nthcdr 1 time0) (or (nth 1 time0) 0)) - (setcar (nthcdr 2 time0) (or (nth 2 time0) 0)) - (setq time (apply 'encode-time time0)))) + (when (eq what 'calendar) + (let ((cal-date (org-get-date-from-calendar))) + (setcar (nthcdr 4 time0) (nth 0 cal-date)) ; month + (setcar (nthcdr 3 time0) (nth 1 cal-date)) ; day + (setcar (nthcdr 5 time0) (nth 2 cal-date)) ; year + (setcar time0 (or (car time0) 0)) + (setcar (nthcdr 1 time0) (or (nth 1 time0) 0)) + (setcar (nthcdr 2 time0) (or (nth 2 time0) 0)) + (setq time (apply 'encode-time time0)))) ;; Insert the new time-stamp, and ensure point stays in the same ;; category as before (i.e. not after the last position in that ;; category). @@ -17489,17 +18150,21 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like \"--2d\"." (goto-char pos)) (save-match-data (looking-at org-ts-regexp3) - (goto-char (cond - ;; `day' category ends before `hour' if any, or at - ;; the end of the day name. - ((eq origin-cat 'day) - (min (or (match-beginning 7) (1- (match-end 5))) origin)) - ((eq origin-cat 'hour) (min (match-end 7) origin)) - ((eq origin-cat 'minute) (min (1- (match-end 8)) origin)) - ((integerp origin-cat) (min (1- (match-end 0)) origin)) - ;; `year' and `month' have both fixed size: point - ;; couldn't have moved into another part. - (t origin)))) + (goto-char + (pcase origin-cat + ;; `day' category ends before `hour' if any, or at the end + ;; of the day name. + (`day (min (or (match-beginning 7) (1- (match-end 5))) origin)) + (`hour (min (match-end 7) origin)) + (`minute (min (1- (match-end 8)) origin)) + ((pred integerp) (min (1- (match-end 0)) origin)) + ;; Point was right after the time-stamp. However, the + ;; time-stamp length might have changed, so refer to + ;; (match-end 0) instead. + (`after (match-end 0)) + ;; `year' and `month' have both fixed size: point couldn't + ;; have moved into another part. + (_ origin)))) ;; Update clock if on a CLOCK line. (org-clock-update-time-maybe) ;; Maybe adjust the closest clock in `org-clock-history' @@ -17508,11 +18173,12 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like \"--2d\"." (< 1 (length (delq nil (mapcar 'marker-position org-clock-history)))))) (message "No clock to adjust") - (cond ((save-excursion ; fix previous clock? + (cond ((save-excursion ; fix previous clock? (re-search-backward org-ts-regexp0 nil t) - (org-looking-back (concat org-clock-string " \\["))) + (looking-back (concat org-clock-string " \\[") + (line-beginning-position))) (setq fixnext 1 clrgx (concat org-ts-regexp0 "\\] =>.*$"))) - ((save-excursion ; fix next clock? + ((save-excursion ; fix next clock? (re-search-backward org-ts-regexp0 nil t) (looking-at (concat org-ts-regexp0 "\\] =>"))) (setq fixnext -1 clrgx (concat org-clock-string " \\[" org-ts-regexp0)))) @@ -17521,8 +18187,8 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like \"--2d\"." (let* ((p (save-excursion (org-back-to-heading t))) (cl (mapcar (lambda(c) (abs (- (marker-position c) p))) org-clock-history)) (clfixnth - (+ fixnext (- (length cl) (or (length (member (apply #'min cl) cl)) 100)))) - (clfixpos (if (> 0 clfixnth) nil (nth clfixnth org-clock-history)))) + (+ fixnext (- (length cl) (or (length (member (apply 'min cl) cl)) 100)))) + (clfixpos (unless (> 0 clfixnth) (nth clfixnth org-clock-history)))) (if (not clfixpos) (message "No clock to adjust") (save-excursion @@ -17536,10 +18202,10 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like \"--2d\"." (file-name-nondirectory (buffer-file-name)) (org-get-heading t t))))))))) ;; Try to recenter the calendar window, if any. - (if (and org-calendar-follow-timestamp-change - (get-buffer-window "*Calendar*" t) - (memq org-ts-what '(day month year))) - (org-recenter-calendar (time-to-days time)))))) + (when (and org-calendar-follow-timestamp-change + (get-buffer-window "*Calendar*" t) + (memq org-ts-what '(day month year))) + (org-recenter-calendar (time-to-days time)))))) (defun org-modify-ts-extra (s pos n dm) "Change the different parts of the lead-time and repeat fields in timestamp." @@ -17553,13 +18219,13 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like \"--2d\"." h (string-to-number (match-string 2 s))) (if (org-pos-in-match-range pos 2) (setq h (+ h n)) - (setq n (* dm (org-no-warnings (signum n)))) - (when (not (= 0 (setq rem (% m dm)))) + (setq n (* dm (with-no-warnings (signum n)))) + (unless (= 0 (setq rem (% m dm))) (setq m (+ m (if (> n 0) (- rem) (- dm rem))))) (setq m (+ m n))) - (if (< m 0) (setq m (+ m 60) h (1- h))) - (if (> m 59) (setq m (- m 60) h (1+ h))) - (setq h (min 24 (max 0 h))) + (when (< m 0) (setq m (+ m 60) h (1- h))) + (when (> m 59) (setq m (- m 60) h (1+ h))) + (setq h (mod h 24)) (setq ng 1 new (format "-%02d:%02d" h m))) ((org-pos-in-match-range pos 6) (setq ng 6 new (car (rassoc (+ n (cdr (assoc (match-string 6 s) idx))) idx)))) @@ -17578,14 +18244,14 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like \"--2d\"." (substring s (match-end ng)))))) s)) -(defun org-recenter-calendar (date) - "If the calendar is visible, recenter it to DATE." +(defun org-recenter-calendar (d) + "If the calendar is visible, recenter it to date D." (let ((cwin (get-buffer-window "*Calendar*" t))) (when cwin (let ((calendar-move-hook nil)) (with-selected-window cwin - (calendar-goto-date (if (listp date) date - (calendar-gregorian-from-absolute date)))))))) + (calendar-goto-date + (if (listp d) d (calendar-gregorian-from-absolute d)))))))) (defun org-goto-calendar (&optional arg) "Go to the Emacs calendar at the current date. @@ -17596,17 +18262,17 @@ A prefix ARG can be used to force the current date." (calendar-move-hook nil) (calendar-view-holidays-initially-flag nil) (calendar-view-diary-initially-flag nil)) - (if (or (org-at-timestamp-p) - (save-excursion - (beginning-of-line 1) - (looking-at (concat ".*" tsr)))) - (let ((d1 (time-to-days (current-time))) - (d2 (time-to-days - (org-time-string-to-time (match-string 1))))) - (setq diff (- d2 d1)))) + (when (or (org-at-timestamp-p) + (save-excursion + (beginning-of-line 1) + (looking-at (concat ".*" tsr)))) + (let ((d1 (time-to-days (current-time))) + (d2 (time-to-days + (org-time-string-to-time (match-string 1))))) + (setq diff (- d2 d1)))) (calendar) (calendar-goto-today) - (if (and diff (not arg)) (calendar-forward-day diff)))) + (when (and diff (not arg)) (calendar-forward-day diff)))) (defun org-get-date-from-calendar () "Return a list (month day year) of date at point in calendar." @@ -17625,7 +18291,8 @@ If there is already a time stamp at the cursor position, update it." (encode-time 0 0 0 (nth 1 cal-date) (car cal-date) (nth 2 cal-date)))))) (defcustom org-effort-durations - `(("h" . 60) + `(("min" . 1) + ("h" . 60) ("d" . ,(* 60 8)) ("w" . ,(* 60 8 5)) ("m" . ,(* 60 8 5 4)) @@ -17641,7 +18308,8 @@ minutes. For example, if the value of this variable is ((\"hours\" . 60)), then an effort string \"2hours\" is equivalent to 120 minutes." :group 'org-agenda - :version "24.1" + :version "26.1" + :package-version '(Org . "8.3") :type '(alist :key-type (string :tag "Modifier") :value-type (number :tag "Minutes"))) @@ -17734,10 +18402,6 @@ The format is determined by `org-time-clocksum-format', ;; return formatted time duration clocksum)))) -(defalias 'org-minutes-to-hh:mm-string 'org-minutes-to-clocksum-string) -(make-obsolete 'org-minutes-to-hh:mm-string 'org-minutes-to-clocksum-string - "Org mode version 8.0") - (defun org-hours-to-clocksum-string (n) (org-minutes-to-clocksum-string (* n 60))) @@ -17793,19 +18457,21 @@ tables are not re-aligned, etc." :version "24.3" :group 'org-agenda) -(defcustom org-agenda-ignore-drawer-properties nil +(defcustom org-agenda-ignore-properties nil "Avoid updating text properties when building the agenda. -Properties are used to prepare buffers for effort estimates, appointments, -and subtree-local categories. -If you don't use these in the agenda, you can add them to this list and -agenda building will be a bit faster. +Properties are used to prepare buffers for effort estimates, +appointments, statistics and subtree-local categories. +If you don't use these in the agenda, you can add them to this +list and agenda building will be a bit faster. The value is a list, with zero or more of the symbols `effort', `appt', -or `category'." +`stats' or `category'." :type '(set :greedy t (const effort) (const appt) + (const stats) (const category)) - :version "24.3" + :version "26.1" + :package-version '(Org . "8.3") :group 'org-agenda) (defun org-duration-string-to-minutes (s &optional output-to-string) @@ -17821,25 +18487,25 @@ Entries containing a colon are interpreted as H:MM by (regexp-opt (mapcar 'car org-effort-durations)) "\\)"))) (while (string-match re s) - (incf result (* (cdr (assoc (match-string 2 s) org-effort-durations)) - (string-to-number (match-string 1 s)))) + (cl-incf result (* (cdr (assoc (match-string 2 s) org-effort-durations)) + (string-to-number (match-string 1 s)))) (setq s (replace-match "" nil t s))) (setq result (floor result)) - (incf result (org-hh:mm-string-to-minutes s)) + (cl-incf result (org-hh:mm-string-to-minutes s)) (if output-to-string (number-to-string result) result))) ;;;; Files (defun org-save-all-org-buffers () - "Save all Org-mode buffers without user confirmation." + "Save all Org buffers without user confirmation." (interactive) - (message "Saving all Org-mode buffers...") + (message "Saving all Org buffers...") (save-some-buffers t (lambda () (derived-mode-p 'org-mode))) (when (featurep 'org-id) (org-id-locations-save)) - (message "Saving all Org-mode buffers... done")) + (message "Saving all Org buffers... done")) (defun org-revert-all-org-buffers () - "Revert all Org-mode buffers. + "Revert all Org buffers. Prompt for confirmation when there are unsaved changes. Be sure you know what you are doing before letting this function overwrite your changes. @@ -17856,13 +18522,11 @@ changes from another. I believe the procedure must be like this: (user-error "Abort")) (save-excursion (save-window-excursion - (mapc - (lambda (b) - (when (and (with-current-buffer b (derived-mode-p 'org-mode)) - (with-current-buffer b buffer-file-name)) - (org-pop-to-buffer-same-window b) - (revert-buffer t 'no-confirm))) - (buffer-list)) + (dolist (b (buffer-list)) + (when (and (with-current-buffer b (derived-mode-p 'org-mode)) + (with-current-buffer b buffer-file-name)) + (pop-to-buffer-same-window b) + (revert-buffer t 'no-confirm))) (when (and (featurep 'org-id) org-id-track-globally) (org-id-locations-load))))) @@ -17871,29 +18535,19 @@ changes from another. I believe the procedure must be like this: ;;;###autoload (defun org-switchb (&optional arg) "Switch between Org buffers. -With one prefix argument, restrict available buffers to files. -With two prefix arguments, restrict available buffers to agenda files. -Defaults to `iswitchb' for buffer name completion. -Set `org-completion-use-ido' to make it use ido instead." +With `\\[universal-argument]' prefix, restrict available buffers to files. + +With `\\[universal-argument] \\[universal-argument]' \ +prefix, restrict available buffers to agenda files." (interactive "P") - (let ((blist (cond ((equal arg '(4)) (org-buffer-list 'files)) - ((equal arg '(16)) (org-buffer-list 'agenda)) - (t (org-buffer-list)))) - (org-completion-use-iswitchb org-completion-use-iswitchb) - (org-completion-use-ido org-completion-use-ido)) - (unless (or org-completion-use-ido org-completion-use-iswitchb) - (setq org-completion-use-iswitchb t)) - (org-pop-to-buffer-same-window - (org-icompleting-read "Org buffer: " - (mapcar 'list (mapcar 'buffer-name blist)) - nil t)))) - -;;; Define some older names previously used for this functionality -;;;###autoload -(defalias 'org-ido-switchb 'org-switchb) -;;;###autoload -(defalias 'org-iswitchb 'org-switchb) + (let ((blist (org-buffer-list + (cond ((equal arg '(4)) 'files) + ((equal arg '(16)) 'agenda))))) + (pop-to-buffer-same-window + (completing-read "Org buffer: " + (mapcar #'list (mapcar #'buffer-name blist)) + nil t)))) (defun org-buffer-list (&optional predicate exclude-tmp) "Return a list of Org buffers. @@ -17968,8 +18622,10 @@ used by the agenda files. If ARCHIVE is `ifmode', do this only if "Return non-nil, if FILE is an agenda file. If FILE is omitted, use the file associated with the current buffer." - (member (or file (buffer-file-name)) - (org-agenda-files t))) + (let ((fname (or file (buffer-file-name)))) + (and fname + (member (file-truename fname) + (mapcar #'file-truename (org-agenda-files t)))))) (defun org-edit-agenda-file-list () "Edit the list of agenda files. @@ -17981,15 +18637,15 @@ the buffer and restores the previous window configuration." (if (stringp org-agenda-files) (let ((cw (current-window-configuration))) (find-file org-agenda-files) - (org-set-local 'org-window-configuration cw) - (org-add-hook 'after-save-hook - (lambda () - (set-window-configuration - (prog1 org-window-configuration - (kill-buffer (current-buffer)))) - (org-install-agenda-files-menu) - (message "New agenda file list installed")) - nil 'local) + (setq-local org-window-configuration cw) + (add-hook 'after-save-hook + (lambda () + (set-window-configuration + (prog1 org-window-configuration + (kill-buffer (current-buffer)))) + (org-install-agenda-files-menu) + (message "New agenda file list installed")) + nil 'local) (message "%s" (substitute-command-keys "Edit list and finish with \\[save-buffer]"))) (customize-variable 'org-agenda-files))) @@ -18039,19 +18695,16 @@ un-expanded file names." If the current buffer visits an agenda file, find the next one in the list. If the current buffer does not, find the first agenda file." (interactive) - (let* ((fs (org-agenda-files t)) - (files (append fs (list (car fs)))) - (tcf (if buffer-file-name (file-truename buffer-file-name))) + (let* ((fs (or (org-agenda-files t) + (user-error "No agenda files"))) + (files (copy-sequence fs)) + (tcf (and buffer-file-name (file-truename buffer-file-name))) file) - (unless files (user-error "No agenda files")) - (catch 'exit - (dolist (file files) - (if (equal (file-truename file) tcf) - (when (car files) - (find-file (car files)) - (throw 'exit t)))) - (find-file (car fs))) - (if (buffer-base-buffer) (org-pop-to-buffer-same-window (buffer-base-buffer))))) + (when tcf + (while (and (setq file (pop files)) + (not (equal (file-truename file) tcf))))) + (find-file (car (or files fs))) + (when (buffer-base-buffer) (pop-to-buffer-same-window (buffer-base-buffer))))) (defun org-agenda-file-to-front (&optional to-end) "Move/add the current file to the top of the agenda file list. @@ -18069,7 +18722,7 @@ end of the list." x had) (setq x (assoc ctf file-alist) had x) - (if (not x) (setq x (cons ctf (abbreviate-file-name buffer-file-name)))) + (unless x (setq x (cons ctf (abbreviate-file-name buffer-file-name)))) (if to-end (setq file-alist (append (delq x file-alist) (list x))) (setq file-alist (cons x (delq x file-alist)))) @@ -18090,15 +18743,15 @@ Optional argument FILE means use this file instead of the current." (afile (abbreviate-file-name file)) (files (delq nil (mapcar (lambda (x) - (if (equal true-file - (file-truename x)) - nil x)) + (unless (equal true-file + (file-truename x)) + x)) (org-agenda-files t))))) (if (not (= (length files) (length (org-agenda-files t)))) (progn (org-store-new-agenda-file-list files) (org-install-agenda-files-menu) - (message "Removed file: %s" afile)) + (message "Removed from Org Agenda list: %s" afile)) (message "File was not in list: %s (not removed)" afile)))) (defun org-file-menu-entry (file) @@ -18106,7 +18759,7 @@ Optional argument FILE means use this file instead of the current." (defun org-check-agenda-file (file) "Make sure FILE exists. If not, ask user what to do." - (when (not (file-exists-p file)) + (unless (file-exists-p file) (message "Non-existent agenda file %s. [R]emove from list or [A]bort?" (abbreviate-file-name file)) (let ((r (downcase (read-char-exclusive)))) @@ -18114,17 +18767,18 @@ Optional argument FILE means use this file instead of the current." ((equal r ?r) (org-remove-file file) (throw 'nextfile t)) - (t (error "Abort")))))) + (t (user-error "Abort")))))) (defun org-get-agenda-file-buffer (file) - "Get a buffer visiting FILE. If the buffer needs to be created, add -it to the list of buffers which might be released later." + "Get an agenda buffer visiting FILE. +If the buffer needs to be created, add it to the list of buffers +which might be released later." (let ((buf (org-find-base-buffer-visiting file))) (if buf buf ; just return it ;; Make a new buffer and remember it (setq buf (find-file-noselect file)) - (if buf (push buf org-agenda-new-buffers)) + (when buf (push buf org-agenda-new-buffers)) buf))) (defun org-release-buffers (blist) @@ -18149,7 +18803,7 @@ When a buffer is unmodified, it is just killed. When modified, it is saved (inhibit-read-only t) (org-inhibit-startup org-agenda-inhibit-startup) (rea (concat ":" org-archive-tag ":")) - file re pos) + re pos) (setq org-tag-alist-for-agenda nil org-tag-groups-alist-for-agenda nil) (save-excursion @@ -18161,20 +18815,15 @@ When a buffer is unmodified, it is just killed. When modified, it is saved (org-check-agenda-file file) (set-buffer (org-get-agenda-file-buffer file))) (widen) - (org-set-regexps-and-options-for-tags) + (org-set-regexps-and-options 'tags-only) (setq pos (point)) - (goto-char (point-min)) - (let ((case-fold-search t)) - (when (search-forward "#+setupfile" nil t) - ;; Don't set all regexps and options systematically as - ;; this is only run for setting agenda tags from setup - ;; file - (org-set-regexps-and-options))) - (or (memq 'category org-agenda-ignore-drawer-properties) + (or (memq 'category org-agenda-ignore-properties) (org-refresh-category-properties)) - (or (memq 'effort org-agenda-ignore-drawer-properties) - (org-refresh-properties org-effort-property 'org-effort)) - (or (memq 'appt org-agenda-ignore-drawer-properties) + (or (memq 'stats org-agenda-ignore-properties) + (org-refresh-stats-properties)) + (or (memq 'effort org-agenda-ignore-properties) + (org-refresh-effort-properties)) + (or (memq 'appt org-agenda-ignore-properties) (org-refresh-properties "APPT_WARNTIME" 'org-appt-warntime)) (setq org-todo-keywords-for-agenda (append org-todo-keywords-for-agenda org-todo-keywords-1)) @@ -18182,31 +18831,32 @@ When a buffer is unmodified, it is just killed. When modified, it is saved (append org-done-keywords-for-agenda org-done-keywords)) (setq org-todo-keyword-alist-for-agenda (append org-todo-keyword-alist-for-agenda org-todo-key-alist)) - (setq org-drawers-for-agenda - (append org-drawers-for-agenda org-drawers)) (setq org-tag-alist-for-agenda (org-uniquify (append org-tag-alist-for-agenda - org-tag-alist - org-tag-persistent-alist))) - (if org-group-tags - (setq org-tag-groups-alist-for-agenda - (org-uniquify-alist - (append org-tag-groups-alist-for-agenda org-tag-groups-alist)))) + org-current-tag-alist))) + ;; Merge current file's tag groups into global + ;; `org-tag-groups-alist-for-agenda'. + (when org-group-tags + (dolist (alist org-tag-groups-alist) + (let ((old (assoc (car alist) org-tag-groups-alist-for-agenda))) + (if old + (setcdr old (org-uniquify (append (cdr old) (cdr alist)))) + (push alist org-tag-groups-alist-for-agenda))))) (org-with-silent-modifications (save-excursion (remove-text-properties (point-min) (point-max) pall) (when org-agenda-skip-archived-trees (goto-char (point-min)) (while (re-search-forward rea nil t) - (if (org-at-heading-p t) - (add-text-properties (point-at-bol) (org-end-of-subtree t) pa)))) + (when (org-at-heading-p t) + (add-text-properties (point-at-bol) (org-end-of-subtree t) pa)))) (goto-char (point-min)) - (setq re (format org-heading-keyword-regexp-format - org-comment-string)) + (setq re (format "^\\*+ .*\\<%s\\>" org-comment-string)) (while (re-search-forward re nil t) - (add-text-properties - (match-beginning 0) (org-end-of-subtree t) pc)))) + (when (save-match-data (org-in-commented-heading-p t)) + (add-text-properties + (match-beginning 0) (org-end-of-subtree t) pc))))) (goto-char pos))))) (setq org-todo-keywords-for-agenda (org-uniquify org-todo-keywords-for-agenda)) @@ -18223,7 +18873,7 @@ When a buffer is unmodified, it is just killed. When modified, it is saved (org-defkey org-cdlatex-mode-map "^" 'org-cdlatex-underscore-caret) (org-defkey org-cdlatex-mode-map "`" 'cdlatex-math-symbol) (org-defkey org-cdlatex-mode-map "'" 'org-cdlatex-math-modify) -(org-defkey org-cdlatex-mode-map "\C-c{" 'cdlatex-environment) +(org-defkey org-cdlatex-mode-map "\C-c{" 'org-cdlatex-environment-indent) (defvar org-cdlatex-texmathp-advice-is-done nil "Flag remembering if we have applied the advice to texmathp already.") @@ -18231,7 +18881,7 @@ When a buffer is unmodified, it is just killed. When modified, it is saved (define-minor-mode org-cdlatex-mode "Toggle the minor `org-cdlatex-mode'. This mode supports entering LaTeX environment and math in LaTeX fragments -in Org-mode. +in Org mode. \\{org-cdlatex-mode-map}" nil " OCDL" nil (when org-cdlatex-mode @@ -18241,11 +18891,11 @@ in Org-mode. (unless org-cdlatex-texmathp-advice-is-done (setq org-cdlatex-texmathp-advice-is-done t) (defadvice texmathp (around org-math-always-on activate) - "Always return t in org-mode buffers. + "Always return t in Org buffers. This is because we want to insert math symbols without dollars even outside -the LaTeX math segments. If Orgmode thinks that point is actually inside -an embedded LaTeX fragment, let texmathp do its job. -\\[org-cdlatex-mode-map]" +the LaTeX math segments. If Org mode thinks that point is actually inside +an embedded LaTeX fragment, let `texmathp' do its job. +`\\[org-cdlatex-mode-map]'" (interactive) (let (p) (cond @@ -18257,8 +18907,8 @@ an embedded LaTeX fragment, let texmathp do its job. (let ((p (org-inside-LaTeX-fragment-p))) (if (and p (member (car p) (plist-get org-format-latex-options :matchers))) (setq ad-return-value t - texmathp-why '("Org-mode embedded math" . 0)) - (if p ad-do-it))))))))) + texmathp-why '("Org mode embedded math" . 0)) + (when p ad-do-it))))))))) (defun turn-on-org-cdlatex () "Unconditionally turn on `org-cdlatex-mode'." @@ -18283,7 +18933,7 @@ It makes sense to do so if `org-cdlatex-mode' is active and if the cursor is (cdlatex-tab) t) ((org-inside-LaTeX-fragment-p) (cdlatex-tab) t)))) -(defun org-cdlatex-underscore-caret (&optional arg) +(defun org-cdlatex-underscore-caret (&optional _arg) "Execute `cdlatex-sub-superscript' in LaTeX fragments. Revert to the normal definition outside of these fragments." (interactive "P") @@ -18292,7 +18942,7 @@ Revert to the normal definition outside of these fragments." (let (org-cdlatex-mode) (call-interactively (key-binding (vector last-input-event)))))) -(defun org-cdlatex-math-modify (&optional arg) +(defun org-cdlatex-math-modify (&optional _arg) "Execute `cdlatex-math-modify' in LaTeX fragments. Revert to the normal definition outside of these fragments." (interactive "P") @@ -18301,21 +18951,66 @@ Revert to the normal definition outside of these fragments." (let (org-cdlatex-mode) (call-interactively (key-binding (vector last-input-event)))))) +(defun org-cdlatex-environment-indent (&optional environment item) + "Execute `cdlatex-environment' and indent the inserted environment. + +ENVIRONMENT and ITEM are passed to `cdlatex-environment'. + +The inserted environment is indented to current indentation +unless point is at the beginning of the line, in which the +environment remains unintended." + (interactive) + ;; cdlatex-environment always return nil. Therefore, capture output + ;; first and determine if an environment was selected. + (let* ((beg (point-marker)) + (end (copy-marker (point) t)) + (inserted (progn + (ignore-errors (cdlatex-environment environment item)) + (< beg end))) + ;; Figure out how many lines to move forward after the + ;; environment has been inserted. + (lines (when inserted + (save-excursion + (- (cl-loop while (< beg (point)) + with x = 0 + do (forward-line -1) + (cl-incf x) + finally return x) + (if (progn (goto-char beg) + (and (progn (skip-chars-forward " \t") (eolp)) + (progn (skip-chars-backward " \t") (bolp)))) + 1 0))))) + (env (org-trim (delete-and-extract-region beg end)))) + (when inserted + ;; Get indentation of next line unless at column 0. + (let ((ind (if (bolp) 0 + (save-excursion + (org-return-indent) + (prog1 (org-get-indentation) + (when (progn (skip-chars-forward " \t") (eolp)) + (delete-region beg (point))))))) + (bol (progn (skip-chars-backward " \t") (bolp)))) + ;; Insert a newline before environment unless at column zero + ;; to "escape" the current line. Insert a newline if + ;; something is one the same line as \end{ENVIRONMENT}. + (insert + (concat (unless bol "\n") env + (when (and (skip-chars-forward " \t") (not (eolp))) "\n"))) + (unless (zerop ind) + (save-excursion + (goto-char beg) + (while (< (point) end) + (unless (eolp) (indent-line-to ind)) + (forward-line)))) + (goto-char beg) + (forward-line lines) + (indent-line-to ind))) + (set-marker beg nil) + (set-marker end nil))) ;;;; LaTeX fragments -(defvar org-latex-regexps - '(("begin" "^[ \t]*\\(\\\\begin{\\([a-zA-Z0-9\\*]+\\)[^\000]+?\\\\end{\\2}\\)" 1 t) - ;; ("$" "\\([ (]\\|^\\)\\(\\(\\([$]\\)\\([^ \r\n,.$].*?\\(\n.*?\\)\\{0,5\\}[^ \r\n,.$]\\)\\4\\)\\)\\([ .,?;:'\")]\\|$\\)" 2 nil) - ;; \000 in the following regex is needed for org-inside-LaTeX-fragment-p - ("$1" "\\([^$]\\|^\\)\\(\\$[^ \r\n,;.$]\\$\\)\\([- .,?;:'\")\000]\\|$\\)" 2 nil) - ("$" "\\([^$]\\|^\\)\\(\\(\\$\\([^ \r\n,;.$][^$\n\r]*?\\(\n[^$\n\r]*?\\)\\{0,2\\}[^ \r\n,.$]\\)\\$\\)\\)\\([- .,?;:'\")\000]\\|$\\)" 2 nil) - ("\\(" "\\\\([^\000]*?\\\\)" 0 nil) - ("\\[" "\\\\\\[[^\000]*?\\\\\\]" 0 nil) - ("$$" "\\$\\$[^\000]*?\\$\\$" 0 nil)) - "Regular expressions for matching embedded LaTeX.") - (defun org-inside-LaTeX-fragment-p () "Test if point is inside a LaTeX fragment. I.e. after a \\begin, \\(, \\[, $, or $$, without the corresponding closing @@ -18358,7 +19053,7 @@ looks only before point, not after." (while (re-search-backward "\\$\\$" lim t) (setq dd-on (not dd-on))) (goto-char pos) - (if dd-on (cons "$$" m)))))) + (when dd-on (cons "$$" m)))))) (defun org-inside-latex-macro-p () "Is point inside a LaTeX macro or its arguments?" @@ -18366,179 +19061,226 @@ looks only before point, not after." (org-in-regexp "\\\\[a-zA-Z]+\\*?\\(\\(\\[[^][\n{}]*\\]\\)\\|\\({[^{}\n]*}\\)\\)*"))) -(defvar org-latex-fragment-image-overlays nil - "List of overlays carrying the images of latex fragments.") -(make-variable-buffer-local 'org-latex-fragment-image-overlays) +(defun org--format-latex-make-overlay (beg end image &optional imagetype) + "Build an overlay between BEG and END using IMAGE file. +Argument IMAGETYPE is the extension of the displayed image, +as a string. It defaults to \"png\"." + (let ((ov (make-overlay beg end)) + (imagetype (or (intern imagetype) 'png))) + (overlay-put ov 'org-overlay-type 'org-latex-overlay) + (overlay-put ov 'evaporate t) + (overlay-put ov + 'modification-hooks + (list (lambda (o _flag _beg _end &optional _l) + (delete-overlay o)))) + (overlay-put ov + 'display + (list 'image :type imagetype :file image :ascent 'center)))) + +(defun org--list-latex-overlays (&optional beg end) + "List all Org LaTeX overlays in current buffer. +Limit to overlays between BEG and END when those are provided." + (cl-remove-if-not + (lambda (o) (eq (overlay-get o 'org-overlay-type) 'org-latex-overlay)) + (overlays-in (or beg (point-min)) (or end (point-max))))) + +(defun org-remove-latex-fragment-image-overlays (&optional beg end) + "Remove all overlays with LaTeX fragment images in current buffer. +When optional arguments BEG and END are non-nil, remove all +overlays between them instead. Return a non-nil value when some +overlays were removed, nil otherwise." + (let ((overlays (org--list-latex-overlays beg end))) + (mapc #'delete-overlay overlays) + overlays)) + +(defun org-toggle-latex-fragment (&optional arg) + "Preview the LaTeX fragment at point, or all locally or globally. -(defun org-remove-latex-fragment-image-overlays () - "Remove all overlays with LaTeX fragment images in current buffer." - (mapc 'delete-overlay org-latex-fragment-image-overlays) - (setq org-latex-fragment-image-overlays nil)) +If the cursor is on a LaTeX fragment, create the image and overlay +it over the source code, if there is none. Remove it otherwise. +If there is no fragment at point, display all fragments in the +current section. -(defun org-preview-latex-fragment (&optional subtree) - "Preview the LaTeX fragment at point, or all locally or globally. -If the cursor is in a LaTeX fragment, create the image and overlay -it over the source code. If there is no fragment at point, display -all fragments in the current text, from one headline to the next. With -prefix SUBTREE, display all fragments in the current subtree. With a -double prefix arg \\[universal-argument] \\[universal-argument], or when \ -the cursor is before the first headline, -display all fragments in the buffer. -The images can be removed again with \\[org-ctrl-c-ctrl-c]." +With prefix ARG, preview or clear image for all fragments in the +current subtree or in the whole buffer when used before the first +headline. With a prefix ARG `\\[universal-argument] \ +\\[universal-argument]' preview or clear images +for all fragments in the buffer." (interactive "P") - (unless buffer-file-name - (user-error "Can't preview LaTeX fragment in a non-file buffer")) (when (display-graphic-p) - (org-remove-latex-fragment-image-overlays) - (save-excursion - (save-restriction - (let (beg end at msg) + (catch 'exit + (save-excursion + (let (beg end msg) (cond - ((or (equal subtree '(16)) - (not (save-excursion - (re-search-backward org-outline-regexp-bol nil t)))) - (setq beg (point-min) end (point-max) - msg "Creating images for buffer...%s")) - ((equal subtree '(4)) - (org-back-to-heading) - (setq beg (point) end (org-end-of-subtree t) - msg "Creating images for subtree...%s")) + ((or (equal arg '(16)) + (and (equal arg '(4)) + (org-with-limited-levels (org-before-first-heading-p)))) + (if (org-remove-latex-fragment-image-overlays) + (progn (message "LaTeX fragments images removed from buffer") + (throw 'exit nil)) + (setq msg "Creating images for buffer..."))) + ((equal arg '(4)) + (org-with-limited-levels (org-back-to-heading t)) + (setq beg (point)) + (setq end (progn (org-end-of-subtree t) (point))) + (if (org-remove-latex-fragment-image-overlays beg end) + (progn + (message "LaTeX fragment images removed from subtree") + (throw 'exit nil)) + (setq msg "Creating images for subtree..."))) + ((let ((datum (org-element-context))) + (when (memq (org-element-type datum) + '(latex-environment latex-fragment)) + (setq beg (org-element-property :begin datum)) + (setq end (org-element-property :end datum)) + (if (org-remove-latex-fragment-image-overlays beg end) + (progn (message "LaTeX fragment image removed") + (throw 'exit nil)) + (setq msg "Creating image..."))))) (t - (if (setq at (org-inside-LaTeX-fragment-p)) - (goto-char (max (point-min) (- (cdr at) 2))) - (org-back-to-heading)) - (setq beg (point) end (progn (outline-next-heading) (point)) - msg (if at "Creating image...%s" - "Creating images for entry...%s")))) - (message msg "") - (narrow-to-region beg end) - (goto-char beg) - (org-format-latex - (concat org-latex-preview-ltxpng-directory (file-name-sans-extension - (file-name-nondirectory - buffer-file-name))) - default-directory 'overlays msg at 'forbuffer - org-latex-create-formula-image-program) - (message msg "done. Use `C-c C-c' to remove images.")))))) - -(defun org-format-latex (prefix &optional dir overlays msg at - forbuffer processing-type) - "Replace LaTeX fragments with links to an image, and produce images. + (org-with-limited-levels + (setq beg (if (org-at-heading-p) (line-beginning-position) + (outline-previous-heading) + (point))) + (setq end (progn (outline-next-heading) (point))) + (if (org-remove-latex-fragment-image-overlays beg end) + (progn + (message "LaTeX fragment images removed from section") + (throw 'exit nil)) + (setq msg "Creating images for section..."))))) + (let ((file (buffer-file-name (buffer-base-buffer)))) + (org-format-latex + (concat org-preview-latex-image-directory "org-ltximg") + beg end + ;; Emacs cannot overlay images from remote hosts. Create + ;; it in `temporary-file-directory' instead. + (if (or (not file) (file-remote-p file)) + temporary-file-directory + default-directory) + 'overlays msg 'forbuffer org-preview-latex-default-process)) + (message (concat msg "done"))))))) + +(defun org-format-latex + (prefix &optional beg end dir overlays msg forbuffer processing-type) + "Replace LaTeX fragments with links to an image. + +The function takes care of creating the replacement image. + +Only consider fragments between BEG and END when those are +provided. + +When optional argument OVERLAYS is non-nil, display the image on +top of the fragment instead of replacing it. + +PROCESSING-TYPE is the conversion method to use, as a symbol. + Some of the options can be changed using the variable -`org-format-latex-options'." - (if (and overlays (fboundp 'clear-image-cache)) (clear-image-cache)) - (let* ((prefixnodir (file-name-nondirectory prefix)) - (absprefix (expand-file-name prefix dir)) - (todir (file-name-directory absprefix)) - (opt org-format-latex-options) - (optnew org-format-latex-options) - (matchers (plist-get opt :matchers)) - (re-list org-latex-regexps) - (cnt 0) txt hash link beg end re checkdir - string - m n block-type block linkfile movefile ov) - ;; Check the different regular expressions - (dolist (e re-list) - (setq m (car e) re (nth 1 e) n (nth 2 e) block-type (nth 3 e) - block (if block-type "\n\n" "")) - (when (member m matchers) - (goto-char (point-min)) - (while (re-search-forward re nil t) - (when (and (or (not at) (equal (cdr at) (match-beginning n))) - (or (not overlays) - (not (eq (get-char-property (match-beginning n) - 'org-overlay-type) - 'org-latex-overlay)))) - (cond - ((eq processing-type 'verbatim)) - ((eq processing-type 'mathjax) - ;; Prepare for MathJax processing. - (setq string (match-string n)) - (when (member m '("$" "$1")) - (save-excursion - (delete-region (match-beginning n) (match-end n)) - (goto-char (match-beginning n)) - (insert (concat "\\(" (substring string 1 -1) "\\)"))))) - ((or (eq processing-type 'dvipng) - (eq processing-type 'imagemagick)) - ;; Process to an image. - (setq txt (match-string n) - beg (match-beginning n) end (match-end n) - cnt (1+ cnt)) - (let ((face (face-at-point)) - (fg (plist-get opt :foreground)) - (bg (plist-get opt :background)) - ;; Ensure full list is printed. - print-length print-level) - (when forbuffer - ;; Get the colors from the face at point. +`org-format-latex-options', which see." + (when (and overlays (fboundp 'clear-image-cache)) (clear-image-cache)) + (unless (eq processing-type 'verbatim) + (let* ((math-regexp "\\$\\|\\\\[([]\\|^[ \t]*\\\\begin{[A-Za-z0-9*]+}") + (cnt 0) + checkdir-flag) + (goto-char (or beg (point-min))) + ;; Optimize overlay creation: (info "(elisp) Managing Overlays"). + (when (and overlays (memq processing-type '(dvipng imagemagick))) + (overlay-recenter (or end (point-max)))) + (while (re-search-forward math-regexp end t) + (unless (and overlays + (eq (get-char-property (point) 'org-overlay-type) + 'org-latex-overlay)) + (let* ((context (org-element-context)) + (type (org-element-type context))) + (when (memq type '(latex-environment latex-fragment)) + (let ((block-type (eq type 'latex-environment)) + (value (org-element-property :value context)) + (beg (org-element-property :begin context)) + (end (save-excursion + (goto-char (org-element-property :end context)) + (skip-chars-backward " \r\t\n") + (point)))) + (cond + ((eq processing-type 'mathjax) + ;; Prepare for MathJax processing. + (if (not (string-match "\\`\\$\\$?" value)) + (goto-char end) + (delete-region beg end) + (if (string= (match-string 0 value) "$$") + (insert "\\[" (substring value 2 -2) "\\]") + (insert "\\(" (substring value 1 -1) "\\)")))) + ((assq processing-type org-preview-latex-process-alist) + ;; Process to an image. + (cl-incf cnt) (goto-char beg) - (when (eq fg 'auto) - (setq fg (face-attribute face :foreground nil 'default))) - (when (eq bg 'auto) - (setq bg (face-attribute face :background nil 'default))) - (setq optnew (copy-sequence opt)) - (plist-put optnew :foreground fg) - (plist-put optnew :background bg)) - (setq hash (sha1 (prin1-to-string - (list org-format-latex-header - org-latex-default-packages-alist - org-latex-packages-alist - org-format-latex-options - forbuffer txt fg bg))) - linkfile (format "%s_%s.png" prefix hash) - movefile (format "%s_%s.png" absprefix hash))) - (setq link (concat block "[[file:" linkfile "]]" block)) - (if msg (message msg cnt)) - (goto-char beg) - (unless checkdir ; Ensure the directory exists. - (setq checkdir t) - (or (file-directory-p todir) (make-directory todir t))) - (unless (file-exists-p movefile) - (org-create-formula-image - txt movefile optnew forbuffer processing-type)) - (if overlays - (progn - (mapc (lambda (o) - (if (eq (overlay-get o 'org-overlay-type) - 'org-latex-overlay) - (delete-overlay o))) - (overlays-in beg end)) - (setq ov (make-overlay beg end)) - (overlay-put ov 'org-overlay-type 'org-latex-overlay) - (if (featurep 'xemacs) + (let* ((processing-info + (cdr (assq processing-type org-preview-latex-process-alist))) + (face (face-at-point)) + ;; Get the colors from the face at point. + (fg + (let ((color (plist-get org-format-latex-options + :foreground))) + (if (and forbuffer (eq color 'auto)) + (face-attribute face :foreground nil 'default) + color))) + (bg + (let ((color (plist-get org-format-latex-options + :background))) + (if (and forbuffer (eq color 'auto)) + (face-attribute face :background nil 'default) + color))) + (hash (sha1 (prin1-to-string + (list org-format-latex-header + org-latex-default-packages-alist + org-latex-packages-alist + org-format-latex-options + forbuffer value fg bg)))) + (imagetype (or (plist-get processing-info :image-output-type) "png")) + (absprefix (expand-file-name prefix dir)) + (linkfile (format "%s_%s.%s" prefix hash imagetype)) + (movefile (format "%s_%s.%s" absprefix hash imagetype)) + (sep (and block-type "\n\n")) + (link (concat sep "[[file:" linkfile "]]" sep)) + (options + (org-combine-plists + org-format-latex-options + `(:foreground ,fg :background ,bg)))) + (when msg (message msg cnt)) + (unless checkdir-flag ; Ensure the directory exists. + (setq checkdir-flag t) + (let ((todir (file-name-directory absprefix))) + (unless (file-directory-p todir) + (make-directory todir t)))) + (unless (file-exists-p movefile) + (org-create-formula-image + value movefile options forbuffer processing-type)) + (if overlays (progn - (overlay-put ov 'invisible t) - (overlay-put - ov 'end-glyph - (make-glyph (vector 'png :file movefile)))) - (overlay-put - ov 'display - (list 'image :type 'png :file movefile :ascent 'center))) - (push ov org-latex-fragment-image-overlays) - (goto-char end)) - (delete-region beg end) - (insert (org-add-props link - (list 'org-latex-src - (replace-regexp-in-string - "\"" "" txt) - 'org-latex-src-embed-type - (if block-type 'paragraph 'character)))))) - ((eq processing-type 'mathml) - ;; Process to MathML - (unless (save-match-data (org-format-latex-mathml-available-p)) - (user-error "LaTeX to MathML converter not configured")) - (setq txt (match-string n) - beg (match-beginning n) end (match-end n) - cnt (1+ cnt)) - (if msg (message msg cnt)) - (goto-char beg) - (delete-region beg end) - (insert (org-format-latex-as-mathml - txt block-type prefix dir))) - (t - (error "Unknown conversion type %s for LaTeX fragments" - processing-type))))))))) + (dolist (o (overlays-in beg end)) + (when (eq (overlay-get o 'org-overlay-type) + 'org-latex-overlay) + (delete-overlay o))) + (org--format-latex-make-overlay beg end movefile imagetype) + (goto-char end)) + (delete-region beg end) + (insert + (org-add-props link + (list 'org-latex-src + (replace-regexp-in-string "\"" "" value) + 'org-latex-src-embed-type + (if block-type 'paragraph 'character))))))) + ((eq processing-type 'mathml) + ;; Process to MathML. + (unless (org-format-latex-mathml-available-p) + (user-error "LaTeX to MathML converter not configured")) + (cl-incf cnt) + (when msg (message msg cnt)) + (goto-char beg) + (delete-region beg end) + (insert (org-format-latex-as-mathml + value block-type prefix dir))) + (t + (error "Unknown conversion process %s for LaTeX fragments" + processing-type))))))))))) (defun org-create-math-formula (latex-frag &optional mathml-file) "Convert LATEX-FRAG to MathML and store it in MATHML-FILE. @@ -18553,20 +19295,25 @@ inspection." (buffer-substring-no-properties (region-beginning) (region-end))))) (read-string "LaTeX Fragment: " frag nil frag)))) - (unless latex-frag (error "Invalid LaTeX fragment")) - (let* ((tmp-in-file (file-relative-name - (make-temp-name (expand-file-name "ltxmathml-in")))) - (ignore (write-region latex-frag nil tmp-in-file)) + (unless latex-frag (user-error "Invalid LaTeX fragment")) + (let* ((tmp-in-file + (let ((file (file-relative-name + (make-temp-name (expand-file-name "ltxmathml-in"))))) + (write-region latex-frag nil file) + file)) (tmp-out-file (file-relative-name (make-temp-name (expand-file-name "ltxmathml-out")))) (cmd (format-spec org-latex-to-mathml-convert-command - `((?j . ,(shell-quote-argument - (expand-file-name org-latex-to-mathml-jar-file))) + `((?j . ,(and org-latex-to-mathml-jar-file + (shell-quote-argument + (expand-file-name + org-latex-to-mathml-jar-file)))) (?I . ,(shell-quote-argument tmp-in-file)) + (?i . ,latex-frag) (?o . ,(shell-quote-argument tmp-out-file))))) mathml shell-command-output) - (when (org-called-interactively-p 'any) + (when (called-interactively-p 'any) (unless (org-format-latex-mathml-available-p) (user-error "LaTeX to MathML converter not configured"))) (message "Running %s" cmd) @@ -18576,11 +19323,10 @@ inspection." (with-current-buffer (find-file-noselect tmp-out-file t) (goto-char (point-min)) (when (re-search-forward - (concat - (regexp-quote - "") - "\\(.\\|\n\\)*" - (regexp-quote "")) nil t) + (format "]*?%s[^>]*?>\\(.\\|\n\\)*" + (regexp-quote + "xmlns=\"http://www.w3.org/1998/Math/MathML\"")) + nil t) (prog1 (match-string 0) (kill-buffer)))))) (cond (mathml @@ -18588,7 +19334,7 @@ inspection." (concat "\n" mathml)) (when mathml-file (write-region mathml nil mathml-file)) - (when (org-called-interactively-p 'any) + (when (called-interactively-p 'any) (message mathml))) ((message "LaTeX to MathML conversion failed") (message shell-command-output))) @@ -18627,186 +19373,117 @@ inspection." ;; Failed conversion. Return the LaTeX fragment verbatim latex-frag))) -(defun org-create-formula-image (string tofile options buffer &optional type) - "Create an image from LaTeX source using dvipng or convert. -This function calls either `org-create-formula-image-with-dvipng' -or `org-create-formula-image-with-imagemagick' depending on the -value of `org-latex-create-formula-image-program' or on the value -of the optional TYPE variable. - -Note: ultimately these two function should be combined as they -share a good deal of logic." - (org-check-external-command - "latex" "needed to convert LaTeX fragments to images") - (funcall - (case (or type org-latex-create-formula-image-program) - ('dvipng - (org-check-external-command - "dvipng" "needed to convert LaTeX fragments to images") - #'org-create-formula-image-with-dvipng) - ('imagemagick - (org-check-external-command - "convert" "you need to install imagemagick") - #'org-create-formula-image-with-imagemagick) - (t (error - "Invalid value of `org-latex-create-formula-image-program'"))) - string tofile options buffer)) - -(declare-function org-export-get-backend "ox" (name)) -(declare-function org-export--get-global-options "ox" (&optional backend)) -(declare-function org-export--get-inbuffer-options "ox" (&optional backend)) -(declare-function org-latex-guess-inputenc "ox-latex" (header)) -(declare-function org-latex-guess-babel-language "ox-latex" (header info)) -(defun org-create-formula--latex-header () - "Return LaTeX header appropriate for previewing a LaTeX snippet." - (let ((info (org-combine-plists (org-export--get-global-options - (org-export-get-backend 'latex)) - (org-export--get-inbuffer-options - (org-export-get-backend 'latex))))) - (org-latex-guess-babel-language - (org-latex-guess-inputenc - (org-splice-latex-header - org-format-latex-header - org-latex-default-packages-alist - org-latex-packages-alist t - (plist-get info :latex-header))) - info))) - -;; This function borrows from Ganesh Swami's latex2png.el -(defun org-create-formula-image-with-dvipng (string tofile options buffer) - "This calls dvipng." - (require 'ox-latex) - (let* ((tmpdir (if (featurep 'xemacs) - (temp-directory) - temporary-file-directory)) +(defun org--get-display-dpi () + "Get the DPI of the display. +The function assumes that the display has the same pixel width in +the horizontal and vertical directions." + (if (display-graphic-p) + (round (/ (display-pixel-height) + (/ (display-mm-height) 25.4))) + (error "Attempt to calculate the dpi of a non-graphic display"))) + +(defun org-create-formula-image + (string tofile options buffer &optional processing-type) + "Create an image from LaTeX source using external processes. + +The LaTeX STRING is saved to a temporary LaTeX file, then +converted to an image file by process PROCESSING-TYPE defined in +`org-preview-latex-process-alist'. A nil value defaults to +`org-preview-latex-default-process'. + +The generated image file is eventually moved to TOFILE. + +The OPTIONS argument controls the size, foreground color and +background color of the generated image. + +When BUFFER non-nil, this function is used for LaTeX previewing. +Otherwise, it is used to deal with LaTeX snippets showed in +a HTML file." + (let* ((processing-type (or processing-type + org-preview-latex-default-process)) + (processing-info + (cdr (assq processing-type org-preview-latex-process-alist))) + (programs (plist-get processing-info :programs)) + (error-message (or (plist-get processing-info :message) "")) + (use-xcolor (plist-get processing-info :use-xcolor)) + (image-input-type (plist-get processing-info :image-input-type)) + (image-output-type (plist-get processing-info :image-output-type)) + (post-clean (or (plist-get processing-info :post-clean) + '(".dvi" ".xdv" ".pdf" ".tex" ".aux" ".log" + ".svg" ".png" ".jpg" ".jpeg" ".out"))) + (latex-header + (or (plist-get processing-info :latex-header) + (org-latex-make-preamble + (org-export-get-environment (org-export-get-backend 'latex)) + org-format-latex-header + 'snippet))) + (latex-compiler (plist-get processing-info :latex-compiler)) + (image-converter (plist-get processing-info :image-converter)) + (tmpdir temporary-file-directory) (texfilebase (make-temp-name (expand-file-name "orgtex" tmpdir))) (texfile (concat texfilebase ".tex")) - (dvifile (concat texfilebase ".dvi")) - (pngfile (concat texfilebase ".png")) - (fnh (if (featurep 'xemacs) - (font-height (face-font 'default)) - (face-attribute 'default :height nil))) - (scale (or (plist-get options (if buffer :scale :html-scale)) 1.0)) - (dpi (number-to-string (* scale (floor (* 0.9 (if buffer fnh 140.)))))) + (image-size-adjust (or (plist-get processing-info :image-size-adjust) + '(1.0 . 1.0))) + (scale (* (if buffer (car image-size-adjust) (cdr image-size-adjust)) + (or (plist-get options (if buffer :scale :html-scale)) 1.0))) + (dpi (* scale (if buffer (org--get-display-dpi) 140.0))) (fg (or (plist-get options (if buffer :foreground :html-foreground)) "Black")) (bg (or (plist-get options (if buffer :background :html-background)) - "Transparent"))) - (if (eq fg 'default) (setq fg (org-dvipng-color :foreground)) - (unless (string= fg "Transparent") (setq fg (org-dvipng-color-format fg)))) - (if (eq bg 'default) (setq bg (org-dvipng-color :background)) - (unless (string= bg "Transparent") (setq bg (org-dvipng-color-format bg)))) - (let ((latex-header (org-create-formula--latex-header))) + "Transparent")) + (log-buf (get-buffer-create "*Org Preview LaTeX Output*")) + (resize-mini-windows nil)) ;Fix Emacs flicker when creating image. + (dolist (program programs) + (org-check-external-command program error-message)) + (if use-xcolor + (progn (if (eq fg 'default) + (setq fg (org-latex-color :foreground)) + (setq fg (org-latex-color-format fg))) + (if (eq bg 'default) + (setq bg (org-latex-color :background)) + (setq bg (org-latex-color-format + (if (string= bg "Transparent") "white" bg)))) + (with-temp-file texfile + (insert latex-header) + (insert "\n\\begin{document}\n" + "\\definecolor{fg}{rgb}{" fg "}\n" + "\\definecolor{bg}{rgb}{" bg "}\n" + "\n\\pagecolor{bg}\n" + "\n{\\color{fg}\n" + string + "\n}\n" + "\n\\end{document}\n"))) + (if (eq fg 'default) + (setq fg (org-dvipng-color :foreground)) + (unless (string= fg "Transparent") + (setq fg (org-dvipng-color-format fg)))) + (if (eq bg 'default) + (setq bg (org-dvipng-color :background)) + (unless (string= bg "Transparent") + (setq bg (org-dvipng-color-format bg)))) (with-temp-file texfile (insert latex-header) (insert "\n\\begin{document}\n" string "\n\\end{document}\n"))) - (let ((dir default-directory)) - (condition-case nil - (progn - (cd tmpdir) - (call-process "latex" nil nil nil texfile)) - (error nil)) - (cd dir)) - (if (not (file-exists-p dvifile)) - (progn (message "Failed to create dvi file from %s" texfile) nil) - (condition-case nil - (if (featurep 'xemacs) - (call-process "dvipng" nil nil nil - "-fg" fg "-bg" bg - "-T" "tight" - "-o" pngfile - dvifile) - (call-process "dvipng" nil nil nil - "-fg" fg "-bg" bg - "-D" dpi - ;;"-x" scale "-y" scale - "-T" "tight" - "-o" pngfile - dvifile)) - (error nil)) - (if (not (file-exists-p pngfile)) - (if org-format-latex-signal-error - (error "Failed to create png file from %s" texfile) - (message "Failed to create png file from %s" texfile) - nil) - ;; Use the requested file name and clean up - (copy-file pngfile tofile 'replace) - (loop for e in '(".dvi" ".tex" ".aux" ".log" ".png" ".out") do - (if (file-exists-p (concat texfilebase e)) - (delete-file (concat texfilebase e)))) - pngfile)))) - -(declare-function org-latex-compile "ox-latex" (texfile &optional snippet)) -(defun org-create-formula-image-with-imagemagick (string tofile options buffer) - "This calls convert, which is included into imagemagick." - (require 'ox-latex) - (let* ((tmpdir (if (featurep 'xemacs) - (temp-directory) - temporary-file-directory)) - (texfilebase (make-temp-name - (expand-file-name "orgtex" tmpdir))) - (texfile (concat texfilebase ".tex")) - (pdffile (concat texfilebase ".pdf")) - (pngfile (concat texfilebase ".png")) - (fnh (if (featurep 'xemacs) - (font-height (face-font 'default)) - (face-attribute 'default :height nil))) - (scale (or (plist-get options (if buffer :scale :html-scale)) 1.0)) - (dpi (number-to-string (* scale (floor (if buffer fnh 120.))))) - (fg (or (plist-get options (if buffer :foreground :html-foreground)) - "black")) - (bg (or (plist-get options (if buffer :background :html-background)) - "white"))) - (if (eq fg 'default) (setq fg (org-latex-color :foreground)) - (setq fg (org-latex-color-format fg))) - (if (eq bg 'default) (setq bg (org-latex-color :background)) - (setq bg (org-latex-color-format - (if (string= bg "Transparent") "white" bg)))) - (let ((latex-header (org-create-formula--latex-header))) - (with-temp-file texfile - (insert latex-header) - (insert "\n\\begin{document}\n" - "\\definecolor{fg}{rgb}{" fg "}\n" - "\\definecolor{bg}{rgb}{" bg "}\n" - "\n\\pagecolor{bg}\n" - "\n{\\color{fg}\n" - string - "\n}\n" - "\n\\end{document}\n"))) - (org-latex-compile texfile t) - (if (not (file-exists-p pdffile)) - (progn (message "Failed to create pdf file from %s" texfile) nil) - (condition-case nil - (if (featurep 'xemacs) - (call-process "convert" nil nil nil - "-density" "96" - "-trim" - "-antialias" - pdffile - "-quality" "100" - ;; "-sharpen" "0x1.0" - pngfile) - (call-process "convert" nil nil nil - "-density" dpi - "-trim" - "-antialias" - pdffile - "-quality" "100" - ;; "-sharpen" "0x1.0" - pngfile)) - (error nil)) - (if (not (file-exists-p pngfile)) - (if org-format-latex-signal-error - (error "Failed to create png file from %s" texfile) - (message "Failed to create png file from %s" texfile) - nil) - ;; Use the requested file name and clean up - (copy-file pngfile tofile 'replace) - (loop for e in '(".pdf" ".tex" ".aux" ".log" ".png") do - (if (file-exists-p (concat texfilebase e)) - (delete-file (concat texfilebase e)))) - pngfile)))) + + (let* ((err-msg (format "Please adjust '%s' part of \ +`org-preview-latex-process-alist'." + processing-type)) + (image-input-file + (org-compile-file + texfile latex-compiler image-input-type err-msg log-buf)) + (image-output-file + (org-compile-file + image-input-file image-converter image-output-type err-msg log-buf + `((?F . ,(shell-quote-argument fg)) + (?B . ,(shell-quote-argument bg)) + (?D . ,(shell-quote-argument (format "%s" dpi))) + (?S . ,(shell-quote-argument (format "%s" (/ dpi 140.0)))))))) + (copy-file image-output-file tofile 'replace) + (dolist (e post-clean) + (when (file-exists-p (concat texfilebase e)) + (delete-file (concat texfilebase e)))) + image-output-file))) (defun org-splice-latex-header (tpl def-pkg pkg snippets-p &optional extra) "Fill a LaTeX header template TPL. @@ -18830,22 +19507,22 @@ SNIPPETS-P indicates if this is run to create snippet images for HTML." (setq rpl (if (or (match-end 1) (not def-pkg)) "" (org-latex-packages-to-string def-pkg snippets-p t)) tpl (replace-match rpl t t tpl)) - (if def-pkg (setq end (org-latex-packages-to-string def-pkg snippets-p)))) + (when def-pkg (setq end (org-latex-packages-to-string def-pkg snippets-p)))) (if (string-match "\\[\\(NO-\\)?PACKAGES\\][ \t]*\n?" tpl) (setq rpl (if (or (match-end 1) (not pkg)) "" (org-latex-packages-to-string pkg snippets-p t)) tpl (replace-match rpl t t tpl)) - (if pkg (setq end - (concat end "\n" - (org-latex-packages-to-string pkg snippets-p))))) + (when pkg (setq end + (concat end "\n" + (org-latex-packages-to-string pkg snippets-p))))) (if (string-match "\\[\\(NO-\\)?EXTRA\\][ \t]*\n?" tpl) (setq rpl (if (or (match-end 1) (not extra)) "" (concat extra "\n")) tpl (replace-match rpl t t tpl)) - (if (and extra (string-match "\\S-" extra)) - (setq end (concat end "\n" extra)))) + (when (and extra (string-match "\\S-" extra)) + (setq end (concat end "\n" extra)))) (if (string-match "\\S-" end) (concat tpl "\n" end) @@ -18869,35 +19546,21 @@ SNIPPETS-P indicates if this is run to create snippet images for HTML." (defun org-dvipng-color (attr) "Return a RGB color specification for dvipng." - (apply 'format "rgb %s %s %s" - (mapcar 'org-normalize-color - (if (featurep 'xemacs) - (color-rgb-components - (face-property 'default - (cond ((eq attr :foreground) 'foreground) - ((eq attr :background) 'background)))) - (color-values (face-attribute 'default attr nil)))))) + (org-dvipng-color-format (face-attribute 'default attr nil))) (defun org-dvipng-color-format (color-name) "Convert COLOR-NAME to a RGB color value for dvipng." - (apply 'format "rgb %s %s %s" + (apply #'format "rgb %s %s %s" (mapcar 'org-normalize-color - (color-values color-name)))) + (color-values color-name)))) (defun org-latex-color (attr) "Return a RGB color for the LaTeX color package." - (apply 'format "%s,%s,%s" - (mapcar 'org-normalize-color - (if (featurep 'xemacs) - (color-rgb-components - (face-property 'default - (cond ((eq attr :foreground) 'foreground) - ((eq attr :background) 'background)))) - (color-values (face-attribute 'default attr nil)))))) + (org-latex-color-format (face-attribute 'default attr nil))) (defun org-latex-color-format (color-name) "Convert COLOR-NAME to a RGB color value." - (apply 'format "%s,%s,%s" + (apply #'format "%s,%s,%s" (mapcar 'org-normalize-color (color-values color-name)))) @@ -18909,8 +19572,7 @@ SNIPPETS-P indicates if this is run to create snippet images for HTML." ;; Image display -(defvar org-inline-image-overlays nil) -(make-variable-buffer-local 'org-inline-image-overlays) +(defvar-local org-inline-image-overlays nil) (defun org-toggle-inline-images (&optional include-linked) "Toggle the display of inline images. @@ -18919,13 +19581,14 @@ INCLUDE-LINKED is passed to `org-display-inline-images'." (if org-inline-image-overlays (progn (org-remove-inline-images) - (message "Inline image display turned off")) + (when (called-interactively-p 'interactive) + (message "Inline image display turned off"))) (org-display-inline-images include-linked) - (if (and (org-called-interactively-p) - org-inline-image-overlays) - (message "%d images displayed inline" - (length org-inline-image-overlays)) - (message "No images to display inline")))) + (when (called-interactively-p 'interactive) + (message (if org-inline-image-overlays + (format "%d images displayed inline" + (length org-inline-image-overlays)) + "No images to display inline"))))) (defun org-redisplay-inline-images () "Refresh the display of inline images." @@ -18937,68 +19600,116 @@ INCLUDE-LINKED is passed to `org-display-inline-images'." (defun org-display-inline-images (&optional include-linked refresh beg end) "Display inline images. -Normally only links without a description part are inlined, because this -is how it will work for export. When INCLUDE-LINKED is set, also links -with a description part will be inlined. This can be nice for a quick -look at those images, but it does not reflect what exported files will look -like. -When REFRESH is set, refresh existing images between BEG and END. -This will create new image displays only if necessary. -BEG and END default to the buffer boundaries." + +An inline image is a link which follows either of these +conventions: + + 1. Its path is a file with an extension matching return value + from `image-file-name-regexp' and it has no contents. + + 2. Its description consists in a single link of the previous + type. + +When optional argument INCLUDE-LINKED is non-nil, also links with +a text description part will be inlined. This can be nice for +a quick look at those images, but it does not reflect what +exported files will look like. + +When optional argument REFRESH is non-nil, refresh existing +images between BEG and END. This will create new image displays +only if necessary. BEG and END default to the buffer +boundaries." (interactive "P") (when (display-graphic-p) (unless refresh (org-remove-inline-images) - (if (fboundp 'clear-image-cache) (clear-image-cache))) - (save-excursion - (save-restriction - (widen) - (setq beg (or beg (point-min)) end (or end (point-max))) - (goto-char beg) - (let ((re (concat "\\[\\[\\(\\(file:\\)\\|\\([./~]\\)\\)\\([^]\n]+?" - (substring (org-image-file-name-regexp) 0 -2) - "\\)\\]" (if include-linked "" "\\]"))) - (case-fold-search t) - old file ov img type attrwidth width) - (while (re-search-forward re end t) - (setq old (get-char-property-and-overlay (match-beginning 1) - 'org-image-overlay) - file (expand-file-name - (concat (or (match-string 3) "") (match-string 4)))) - (when (image-type-available-p 'imagemagick) - (setq attrwidth (if (or (listp org-image-actual-width) - (null org-image-actual-width)) - (save-excursion - (save-match-data - (when (re-search-backward - "#\\+attr.*:width[ \t]+\\([^ ]+\\)" - (save-excursion - (re-search-backward "^[ \t]*$\\|\\`" nil t)) t) - (string-to-number (match-string 1)))))) - width (cond ((eq org-image-actual-width t) nil) - ((null org-image-actual-width) attrwidth) - ((numberp org-image-actual-width) - org-image-actual-width) - ((listp org-image-actual-width) - (or attrwidth (car org-image-actual-width)))) - type (if width 'imagemagick))) - (when (file-exists-p file) - (if (and (car-safe old) refresh) - (image-refresh (overlay-get (cdr old) 'display)) - (setq img (save-match-data (create-image file type nil :width width))) - (when img - (setq ov (make-overlay (match-beginning 0) (match-end 0))) - (overlay-put ov 'display img) - (overlay-put ov 'face 'default) - (overlay-put ov 'org-image-overlay t) - (overlay-put ov 'modification-hooks - (list 'org-display-inline-remove-overlay)) - (push ov org-inline-image-overlays)))))))))) - -(define-obsolete-function-alias - 'org-display-inline-modification-hook 'org-display-inline-remove-overlay "24.3") - -(defun org-display-inline-remove-overlay (ov after beg end &optional len) + (when (fboundp 'clear-image-cache) (clear-image-cache))) + (org-with-wide-buffer + (goto-char (or beg (point-min))) + (let ((case-fold-search t) + (file-extension-re (image-file-name-regexp))) + (while (re-search-forward "[][]\\[\\(?:file\\|[./~]\\)" end t) + (let ((link (save-match-data (org-element-context)))) + ;; Check if we're at an inline image. + (when (and (equal (org-element-property :type link) "file") + (or include-linked + (not (org-element-property :contents-begin link))) + (let ((parent (org-element-property :parent link))) + (or (not (eq (org-element-type parent) 'link)) + (not (cdr (org-element-contents parent))))) + (string-match-p file-extension-re + (org-element-property :path link))) + (let ((file (expand-file-name + (org-link-unescape + (org-element-property :path link))))) + (when (file-exists-p file) + (let ((width + ;; Apply `org-image-actual-width' specifications. + (cond + ((not (image-type-available-p 'imagemagick)) nil) + ((eq org-image-actual-width t) nil) + ((listp org-image-actual-width) + (or + ;; First try to find a width among + ;; attributes associated to the paragraph + ;; containing link. + (let ((paragraph + (let ((e link)) + (while (and (setq e (org-element-property + :parent e)) + (not (eq (org-element-type e) + 'paragraph)))) + e))) + (when paragraph + (save-excursion + (goto-char (org-element-property :begin paragraph)) + (when + (re-search-forward + "^[ \t]*#\\+attr_.*?: +.*?:width +\\(\\S-+\\)" + (org-element-property + :post-affiliated paragraph) + t) + (string-to-number (match-string 1)))))) + ;; Otherwise, fall-back to provided number. + (car org-image-actual-width))) + ((numberp org-image-actual-width) + org-image-actual-width))) + (old (get-char-property-and-overlay + (org-element-property :begin link) + 'org-image-overlay))) + (if (and (car-safe old) refresh) + (image-refresh (overlay-get (cdr old) 'display)) + (let ((image (create-image file + (and width 'imagemagick) + nil + :width width))) + (when image + (let* ((link + ;; If inline image is the description + ;; of another link, be sure to + ;; consider the latter as the one to + ;; apply the overlay on. + (let ((parent + (org-element-property :parent link))) + (if (eq (org-element-type parent) 'link) + parent + link))) + (ov (make-overlay + (org-element-property :begin link) + (progn + (goto-char + (org-element-property :end link)) + (skip-chars-backward " \t") + (point))))) + (overlay-put ov 'display image) + (overlay-put ov 'face 'default) + (overlay-put ov 'org-image-overlay t) + (overlay-put + ov 'modification-hooks + (list 'org-display-inline-remove-overlay)) + (push ov org-inline-image-overlays))))))))))))))) + +(defun org-display-inline-remove-overlay (ov after _beg _end &optional _len) "Remove inline-display overlay if a corresponding region is modified." (let ((inhibit-modification-hooks t)) (when (and ov after) @@ -19008,7 +19719,7 @@ BEG and END default to the buffer boundaries." (defun org-remove-inline-images () "Remove inline display of images." (interactive) - (mapc 'delete-overlay org-inline-image-overlays) + (mapc #'delete-overlay org-inline-image-overlays) (setq org-inline-image-overlays nil)) ;;;; Key bindings @@ -19016,44 +19727,46 @@ BEG and END default to the buffer boundaries." ;; Outline functions from `outline-mode-prefix-map' ;; that can be remapped in Org: (define-key org-mode-map [remap outline-mark-subtree] 'org-mark-subtree) -(define-key org-mode-map [remap show-subtree] 'org-show-subtree) +(define-key org-mode-map [remap outline-show-subtree] 'org-show-subtree) (define-key org-mode-map [remap outline-forward-same-level] 'org-forward-heading-same-level) (define-key org-mode-map [remap outline-backward-same-level] 'org-backward-heading-same-level) -(define-key org-mode-map [remap show-branches] +(define-key org-mode-map [remap outline-show-branches] 'org-kill-note-or-show-branches) (define-key org-mode-map [remap outline-promote] 'org-promote-subtree) (define-key org-mode-map [remap outline-demote] 'org-demote-subtree) (define-key org-mode-map [remap outline-insert-heading] 'org-ctrl-c-ret) +(define-key org-mode-map [remap outline-next-visible-heading] + 'org-next-visible-heading) +(define-key org-mode-map [remap outline-previous-visible-heading] + 'org-previous-visible-heading) +(define-key org-mode-map [remap show-children] 'org-show-children) ;; Outline functions from `outline-mode-prefix-map' that can not ;; be remapped in Org: -;; + ;; - the column "key binding" shows whether the Outline function is still ;; available in Org mode on the same key that it has been bound to in ;; Outline mode: ;; - "overridden": key used for a different functionality in Org mode ;; - else: key still bound to the same Outline function in Org mode -;; -;; | Outline function | key binding | Org replacement | -;; |------------------------------------+-------------+-----------------------| -;; | `outline-next-visible-heading' | `C-c C-n' | still same function | -;; | `outline-previous-visible-heading' | `C-c C-p' | still same function | -;; | `outline-up-heading' | `C-c C-u' | still same function | -;; | `outline-move-subtree-up' | overridden | better: org-shiftup | -;; | `outline-move-subtree-down' | overridden | better: org-shiftdown | -;; | `show-entry' | overridden | no replacement | -;; | `show-children' | `C-c C-i' | visibility cycling | -;; | `show-branches' | `C-c C-k' | still same function | -;; | `show-subtree' | overridden | visibility cycling | -;; | `show-all' | overridden | no replacement | -;; | `hide-subtree' | overridden | visibility cycling | -;; | `hide-body' | overridden | no replacement | -;; | `hide-entry' | overridden | visibility cycling | -;; | `hide-leaves' | overridden | no replacement | -;; | `hide-sublevels' | overridden | no replacement | -;; | `hide-other' | overridden | no replacement | + +;; | Outline function | key binding | Org replacement | +;; |------------------------------------+-------------+--------------------------| +;; | `outline-up-heading' | `C-c C-u' | still same function | +;; | `outline-move-subtree-up' | overridden | better: org-shiftup | +;; | `outline-move-subtree-down' | overridden | better: org-shiftdown | +;; | `show-entry' | overridden | no replacement | +;; | `show-branches' | `C-c C-k' | still same function | +;; | `show-subtree' | overridden | visibility cycling | +;; | `show-all' | overridden | no replacement | +;; | `hide-subtree' | overridden | visibility cycling | +;; | `hide-body' | overridden | no replacement | +;; | `hide-entry' | overridden | visibility cycling | +;; | `hide-leaves' | overridden | no replacement | +;; | `hide-sublevels' | overridden | no replacement | +;; | `hide-other' | overridden | no replacement | ;; Make `C-c C-x' a prefix key (org-defkey org-mode-map "\C-c\C-x" (make-sparse-keymap)) @@ -19064,8 +19777,7 @@ BEG and END default to the buffer boundaries." (org-defkey org-mode-map [(control tab)] 'org-force-cycle-archived) (org-defkey org-mode-map "\M-\t" #'pcomplete) ;; The following line is necessary under Suse GNU/Linux -(unless (featurep 'xemacs) - (org-defkey org-mode-map [S-iso-lefttab] 'org-shifttab)) +(org-defkey org-mode-map [S-iso-lefttab] 'org-shifttab) (org-defkey org-mode-map [(shift tab)] 'org-shifttab) (define-key org-mode-map [backtab] 'org-shifttab) @@ -19079,6 +19791,8 @@ BEG and END default to the buffer boundaries." (org-defkey org-mode-map [(meta up)] 'org-metaup) (org-defkey org-mode-map [(meta down)] 'org-metadown) +(org-defkey org-mode-map [(control meta shift right)] 'org-increase-number-at-point) +(org-defkey org-mode-map [(control meta shift left)] 'org-decrease-number-at-point) (org-defkey org-mode-map [(meta shift left)] 'org-shiftmetaleft) (org-defkey org-mode-map [(meta shift right)] 'org-shiftmetaright) (org-defkey org-mode-map [(meta shift up)] 'org-shiftmetaup) @@ -19096,17 +19810,14 @@ BEG and END default to the buffer boundaries." ;; Babel keys (define-key org-mode-map org-babel-key-prefix org-babel-map) -(mapc (lambda (pair) - (define-key org-babel-map (car pair) (cdr pair))) - org-babel-key-bindings) +(dolist (pair org-babel-key-bindings) + (define-key org-babel-map (car pair) (cdr pair))) ;;; Extra keys for tty access. ;; We only set them when really needed because otherwise the ;; menus don't show the simple keys -(when (or org-use-extra-keys - (featurep 'xemacs) ;; because XEmacs supports multi-device stuff - (not window-system)) +(when (or org-use-extra-keys (not window-system)) (org-defkey org-mode-map "\C-c\C-xc" 'org-table-copy-down) (org-defkey org-mode-map "\C-c\C-xM" 'org-insert-todo-heading) (org-defkey org-mode-map "\C-c\C-xm" 'org-meta-return) @@ -19138,7 +19849,7 @@ BEG and END default to the buffer boundaries." ;; All the other keys -(org-defkey org-mode-map "\C-c\C-a" 'show-all) ; in case allout messed up. +(org-defkey org-mode-map "\C-c\C-a" 'outline-show-all) ; in case allout messed up. (org-defkey org-mode-map "\C-c\C-r" 'org-reveal) (if (boundp 'narrow-map) (org-defkey narrow-map "s" 'org-narrow-to-subtree) @@ -19185,6 +19896,7 @@ BEG and END default to the buffer boundaries." (org-defkey org-mode-map "\C-c\C-x\C-n" 'org-next-link) (org-defkey org-mode-map "\C-c\C-x\C-p" 'org-previous-link) (org-defkey org-mode-map "\C-c\C-l" 'org-insert-link) +(org-defkey org-mode-map "\C-c\M-l" 'org-insert-last-stored-link) (org-defkey org-mode-map "\C-c\C-\M-l" 'org-insert-all-links) (org-defkey org-mode-map "\C-c\C-o" 'org-open-at-point) (org-defkey org-mode-map "\C-c%" 'org-mark-ring-push) @@ -19209,8 +19921,10 @@ BEG and END default to the buffer boundaries." (org-defkey org-mode-map "\C-c\C-k" 'org-kill-note-or-show-branches) (org-defkey org-mode-map "\C-c#" 'org-update-statistics-cookies) (org-defkey org-mode-map [remap open-line] 'org-open-line) +(org-defkey org-mode-map [remap comment-dwim] 'org-comment-dwim) (org-defkey org-mode-map [remap forward-paragraph] 'org-forward-paragraph) (org-defkey org-mode-map [remap backward-paragraph] 'org-backward-paragraph) +(org-defkey org-mode-map "\M-^" 'org-delete-indentation) (org-defkey org-mode-map "\C-m" 'org-return) (org-defkey org-mode-map "\C-j" 'org-return-indent) (org-defkey org-mode-map "\C-c?" 'org-table-field-info) @@ -19219,6 +19933,8 @@ BEG and END default to the buffer boundaries." (org-defkey org-mode-map "\C-c=" 'org-table-eval-formula) (org-defkey org-mode-map "\C-c'" 'org-edit-special) (org-defkey org-mode-map "\C-c`" 'org-table-edit-field) +(org-defkey org-mode-map "\C-c\"a" 'orgtbl-ascii-plot) +(org-defkey org-mode-map "\C-c\"g" 'org-plot/gnuplot) (org-defkey org-mode-map "\C-c|" 'org-table-create-or-convert-from-region) (org-defkey org-mode-map [(control ?#)] 'org-table-rotate-recalc-marks) (org-defkey org-mode-map "\C-c~" 'org-table-create-with-table.el) @@ -19226,7 +19942,7 @@ BEG and END default to the buffer boundaries." (org-defkey org-mode-map "\C-c}" 'org-table-toggle-coordinate-overlays) (org-defkey org-mode-map "\C-c{" 'org-table-toggle-formula-debugger) (org-defkey org-mode-map "\C-c\C-e" 'org-export-dispatch) -(org-defkey org-mode-map "\C-c:" 'org-toggle-fixed-width-section) +(org-defkey org-mode-map "\C-c:" 'org-toggle-fixed-width) (org-defkey org-mode-map "\C-c\C-x\C-f" 'org-emphasize) (org-defkey org-mode-map "\C-c\C-xf" 'org-footnote-action) (org-defkey org-mode-map "\C-c\C-x\C-mg" 'org-mobile-pull) @@ -19250,7 +19966,7 @@ BEG and END default to the buffer boundaries." (org-defkey org-mode-map "\C-c\C-x\C-d" 'org-clock-display) (org-defkey org-mode-map "\C-c\C-x\C-r" 'org-clock-report) (org-defkey org-mode-map "\C-c\C-x\C-u" 'org-dblock-update) -(org-defkey org-mode-map "\C-c\C-x\C-l" 'org-preview-latex-fragment) +(org-defkey org-mode-map "\C-c\C-x\C-l" 'org-toggle-latex-fragment) (org-defkey org-mode-map "\C-c\C-x\C-v" 'org-toggle-inline-images) (org-defkey org-mode-map "\C-c\C-x\C-\M-v" 'org-redisplay-inline-images) (org-defkey org-mode-map "\C-c\C-x\\" 'org-toggle-pretty-entities) @@ -19260,9 +19976,8 @@ BEG and END default to the buffer boundaries." (org-defkey org-mode-map "\C-c\C-xe" 'org-set-effort) (org-defkey org-mode-map "\C-c\C-xE" 'org-inc-effort) (org-defkey org-mode-map "\C-c\C-xo" 'org-toggle-ordered-property) -(org-defkey org-mode-map "\C-c\C-xi" 'org-insert-columns-dblock) +(org-defkey org-mode-map "\C-c\C-xi" 'org-columns-insert-dblock) (org-defkey org-mode-map [(control ?c) (control ?x) ?\;] 'org-timer-set-timer) -(org-defkey org-mode-map [(control ?c) (control ?x) ?\:] 'org-timer-cancel-timer) (org-defkey org-mode-map "\C-c\C-x." 'org-timer) (org-defkey org-mode-map "\C-c\C-x-" 'org-timer-item) @@ -19280,15 +19995,11 @@ BEG and END default to the buffer boundaries." (define-key org-mode-map "\C-c\C-x[" 'org-reftex-citation) -(when (featurep 'xemacs) - (org-defkey org-mode-map 'button3 'popup-mode-menu)) - - (defconst org-speed-commands-default '( ("Outline Navigation") - ("n" . (org-speed-move-safe 'outline-next-visible-heading)) - ("p" . (org-speed-move-safe 'outline-previous-visible-heading)) + ("n" . (org-speed-move-safe 'org-next-visible-heading)) + ("p" . (org-speed-move-safe 'org-previous-visible-heading)) ("f" . (org-speed-move-safe 'org-forward-heading-same-level)) ("b" . (org-speed-move-safe 'org-backward-heading-same-level)) ("F" . org-next-block) @@ -19303,8 +20014,8 @@ BEG and END default to the buffer boundaries." ("s" . org-narrow-to-subtree) ("=" . org-columns) ("Outline Structure Editing") - ("U" . org-shiftmetaup) - ("D" . org-shiftmetadown) + ("U" . org-metaup) + ("D" . org-metadown) ("r" . org-metaright) ("l" . org-metaleft) ("R" . org-shiftmetaright) @@ -19364,10 +20075,10 @@ BEG and END default to the buffer boundaries." (user-error "Speed commands are not activated, customize `org-use-speed-commands'") (with-output-to-temp-buffer "*Help*" (princ "User-defined Speed commands\n===========================\n") - (mapc 'org-print-speed-command org-speed-commands-user) + (mapc #'org-print-speed-command org-speed-commands-user) (princ "\n") (princ "Built-in Speed commands\n=======================\n") - (mapc 'org-print-speed-command org-speed-commands-default)) + (mapc #'org-print-speed-command org-speed-commands-default)) (with-current-buffer "*Help*" (setq truncate-lines t)))) @@ -19386,9 +20097,6 @@ If not, return to the original position and throw an error." (defvar org-table-auto-blank-field) ; defined in org-table.el (defvar org-speed-command nil) -(define-obsolete-function-alias - 'org-speed-command-default-hook 'org-speed-command-activate "24.3") - (defun org-speed-command-activate (keys) "Hook for activating single-letter speed commands. `org-speed-commands-default' specifies a minimal command set. @@ -19399,9 +20107,6 @@ Use `org-speed-commands-user' for further customization." (cdr (assoc keys (append org-speed-commands-user org-speed-commands-default))))) -(define-obsolete-function-alias - 'org-babel-speed-command-hook 'org-babel-speed-command-activate "24.3") - (defun org-babel-speed-command-activate (keys) "Hook for activating single-letter code block commands." (when (and (bolp) (looking-at org-babel-src-block-regexp)) @@ -19434,9 +20139,11 @@ overwritten, and the table is not marked as requiring realignment." (org-check-before-invisible-edit 'insert) (cond ((and org-use-speed-commands - (setq org-speed-command - (run-hook-with-args-until-success - 'org-speed-command-hook (this-command-keys)))) + (let ((kv (this-command-keys-vector))) + (setq org-speed-command + (run-hook-with-args-until-success + 'org-speed-command-hook + (make-string 1 (aref kv (1- (length kv)))))))) (cond ((commandp org-speed-command) (setq this-command org-speed-command) @@ -19448,94 +20155,98 @@ overwritten, and the table is not marked as requiring realignment." (t (let (org-use-speed-commands) (call-interactively 'org-self-insert-command))))) ((and - (org-table-p) + (org-at-table-p) (progn - ;; check if we blank the field, and if that triggers align + ;; Check if we blank the field, and if that triggers align. (and (featurep 'org-table) org-table-auto-blank-field - (member last-command - '(org-cycle org-return org-shifttab org-ctrl-c-ctrl-c yas/expand)) - (if (or (equal (char-after) ?\ ) (looking-at "[^|\n]* |")) - ;; got extra space, this field does not determine column width + (memq last-command + '(org-cycle org-return org-shifttab org-ctrl-c-ctrl-c)) + (if (or (eq (char-after) ?\s) (looking-at "[^|\n]* |")) + ;; Got extra space, this field does not determine + ;; column width. (let (org-table-may-need-update) (org-table-blank-field)) - ;; no extra space, this field may determine column width + ;; No extra space, this field may determine column + ;; width. (org-table-blank-field))) t) (eq N 1) - (looking-at "[^|\n]* |")) - (let (org-table-may-need-update) - (goto-char (1- (match-end 0))) - (backward-delete-char 1) - (goto-char (match-beginning 0)) - (self-insert-command N))) + (looking-at "[^|\n]* \\( \\)|")) + ;; There is room for insertion without re-aligning the table. + (delete-region (match-beginning 1) (match-end 1)) + (self-insert-command N)) (t (setq org-table-may-need-update t) (self-insert-command N) (org-fix-tags-on-the-fly) - (if org-self-insert-cluster-for-undo - (if (not (eq last-command 'org-self-insert-command)) + (when org-self-insert-cluster-for-undo + (if (not (eq last-command 'org-self-insert-command)) + (setq org-self-insert-command-undo-counter 1) + (if (>= org-self-insert-command-undo-counter 20) (setq org-self-insert-command-undo-counter 1) - (if (>= org-self-insert-command-undo-counter 20) - (setq org-self-insert-command-undo-counter 1) - (and (> org-self-insert-command-undo-counter 0) - buffer-undo-list (listp buffer-undo-list) - (not (cadr buffer-undo-list)) ; remove nil entry - (setcdr buffer-undo-list (cddr buffer-undo-list))) - (setq org-self-insert-command-undo-counter - (1+ org-self-insert-command-undo-counter)))))))) + (and (> org-self-insert-command-undo-counter 0) + buffer-undo-list (listp buffer-undo-list) + (not (cadr buffer-undo-list)) ; remove nil entry + (setcdr buffer-undo-list (cddr buffer-undo-list))) + (setq org-self-insert-command-undo-counter + (1+ org-self-insert-command-undo-counter)))))))) (defun org-check-before-invisible-edit (kind) "Check is editing if kind KIND would be dangerous with invisible text around. The detailed reaction depends on the user option `org-catch-invisible-edits'." ;; First, try to get out of here as quickly as possible, to reduce overhead - (if (and org-catch-invisible-edits - (or (not (boundp 'visible-mode)) (not visible-mode)) - (or (get-char-property (point) 'invisible) - (get-char-property (max (point-min) (1- (point))) 'invisible))) - ;; OK, we need to take a closer look - (let* ((invisible-at-point (get-char-property (point) 'invisible)) - (invisible-before-point (if (bobp) nil (get-char-property - (1- (point)) 'invisible))) - (border-and-ok-direction - (or - ;; Check if we are acting predictably before invisible text - (and invisible-at-point (not invisible-before-point) - (memq kind '(insert delete-backward))) - ;; Check if we are acting predictably after invisible text - ;; This works not well, and I have turned it off. It seems - ;; better to always show and stop after invisible text. - ;; (and (not invisible-at-point) invisible-before-point - ;; (memq kind '(insert delete))) - ))) - (when (or (memq invisible-at-point '(outline org-hide-block t)) - (memq invisible-before-point '(outline org-hide-block t))) - (if (eq org-catch-invisible-edits 'error) - (user-error "Editing in invisible areas is prohibited, make them visible first")) - (if (and org-custom-properties-overlays - (y-or-n-p "Display invisible properties in this buffer? ")) - (org-toggle-custom-properties-visibility) - ;; Make the area visible - (save-excursion - (if invisible-before-point - (goto-char (previous-single-char-property-change - (point) 'invisible))) - (show-subtree)) - (cond - ((eq org-catch-invisible-edits 'show) - ;; That's it, we do the edit after showing - (message - "Unfolding invisible region around point before editing") - (sit-for 1)) - ((and (eq org-catch-invisible-edits 'smart) - border-and-ok-direction) - (message "Unfolding invisible region around point before editing")) - (t - ;; Don't do the edit, make the user repeat it in full visibility - (user-error "Edit in invisible region aborted, repeat to confirm with text visible")))))))) + (when (and org-catch-invisible-edits + (or (not (boundp 'visible-mode)) (not visible-mode)) + (or (get-char-property (point) 'invisible) + (get-char-property (max (point-min) (1- (point))) 'invisible))) + ;; OK, we need to take a closer look + (let* ((invisible-at-point (get-char-property (point) 'invisible)) + (invisible-before-point (unless (bobp) (get-char-property + (1- (point)) 'invisible))) + (border-and-ok-direction + (or + ;; Check if we are acting predictably before invisible text + (and invisible-at-point (not invisible-before-point) + (memq kind '(insert delete-backward))) + ;; Check if we are acting predictably after invisible text + ;; This works not well, and I have turned it off. It seems + ;; better to always show and stop after invisible text. + ;; (and (not invisible-at-point) invisible-before-point + ;; (memq kind '(insert delete))) + ))) + (when (or (memq invisible-at-point '(outline org-hide-block t)) + (memq invisible-before-point '(outline org-hide-block t))) + (when (eq org-catch-invisible-edits 'error) + (user-error "Editing in invisible areas is prohibited, make them visible first")) + (if (and org-custom-properties-overlays + (y-or-n-p "Display invisible properties in this buffer? ")) + (org-toggle-custom-properties-visibility) + ;; Make the area visible + (save-excursion + (when invisible-before-point + (goto-char (previous-single-char-property-change + (point) 'invisible))) + (outline-show-subtree)) + (cond + ((eq org-catch-invisible-edits 'show) + ;; That's it, we do the edit after showing + (message + "Unfolding invisible region around point before editing") + (sit-for 1)) + ((and (eq org-catch-invisible-edits 'smart) + border-and-ok-direction) + (message "Unfolding invisible region around point before editing")) + (t + ;; Don't do the edit, make the user repeat it in full visibility + (user-error "Edit in invisible region aborted, repeat to confirm with text visible")))))))) (defun org-fix-tags-on-the-fly () - (when (and (equal (char-after (point-at-bol)) ?*) + "Align tags in headline at point. +Unlike to `org-set-tags', it ignores region and sorting." + (when (and (eq (char-after (line-beginning-position)) ?*) ;short-circuit (org-at-heading-p)) - (org-align-tags-here org-tags-column))) + (let ((org-ignore-region t) + (org-tags-sort-function nil)) + (org-set-tags nil t)))) (defun org-delete-backward-char (N) "Like `delete-backward-char', insert whitespace at field end in tables. @@ -19546,7 +20257,7 @@ because, in this case the deletion might narrow the column." (interactive "p") (save-match-data (org-check-before-invisible-edit 'delete-backward) - (if (and (org-table-p) + (if (and (org-at-table-p) (eq N 1) (string-match "|" (buffer-substring (point-at-bol) (point))) (looking-at ".*?|")) @@ -19554,14 +20265,13 @@ because, in this case the deletion might narrow the column." (noalign (looking-at "[^|\n\r]* |")) (c org-table-may-need-update)) (backward-delete-char N) - (if (not overwrite-mode) - (progn - (skip-chars-forward "^|") - (insert " ") - (goto-char (1- pos)))) + (unless overwrite-mode + (skip-chars-forward "^|") + (insert " ") + (goto-char (1- pos))) ;; noalign: if there were two spaces at the end, this field ;; does not determine the width of the column. - (if noalign (setq org-table-may-need-update c))) + (when noalign (setq org-table-may-need-update c))) (backward-delete-char N) (org-fix-tags-on-the-fly)))) @@ -19574,7 +20284,7 @@ because, in this case the deletion might narrow the column." (interactive "p") (save-match-data (org-check-before-invisible-edit 'delete) - (if (and (org-table-p) + (if (and (org-at-table-p) (not (bolp)) (not (= (char-after) ?|)) (eq N 1)) @@ -19587,12 +20297,12 @@ because, in this case the deletion might narrow the column." (goto-char pos) ;; noalign: if there were two spaces at the end, this field ;; does not determine the width of the column. - (if noalign (setq org-table-may-need-update c))) + (when noalign (setq org-table-may-need-update c))) (delete-char N)) (delete-char N) (org-fix-tags-on-the-fly)))) -;; Make `delete-selection-mode' work with org-mode and orgtbl-mode +;; Make `delete-selection-mode' work with Org mode and Orgtbl mode (put 'org-self-insert-command 'delete-selection (lambda () (not (run-hook-with-args-until-success @@ -19611,7 +20321,7 @@ because, in this case the deletion might narrow the column." (put 'org-delete-char 'flyspell-delayed t) (put 'org-delete-backward-char 'flyspell-delayed t) -;; Make pabbrev-mode expand after org-mode commands +;; Make pabbrev-mode expand after Org mode commands (put 'org-self-insert-command 'pabbrev-expand-after-command t) (put 'orgtbl-self-insert-command 'pabbrev-expand-after-command t) @@ -19621,9 +20331,7 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names." (let (new old) (while commands (setq old (pop commands) new (pop commands)) - (if (fboundp 'command-remapping) - (org-defkey map (vector 'remap old) new) - (substitute-key-definition old new map global-map))))) + (org-defkey map (vector 'remap old) new)))) (defun org-transpose-words () "Transpose words for Org. @@ -19765,7 +20473,7 @@ See `org-ctrl-c-ctrl-c-hook' for more information.") (defun org-shiftselect-error () "Throw an error because Shift-Cursor command was applied in wrong context." (if (and (boundp 'shift-select-mode) shift-select-mode) - (user-error "To use shift-selection with Org-mode, customize `org-support-shift-select'") + (user-error "To use shift-selection with Org mode, customize `org-support-shift-select'") (user-error "This command works only in special context like headlines or timestamps"))) (defun org-call-for-shift-select (cmd) @@ -19820,32 +20528,30 @@ individual commands for more information." (call-interactively 'org-indent-item-tree)) (t (org-modifier-cursor-error)))) -(defun org-shiftmetaup (&optional arg) - "Move subtree up or kill table row. -Calls `org-move-subtree-up' or `org-table-kill-row' or -`org-move-item-up' or `org-timestamp-up', depending on context. -See the individual commands for more information." +(defun org-shiftmetaup (&optional _arg) + "Drag the line at point up. +In a table, kill the current row. +On a clock timestamp, update the value of the timestamp like `S-' +but also adjust the previous clocked item in the clock history. +Everywhere else, drag the line at point up." (interactive "P") (cond ((run-hook-with-args-until-success 'org-shiftmetaup-hook)) ((org-at-table-p) (call-interactively 'org-table-kill-row)) - ((org-at-heading-p) (call-interactively 'org-move-subtree-up)) - ((org-at-item-p) (call-interactively 'org-move-item-up)) ((org-at-clock-log-p) (let ((org-clock-adjust-closest t)) (call-interactively 'org-timestamp-up))) (t (call-interactively 'org-drag-line-backward)))) -(defun org-shiftmetadown (&optional arg) - "Move subtree down or insert table row. -Calls `org-move-subtree-down' or `org-table-insert-row' or -`org-move-item-down' or `org-timestamp-up', depending on context. -See the individual commands for more information." +(defun org-shiftmetadown (&optional _arg) + "Drag the line at point down. +In a table, insert an empty row at the current line. +On a clock timestamp, update the value of the timestamp like `S-' +but also adjust the previous clocked item in the clock history. +Everywhere else, drag the line at point down." (interactive "P") (cond ((run-hook-with-args-until-success 'org-shiftmetadown-hook)) ((org-at-table-p) (call-interactively 'org-table-insert-row)) - ((org-at-heading-p) (call-interactively 'org-move-subtree-down)) - ((org-at-item-p) (call-interactively 'org-move-item-down)) ((org-at-clock-log-p) (let ((org-clock-adjust-closest t)) (call-interactively 'org-timestamp-down))) (t (call-interactively 'org-drag-line-forward)))) @@ -19854,11 +20560,16 @@ See the individual commands for more information." (user-error "Hidden subtree, open with TAB or use subtree command M-S-/")) -(defun org-metaleft (&optional arg) - "Promote heading or move table column to left. -Calls `org-do-promote' or `org-table-move-column', depending on context. -With no specific context, calls the Emacs default `backward-word'. -See the individual commands for more information." +(defun org-metaleft (&optional _arg) + "Promote heading, list item at point or move table column left. + +Calls `org-do-promote', `org-outdent-item' or `org-table-move-column', +depending on context. With no specific context, calls the Emacs +default `backward-word'. See the individual commands for more +information. + +This function runs the hook `org-metaleft-hook' as a first step, +and returns at first non-nil value." (interactive "P") (cond ((run-hook-with-args-until-success 'org-metaleft-hook)) @@ -19883,11 +20594,18 @@ See the individual commands for more information." (call-interactively 'org-outdent-item)) (t (call-interactively 'backward-word)))) -(defun org-metaright (&optional arg) - "Demote a subtree, a list item or move table column to right. +(defun org-metaright (&optional _arg) + "Demote heading, list item at point or move table column right. + In front of a drawer or a block keyword, indent it correctly. + +Calls `org-do-demote', `org-indent-item', `org-table-move-column', +`org-indent-drawer' or `org-indent-block' depending on context. With no specific context, calls the Emacs default `forward-word'. -See the individual commands for more information." +See the individual commands for more information. + +This function runs the hook `org-metaright-hook' as a first step, +and returns at first non-nil value." (interactive "P") (cond ((run-hook-with-args-until-success 'org-metaright-hook)) @@ -19937,11 +20655,11 @@ this function returns t, nil otherwise." (goto-char (point-at-eol)) (setq end (max end (point))) (while (re-search-forward re end t) - (if (get-char-property (match-beginning 0) 'invisible) - (throw 'exit t)))) + (when (get-char-property (match-beginning 0) 'invisible) + (throw 'exit t)))) nil)))) -(defun org-metaup (&optional arg) +(defun org-metaup (&optional _arg) "Move subtree up or move table row up. Calls `org-move-subtree-up' or `org-table-move-row' or `org-move-item-up', depending on context. See the individual commands @@ -19963,7 +20681,7 @@ for more information." ((org-at-item-p) (call-interactively 'org-move-item-up)) (t (org-drag-element-backward)))) -(defun org-metadown (&optional arg) +(defun org-metadown (&optional _arg) "Move subtree down or move table row down. Calls `org-move-subtree-down' or `org-table-move-row' or `org-move-item-down', depending on context. See the individual @@ -20149,6 +20867,32 @@ Optional argument N tells to change by that many units." (org-clock-timestamps-down n)) (user-error "Not at a clock log"))) +(defun org-increase-number-at-point (&optional inc) + "Increment the number at point. +With an optional prefix numeric argument INC, increment using +this numeric value." + (interactive "p") + (if (not (number-at-point)) + (user-error "Not on a number") + (unless inc (setq inc 1)) + (let ((pos (point)) + (beg (skip-chars-backward "-+^/*0-9eE.")) + (end (skip-chars-forward "-+^/*0-9eE^.")) nap) + (setq nap (buffer-substring-no-properties + (+ pos beg) (+ pos beg end))) + (delete-region (+ pos beg) (+ pos beg end)) + (insert (calc-eval (concat (number-to-string inc) "+" nap)))) + (when (org-at-table-p) + (org-table-align) + (org-table-end-of-field 1)))) + +(defun org-decrease-number-at-point (&optional inc) + "Decrement the number at point. +With an optional prefix numeric argument INC, decrement using +this numeric value." + (interactive "p") + (org-increase-number-at-point (- (or inc 1)))) + (defun org-ctrl-c-ret () "Call `org-table-hline-and-move' or `org-insert-heading' dep. on context." (interactive) @@ -20183,19 +20927,19 @@ Optional argument N tells to change by that many units." (defun org-copy-special () "Copy region in table or copy current subtree. -Calls `org-table-copy' or `org-copy-subtree', depending on context. -See the individual commands for more information." +Calls `org-table-copy-region' or `org-copy-subtree', depending on +context. See the individual commands for more information." (interactive) (call-interactively - (if (org-at-table-p) 'org-table-copy-region 'org-copy-subtree))) + (if (org-at-table-p) #'org-table-copy-region #'org-copy-subtree))) (defun org-cut-special () "Cut region in table or cut current subtree. -Calls `org-table-copy' or `org-cut-subtree', depending on context. -See the individual commands for more information." +Calls `org-table-cut-region' or `org-cut-subtree', depending on +context. See the individual commands for more information." (interactive) (call-interactively - (if (org-at-table-p) 'org-table-cut-region 'org-cut-subtree))) + (if (org-at-table-p) #'org-table-cut-region #'org-cut-subtree))) (defun org-paste-special (arg) "Paste rectangular region into table, or past subtree relative to level. @@ -20206,57 +20950,65 @@ See the individual commands for more information." (org-table-paste-rectangle) (org-paste-subtree arg))) -(defsubst org-in-fixed-width-region-p () - "Is point in a fixed-width region?" - (save-match-data - (eq 'fixed-width (org-element-type (org-element-at-point))))) - (defun org-edit-special (&optional arg) "Call a special editor for the element at point. When at a table, call the formula editor with `org-table-edit-formulas'. When in a source code block, call `org-edit-src-code'. When in a fixed-width region, call `org-edit-fixed-width-region'. +When in an export block, call `org-edit-export-block'. When at an #+INCLUDE keyword, visit the included file. +When at a footnote reference, call `org-edit-footnote-reference' On a link, call `ffap' to visit the link at point. Otherwise, return a user error." (interactive "P") (let ((element (org-element-at-point))) - (assert (not buffer-read-only) nil - "Buffer is read-only: %s" (buffer-name)) - (case (org-element-type element) - (src-block + (barf-if-buffer-read-only) + (pcase (org-element-type element) + (`src-block (if (not arg) (org-edit-src-code) - (let* ((info (org-babel-get-src-block-info)) - (lang (nth 0 info)) - (params (nth 2 info)) - (session (cdr (assq :session params)))) - (if (not session) (org-edit-src-code) - ;; At a src-block with a session and function called with - ;; an ARG: switch to the buffer related to the inferior - ;; process. - (switch-to-buffer + (let* ((info (org-babel-get-src-block-info)) + (lang (nth 0 info)) + (params (nth 2 info)) + (session (cdr (assq :session params)))) + (if (not session) (org-edit-src-code) + ;; At a src-block with a session and function called with + ;; an ARG: switch to the buffer related to the inferior + ;; process. + (switch-to-buffer (funcall (intern (concat "org-babel-prep-session:" lang)) session params)))))) - (keyword + (`keyword (if (member (org-element-property :key element) '("INCLUDE" "SETUPFILE")) - (find-file - (org-remove-double-quotes - (car (org-split-string (org-element-property :value element))))) + (org-open-link-from-string + (format "[[%s]]" + (expand-file-name + (let ((value (org-element-property :value element))) + (cond ((not (org-string-nw-p value)) + (user-error "No file to edit")) + ((string-match "\\`\"\\(.*?\\)\"" value) + (match-string 1 value)) + ((string-match "\\`[^ \t\"]\\S-*" value) + (match-string 0 value)) + (t (user-error "No valid file specified"))))))) (user-error "No special environment to edit here"))) - (table + (`table (if (eq (org-element-property :type element) 'table.el) - (org-edit-src-code) + (org-edit-table.el) (call-interactively 'org-table-edit-formulas))) ;; Only Org tables contain `table-row' type elements. - (table-row (call-interactively 'org-table-edit-formulas)) - ((example-block export-block) (org-edit-src-code)) - (fixed-width (org-edit-fixed-width-region)) - (otherwise - ;; No notable element at point. Though, we may be at a link, - ;; which is an object. Thus, scan deeper. - (if (eq (org-element-type (org-element-context element)) 'link) - (call-interactively 'ffap) - (user-error "No special environment to edit here")))))) + (`table-row (call-interactively 'org-table-edit-formulas)) + (`example-block (org-edit-src-code)) + (`export-block (org-edit-export-block)) + (`fixed-width (org-edit-fixed-width-region)) + (_ + ;; No notable element at point. Though, we may be at a link or + ;; a footnote reference, which are objects. Thus, scan deeper. + (let ((context (org-element-context element))) + (pcase (org-element-type context) + (`footnote-reference (org-edit-footnote-reference)) + (`inline-src-block (org-edit-inline-src-code)) + (`link (call-interactively #'ffap)) + (_ (user-error "No special environment to edit here")))))))) (defvar org-table-coordinate-overlays) ; defined in org-table.el (defun org-ctrl-c-ctrl-c (&optional arg) @@ -20305,240 +21057,314 @@ This command does many different things, depending on context: inhibited by setting `org-babel-no-eval-on-ctrl-c-ctrl-c'." (interactive "P") (cond - ((or (and (boundp 'org-clock-overlays) org-clock-overlays) - org-occur-highlights - org-latex-fragment-image-overlays) - (and (boundp 'org-clock-overlays) (org-clock-remove-overlays)) + ((or (bound-and-true-p org-clock-overlays) org-occur-highlights) + (when (boundp 'org-clock-overlays) (org-clock-remove-overlays)) (org-remove-occur-highlights) - (org-remove-latex-fragment-image-overlays) (message "Temporary highlights/overlays removed from current buffer")) - ((and (local-variable-p 'org-finish-function (current-buffer)) + ((and (local-variable-p 'org-finish-function) (fboundp org-finish-function)) (funcall org-finish-function)) + ((org-babel-hash-at-point)) ((run-hook-with-args-until-success 'org-ctrl-c-ctrl-c-hook)) (t - (let* ((context (org-element-context)) (type (org-element-type context))) - ;; Test if point is within a blank line. - (if (save-excursion (beginning-of-line) (looking-at "[ \t]*$")) - (or (run-hook-with-args-until-success 'org-ctrl-c-ctrl-c-final-hook) - (user-error "C-c C-c can do nothing useful at this location")) - (case type - ;; When at a link, act according to the parent instead. - (link (setq context (org-element-property :parent context)) - (setq type (org-element-type context))) - ;; Unsupported object types: refer to the first supported - ;; element or object containing it. - ((bold code entity export-snippet inline-babel-call inline-src-block - italic latex-fragment line-break macro strike-through subscript - superscript underline verbatim) - (while (and (setq context (org-element-property :parent context)) - (not (memq (setq type (org-element-type context)) - '(radio-target paragraph verse-block - table-cell))))))) - ;; For convenience: at the first line of a paragraph on the - ;; same line as an item, apply function on that item instead. - (when (eq type 'paragraph) - (let ((parent (org-element-property :parent context))) - (when (and (eq (org-element-type parent) 'item) - (= (point-at-bol) (org-element-property :begin parent))) - (setq context parent type 'item)))) - ;; Act according to type of element or object at point. - (case type - (clock (org-clock-update-time-maybe)) - (dynamic-block - (save-excursion - (goto-char (org-element-property :post-affiliated context)) - (org-update-dblock))) - (footnote-definition + (let* ((context + (org-element-lineage + (org-element-context) + ;; Limit to supported contexts. + '(babel-call clock dynamic-block footnote-definition + footnote-reference inline-babel-call inline-src-block + inlinetask item keyword node-property paragraph + plain-list property-drawer radio-target src-block + statistics-cookie table table-cell table-row + timestamp) + t)) + (type (org-element-type context))) + ;; For convenience: at the first line of a paragraph on the same + ;; line as an item, apply function on that item instead. + (when (eq type 'paragraph) + (let ((parent (org-element-property :parent context))) + (when (and (eq (org-element-type parent) 'item) + (= (line-beginning-position) + (org-element-property :begin parent))) + (setq context parent) + (setq type 'item)))) + ;; Act according to type of element or object at point. + ;; + ;; Do nothing on a blank line, except if it is contained in + ;; a src block. Hence, we first check if point is in such + ;; a block and then if it is at a blank line. + (pcase type + ((or `inline-src-block `src-block) + (unless org-babel-no-eval-on-ctrl-c-ctrl-c + (org-babel-eval-wipe-error-buffer) + (org-babel-execute-src-block + current-prefix-arg (org-babel-get-src-block-info nil context)))) + ((guard (org-match-line "[ \t]*$")) + (or (run-hook-with-args-until-success 'org-ctrl-c-ctrl-c-final-hook) + (user-error + (substitute-command-keys + "`\\[org-ctrl-c-ctrl-c]' can do nothing useful here")))) + ((or `babel-call `inline-babel-call) + (let ((info (org-babel-lob-get-info context))) + (when info (org-babel-execute-src-block nil info)))) + (`clock (org-clock-update-time-maybe)) + (`dynamic-block + (save-excursion (goto-char (org-element-property :post-affiliated context)) - (call-interactively 'org-footnote-action)) - (footnote-reference (call-interactively 'org-footnote-action)) - ((headline inlinetask) - (save-excursion (goto-char (org-element-property :begin context)) - (call-interactively 'org-set-tags))) - (item - ;; At an item: a double C-u set checkbox to "[-]" - ;; unconditionally, whereas a single one will toggle its - ;; presence. Without a universal argument, if the item - ;; has a checkbox, toggle it. Otherwise repair the list. - (let* ((box (org-element-property :checkbox context)) - (struct (org-element-property :structure context)) - (old-struct (copy-tree struct)) - (parents (org-list-parents-alist struct)) - (prevs (org-list-prevs-alist struct)) - (orderedp (org-not-nil (org-entry-get nil "ORDERED")))) - (org-list-set-checkbox - (org-element-property :begin context) struct - (cond ((equal arg '(16)) "[-]") - ((and (not box) (equal arg '(4))) "[ ]") - ((or (not box) (equal arg '(4))) nil) - ((eq box 'on) "[ ]") - (t "[X]"))) - ;; Mimic `org-list-write-struct' but with grabbing - ;; a return value from `org-list-struct-fix-box'. - (org-list-struct-fix-ind struct parents 2) - (org-list-struct-fix-item-end struct) - (org-list-struct-fix-bul struct prevs) - (org-list-struct-fix-ind struct parents) - (let ((block-item - (org-list-struct-fix-box struct parents prevs orderedp))) - (if (and box (equal struct old-struct)) - (if (equal arg '(16)) - (message "Checkboxes already reset") - (user-error "Cannot toggle this checkbox: %s" - (if (eq box 'on) - "all subitems checked" - "unchecked subitems"))) - (org-list-struct-apply-struct struct old-struct) - (org-update-checkbox-count-maybe)) - (when block-item - (message "Checkboxes were removed due to empty box at line %d" - (org-current-line block-item)))))) - (keyword - (let ((org-inhibit-startup-visibility-stuff t) - (org-startup-align-all-tables nil)) - (when (boundp 'org-table-coordinate-overlays) - (mapc 'delete-overlay org-table-coordinate-overlays) - (setq org-table-coordinate-overlays nil)) - (org-save-outline-visibility 'use-markers (org-mode-restart))) - (message "Local setup has been refreshed")) - (plain-list - ;; At a plain list, with a double C-u argument, set - ;; checkboxes of each item to "[-]", whereas a single one - ;; will toggle their presence according to the state of the - ;; first item in the list. Without an argument, repair the - ;; list. - (let* ((begin (org-element-property :contents-begin context)) - (beginm (move-marker (make-marker) begin)) - (struct (org-element-property :structure context)) - (old-struct (copy-tree struct)) - (first-box (save-excursion - (goto-char begin) - (looking-at org-list-full-item-re) - (match-string-no-properties 3))) - (new-box (cond ((equal arg '(16)) "[-]") - ((equal arg '(4)) (unless first-box "[ ]")) - ((equal first-box "[X]") "[ ]") - (t "[X]")))) - (cond - (arg - (mapc (lambda (pos) (org-list-set-checkbox pos struct new-box)) - (org-list-get-all-items - begin struct (org-list-prevs-alist struct)))) - ((and first-box (eq (point) begin)) - ;; For convenience, when point is at bol on the first - ;; item of the list and no argument is provided, simply - ;; toggle checkbox of that item, if any. - (org-list-set-checkbox begin struct new-box))) - (org-list-write-struct - struct (org-list-parents-alist struct) old-struct) - (org-update-checkbox-count-maybe) - (save-excursion (goto-char beginm) (org-list-send-list 'maybe)))) - ((property-drawer node-property) - (call-interactively 'org-property-action)) - ((radio-target target) - (call-interactively 'org-update-radio-target-regexp)) - (statistics-cookie - (call-interactively 'org-update-statistics-cookies)) - ((table table-cell table-row) - ;; At a table, recalculate every field and align it. Also - ;; send the table if necessary. If the table has - ;; a `table.el' type, just give up. At a table row or - ;; cell, maybe recalculate line but always align table. - (if (eq (org-element-property :type context) 'table.el) - (message "%s" "Use C-c ' to edit table.el tables") - (let ((org-enable-table-editor t)) - (if (or (eq type 'table) - ;; Check if point is at a TBLFM line. - (and (eq type 'table-row) - (= (point) (org-element-property :end context)))) - (save-excursion - (if (org-at-TBLFM-p) - (progn (require 'org-table) - (org-table-calc-current-TBLFM)) - (goto-char (org-element-property :contents-begin context)) - (org-call-with-arg 'org-table-recalculate (or arg t)) - (orgtbl-send-table 'maybe))) - (org-table-maybe-eval-formula) - (cond (arg (call-interactively 'org-table-recalculate)) - ((org-table-maybe-recalculate-line)) - (t (org-table-align))))))) - (timestamp (org-timestamp-change 0 'day)) - (otherwise - (or (run-hook-with-args-until-success 'org-ctrl-c-ctrl-c-final-hook) - (user-error - "C-c C-c can do nothing useful at this location"))))))))) + (org-update-dblock))) + (`footnote-definition + (goto-char (org-element-property :post-affiliated context)) + (call-interactively 'org-footnote-action)) + (`footnote-reference (call-interactively #'org-footnote-action)) + ((or `headline `inlinetask) + (save-excursion (goto-char (org-element-property :begin context)) + (call-interactively #'org-set-tags))) + (`item + ;; At an item: `C-u C-u' sets checkbox to "[-]" + ;; unconditionally, whereas `C-u' will toggle its presence. + ;; Without a universal argument, if the item has a checkbox, + ;; toggle it. Otherwise repair the list. + (let* ((box (org-element-property :checkbox context)) + (struct (org-element-property :structure context)) + (old-struct (copy-tree struct)) + (parents (org-list-parents-alist struct)) + (prevs (org-list-prevs-alist struct)) + (orderedp (org-not-nil (org-entry-get nil "ORDERED")))) + (org-list-set-checkbox + (org-element-property :begin context) struct + (cond ((equal arg '(16)) "[-]") + ((and (not box) (equal arg '(4))) "[ ]") + ((or (not box) (equal arg '(4))) nil) + ((eq box 'on) "[ ]") + (t "[X]"))) + ;; Mimic `org-list-write-struct' but with grabbing a return + ;; value from `org-list-struct-fix-box'. + (org-list-struct-fix-ind struct parents 2) + (org-list-struct-fix-item-end struct) + (org-list-struct-fix-bul struct prevs) + (org-list-struct-fix-ind struct parents) + (let ((block-item + (org-list-struct-fix-box struct parents prevs orderedp))) + (if (and box (equal struct old-struct)) + (if (equal arg '(16)) + (message "Checkboxes already reset") + (user-error "Cannot toggle this checkbox: %s" + (if (eq box 'on) + "all subitems checked" + "unchecked subitems"))) + (org-list-struct-apply-struct struct old-struct) + (org-update-checkbox-count-maybe)) + (when block-item + (message "Checkboxes were removed due to empty box at line %d" + (org-current-line block-item)))))) + (`keyword + (let ((org-inhibit-startup-visibility-stuff t) + (org-startup-align-all-tables nil)) + (when (boundp 'org-table-coordinate-overlays) + (mapc #'delete-overlay org-table-coordinate-overlays) + (setq org-table-coordinate-overlays nil)) + (org-save-outline-visibility 'use-markers (org-mode-restart))) + (message "Local setup has been refreshed")) + (`plain-list + ;; At a plain list, with a double C-u argument, set + ;; checkboxes of each item to "[-]", whereas a single one + ;; will toggle their presence according to the state of the + ;; first item in the list. Without an argument, repair the + ;; list. + (let* ((begin (org-element-property :contents-begin context)) + (beginm (move-marker (make-marker) begin)) + (struct (org-element-property :structure context)) + (old-struct (copy-tree struct)) + (first-box (save-excursion + (goto-char begin) + (looking-at org-list-full-item-re) + (match-string-no-properties 3))) + (new-box (cond ((equal arg '(16)) "[-]") + ((equal arg '(4)) (unless first-box "[ ]")) + ((equal first-box "[X]") "[ ]") + (t "[X]")))) + (cond + (arg + (dolist (pos + (org-list-get-all-items + begin struct (org-list-prevs-alist struct))) + (org-list-set-checkbox pos struct new-box))) + ((and first-box (eq (point) begin)) + ;; For convenience, when point is at bol on the first + ;; item of the list and no argument is provided, simply + ;; toggle checkbox of that item, if any. + (org-list-set-checkbox begin struct new-box))) + (org-list-write-struct + struct (org-list-parents-alist struct) old-struct) + (org-update-checkbox-count-maybe) + (save-excursion (goto-char beginm) (org-list-send-list 'maybe)))) + ((or `property-drawer `node-property) + (call-interactively #'org-property-action)) + (`radio-target + (call-interactively #'org-update-radio-target-regexp)) + (`statistics-cookie + (call-interactively #'org-update-statistics-cookies)) + ((or `table `table-cell `table-row) + ;; At a table, recalculate every field and align it. Also + ;; send the table if necessary. If the table has + ;; a `table.el' type, just give up. At a table row or cell, + ;; maybe recalculate line but always align table. + (if (eq (org-element-property :type context) 'table.el) + (message "%s" (substitute-command-keys "\\\ +Use `\\[org-edit-special]' to edit table.el tables")) + (let ((org-enable-table-editor t)) + (if (or (eq type 'table) + ;; Check if point is at a TBLFM line. + (and (eq type 'table-row) + (= (point) (org-element-property :end context)))) + (save-excursion + (if (org-at-TBLFM-p) + (progn (require 'org-table) + (org-table-calc-current-TBLFM)) + (goto-char (org-element-property :contents-begin context)) + (org-call-with-arg 'org-table-recalculate (or arg t)) + (orgtbl-send-table 'maybe))) + (org-table-maybe-eval-formula) + (cond (arg (call-interactively #'org-table-recalculate)) + ((org-table-maybe-recalculate-line)) + (t (org-table-align))))))) + (`timestamp (org-timestamp-change 0 'day)) + ((and `nil (guard (org-at-heading-p))) + ;; When point is on an unsupported object type, we can miss + ;; the fact that it also is at a heading. Handle it here. + (call-interactively #'org-set-tags)) + ((guard + (run-hook-with-args-until-success 'org-ctrl-c-ctrl-c-final-hook))) + (_ + (user-error + (substitute-command-keys + "`\\[org-ctrl-c-ctrl-c]' can do nothing useful here")))))))) (defun org-mode-restart () (interactive) - (let ((indent-status (org-bound-and-true-p org-indent-mode))) + (let ((indent-status (bound-and-true-p org-indent-mode))) (funcall major-mode) (hack-local-variables) - (when (and indent-status (not (org-bound-and-true-p org-indent-mode))) + (when (and indent-status (not (bound-and-true-p org-indent-mode))) (org-indent-mode -1))) (message "%s restarted" major-mode)) (defun org-kill-note-or-show-branches () - "If this is a Note buffer, abort storing the note. Else call `show-branches'." + "Abort storing current note, or call `outline-show-branches'." (interactive) (if (not org-finish-function) (progn - (hide-subtree) - (call-interactively 'show-branches)) + (outline-hide-subtree) + (call-interactively 'outline-show-branches)) (let ((org-note-abort t)) (funcall org-finish-function)))) +(defun org-delete-indentation (&optional arg) + "Join current line to previous and fix whitespace at join. + +If previous line is a headline add to headline title. Otherwise +the function calls `delete-indentation'. + +With a non-nil optional argument, join it to the following one." + (interactive "*P") + (if (save-excursion + (beginning-of-line (if arg 1 0)) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp))) + ;; At headline. + (let ((tags-column (when (match-beginning 5) + (save-excursion (goto-char (match-beginning 5)) + (current-column)))) + (string (concat " " (progn (when arg (forward-line 1)) + (org-trim (delete-and-extract-region + (line-beginning-position) + (line-end-position))))))) + (unless (bobp) (delete-region (point) (1- (point)))) + (goto-char (or (match-end 4) + (match-beginning 5) + (match-end 0))) + (skip-chars-backward " \t") + (save-excursion (insert string)) + ;; Adjust alignment of tags. + (cond + ((not tags-column)) ;no tags + (org-auto-align-tags (org-set-tags nil t)) + (t (org--align-tags-here tags-column)))) ;preserve tags column + (delete-indentation arg))) + (defun org-open-line (n) "Insert a new row in tables, call `open-line' elsewhere. -If `org-special-ctrl-o' is nil, just call `open-line' everywhere." +If `org-special-ctrl-o' is nil, just call `open-line' everywhere. +As a special case, when a document starts with a table, allow to +call `open-line' on the very first character." (interactive "*p") - (cond - ((not org-special-ctrl-o) - (open-line n)) - ((org-at-table-p) - (org-table-insert-row)) - (t - (open-line n)))) + (if (and org-special-ctrl-o (/= (point) 1) (org-at-table-p)) + (org-table-insert-row) + (open-line n))) (defun org-return (&optional indent) "Goto next table row or insert a newline. + Calls `org-table-next-row' or `newline', depending on context. -See the individual commands for more information." + +When optional INDENT argument is non-nil, call +`newline-and-indent' instead of `newline'. + +When `org-return-follows-link' is non-nil and point is on +a timestamp or a link, call `org-open-at-point'. However, it +will not happen if point is in a table or on a \"dead\" +object (e.g., within a comment). In these case, you need to use +`org-open-at-point' directly." (interactive) - (let (org-ts-what) + (let ((context (if org-return-follows-link (org-element-context) + (org-element-at-point)))) (cond - ((or (bobp) (org-in-src-block-p)) - (if indent (newline-and-indent) (newline))) - ((org-at-table-p) + ;; In a table, call `org-table-next-row'. + ((or (and (eq (org-element-type context) 'table) + (>= (point) (org-element-property :contents-begin context)) + (< (point) (org-element-property :contents-end context))) + (org-element-lineage context '(table-row table-cell) t)) (org-table-justify-field-maybe) - (call-interactively 'org-table-next-row)) - ;; when `newline-and-indent' is called within a list, make sure - ;; text moved stays inside the item. - ((and (org-in-item-p) indent) - (if (and (org-at-item-p) (>= (point) (match-end 0))) - (progn - (save-match-data (newline)) - (org-indent-line-to (length (match-string 0)))) - (let ((ind (org-get-indentation))) - (newline) - (if (org-looking-back org-list-end-re) - (org-indent-line) - (org-indent-line-to ind))))) - ((and org-return-follows-link - (org-at-timestamp-p t) - (not (eq org-ts-what 'after))) - (org-follow-timestamp-link)) + (call-interactively #'org-table-next-row)) + ;; On a link or a timestamp, call `org-open-at-point' if + ;; `org-return-follows-link' allows it. Tolerate fuzzy + ;; locations, e.g., in a comment, as `org-open-at-point'. ((and org-return-follows-link - (let ((tprop (get-text-property (point) 'face))) - (or (eq tprop 'org-link) - (and (listp tprop) (memq 'org-link tprop))))) - (call-interactively 'org-open-at-point)) - ((and (org-at-heading-p) - (looking-at - (org-re "\\([ \t]+\\(:[[:alnum:]_@#%:]+:\\)\\)[ \t]*$"))) - (org-show-entry) - (end-of-line 1) - (newline)) + (or (org-in-regexp org-ts-regexp-both nil t) + (org-in-regexp org-tsr-regexp-both nil t) + (org-in-regexp org-any-link-re nil t))) + (call-interactively #'org-open-at-point)) + ;; Insert newline in heading, but preserve tags. + ((and (not (bolp)) + (save-excursion (beginning-of-line) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp)))) + ;; At headline. Split line. However, if point is on keyword, + ;; priority cookie or tags, do not break any of them: add + ;; a newline after the headline instead. + (let ((tags-column (and (match-beginning 5) + (save-excursion (goto-char (match-beginning 5)) + (current-column)))) + (string + (when (and (match-end 4) (org-point-in-group (point) 4)) + (delete-and-extract-region (point) (match-end 4))))) + ;; Adjust tag alignment. + (cond + ((not (and tags-column string))) + (org-auto-align-tags (org-set-tags nil t)) + (t (org--align-tags-here tags-column))) ;preserve tags column + (end-of-line) + (org-show-entry) + (if indent (newline-and-indent) (newline)) + (when string (save-excursion (insert (org-trim string)))))) + ;; In a list, make sure indenting keeps trailing text within. + ((and indent + (not (eolp)) + (org-element-lineage context '(item))) + (let ((trailing-data + (delete-and-extract-region (point) (line-end-position)))) + (newline-and-indent) + (save-excursion (insert trailing-data)))) (t (if indent (newline-and-indent) (newline)))))) (defun org-return-indent () @@ -20571,146 +21397,16 @@ Calls `org-table-insert-hline', `org-toggle-item', or (call-interactively 'org-table-insert-hline)) ((org-region-active-p) (call-interactively 'org-toggle-item)) - ((org-in-item-p) - (call-interactively 'org-cycle-list-bullet)) - (t - (call-interactively 'org-toggle-item)))) - -(defun org-toggle-item (arg) - "Convert headings or normal lines to items, items to normal lines. -If there is no active region, only the current line is considered. - -If the first non blank line in the region is a headline, convert -all headlines to items, shifting text accordingly. - -If it is an item, convert all items to normal lines. - -If it is normal text, change region into a list of items. -With a prefix argument ARG, change the region in a single item." - (interactive "P") - (let ((shift-text - (function - ;; Shift text in current section to IND, from point to END. - ;; The function leaves point to END line. - (lambda (ind end) - (let ((min-i 1000) (end (copy-marker end))) - ;; First determine the minimum indentation (MIN-I) of - ;; the text. - (save-excursion - (catch 'exit - (while (< (point) end) - (let ((i (org-get-indentation))) - (cond - ;; Skip blank lines and inline tasks. - ((looking-at "^[ \t]*$")) - ((looking-at org-outline-regexp-bol)) - ;; We can't find less than 0 indentation. - ((zerop i) (throw 'exit (setq min-i 0))) - ((< i min-i) (setq min-i i)))) - (forward-line)))) - ;; Then indent each line so that a line indented to - ;; MIN-I becomes indented to IND. Ignore blank lines - ;; and inline tasks in the process. - (let ((delta (- ind min-i))) - (while (< (point) end) - (unless (or (looking-at "^[ \t]*$") - (looking-at org-outline-regexp-bol)) - (org-indent-line-to (+ (org-get-indentation) delta))) - (forward-line))))))) - (skip-blanks - (function - ;; Return beginning of first non-blank line, starting from - ;; line at POS. - (lambda (pos) - (save-excursion - (goto-char pos) - (skip-chars-forward " \r\t\n") - (point-at-bol))))) - beg end) - ;; Determine boundaries of changes. - (if (org-region-active-p) - (setq beg (funcall skip-blanks (region-beginning)) - end (copy-marker (region-end))) - (setq beg (funcall skip-blanks (point-at-bol)) - end (copy-marker (point-at-eol)))) - ;; Depending on the starting line, choose an action on the text - ;; between BEG and END. - (org-with-limited-levels - (save-excursion - (goto-char beg) - (cond - ;; Case 1. Start at an item: de-itemize. Note that it only - ;; happens when a region is active: `org-ctrl-c-minus' - ;; would call `org-cycle-list-bullet' otherwise. - ((org-at-item-p) - (while (< (point) end) - (when (org-at-item-p) - (skip-chars-forward " \t") - (delete-region (point) (match-end 0))) - (forward-line))) - ;; Case 2. Start at an heading: convert to items. - ((org-at-heading-p) - (let* ((bul (org-list-bullet-string "-")) - (bul-len (length bul)) - ;; Indentation of the first heading. It should be - ;; relative to the indentation of its parent, if any. - (start-ind (save-excursion - (cond - ((not org-adapt-indentation) 0) - ((not (outline-previous-heading)) 0) - (t (length (match-string 0)))))) - ;; Level of first heading. Further headings will be - ;; compared to it to determine hierarchy in the list. - (ref-level (org-reduced-level (org-outline-level)))) - (while (< (point) end) - (let* ((level (org-reduced-level (org-outline-level))) - (delta (max 0 (- level ref-level)))) - ;; If current headline is less indented than the first - ;; one, set it as reference, in order to preserve - ;; subtrees. - (when (< level ref-level) (setq ref-level level)) - (replace-match bul t t) - (org-indent-line-to (+ start-ind (* delta bul-len))) - ;; Ensure all text down to END (or SECTION-END) belongs - ;; to the newly created item. - (let ((section-end (save-excursion - (or (outline-next-heading) (point))))) - (forward-line) - (funcall shift-text - (+ start-ind (* (1+ delta) bul-len)) - (min end section-end))))))) - ;; Case 3. Normal line with ARG: make the first line of region - ;; an item, and shift indentation of others lines to - ;; set them as item's body. - (arg (let* ((bul (org-list-bullet-string "-")) - (bul-len (length bul)) - (ref-ind (org-get-indentation))) - (skip-chars-forward " \t") - (insert bul) - (forward-line) - (while (< (point) end) - ;; Ensure that lines less indented than first one - ;; still get included in item body. - (funcall shift-text - (+ ref-ind bul-len) - (min end (save-excursion (or (outline-next-heading) - (point))))) - (forward-line)))) - ;; Case 4. Normal line without ARG: turn each non-item line - ;; into an item. - (t - (while (< (point) end) - (unless (or (org-at-heading-p) (org-at-item-p)) - (if (looking-at "\\([ \t]*\\)\\(\\S-\\)") - (replace-match - (concat "\\1" (org-list-bullet-string "-") "\\2")))) - (forward-line)))))))) + ((org-in-item-p) + (call-interactively 'org-cycle-list-bullet)) + (t + (call-interactively 'org-toggle-item)))) (defun org-toggle-heading (&optional nstars) "Convert headings to normal text, or items or text to headings. If there is no active region, only convert the current line. -With a \\[universal-argument] prefix, convert the whole list at +With a `\\[universal-argument]' prefix, convert the whole list at point into heading. In a region: @@ -20746,7 +21442,7 @@ number of stars to add." ;; do not consider the last line to be in the region. (when (and current-prefix-arg (org-at-item-p)) - (if (listp current-prefix-arg) (setq current-prefix-arg 1)) + (when (listp current-prefix-arg) (setq current-prefix-arg 1)) (org-mark-element)) (if (org-region-active-p) @@ -20771,31 +21467,17 @@ number of stars to add." ;; Case 2. Started at an item: change items into headlines. ;; One star will be added by `org-list-to-subtree'. ((org-at-item-p) - (let* ((stars (make-string - ;; subtract the star that will be added again by - ;; `org-list-to-subtree' - (if (numberp nstars) (1- nstars) - (or (org-current-level) 0)) - ?*)) - (add-stars - (cond (nstars "") ; stars from prefix only - ((equal stars "") "") ; before first heading - (org-odd-levels-only "*") ; inside heading, odd - (t "")))) ; inside heading, oddeven - (while (< (point) end) - (when (org-at-item-p) - ;; Pay attention to cases when region ends before list. - (let* ((struct (org-list-struct)) - (list-end (min (org-list-get-bottom-point struct) (1+ end)))) - (save-restriction - (narrow-to-region (point) list-end) - (insert - (org-list-to-subtree - (org-list-parse-list t) - `(:istart (concat ',stars ',add-stars (funcall get-stars depth)) - :icount (concat ',stars ',add-stars (funcall get-stars depth))))))) - (setq toggled t)) - (forward-line)))) + (while (< (point) end) + (when (org-at-item-p) + ;; Pay attention to cases when region ends before list. + (let* ((struct (org-list-struct)) + (list-end + (min (org-list-get-bottom-point struct) (1+ end)))) + (save-restriction + (narrow-to-region (point) list-end) + (insert (org-list-to-subtree (org-list-to-lisp t)) "\n"))) + (setq toggled t)) + (forward-line))) ;; Case 3. Started at normal text: make every line an heading, ;; skipping headlines and items. (t (let* ((stars @@ -20807,7 +21489,7 @@ number of stars to add." (org-odd-levels-only "**") ; inside heading, odd (t "*"))) ; inside heading, oddeven (rpl (concat stars add-stars " ")) - (lend (if (listp nstars) (save-excursion (end-of-line) (point))))) + (lend (when (listp nstars) (save-excursion (end-of-line) (point))))) (while (< (point) (if (equal nstars '(4)) lend end)) (when (and (not (or (org-at-heading-p) (org-at-item-p) (org-at-comment-p))) (looking-at "\\([ \t]*\\)\\(\\S-\\)")) @@ -20822,17 +21504,8 @@ on context. See the individual commands for more information." (interactive) (org-check-before-invisible-edit 'insert) (or (run-hook-with-args-until-success 'org-metareturn-hook) - (let* ((element (org-element-at-point)) - (type (org-element-type element))) - (when (eq type 'table-row) - (setq element (org-element-property :parent element)) - (setq type 'table)) - (if (and (eq type 'table) - (eq (org-element-property :type element) 'org) - (>= (point) (org-element-property :contents-begin element)) - (< (point) (org-element-property :contents-end element))) - (call-interactively 'org-table-wrap-region) - (call-interactively 'org-insert-heading))))) + (call-interactively (if (org-at-table-p) #'org-table-wrap-region + #'org-insert-heading)))) ;;; Menu entries @@ -20841,7 +21514,7 @@ on context. See the individual commands for more information." (and (not (org-before-first-heading-p)) (not (org-at-table-p)))) -;; Define the Org-mode menus +;; Define the Org mode menus (easy-menu-define org-tbl-menu org-mode-map "Tbl menu" '("Tbl" ["Align" org-ctrl-c-ctrl-c :active (org-at-table-p)] @@ -20888,11 +21561,11 @@ on context. See the individual commands for more information." ["Which Column?" org-table-current-column (org-at-table-p)]) ["Debug Formulas" org-table-toggle-formula-debugger - :style toggle :selected (org-bound-and-true-p org-table-formula-debug)] + :style toggle :selected (bound-and-true-p org-table-formula-debug)] ["Show Col/Row Numbers" org-table-toggle-coordinate-overlays :style toggle - :selected (org-bound-and-true-p org-table-overlay-coordinates)] + :selected (bound-and-true-p org-table-overlay-coordinates)] "--" ["Create" org-table-create (and (not (org-at-table-p)) org-enable-table-editor)] @@ -20900,7 +21573,11 @@ on context. See the individual commands for more information." ["Import from File" org-table-import (not (org-at-table-p))] ["Export to File" org-table-export (org-at-table-p)] "--" - ["Create/Convert from/to table.el" org-table-create-with-table.el t])) + ["Create/Convert from/to table.el" org-table-create-with-table.el t] + "--" + ("Plot" + ["Ascii plot" orgtbl-ascii-plot :active (org-at-table-p) :keys "C-c \" a"] + ["Gnuplot" org-plot/gnuplot :active (org-at-table-p) :keys "C-c \" g"]))) (easy-menu-define org-org-menu org-mode-map "Org menu" '("Org" @@ -20909,7 +21586,7 @@ on context. See the individual commands for more information." ["Cycle Global Visibility" org-shifttab :active (not (org-at-table-p))] ["Sparse Tree..." org-sparse-tree t] ["Reveal Context" org-reveal t] - ["Show All" show-all t] + ["Show All" outline-show-all t] "--" ["Subtree to indirect buffer" org-tree-to-indirect-buffer t]) "--" @@ -20925,8 +21602,8 @@ on context. See the individual commands for more information." ("Edit Structure" ["Refile Subtree" org-refile (org-in-subtree-not-table-p)] "--" - ["Move Subtree Up" org-shiftmetaup (org-in-subtree-not-table-p)] - ["Move Subtree Down" org-shiftmetadown (org-in-subtree-not-table-p)] + ["Move Subtree Up" org-metaup (org-at-heading-p)] + ["Move Subtree Down" org-metadown (org-at-heading-p)] "--" ["Copy Subtree" org-copy-special (org-in-subtree-not-table-p)] ["Cut Subtree" org-cut-special (org-in-subtree-not-table-p)] @@ -21012,7 +21689,7 @@ on context. See the individual commands for more information." "--" ["Set property" org-set-property (not (org-before-first-heading-p))] ["Column view of properties" org-columns t] - ["Insert Column View DBlock" org-insert-columns-dblock t]) + ["Insert Column View DBlock" org-columns-insert-dblock t]) ("Dates and Scheduling" ["Timestamp" org-time-stamp (not (org-before-first-heading-p))] ["Timestamp (inactive)" org-time-stamp-inactive (not (org-before-first-heading-p))] @@ -21073,9 +21750,7 @@ on context. See the individual commands for more information." ["Insert math symbol" cdlatex-math-symbol (fboundp 'cdlatex-math-symbol)] ["Modify math symbol" org-cdlatex-math-modify (org-inside-LaTeX-fragment-p)] - ["Insert citation" org-reftex-citation t] - "--" - ["Template for BEAMER" (org-beamer-insert-options-template) t]) + ["Insert citation" org-reftex-citation t]) "--" ("MobileOrg" ["Push Files and Views" org-mobile-push t] @@ -21101,20 +21776,20 @@ on context. See the individual commands for more information." )) (defun org-info (&optional node) - "Read documentation for Org-mode in the info system. + "Read documentation for Org in the info system. With optional NODE, go directly to that node." (interactive) (info (format "(org)%s" (or node "")))) ;;;###autoload (defun org-submit-bug-report () - "Submit a bug report on Org-mode via mail. + "Submit a bug report on Org via mail. Don't hesitate to report any problems or inaccurate documentation. If you don't have setup sending mail from (X)Emacs, please copy the output buffer into your mail program, as it gives us important -information about your Org-mode version and configuration." +information about your Org version and configuration." (interactive) (require 'reporter) (defvar reporter-prompt-for-summary-p) @@ -21126,12 +21801,12 @@ information about your Org-mode version and configuration." (org-version nil 'full) (let (list) (save-window-excursion - (org-pop-to-buffer-same-window (get-buffer-create "*Warn about privacy*")) + (pop-to-buffer-same-window (get-buffer-create "*Warn about privacy*")) (delete-other-windows) (erase-buffer) - (insert "You are about to submit a bug report to the Org-mode mailing list. + (insert "You are about to submit a bug report to the Org mailing list. -We would like to add your full Org-mode and Outline configuration to the +We would like to add your full Org and Outline configuration to the bug report. This greatly simplifies the work of the maintainer and other experts on the mailing list. @@ -21141,7 +21816,7 @@ appear in the form of file names, tags, todo states, or search strings. If you answer yes to the prompt, you might want to check and remove such private information before sending the email.") (add-text-properties (point-min) (point-max) '(face org-warning)) - (when (yes-or-no-p "Include your Org-mode configuration ") + (when (yes-or-no-p "Include your Org configuration ") (mapatoms (lambda (v) (and (boundp v) @@ -21160,11 +21835,11 @@ what in fact did happen. You don't know how to make a good report? See http://orgmode.org/manual/Feedback.html#Feedback -Your bug report will be posted to the Org-mode mailing list. +Your bug report will be posted to the Org mailing list. ------------------------------------------------------------------------") (save-excursion - (if (re-search-backward "^\\(Subject: \\)Org-mode version \\(.*?\\);[ \t]*\\(.*\\)" nil t) - (replace-match "\\1Bug: \\3 [\\2]"))))) + (when (re-search-backward "^\\(Subject: \\)Org mode version \\(.*?\\);[ \t]*\\(.*\\)" nil t) + (replace-match "\\1Bug: \\3 [\\2]"))))) (defun org-install-agenda-files-menu () @@ -21172,7 +21847,7 @@ Your bug report will be posted to the Org-mode mailing list. (save-excursion (while bl (set-buffer (pop bl)) - (if (derived-mode-p 'org-mode) (setq bl nil))) + (when (derived-mode-p 'org-mode) (setq bl nil))) (when (derived-mode-p 'org-mode) (easy-menu-change '("Org") "File List for Agenda" @@ -21190,7 +21865,7 @@ Your bug report will be posted to the Org-mode mailing list. (defun org-require-autoloaded-modules () (interactive) - (mapc 'require + (mapc #'require '(org-agenda org-archive org-attach org-clock org-colview org-id org-table org-timer))) @@ -21203,13 +21878,8 @@ With prefix arg UNCOMPILED, load the uncompiled versions." (let* ((org-dir (org-find-library-dir "org")) (contrib-dir (or (org-find-library-dir "org-contribdir") org-dir)) (feature-re "^\\(org\\|ob\\|ox\\)\\(-.*\\)?") - (remove-re (mapconcat 'identity - (mapcar (lambda (f) (concat "^" f "$")) - (list (if (featurep 'xemacs) - "org-colview" - "org-colview-xemacs") - "org" "org-loaddefs" "org-version")) - "\\|")) + (remove-re (format "\\`%s\\'" + (regexp-opt '("org" "org-loaddefs" "org-version")))) (feats (delete-dups (mapcar 'file-name-sans-extension (mapcar 'file-name-nondirectory @@ -21241,9 +21911,9 @@ With prefix arg UNCOMPILED, load the uncompiled versions." 't) f)) lfeat))) - (if load-uncore - (message "The following feature%s found in load-path, please check if that's correct:\n%s" - (if (> (length load-uncore) 1) "s were" " was") load-uncore)) + (when load-uncore + (message "The following feature%s found in load-path, please check if that's correct:\n%s" + (if (> (length load-uncore) 1) "s were" " was") load-uncore)) (if load-misses (message "Some error occurred while reloading Org feature%s\n%s\nPlease check *Messages*!\n%s" (if (> (length load-misses) 1) "s" "") load-misses (org-version nil 'full)) @@ -21258,7 +21928,7 @@ With prefix arg UNCOMPILED, load the uncompiled versions." (customize-browse 'org)) (defun org-create-customize-menu () - "Create a full customization menu for Org-mode, insert it into the menu." + "Create a full customization menu for Org mode, insert it into the menu." (interactive) (org-load-modules-maybe) (org-require-autoloaded-modules) @@ -21281,9 +21951,9 @@ With prefix arg UNCOMPILED, load the uncompiled versions." ;;; Generally useful functions -(defun org-get-at-bol (property) - "Get text property PROPERTY at beginning of line." - (get-text-property (point-at-bol) property)) +(defun org-get-at-eol (property n) + "Get text property PROPERTY at the end of line less N characters." + (get-text-property (- (point-at-eol) n) property)) (defun org-find-text-property-in-string (prop s) "Return the first non-nil value of property PROP in string S." @@ -21291,19 +21961,9 @@ With prefix arg UNCOMPILED, load the uncompiled versions." (get-text-property (or (next-single-property-change 0 prop s) 0) prop s))) -(defun org-display-warning (message) ;; Copied from Emacs-Muse +(defun org-display-warning (message) "Display the given MESSAGE as a warning." - (if (fboundp 'display-warning) - (display-warning 'org message - (if (featurep 'xemacs) 'warning :warning)) - (let ((buf (get-buffer-create "*Org warnings*"))) - (with-current-buffer buf - (goto-char (point-max)) - (insert "Warning (Org): " message) - (unless (bolp) - (newline))) - (display-buffer buf) - (sit-for 0)))) + (display-warning 'org message :warning)) (defun org-eval (form) "Eval FORM and return result." @@ -21322,17 +21982,6 @@ With prefix arg UNCOMPILED, load the uncompiled versions." (>= (match-end 0) pos) start)))) -(defun org-in-commented-line () - "Is point in a line starting with `#'?" - (equal (char-after (point-at-bol)) ?#)) - -(defun org-in-indented-comment-line () - "Is point in a line starting with `#' after some white space?" - (save-excursion - (save-match-data - (goto-char (point-at-bol)) - (looking-at "[ \t]*#")))) - (defun org-in-verbatim-emphasis () (save-match-data (and (org-in-regexp org-emph-re 2) @@ -21340,14 +21989,35 @@ With prefix arg UNCOMPILED, load the uncompiled versions." (<= (point) (match-end 4)) (member (match-string 3) '("=" "~"))))) +(defun org-overlay-display (ovl text &optional face evap) + "Make overlay OVL display TEXT with face FACE." + (overlay-put ovl 'display text) + (if face (overlay-put ovl 'face face)) + (if evap (overlay-put ovl 'evaporate t))) + +(defun org-overlay-before-string (ovl text &optional face evap) + "Make overlay OVL display TEXT with face FACE." + (if face (org-add-props text nil 'face face)) + (overlay-put ovl 'before-string text) + (if evap (overlay-put ovl 'evaporate t))) + +(defun org-find-overlays (prop &optional pos delete) + "Find all overlays specifying PROP at POS or point. +If DELETE is non-nil, delete all those overlays." + (let (found) + (dolist (ov (overlays-at (or pos (point))) found) + (cond ((not (overlay-get ov prop))) + (delete (delete-overlay ov)) + (t (push ov found)))))) + (defun org-goto-marker-or-bmk (marker &optional bookmark) "Go to MARKER, widen if necessary. When marker is not live, try BOOKMARK." (if (and marker (marker-buffer marker) (buffer-live-p (marker-buffer marker))) (progn - (org-pop-to-buffer-same-window (marker-buffer marker)) - (if (or (> marker (point-max)) (< marker (point-min))) - (widen)) + (pop-to-buffer-same-window (marker-buffer marker)) + (when (or (> marker (point-max)) (< marker (point-min))) + (widen)) (goto-char marker) (org-show-context 'org-goto)) (if bookmark @@ -21390,7 +22060,7 @@ upon the next fontification round." l)) (defun org-shorten-string (s maxlength) - "Shorten string S so tht it is no longer than MAXLENGTH characters. + "Shorten string S so that it is no longer than MAXLENGTH characters. If the string is shorter or has length MAXLENGTH, just return the original string. If it is longer, the functions finds a space in the string, breaks this string off at that locations and adds three dots @@ -21410,8 +22080,8 @@ if necessary." "Get the indentation of the current line, interpreting tabs. When LINE is given, assume it represents a line and compute its indentation." (if line - (if (string-match "^ *" (org-remove-tabs line)) - (match-end 0)) + (when (string-match "^ *" (org-remove-tabs line)) + (match-end 0)) (save-excursion (beginning-of-line 1) (skip-chars-forward " \t") @@ -21448,35 +22118,45 @@ leave it alone. If it is larger than ind, set it to the target." (let* ((l (org-remove-tabs line)) (i (org-get-indentation l)) (i1 (car ind)) (i2 (cdr ind))) - (if (>= i i2) (setq l (substring line i2))) + (when (>= i i2) (setq l (substring line i2))) (if (> i1 0) (concat (make-string i1 ?\ ) l) l))) (defun org-remove-indentation (code &optional n) - "Remove the maximum common indentation from the lines in CODE. -N may optionally be the number of spaces to remove." + "Remove maximum common indentation in string CODE and return it. +N may optionally be the number of columns to remove. Return CODE +as-is if removal failed." (with-temp-buffer (insert code) - (org-do-remove-indentation n) - (buffer-string))) + (if (org-do-remove-indentation n) (buffer-string) code))) (defun org-do-remove-indentation (&optional n) - "Remove the maximum common indentation from the buffer." - (untabify (point-min) (point-max)) - (let ((min 10000) re) - (if n - (setq min n) - (goto-char (point-min)) - (while (re-search-forward "^ *[^ \n]" nil t) - (setq min (min min (1- (- (match-end 0) (match-beginning 0))))))) - (unless (or (= min 0) (= min 10000)) - (setq re (format "^ \\{%d\\}" min)) - (goto-char (point-min)) - (while (re-search-forward re nil t) - (replace-match "") - (end-of-line 1)) - min))) + "Remove the maximum common indentation from the buffer. +When optional argument N is a positive integer, remove exactly +that much characters from indentation, if possible. Return nil +if it fails." + (catch :exit + (goto-char (point-min)) + ;; Find maximum common indentation, if not specified. + (let ((n (or n + (let ((min-ind (point-max))) + (save-excursion + (while (re-search-forward "^[ \t]*\\S-" nil t) + (let ((ind (1- (current-column)))) + (if (zerop ind) (throw :exit nil) + (setq min-ind (min min-ind ind)))))) + min-ind)))) + (if (zerop n) (throw :exit nil) + ;; Remove exactly N indentation, but give up if not possible. + (while (not (eobp)) + (let ((ind (progn (skip-chars-forward " \t") (current-column)))) + (cond ((eolp) (delete-region (line-beginning-position) (point))) + ((< ind n) (throw :exit nil)) + (t (indent-line-to (- ind n)))) + (forward-line))) + ;; Signal success. + t)))) (defun org-fill-template (template alist) "Find each %key of ALIST in TEMPLATE and replace it." @@ -21496,12 +22176,6 @@ N may optionally be the number of spaces to remove." (or (buffer-base-buffer buffer) buffer))) -(defun org-trim (s) - "Remove whitespace at beginning and end of string." - (if (string-match "\\`[ \t\n\r]+" s) (setq s (replace-match "" t t s))) - (if (string-match "[ \t\n\r]+\\'" s) (setq s (replace-match "" t t s))) - s) - (defun org-wrap (string &optional width lines) "Wrap string to either a number of lines, or a width in characters. If WIDTH is non-nil, the string is wrapped to that width, however many lines @@ -21539,13 +22213,12 @@ The return value is a list of lines, without newlines at the end." (defun org-split-string (string &optional separators) "Splits STRING into substrings at SEPARATORS. +SEPARATORS is a regular expression. No empty strings are returned if there are matches at the beginning and end of string." - (let ((rexp (or separators "[ \f\t\n\r\v]+")) - (start 0) - notfirst - (list nil)) - (while (and (string-match rexp string + ;; FIXME: why not use (split-string STRING SEPARATORS t)? + (let ((start 0) notfirst list) + (while (and (string-match (or separators "[ \f\t\n\r\v]+") string (if (and notfirst (= start (match-beginning 0)) (< start (length string))) @@ -21555,14 +22228,10 @@ and end of string." (or (eq (match-beginning 0) 0) (and (eq (match-beginning 0) (match-end 0)) (eq (match-beginning 0) start)) - (setq list - (cons (substring string start (match-beginning 0)) - list))) + (push (substring string start (match-beginning 0)) list)) (setq start (match-end 0))) (or (eq start (length string)) - (setq list - (cons (substring string start) - list))) + (push (substring string start) list)) (nreverse list))) (defun org-quote-vert (s) @@ -21579,10 +22248,8 @@ and end of string." "Whether point is in a code source block. When INSIDE is non-nil, don't consider we are within a src block when point is at #+BEGIN_SRC or #+END_SRC." - (let ((case-fold-search t) ov) - (or (and (setq ov (overlays-at (point))) - (memq 'org-block-background - (overlay-properties (car ov)))) + (let ((case-fold-search t)) + (or (and (eq (get-char-property (point) 'src-block) t)) (and (not inside) (save-match-data (save-excursion @@ -21604,13 +22271,13 @@ contexts are: :item on the first line of a plain list item :item-bullet on the bullet/number of a plain list item :checkbox on the checkbox in a plain list item -:table in an org-mode table +:table in an Org table :table-special on a special filed in a table :table-table in a table.el table :clocktable in a clocktable :src-block in a source block :link on a hyperlink -:keyword on a keyword: SCHEDULED, DEADLINE, CLOSE, COMMENT, QUOTE. +:keyword on a keyword: SCHEDULED, DEADLINE, CLOSE, COMMENT. :target on a <> :radio-target on a <<>> :latex-fragment on a LaTeX fragment @@ -21635,8 +22302,8 @@ and :keyword." (push (org-point-in-group p 4 :tags) clist)) (goto-char p) (skip-chars-backward "^[\n\r \t") (or (bobp) (backward-char 1)) - (if (looking-at "\\[#[A-Z0-9]\\]") - (push (org-point-in-group p 0 :priority) clist))) + (when (looking-at "\\[#[A-Z0-9]\\]") + (push (org-point-in-group p 0 :priority) clist))) ((org-at-item-p) (push (org-point-in-group p 2 :item-bullet) clist) @@ -21648,10 +22315,10 @@ and :keyword." ((org-at-table-p) (push (list :table (org-table-begin) (org-table-end)) clist) - (if (memq 'org-formula faces) - (push (list :table-special - (previous-single-property-change p 'face) - (next-single-property-change p 'face)) clist))) + (when (memq 'org-formula faces) + (push (list :table-special + (previous-single-property-change p 'face) + (next-single-property-change p 'face)) clist))) ((org-at-table-p 'any) (push (list :table-table) clist))) (goto-char p) @@ -21660,16 +22327,16 @@ and :keyword." ;; New the "medium" contexts: clocktables, source blocks (cond ((org-in-clocktable-p) (push (list :clocktable - (and (or (looking-at "#\\+BEGIN: clocktable") - (search-backward "#+BEGIN: clocktable" nil t)) - (match-beginning 0)) - (and (re-search-forward "#\\+END:?" nil t) + (and (or (looking-at "[ \t]*\\(#\\+BEGIN: clocktable\\)") + (re-search-backward "[ \t]*\\(#+BEGIN: clocktable\\)" nil t)) + (match-beginning 1)) + (and (re-search-forward "[ \t]*#\\+END:?" nil t) (match-end 0))) clist)) ((org-in-src-block-p) (push (list :src-block - (and (or (looking-at "#\\+BEGIN_SRC") - (search-backward "#+BEGIN_SRC" nil t)) - (match-beginning 0)) + (and (or (looking-at "[ \t]*\\(#\\+BEGIN_SRC\\)") + (re-search-backward "[ \t]*\\(#+BEGIN_SRC\\)" nil t)) + (match-beginning 1)) (and (search-forward "#+END_SRC" nil t) (match-beginning 0))) clist)))) (goto-char p) @@ -21689,14 +22356,14 @@ and :keyword." ((org-at-target-p) (push (org-point-in-group p 0 :target) clist) (goto-char (1- (match-beginning 0))) - (if (looking-at org-radio-target-regexp) - (push (org-point-in-group p 0 :radio-target) clist)) + (when (looking-at org-radio-target-regexp) + (push (org-point-in-group p 0 :radio-target) clist)) (goto-char p)) - ((setq o (car (delq nil - (mapcar - (lambda (x) - (if (memq x org-latex-fragment-image-overlays) x)) - (overlays-at (point)))))) + ((setq o (cl-some + (lambda (o) + (and (eq (overlay-get o 'org-overlay-type) 'org-latex-overlay) + o)) + (overlays-at (point)))) (push (list :latex-fragment (overlay-start o) (overlay-end o)) clist) (push (list :latex-preview @@ -21708,35 +22375,27 @@ and :keyword." (setq clist (nreverse (delq nil clist))) clist)) -;; FIXME: Compare with at-regexp-p Do we need both? -(defun org-in-regexp (re &optional nlines visually) - "Check if point is inside a match of regexp. -Normally only the current line is checked, but you can include NLINES extra -lines both before and after point into the search. -If VISUALLY is set, require that the cursor is not after the match but -really on, so that the block visually is on the match." - (catch 'exit +(defun org-in-regexp (regexp &optional nlines visually) + "Check if point is inside a match of REGEXP. + +Normally only the current line is checked, but you can include +NLINES extra lines around point into the search. If VISUALLY is +set, require that the cursor is not after the match but really +on, so that the block visually is on the match. + +Return nil or a cons cell (BEG . END) where BEG and END are, +respectively, the positions at the beginning and the end of the +match." + (catch :exit (let ((pos (point)) - (eol (point-at-eol (+ 1 (or nlines 0)))) - (inc (if visually 1 0))) + (eol (line-end-position (if nlines (1+ nlines) 1)))) (save-excursion (beginning-of-line (- 1 (or nlines 0))) - (while (re-search-forward re eol t) - (if (and (<= (match-beginning 0) pos) - (>= (+ inc (match-end 0)) pos)) - (throw 'exit (cons (match-beginning 0) (match-end 0))))))))) - -(defun org-at-regexp-p (regexp) - "Is point inside a match of REGEXP in the current line?" - (catch 'exit - (save-excursion - (let ((pos (point)) (end (point-at-eol))) - (beginning-of-line 1) - (while (re-search-forward regexp end t) - (if (and (<= (match-beginning 0) pos) - (>= (match-end 0) pos)) - (throw 'exit t))) - nil)))) + (while (and (re-search-forward regexp eol t) + (<= (match-beginning 0) pos)) + (let ((end (match-end 0))) + (when (or (> end pos) (and (= end pos) (not visually))) + (throw :exit (cons (match-beginning 0) (match-end 0)))))))))) (defun org-between-regexps-p (start-re end-re &optional lim-up lim-down) "Non-nil when point is between matches of START-RE and END-RE. @@ -21757,7 +22416,7 @@ position before START-RE (resp. after END-RE)." (save-excursion ;; Point is on a block when on START-RE or if START-RE can be ;; found before it... - (and (or (org-at-regexp-p start-re) + (and (or (org-in-regexp start-re) (re-search-backward start-re limit-up t)) (setq beg (match-beginning 0)) ;; ... and END-RE after it... @@ -21783,27 +22442,15 @@ block from point." (let ((case-fold-search t) (lim-up (save-excursion (outline-previous-heading))) (lim-down (save-excursion (outline-next-heading)))) - (mapc (lambda (name) - (let ((n (regexp-quote name))) - (when (org-between-regexps-p - (concat "^[ \t]*#\\+begin_" n) - (concat "^[ \t]*#\\+end_" n) - lim-up lim-down) - (throw 'exit n)))) - names)) + (dolist (name names) + (let ((n (regexp-quote name))) + (when (org-between-regexps-p + (concat "^[ \t]*#\\+begin_" n) + (concat "^[ \t]*#\\+end_" n) + lim-up lim-down) + (throw 'exit n))))) nil))) -(defun org-in-drawer-p () - "Is point within a drawer?" - (save-match-data - (let ((case-fold-search t) - (lim-up (save-excursion (outline-previous-heading))) - (lim-down (save-excursion (outline-next-heading)))) - (org-between-regexps-p - (concat "^[ \t]*:" (regexp-opt org-drawers) ":") - "^[ \t]*:end:.*$" - lim-up lim-down)))) - (defun org-occur-in-agenda-files (regexp &optional _nlines) "Call `multi-occur' with buffers for all agenda files." (interactive "sOrg-files matching: ") @@ -21815,40 +22462,21 @@ block from point." (setq files (org-add-archive-files files))) (dolist (f extra) (unless (member (file-truename f) tnames) - (unless (member f files) (setq files (append files (list f)))) - (setq tnames (append tnames (list (file-truename f)))))) + (unless (member f files) (setq files (append files (list f)))) + (setq tnames (append tnames (list (file-truename f)))))) (multi-occur (mapcar (lambda (x) (with-current-buffer - ;; FIXME: Why not just (find-file-noselect x)? - ;; Is it to avoid the "revert buffer" prompt? + ;; FIXME: Why not just (find-file-noselect x)? + ;; Is it to avoid the "revert buffer" prompt? (or (get-file-buffer x) (find-file-noselect x)) (widen) (current-buffer))) files) regexp))) -(if (boundp 'occur-mode-find-occurrence-hook) - ;; Emacs 23 - (add-hook 'occur-mode-find-occurrence-hook - (lambda () - (when (derived-mode-p 'org-mode) - (org-reveal)))) - ;; Emacs 22 - (defadvice occur-mode-goto-occurrence - (after org-occur-reveal activate) - (and (derived-mode-p 'org-mode) (org-reveal))) - (defadvice occur-mode-goto-occurrence-other-window - (after org-occur-reveal activate) - (and (derived-mode-p 'org-mode) (org-reveal))) - (defadvice occur-mode-display-occurrence - (after org-occur-reveal activate) - (when (derived-mode-p 'org-mode) - (let ((pos (occur-mode-find-occurrence))) - (with-current-buffer (marker-buffer pos) - (save-excursion - (goto-char pos) - (org-reveal))))))) +(add-hook 'occur-mode-find-occurrence-hook + (lambda () (when (derived-mode-p 'org-mode) (org-reveal)))) (defun org-occur-link-in-agenda-files () "Create a link and search for it in the agendas. @@ -21878,81 +22506,27 @@ merge (a 1) and (a 3) into (a 1 3). The function returns the new ALIST." (let (rtn) - (mapc - (lambda (e) - (let (n) - (if (not (assoc (car e) rtn)) - (push e rtn) - (setq n (cons (car e) (append (cdr (assoc (car e) rtn)) (cdr e)))) - (setq rtn (assq-delete-all (car e) rtn)) - (push n rtn)))) - alist) - rtn)) + (dolist (e alist rtn) + (let (n) + (if (not (assoc (car e) rtn)) + (push e rtn) + (setq n (cons (car e) (append (cdr (assoc (car e) rtn)) (cdr e)))) + (setq rtn (assq-delete-all (car e) rtn)) + (push n rtn)))))) (defun org-delete-all (elts list) - "Remove all elements in ELTS from LIST." + "Remove all elements in ELTS from LIST. +Comparison is done with `equal'. It is a destructive operation +that may remove elements by altering the list structure." (while elts (setq list (delete (pop elts) list))) list) -(defun org-count (cl-item cl-seq) - "Count the number of occurrences of ITEM in SEQ. -Taken from `count' in cl-seq.el with all keyword arguments removed." - (let ((cl-end (length cl-seq)) (cl-start 0) (cl-count 0) cl-x) - (when (consp cl-seq) (setq cl-seq (nthcdr cl-start cl-seq))) - (while (< cl-start cl-end) - (setq cl-x (if (consp cl-seq) (pop cl-seq) (aref cl-seq cl-start))) - (if (equal cl-item cl-x) (setq cl-count (1+ cl-count))) - (setq cl-start (1+ cl-start))) - cl-count)) - -(defun org-remove-if (predicate seq) - "Remove everything from SEQ that fulfills PREDICATE." - (let (res e) - (while seq - (setq e (pop seq)) - (if (not (funcall predicate e)) (push e res))) - (nreverse res))) - -(defun org-remove-if-not (predicate seq) - "Remove everything from SEQ that does not fulfill PREDICATE." - (let (res e) - (while seq - (setq e (pop seq)) - (if (funcall predicate e) (push e res))) - (nreverse res))) - -(defun org-reduce (cl-func cl-seq &rest cl-keys) - "Reduce two-argument FUNCTION across SEQ. -Taken from `reduce' in cl-seq.el with all keyword arguments but -\":initial-value\" removed." - (let ((cl-accum (cond ((memq :initial-value cl-keys) - (cadr (memq :initial-value cl-keys))) - (cl-seq (pop cl-seq)) - (t (funcall cl-func))))) - (while cl-seq - (setq cl-accum (funcall cl-func cl-accum (pop cl-seq)))) - cl-accum)) - -(defun org-every (pred seq) - "Return true if PREDICATE is true of every element of SEQ. -Adapted from `every' in cl.el." - (catch 'org-every - (mapc (lambda (e) (unless (funcall pred e) (throw 'org-every nil))) seq) - t)) - -(defun org-some (pred seq) - "Return true if PREDICATE is true of any element of SEQ. -Adapted from `some' in cl.el." - (catch 'org-some - (mapc (lambda (e) (when (funcall pred e) (throw 'org-some t))) seq) - nil)) - (defun org-back-over-empty-lines () "Move backwards over whitespace, to the beginning of the first empty line. Returns the number of empty lines passed." (let ((pos (point))) - (if (cdr (assoc 'heading org-blank-before-new-entry)) + (if (cdr (assq 'heading org-blank-before-new-entry)) (skip-chars-backward " \t\n\r") (unless (eobp) (forward-line -1))) @@ -22005,7 +22579,7 @@ so values can contain further %-escapes if they are define later in TABLE." (let ((tbl (copy-alist table)) (case-fold-search nil) (pchg 0) - e re rpl) + re rpl) (dolist (e tbl) (setq re (concat "%-?[0-9.]*" (substring (car e) 1))) (when (and (cdr e) (string-match re (cdr e))) @@ -22023,16 +22597,6 @@ so values can contain further %-escapes if they are define later in TABLE." (setq string (replace-match sref t t string))))) string)) -(defun org-sublist (list start end) - "Return a section of LIST, from START to END. -Counting starts at 1." - (let (rtn (c start)) - (setq list (nthcdr (1- start) list)) - (while (and list (<= c end)) - (push (pop list) rtn) - (setq c (1+ c))) - (nreverse rtn))) - (defun org-find-base-buffer-visiting (file) "Like `find-buffer-visiting' but always return the base buffer and not an indirect buffer." @@ -22042,26 +22606,12 @@ not an indirect buffer." (or (buffer-base-buffer buf) buf) nil))) -(defun org-image-file-name-regexp (&optional extensions) - "Return regexp matching the file names of images. -If EXTENSIONS is given, only match these." - (if (and (not extensions) (fboundp 'image-file-name-regexp)) - (image-file-name-regexp) - (let ((image-file-name-extensions - (or extensions - '("png" "jpeg" "jpg" "gif" "tiff" "tif" - "xbm" "xpm" "pbm" "pgm" "ppm")))) - (concat "\\." - (regexp-opt (nconc (mapcar 'upcase - image-file-name-extensions) - image-file-name-extensions) - t) - "\\'")))) - -(defun org-file-image-p (file &optional extensions) +;;; TODO: Only called once, from ox-odt which should probably use +;;; org-export-inline-image-p or something. +(defun org-file-image-p (file) "Return non-nil if FILE is an image." (save-match-data - (string-match (org-image-file-name-regexp extensions) file))) + (string-match (image-file-name-regexp) file))) (defun org-get-cursor-date (&optional with-time) "Return the date at cursor in as a time. @@ -22085,10 +22635,10 @@ the agenda) or the current time of the day." (nth 1 date) (nth 0 date) (nth 2 date)))) ((eq major-mode 'org-agenda-mode) (setq day (get-text-property (point) 'day)) - (if day - (setq date (calendar-gregorian-from-absolute day) - defd (encode-time 0 (or mod 0) (or hod 0) - (nth 1 date) (nth 0 date) (nth 2 date)))))) + (when day + (setq date (calendar-gregorian-from-absolute day) + defd (encode-time 0 (or mod 0) (or hod 0) + (nth 1 date) (nth 0 date) (nth 2 date)))))) (or defd (current-time)))) (defun org-mark-subtree (&optional up) @@ -22101,177 +22651,440 @@ hierarchy of headlines by UP levels before marking the subtree." (cond ((org-at-heading-p) (beginning-of-line)) ((org-before-first-heading-p) (user-error "Not in a subtree")) (t (outline-previous-visible-heading 1)))) - (when up (while (and (> up 0) (org-up-heading-safe)) (decf up))) - (if (org-called-interactively-p 'any) + (when up (while (and (> up 0) (org-up-heading-safe)) (cl-decf up))) + (if (called-interactively-p 'any) (call-interactively 'org-mark-element) (org-mark-element))) +(defun org-file-newer-than-p (file time) + "Non-nil if FILE is newer than TIME. +FILE is a filename, as a string, TIME is a list of integers, as +returned by, e.g., `current-time'." + (and (file-exists-p file) + ;; Only compare times up to whole seconds as some file-systems + ;; (e.g. HFS+) do not retain any finer granularity. As + ;; a consequence, make sure we return non-nil when the two + ;; times are equal. + (not (time-less-p (cl-subseq (nth 5 (file-attributes file)) 0 2) + (cl-subseq time 0 2))))) + +(defun org-compile-file (source process ext &optional err-msg log-buf spec) + "Compile a SOURCE file using PROCESS. + +PROCESS is either a function or a list of shell commands, as +strings. EXT is a file extension, without the leading dot, as +a string. It is used to check if the process actually succeeded. + +PROCESS must create a file with the same base name and directory +as SOURCE, but ending with EXT. The function then returns its +filename. Otherwise, it raises an error. The error message can +then be refined by providing string ERR-MSG, which is appended to +the standard message. + +If PROCESS is a function, it is called with a single argument: +the SOURCE file. + +If it is a list of commands, each of them is called using +`shell-command'. By default, in each command, %b, %f, %F, %o and +%O are replaced with, respectively, SOURCE base name, name, full +name, directory and absolute output file name. It is possible, +however, to use more place-holders by specifying them in optional +argument SPEC, as an alist following the pattern + + (CHARACTER . REPLACEMENT-STRING). + +When PROCESS is a list of commands, optional argument LOG-BUF can +be set to a buffer or a buffer name. `shell-command' then uses +it for output." + (let* ((base-name (file-name-base source)) + (full-name (file-truename source)) + (out-dir (or (file-name-directory source) "./")) + (output (expand-file-name (concat base-name "." ext) out-dir)) + (time (current-time)) + (err-msg (if (stringp err-msg) (concat ". " err-msg) ""))) + (save-window-excursion + (pcase process + ((pred functionp) (funcall process (shell-quote-argument source))) + ((pred consp) + (let ((log-buf (and log-buf (get-buffer-create log-buf))) + (spec (append spec + `((?b . ,(shell-quote-argument base-name)) + (?f . ,(shell-quote-argument source)) + (?F . ,(shell-quote-argument full-name)) + (?o . ,(shell-quote-argument out-dir)) + (?O . ,(shell-quote-argument output)))))) + (dolist (command process) + (shell-command (format-spec command spec) log-buf)))) + (_ (error "No valid command to process %S%s" source err-msg)))) + ;; Check for process failure. Output file is expected to be + ;; located in the same directory as SOURCE. + (unless (org-file-newer-than-p output time) + (error (format "File %S wasn't produced%s" output err-msg))) + output)) ;;; Indentation +(defvar org-element-greater-elements) +(defun org--get-expected-indentation (element contentsp) + "Expected indentation column for current line, according to ELEMENT. +ELEMENT is an element containing point. CONTENTSP is non-nil +when indentation is to be computed according to contents of +ELEMENT." + (let ((type (org-element-type element)) + (start (org-element-property :begin element)) + (post-affiliated (org-element-property :post-affiliated element))) + (org-with-wide-buffer + (cond + (contentsp + (cl-case type + ((diary-sexp footnote-definition) 0) + ((headline inlinetask nil) + (if (not org-adapt-indentation) 0 + (let ((level (org-current-level))) + (if level (1+ level) 0)))) + ((item plain-list) (org-list-item-body-column post-affiliated)) + (t + (goto-char start) + (org-get-indentation)))) + ((memq type '(headline inlinetask nil)) + (if (org-match-line "[ \t]*$") + (org--get-expected-indentation element t) + 0)) + ((memq type '(diary-sexp footnote-definition)) 0) + ;; First paragraph of a footnote definition or an item. + ;; Indent like parent. + ((< (line-beginning-position) start) + (org--get-expected-indentation + (org-element-property :parent element) t)) + ;; At first line: indent according to previous sibling, if any, + ;; ignoring footnote definitions and inline tasks, or parent's + ;; contents. + ((= (line-beginning-position) start) + (catch 'exit + (while t + (if (= (point-min) start) (throw 'exit 0) + (goto-char (1- start)) + (let* ((previous (org-element-at-point)) + (parent previous)) + (while (and parent (<= (org-element-property :end parent) start)) + (setq previous parent + parent (org-element-property :parent parent))) + (cond + ((not previous) (throw 'exit 0)) + ((> (org-element-property :end previous) start) + (throw 'exit (org--get-expected-indentation previous t))) + ((memq (org-element-type previous) + '(footnote-definition inlinetask)) + (setq start (org-element-property :begin previous))) + (t (goto-char (org-element-property :begin previous)) + (throw 'exit + (if (bolp) (org-get-indentation) + ;; At first paragraph in an item or + ;; a footnote definition. + (org--get-expected-indentation + (org-element-property :parent previous) t)))))))))) + ;; Otherwise, move to the first non-blank line above. + (t + (beginning-of-line) + (let ((pos (point))) + (skip-chars-backward " \r\t\n") + (cond + ;; Two blank lines end a footnote definition or a plain + ;; list. When we indent an empty line after them, the + ;; containing list or footnote definition is over, so it + ;; qualifies as a previous sibling. Therefore, we indent + ;; like its first line. + ((and (memq type '(footnote-definition plain-list)) + (> (count-lines (point) pos) 2)) + (goto-char start) + (org-get-indentation)) + ;; Line above is the first one of a paragraph at the + ;; beginning of an item or a footnote definition. Indent + ;; like parent. + ((< (line-beginning-position) start) + (org--get-expected-indentation + (org-element-property :parent element) t)) + ;; Line above is the beginning of an element, i.e., point + ;; was originally on the blank lines between element's start + ;; and contents. + ((= (line-beginning-position) post-affiliated) + (org--get-expected-indentation element t)) + ;; POS is after contents in a greater element. Indent like + ;; the beginning of the element. + ((and (memq type org-element-greater-elements) + (let ((cend (org-element-property :contents-end element))) + (and cend (<= cend pos)))) + ;; As a special case, if point is at the end of a footnote + ;; definition or an item, indent like the very last element + ;; within. If that last element is an item, indent like + ;; its contents. + (if (memq type '(footnote-definition item plain-list)) + (let ((last (org-element-at-point))) + (goto-char pos) + (org--get-expected-indentation + last (eq (org-element-type last) 'item))) + (goto-char start) + (org-get-indentation))) + ;; In any other case, indent like the current line. + (t (org-get-indentation))))))))) + +(defun org--align-node-property () + "Align node property at point. +Alignment is done according to `org-property-format', which see." + (when (save-excursion + (beginning-of-line) + (looking-at org-property-re)) + (replace-match + (concat (match-string 4) + (org-trim + (format org-property-format (match-string 1) (match-string 3)))) + t t))) + (defun org-indent-line () - "Indent line depending on context." + "Indent line depending on context. + +Indentation is done according to the following rules: + + - Footnote definitions, diary sexps, headlines and inline tasks + have to start at column 0. + + - On the very first line of an element, consider, in order, the + next rules until one matches: + + 1. If there's a sibling element before, ignoring footnote + definitions and inline tasks, indent like its first line. + + 2. If element has a parent, indent like its contents. More + precisely, if parent is an item, indent after the + description part, if any, or the bullet (see + `org-list-description-max-indent'). Else, indent like + parent's first line. + + 3. Otherwise, indent relatively to current level, if + `org-adapt-indentation' is non-nil, or to left margin. + + - On a blank line at the end of an element, indent according to + the type of the element. More precisely + + 1. If element is a plain list, an item, or a footnote + definition, indent like the very last element within. + + 2. If element is a paragraph, indent like its last non blank + line. + + 3. Otherwise, indent like its very first line. + + - In the code part of a source block, use language major mode + to indent current line if `org-src-tab-acts-natively' is + non-nil. If it is nil, do nothing. + + - Otherwise, indent like the first non-blank line above. + +The function doesn't indent an item as it could break the whole +list structure. Instead, use \\`\\[org-shiftmetaleft]' or \ +`\\[org-shiftmetaright]'. + +Also align node properties according to `org-property-format'." (interactive) - (let* ((pos (point)) - (itemp (org-at-item-p)) - (case-fold-search t) - (org-drawer-regexp (or org-drawer-regexp "\000")) - (inline-task-p (and (featurep 'org-inlinetask) - (org-inlinetask-in-task-p))) - (inline-re (and inline-task-p - (org-inlinetask-outline-regexp))) - column) - (if (and orgstruct-is-++ (eq pos (point))) - (let ((indent-line-function (cadadr (assoc 'indent-line-function org-fb-vars)))) - (indent-according-to-mode)) - (beginning-of-line 1) - (cond - ;; Headings - ((looking-at org-outline-regexp) (setq column 0)) - ;; Footnote definition - ((looking-at org-footnote-definition-re) (setq column 0)) - ;; Literal examples - ((looking-at "[ \t]*:\\( \\|$\\)") - (setq column (org-get-indentation))) ; do nothing - ;; Lists - ((ignore-errors (goto-char (org-in-item-p))) - (setq column (if itemp - (org-get-indentation) - (org-list-item-body-column (point)))) - (goto-char pos)) - ;; Drawers - ((and (looking-at "[ \t]*:END:") - (save-excursion (re-search-backward org-drawer-regexp nil t))) - (save-excursion - (goto-char (1- (match-beginning 1))) - (setq column (current-column)))) - ;; Special blocks - ((and (looking-at "[ \t]*#\\+end_\\([a-z]+\\)") - (save-excursion - (re-search-backward - (concat "^[ \t]*#\\+begin_" (downcase (match-string 1))) nil t))) - (setq column (org-get-indentation (match-string 0)))) - ((and (not (looking-at "[ \t]*#\\+begin_")) - (org-between-regexps-p "^[ \t]*#\\+begin_" "[ \t]*#\\+end_")) - (save-excursion - (re-search-backward "^[ \t]*#\\+begin_\\([a-z]+\\)" nil t)) - (setq column - (cond ((equal (downcase (match-string 1)) "src") - ;; src blocks: let `org-edit-src-exit' handle them - (org-get-indentation)) - ((equal (downcase (match-string 1)) "example") - (max (org-get-indentation) - (org-get-indentation (match-string 0)))) - (t - (org-get-indentation (match-string 0)))))) - ;; This line has nothing special, look at the previous relevant - ;; line to compute indentation - (t - (beginning-of-line 0) - (while (and (not (bobp)) - (not (looking-at org-table-line-regexp)) - (not (looking-at org-drawer-regexp)) - ;; When point started in an inline task, do not move - ;; above task starting line. - (not (and inline-task-p (looking-at inline-re))) - ;; Skip drawers, blocks, empty lines, verbatim, - ;; comments, tables, footnotes definitions, lists, - ;; inline tasks. - (or (and (looking-at "[ \t]*:END:") - (re-search-backward org-drawer-regexp nil t)) - (and (looking-at "[ \t]*#\\+end_") - (re-search-backward "[ \t]*#\\+begin_"nil t)) - (looking-at "[ \t]*[\n:#|]") - (looking-at org-footnote-definition-re) - (and (not inline-task-p) - (featurep 'org-inlinetask) - (org-inlinetask-in-task-p) - (or (org-inlinetask-goto-beginning) t)))) - (beginning-of-line 0)) - (cond - ;; There was a list item above. - ((ignore-errors (goto-char (org-in-item-p))) - (goto-char (org-list-get-top-point (org-list-struct))) - (setq column (org-get-indentation))) - ;; There was an heading above. - ((looking-at "\\*+[ \t]+") - (if (not org-adapt-indentation) - (setq column 0) - (goto-char (match-end 0)) - (setq column (current-column)))) - ;; A drawer had started and is unfinished - ((looking-at org-drawer-regexp) - (goto-char (1- (match-beginning 1))) - (setq column (current-column))) - ;; Else, nothing noticeable found: get indentation and go on. - (t (setq column (org-get-indentation)))))) - ;; Now apply indentation and move cursor accordingly - (goto-char pos) - (if (<= (current-column) (current-indentation)) - (org-indent-line-to column) - (save-excursion (org-indent-line-to column))) - ;; Special polishing for properties, see `org-property-format' - (setq column (current-column)) - (beginning-of-line 1) - (if (looking-at org-property-re) - (replace-match (concat (match-string 4) - (format org-property-format - (match-string 1) (match-string 3))) - t t)) - (org-move-to-column column)))) + (cond + (orgstruct-is-++ + (let ((indent-line-function + (cl-cadadr (assq 'indent-line-function org-fb-vars)))) + (indent-according-to-mode))) + ((org-at-heading-p) 'noindent) + (t + (let* ((element (save-excursion (beginning-of-line) (org-element-at-point))) + (type (org-element-type element))) + (cond ((and (memq type '(plain-list item)) + (= (line-beginning-position) + (org-element-property :post-affiliated element))) + 'noindent) + ((and (eq type 'latex-environment) + (>= (point) (org-element-property :post-affiliated element)) + (< (point) (org-with-wide-buffer + (goto-char (org-element-property :end element)) + (skip-chars-backward " \r\t\n") + (line-beginning-position 2)))) + 'noindent) + ((and (eq type 'src-block) + org-src-tab-acts-natively + (> (line-beginning-position) + (org-element-property :post-affiliated element)) + (< (line-beginning-position) + (org-with-wide-buffer + (goto-char (org-element-property :end element)) + (skip-chars-backward " \r\t\n") + (line-beginning-position)))) + (org-babel-do-key-sequence-in-edit-buffer (kbd "TAB"))) + (t + (let ((column (org--get-expected-indentation element nil))) + ;; Preserve current column. + (if (<= (current-column) (current-indentation)) + (indent-line-to column) + (save-excursion (indent-line-to column)))) + ;; Align node property. Also preserve current column. + (when (eq type 'node-property) + (let ((column (current-column))) + (org--align-node-property) + (org-move-to-column column))))))))) + +(defun org-indent-region (start end) + "Indent each non-blank line in the region. +Called from a program, START and END specify the region to +indent. The function will not indent contents of example blocks, +verse blocks and export blocks as leading white spaces are +assumed to be significant there." + (interactive "r") + (save-excursion + (goto-char start) + (skip-chars-forward " \r\t\n") + (unless (eobp) (beginning-of-line)) + (let ((indent-to + (lambda (ind pos) + ;; Set IND as indentation for all lines between point and + ;; POS. Blank lines are ignored. Leave point after POS + ;; once done. + (let ((limit (copy-marker pos))) + (while (< (point) limit) + (unless (looking-at-p "[ \t]*$") (indent-line-to ind)) + (forward-line)) + (set-marker limit nil)))) + (end (copy-marker end))) + (while (< (point) end) + (if (or (looking-at-p " \r\t\n") (org-at-heading-p)) (forward-line) + (let* ((element (org-element-at-point)) + (type (org-element-type element)) + (element-end (copy-marker (org-element-property :end element))) + (ind (org--get-expected-indentation element nil))) + (cond + ;; Element indented as a single block. Example blocks + ;; preserving indentation are a special case since the + ;; "contents" must not be indented whereas the block + ;; boundaries can. + ((or (memq type '(export-block latex-environment)) + (and (eq type 'example-block) + (not + (or org-src-preserve-indentation + (org-element-property :preserve-indent element))))) + (let ((offset (- ind (org-get-indentation)))) + (unless (zerop offset) + (indent-rigidly (org-element-property :begin element) + (org-element-property :end element) + offset))) + (goto-char element-end)) + ;; Elements indented line wise. Be sure to exclude + ;; example blocks (preserving indentation) and source + ;; blocks from this category as they are treated + ;; specially later. + ((or (memq type '(paragraph table table-row)) + (not (or (org-element-property :contents-begin element) + (memq type '(example-block src-block))))) + (when (eq type 'node-property) + (org--align-node-property) + (beginning-of-line)) + (funcall indent-to ind (min element-end end))) + ;; Elements consisting of three parts: before the + ;; contents, the contents, and after the contents. The + ;; contents are treated specially, according to the + ;; element type, or not indented at all. Other parts are + ;; indented as a single block. + (t + (let* ((post (copy-marker + (org-element-property :post-affiliated element))) + (cbeg + (copy-marker + (cond + ((not (org-element-property :contents-begin element)) + ;; Fake contents for source blocks. + (org-with-wide-buffer + (goto-char post) + (line-beginning-position 2))) + ((memq type '(footnote-definition item plain-list)) + ;; Contents in these elements could start on + ;; the same line as the beginning of the + ;; element. Make sure we start indenting + ;; from the second line. + (org-with-wide-buffer + (goto-char post) + (end-of-line) + (skip-chars-forward " \r\t\n") + (if (eobp) (point) (line-beginning-position)))) + (t (org-element-property :contents-begin element))))) + (cend (copy-marker + (or (org-element-property :contents-end element) + ;; Fake contents for source blocks. + (org-with-wide-buffer + (goto-char element-end) + (skip-chars-backward " \r\t\n") + (line-beginning-position))) + t))) + ;; Do not change items indentation individually as it + ;; might break the list as a whole. On the other + ;; hand, when at a plain list, indent it as a whole. + (cond ((eq type 'plain-list) + (let ((offset (- ind (org-get-indentation)))) + (unless (zerop offset) + (indent-rigidly (org-element-property :begin element) + (org-element-property :end element) + offset)) + (goto-char cbeg))) + ((eq type 'item) (goto-char cbeg)) + (t (funcall indent-to ind (min cbeg end)))) + (when (< (point) end) + (cl-case type + ((example-block verse-block)) + (src-block + ;; In a source block, indent source code + ;; according to language major mode, but only if + ;; `org-src-tab-acts-natively' is non-nil. + (when (and (< (point) end) org-src-tab-acts-natively) + (ignore-errors + (org-babel-do-in-edit-buffer + (indent-region (point-min) (point-max)))))) + (t (org-indent-region (point) (min cend end)))) + (goto-char (min cend end)) + (when (< (point) end) + (funcall indent-to ind (min element-end end)))) + (set-marker post nil) + (set-marker cbeg nil) + (set-marker cend nil)))) + (set-marker element-end nil)))) + (set-marker end nil)))) (defun org-indent-drawer () "Indent the drawer at point." (interactive) - (let ((p (point)) - (e (and (save-excursion (re-search-forward ":END:" nil t)) - (match-end 0))) - (folded - (save-excursion - (end-of-line) - (when (overlays-at (point)) - (member 'invisible (overlay-properties - (car (overlays-at (point))))))))) - (when folded (org-cycle)) - (indent-for-tab-command) - (while (and (move-beginning-of-line 2) (< (point) e)) - (indent-for-tab-command)) - (goto-char p) - (when folded (org-cycle))) + (unless (save-excursion + (beginning-of-line) + (looking-at-p org-drawer-regexp)) + (user-error "Not at a drawer")) + (let ((element (org-element-at-point))) + (unless (memq (org-element-type element) '(drawer property-drawer)) + (user-error "Not at a drawer")) + (org-with-wide-buffer + (org-indent-region (org-element-property :begin element) + (org-element-property :end element)))) (message "Drawer at point indented")) (defun org-indent-block () "Indent the block at point." (interactive) - (let ((p (point)) - (case-fold-search t) - (e (and (save-excursion (re-search-forward "#\\+end_?\\(?:[a-z]+\\)?" nil t)) - (match-end 0))) - (folded - (save-excursion - (end-of-line) - (when (overlays-at (point)) - (member 'invisible (overlay-properties - (car (overlays-at (point))))))))) - (when folded (org-cycle)) - (indent-for-tab-command) - (while (and (move-beginning-of-line 2) (< (point) e)) - (indent-for-tab-command)) - (goto-char p) - (when folded (org-cycle))) + (unless (save-excursion + (beginning-of-line) + (let ((case-fold-search t)) + (looking-at-p "[ \t]*#\\+\\(begin\\|end\\)_"))) + (user-error "Not at a block")) + (let ((element (org-element-at-point))) + (unless (memq (org-element-type element) + '(comment-block center-block dynamic-block example-block + export-block quote-block special-block + src-block verse-block)) + (user-error "Not at a block")) + (org-with-wide-buffer + (org-indent-region (org-element-property :begin element) + (org-element-property :end element)))) (message "Block at point indented")) -(defun org-indent-region (start end) - "Indent region." - (interactive "r") - (save-excursion - (let ((line-end (org-current-line end))) - (goto-char start) - (while (< (org-current-line) line-end) - (cond ((org-in-src-block-p t) (org-src-native-tab-command-maybe)) - (t (call-interactively 'org-indent-line))) - (move-beginning-of-line 2))))) - ;;; Filling @@ -22294,20 +23107,20 @@ hierarchy of headlines by UP levels before marking the subtree." (require 'org-element) ;; Prevent auto-fill from inserting unwanted new items. (when (boundp 'fill-nobreak-predicate) - (org-set-local - 'fill-nobreak-predicate + (setq-local + fill-nobreak-predicate (org-uniquify (append fill-nobreak-predicate '(org-fill-line-break-nobreak-p org-fill-paragraph-with-timestamp-nobreak-p))))) (let ((paragraph-ending (substring org-element-paragraph-separate 1))) - (org-set-local 'paragraph-start paragraph-ending) - (org-set-local 'paragraph-separate paragraph-ending)) - (org-set-local 'fill-paragraph-function 'org-fill-paragraph) - (org-set-local 'auto-fill-inhibit-regexp nil) - (org-set-local 'adaptive-fill-function 'org-adaptive-fill-function) - (org-set-local 'normal-auto-fill-function 'org-auto-fill-function) - (org-set-local 'comment-line-break-function 'org-comment-line-break-function)) + (setq-local paragraph-start paragraph-ending) + (setq-local paragraph-separate paragraph-ending)) + (setq-local fill-paragraph-function 'org-fill-paragraph) + (setq-local auto-fill-inhibit-regexp nil) + (setq-local adaptive-fill-function 'org-adaptive-fill-function) + (setq-local normal-auto-fill-function 'org-auto-fill-function) + (setq-local comment-line-break-function 'org-comment-line-break-function)) (defun org-fill-line-break-nobreak-p () "Non-nil when a new line at point would create an Org line break." @@ -22332,69 +23145,64 @@ matches in paragraphs or comments, use it." (when (derived-mode-p 'message-mode) (save-excursion (beginning-of-line) - (cond ((or (not (message-in-body-p)) - (looking-at orgtbl-line-start-regexp)) - (throw 'exit nil)) + (cond ((not (message-in-body-p)) (throw 'exit nil)) + ((looking-at-p org-table-line-regexp) (throw 'exit nil)) ((looking-at message-cite-prefix-regexp) (throw 'exit (match-string-no-properties 0))) ((looking-at org-outline-regexp) - (throw 'exit (make-string (length (match-string 0)) ? )))))) + (throw 'exit (make-string (length (match-string 0)) ?\s)))))) (org-with-wide-buffer - (let* ((p (line-beginning-position)) - (element (save-excursion - (beginning-of-line) - (or (ignore-errors (org-element-at-point)) - (user-error "An element cannot be parsed line %d" - (line-number-at-pos (point)))))) - (type (org-element-type element)) - (post-affiliated (org-element-property :post-affiliated element))) - (unless (and post-affiliated (< p post-affiliated)) - (case type - (comment - (save-excursion - (beginning-of-line) - (looking-at "[ \t]*") - (concat (match-string 0) "# "))) - (footnote-definition "") - ((item plain-list) - (make-string (org-list-item-body-column - (or post-affiliated - (org-element-property :begin element))) - ? )) - (paragraph - ;; Fill prefix is usually the same as the current line, - ;; unless the paragraph is at the beginning of an item. - (let ((parent (org-element-property :parent element))) + (unless (org-at-heading-p) + (let* ((p (line-beginning-position)) + (element (save-excursion + (beginning-of-line) + (org-element-at-point))) + (type (org-element-type element)) + (post-affiliated (org-element-property :post-affiliated element))) + (unless (< p post-affiliated) + (cl-case type + (comment (save-excursion (beginning-of-line) - (cond ((eq (org-element-type parent) 'item) - (make-string (org-list-item-body-column - (org-element-property :begin parent)) - ? )) - ((and adaptive-fill-regexp - ;; Locally disable - ;; `adaptive-fill-function' to let - ;; `fill-context-prefix' handle - ;; `adaptive-fill-regexp' variable. - (let (adaptive-fill-function) - (fill-context-prefix - post-affiliated - (org-element-property :end element))))) - ((looking-at "[ \t]+") (match-string 0)) - (t ""))))) - (comment-block - ;; Only fill contents if P is within block boundaries. - (let* ((cbeg (save-excursion (goto-char post-affiliated) - (forward-line) - (point))) - (cend (save-excursion - (goto-char (org-element-property :end element)) - (skip-chars-backward " \r\t\n") - (line-beginning-position)))) - (when (and (>= p cbeg) (< p cend)) - (if (save-excursion (beginning-of-line) (looking-at "[ \t]+")) - (match-string 0) - "")))))))))) + (looking-at "[ \t]*") + (concat (match-string 0) "# "))) + (footnote-definition "") + ((item plain-list) + (make-string (org-list-item-body-column post-affiliated) ?\s)) + (paragraph + ;; Fill prefix is usually the same as the current line, + ;; unless the paragraph is at the beginning of an item. + (let ((parent (org-element-property :parent element))) + (save-excursion + (beginning-of-line) + (cond ((eq (org-element-type parent) 'item) + (make-string (org-list-item-body-column + (org-element-property :begin parent)) + ?\s)) + ((and adaptive-fill-regexp + ;; Locally disable + ;; `adaptive-fill-function' to let + ;; `fill-context-prefix' handle + ;; `adaptive-fill-regexp' variable. + (let (adaptive-fill-function) + (fill-context-prefix + post-affiliated + (org-element-property :end element))))) + ((looking-at "[ \t]+") (match-string 0)) + (t ""))))) + (comment-block + ;; Only fill contents if P is within block boundaries. + (let* ((cbeg (save-excursion (goto-char post-affiliated) + (forward-line) + (point))) + (cend (save-excursion + (goto-char (org-element-property :end element)) + (skip-chars-backward " \r\t\n") + (line-beginning-position)))) + (when (and (>= p cbeg) (< p cend)) + (if (save-excursion (beginning-of-line) (looking-at "[ \t]+")) + (match-string 0) + ""))))))))))) (declare-function message-goto-body "message" ()) (defvar message-cite-prefix-regexp) ; From message.el @@ -22420,11 +23228,11 @@ a footnote definition, try to fill the first paragraph within." (looking-at message-cite-prefix-regexp)))) ;; First ensure filling is correct in 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))) + (cl-cadadr (assq 'fill-paragraph-function org-fb-vars))) + (fill-prefix (cl-cadadr (assq 'fill-prefix org-fb-vars))) + (paragraph-start (cl-cadadr (assq 'paragraph-start org-fb-vars))) (paragraph-separate - (cadadr (assoc 'paragraph-separate org-fb-vars)))) + (cl-cadadr (assq 'paragraph-separate org-fb-vars)))) (fill-paragraph nil)) (with-syntax-table org-mode-transpose-word-syntax-table ;; Move to end of line in order to get the first paragraph @@ -22436,7 +23244,7 @@ a footnote definition, try to fill the first paragraph within." (line-number-at-pos (point))))))) ;; First check if point is in a blank line at the beginning of ;; the buffer. In that case, ignore filling. - (case (org-element-type element) + (cl-case (org-element-type element) ;; Use major mode filling function is src blocks. (src-block (org-babel-do-key-sequence-in-edit-buffer (kbd "M-q"))) ;; Align Org tables, leave table.el tables as-is. @@ -22465,25 +23273,18 @@ a footnote definition, try to fill the first paragraph within." (concat "^" message-cite-prefix-regexp) end t)) (setq end (match-beginning 0)))) ;; Fill paragraph, taking line breaks into account. - ;; For that, slice the paragraph using line breaks as - ;; separators, and fill the parts in reverse order to - ;; avoid messing with markers. (save-excursion - (goto-char end) - (mapc - (lambda (pos) - (fill-region-as-paragraph pos (point) justify) - (goto-char pos)) - ;; Find the list of ending positions for line breaks - ;; in the current paragraph. Add paragraph - ;; beginning to include first slice. - (nreverse - (cons beg - (org-element-map - (org-element--parse-objects - beg end nil (org-element-restriction 'paragraph)) - 'line-break - (lambda (lb) (org-element-property :end lb))))))) + (goto-char beg) + (let ((cuts (list beg))) + (while (re-search-forward "\\\\\\\\[ \t]*\n" end t) + (when (eq 'line-break + (org-element-type + (save-excursion (backward-char) + (org-element-context)))) + (push (point) cuts))) + (dolist (c (delq end cuts)) + (fill-region-as-paragraph c end justify) + (setq end c)))) t))) ;; Contents of `comment-block' type elements should be ;; filled as plain text, but only if point is within block @@ -22564,6 +23365,130 @@ non-nil." (insert-before-markers-and-inherit fill-prefix)) +;;; Fixed Width Areas + +(defun org-toggle-fixed-width () + "Toggle fixed-width markup. + +Add or remove fixed-width markup on current line, whenever it +makes sense. Return an error otherwise. + +If a region is active and if it contains only fixed-width areas +or blank lines, remove all fixed-width markup in it. If the +region contains anything else, convert all non-fixed-width lines +to fixed-width ones. + +Blank lines at the end of the region are ignored unless the +region only contains such lines." + (interactive) + (if (not (org-region-active-p)) + ;; No region: + ;; + ;; Remove fixed width marker only in a fixed-with element. + ;; + ;; Add fixed width maker in paragraphs, in blank lines after + ;; elements or at the beginning of a headline or an inlinetask, + ;; and before any one-line elements (e.g., a clock). + (progn + (beginning-of-line) + (let* ((element (org-element-at-point)) + (type (org-element-type element))) + (cond + ((and (eq type 'fixed-width) + (looking-at "[ \t]*\\(:\\(?: \\|$\\)\\)")) + (replace-match + "" nil nil nil (if (= (line-end-position) (match-end 0)) 0 1))) + ((and (memq type '(babel-call clock comment diary-sexp headline + horizontal-rule keyword paragraph + planning)) + (<= (org-element-property :post-affiliated element) (point))) + (skip-chars-forward " \t") + (insert ": ")) + ((and (looking-at-p "[ \t]*$") + (or (eq type 'inlinetask) + (save-excursion + (skip-chars-forward " \r\t\n") + (<= (org-element-property :end element) (point))))) + (delete-region (point) (line-end-position)) + (org-indent-line) + (insert ": ")) + (t (user-error "Cannot insert a fixed-width line here"))))) + ;; Region active. + (let* ((begin (save-excursion + (goto-char (region-beginning)) + (line-beginning-position))) + (end (copy-marker + (save-excursion + (goto-char (region-end)) + (unless (eolp) (beginning-of-line)) + (if (save-excursion (re-search-backward "\\S-" begin t)) + (progn (skip-chars-backward " \r\t\n") (point)) + (point))))) + (all-fixed-width-p + (catch 'not-all-p + (save-excursion + (goto-char begin) + (skip-chars-forward " \r\t\n") + (when (eobp) (throw 'not-all-p nil)) + (while (< (point) end) + (let ((element (org-element-at-point))) + (if (eq (org-element-type element) 'fixed-width) + (goto-char (org-element-property :end element)) + (throw 'not-all-p nil)))) + t)))) + (if all-fixed-width-p + (save-excursion + (goto-char begin) + (while (< (point) end) + (when (looking-at "[ \t]*\\(:\\(?: \\|$\\)\\)") + (replace-match + "" nil nil nil + (if (= (line-end-position) (match-end 0)) 0 1))) + (forward-line))) + (let ((min-ind (point-max))) + ;; Find minimum indentation across all lines. + (save-excursion + (goto-char begin) + (if (not (save-excursion (re-search-forward "\\S-" end t))) + (setq min-ind 0) + (catch 'zerop + (while (< (point) end) + (unless (looking-at-p "[ \t]*$") + (let ((ind (org-get-indentation))) + (setq min-ind (min min-ind ind)) + (when (zerop ind) (throw 'zerop t)))) + (forward-line))))) + ;; Loop over all lines and add fixed-width markup everywhere + ;; but in fixed-width lines. + (save-excursion + (goto-char begin) + (while (< (point) end) + (cond + ((org-at-heading-p) + (insert ": ") + (forward-line) + (while (and (< (point) end) (looking-at-p "[ \t]*$")) + (insert ":") + (forward-line))) + ((looking-at-p "[ \t]*:\\( \\|$\\)") + (let* ((element (org-element-at-point)) + (element-end (org-element-property :end element))) + (if (eq (org-element-type element) 'fixed-width) + (progn (goto-char element-end) + (skip-chars-backward " \r\t\n") + (forward-line)) + (let ((limit (min end element-end))) + (while (< (point) limit) + (org-move-to-column min-ind t) + (insert ": ") + (forward-line)))))) + (t + (org-move-to-column min-ind t) + (insert ": ") + (forward-line))))))) + (set-marker end nil)))) + + ;;; Comments ;; Org comments syntax is quite complex. It requires the entire line @@ -22584,87 +23509,139 @@ non-nil." (defun org-setup-comments-handling () (interactive) - (org-set-local 'comment-use-syntax nil) - (org-set-local 'comment-start "# ") - (org-set-local 'comment-start-skip "^\\s-*#\\(?: \\|$\\)") - (org-set-local 'comment-insert-comment-function 'org-insert-comment) - (org-set-local 'comment-region-function 'org-comment-or-uncomment-region) - (org-set-local 'uncomment-region-function 'org-comment-or-uncomment-region)) + (setq-local comment-use-syntax nil) + (setq-local comment-start "# ") + (setq-local comment-start-skip "^\\s-*#\\(?: \\|$\\)") + (setq-local comment-insert-comment-function 'org-insert-comment) + (setq-local comment-region-function 'org-comment-or-uncomment-region) + (setq-local uncomment-region-function 'org-comment-or-uncomment-region)) (defun org-insert-comment () "Insert an empty comment above current line. -If the line is empty, insert comment at its beginning." - (beginning-of-line) - (if (looking-at "\\s-*$") (replace-match "") (open-line 1)) - (org-indent-line) - (insert "# ")) +If the line is empty, insert comment at its beginning. When +point is within a source block, comment according to the related +major mode." + (if (let ((element (org-element-at-point))) + (and (eq (org-element-type element) 'src-block) + (< (save-excursion + (goto-char (org-element-property :post-affiliated element)) + (line-end-position)) + (point)) + (> (save-excursion + (goto-char (org-element-property :end element)) + (skip-chars-backward " \r\t\n") + (line-beginning-position)) + (point)))) + (org-babel-do-in-edit-buffer (call-interactively 'comment-dwim)) + (beginning-of-line) + (if (looking-at "\\s-*$") (delete-region (point) (point-at-eol)) + (open-line 1)) + (org-indent-line) + (insert "# "))) (defvar comment-empty-lines) ; From newcomment.el. (defun org-comment-or-uncomment-region (beg end &rest _) "Comment or uncomment each non-blank line in the region. Uncomment each non-blank line between BEG and END if it only -contains commented lines. Otherwise, comment them." - (save-restriction - ;; Restrict region - (narrow-to-region (save-excursion (goto-char beg) - (skip-chars-forward " \r\t\n" end) - (line-beginning-position)) - (save-excursion (goto-char end) - (skip-chars-backward " \r\t\n" beg) - (line-end-position))) - (let ((uncommentp - ;; UNCOMMENTP is non-nil when every non blank line between - ;; BEG and END is a comment. - (save-excursion - (goto-char (point-min)) - (while (and (not (eobp)) - (let ((element (org-element-at-point))) - (and (eq (org-element-type element) 'comment) - (goto-char (min (point-max) - (org-element-property - :end element))))))) - (eobp)))) - (if uncommentp - ;; Only blank lines and comments in region: uncomment it. - (save-excursion - (goto-char (point-min)) - (while (not (eobp)) - (when (looking-at "[ \t]*\\(#\\(?: \\|$\\)\\)") - (replace-match "" nil nil nil 1)) - (forward-line))) - ;; Comment each line in region. - (let ((min-indent (point-max))) - ;; First find the minimum indentation across all lines. - (save-excursion - (goto-char (point-min)) - (while (and (not (eobp)) (not (zerop min-indent))) - (unless (looking-at "[ \t]*$") - (setq min-indent (min min-indent (current-indentation)))) - (forward-line))) - ;; Then loop over all lines. - (save-excursion - (goto-char (point-min)) - (while (not (eobp)) - (unless (and (not comment-empty-lines) (looking-at "[ \t]*$")) - ;; Don't get fooled by invisible text (e.g. link path) - ;; when moving to column MIN-INDENT. - (let ((buffer-invisibility-spec nil)) - (org-move-to-column min-indent t)) - (insert comment-start)) - (forward-line)))))))) +contains commented lines. Otherwise, comment them. If region is +strictly within a source block, use appropriate comment syntax." + (if (let ((element (org-element-at-point))) + (and (eq (org-element-type element) 'src-block) + (< (save-excursion + (goto-char (org-element-property :post-affiliated element)) + (line-end-position)) + beg) + (>= (save-excursion + (goto-char (org-element-property :end element)) + (skip-chars-backward " \r\t\n") + (line-beginning-position)) + end))) + ;; Translate region boundaries for the Org buffer to the source + ;; buffer. + (let ((offset (- end beg))) + (save-excursion + (goto-char beg) + (org-babel-do-in-edit-buffer + (comment-or-uncomment-region (point) (+ offset (point)))))) + (save-restriction + ;; Restrict region + (narrow-to-region (save-excursion (goto-char beg) + (skip-chars-forward " \r\t\n" end) + (line-beginning-position)) + (save-excursion (goto-char end) + (skip-chars-backward " \r\t\n" beg) + (line-end-position))) + (let ((uncommentp + ;; UNCOMMENTP is non-nil when every non blank line between + ;; BEG and END is a comment. + (save-excursion + (goto-char (point-min)) + (while (and (not (eobp)) + (let ((element (org-element-at-point))) + (and (eq (org-element-type element) 'comment) + (goto-char (min (point-max) + (org-element-property + :end element))))))) + (eobp)))) + (if uncommentp + ;; Only blank lines and comments in region: uncomment it. + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (when (looking-at "[ \t]*\\(#\\(?: \\|$\\)\\)") + (replace-match "" nil nil nil 1)) + (forward-line))) + ;; Comment each line in region. + (let ((min-indent (point-max))) + ;; First find the minimum indentation across all lines. + (save-excursion + (goto-char (point-min)) + (while (and (not (eobp)) (not (zerop min-indent))) + (unless (looking-at "[ \t]*$") + (setq min-indent (min min-indent (current-indentation)))) + (forward-line))) + ;; Then loop over all lines. + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (unless (and (not comment-empty-lines) (looking-at "[ \t]*$")) + ;; Don't get fooled by invisible text (e.g. link path) + ;; when moving to column MIN-INDENT. + (let ((buffer-invisibility-spec nil)) + (org-move-to-column min-indent t)) + (insert comment-start)) + (forward-line))))))))) + +(defun org-comment-dwim (_arg) + "Call `comment-dwim' within a source edit buffer if needed." + (interactive "*P") + (if (org-in-src-block-p) + (org-babel-do-in-edit-buffer (call-interactively 'comment-dwim)) + (call-interactively 'comment-dwim))) -;;; Planning +;;; Timestamps API ;; This section contains tools to operate on timestamp objects, as ;; returned by, e.g. `org-element-context'. +(defun org-timestamp--to-internal-time (timestamp &optional end) + "Encode TIMESTAMP object into Emacs internal time. +Use end of date range or time range when END is non-nil." + (apply #'encode-time + (cons 0 + (mapcar + (lambda (prop) (or (org-element-property prop timestamp) 0)) + (if end '(:minute-end :hour-end :day-end :month-end :year-end) + '(:minute-start :hour-start :day-start :month-start + :year-start)))))) + (defun org-timestamp-has-time-p (timestamp) "Non-nil when TIMESTAMP has a time specified." (org-element-property :hour-start timestamp)) -(defun org-timestamp-format (timestamp format &optional end zone) - "Format a TIMESTAMP element into a string. +(defun org-timestamp-format (timestamp format &optional end utc) + "Format a TIMESTAMP object into a string. FORMAT is a format specifier to be passed to `format-time-string'. @@ -22672,33 +23649,22 @@ FORMAT is a format specifier to be passed to When optional argument END is non-nil, use end of date-range or time-range, if possible. -The optional ZONE is omitted or nil for Emacs local time, t for -Universal Time, `wall' for system wall clock time, or a string as -in the TZ environment variable. It can also be a list (as from -`current-time-zone') or an integer (as from `decode-time') -applied without consideration for daylight saving time." +When optional argument UTC is non-nil, time will be expressed as +Universal Time." (format-time-string - format - (apply 'encode-time - (cons 0 - (mapcar - (lambda (prop) (or (org-element-property prop timestamp) 0)) - (if end '(:minute-end :hour-end :day-end :month-end :year-end) - '(:minute-start :hour-start :day-start :month-start - :year-start))))) - zone)) + format (org-timestamp--to-internal-time timestamp end) + (and utc t))) (defun org-timestamp-split-range (timestamp &optional end) - "Extract a timestamp object from a date or time range. + "Extract a TIMESTAMP object from a date or time range. -TIMESTAMP is a timestamp object. END, when non-nil, means extract -the end of the range. Otherwise, extract its start. +END, when non-nil, means extract the end of the range. +Otherwise, extract its start. -Return a new timestamp object sharing the same parent as -TIMESTAMP." +Return a new timestamp object." (let ((type (org-element-property :type timestamp))) (if (memq type '(active inactive diary)) timestamp - (let ((split-ts (list 'timestamp (copy-sequence (nth 1 timestamp))))) + (let ((split-ts (org-element-copy timestamp))) ;; Set new type. (org-element-put-property split-ts :type (if (eq type 'active-range) 'active 'inactive)) @@ -22712,91 +23678,43 @@ TIMESTAMP." (dolist (p-cell p-alist) (org-element-put-property split-ts - (funcall (if end 'car 'cdr) p-cell) + (funcall (if end #'car #'cdr) p-cell) (org-element-property - (funcall (if end 'cdr 'car) p-cell) split-ts))) + (funcall (if end #'cdr #'car) p-cell) split-ts))) ;; Eventually refresh `:raw-value'. (org-element-put-property split-ts :raw-value nil) (org-element-put-property split-ts :raw-value (org-element-interpret-data split-ts))))))) (defun org-timestamp-translate (timestamp &optional boundary) - "Apply `org-translate-time' on a TIMESTAMP object. + "Translate TIMESTAMP object to custom format. + +Format string is defined in `org-time-stamp-custom-formats', +which see. + When optional argument BOUNDARY is non-nil, it is either the symbol `start' or `end'. In this case, only translate the starting or ending part of TIMESTAMP if it is a date or time -range. Otherwise, translate both parts." - (if (and (not boundary) - (memq (org-element-property :type timestamp) - '(active-range inactive-range))) - (concat - (org-translate-time - (org-element-property :raw-value - (org-timestamp-split-range timestamp))) - "--" - (org-translate-time - (org-element-property :raw-value - (org-timestamp-split-range timestamp t)))) - (org-translate-time - (org-element-property - :raw-value - (if (not boundary) timestamp - (org-timestamp-split-range timestamp (eq boundary 'end))))))) +range. Otherwise, translate both parts. +Return timestamp as-is if `org-display-custom-times' is nil or if +it has a `diary' type." + (let ((type (org-element-property :type timestamp))) + (if (or (not org-display-custom-times) (eq type 'diary)) + (org-element-interpret-data timestamp) + (let ((fmt (funcall (if (org-timestamp-has-time-p timestamp) #'cdr #'car) + org-time-stamp-custom-formats))) + (if (and (not boundary) (memq type '(active-range inactive-range))) + (concat (org-timestamp-format timestamp fmt) + "--" + (org-timestamp-format timestamp fmt t)) + (org-timestamp-format timestamp fmt (eq boundary 'end))))))) -;;; Other stuff. -(defun org-toggle-fixed-width-section (arg) - "Toggle the fixed-width export. -If there is no active region, the QUOTE keyword at the current headline is -inserted or removed. When present, it causes the text between this headline -and the next to be exported as fixed-width text, and unmodified. -If there is an active region, this command adds or removes a colon as the -first character of this line. If the first character of a line is a colon, -this line is also exported in fixed-width font." - (interactive "P") - (let* ((cc 0) - (regionp (org-region-active-p)) - (beg (if regionp (region-beginning) (point))) - (end (if regionp (region-end))) - (nlines (or arg (if (and beg end) (count-lines beg end) 1))) - (case-fold-search nil) - (re "[ \t]*\\(:\\(?: \\|$\\)\\)") - off) - (if regionp - (save-excursion - (goto-char beg) - (setq cc (current-column)) - (beginning-of-line 1) - (setq off (looking-at re)) - (while (> nlines 0) - (setq nlines (1- nlines)) - (beginning-of-line 1) - (cond - (arg - (org-move-to-column cc t) - (insert ": \n") - (forward-line -1)) - ((and off (looking-at re)) - (replace-match "" t t nil 1)) - ((not off) (org-move-to-column cc t) (insert ": "))) - (forward-line 1))) - (save-excursion - (org-back-to-heading) - (cond - ((looking-at (format org-heading-keyword-regexp-format - org-quote-string)) - (goto-char (match-end 1)) - (looking-at (concat " +" org-quote-string)) - (replace-match "" t t) - (when (eolp) (insert " "))) - ((looking-at org-outline-regexp) - (goto-char (match-end 0)) - (insert org-quote-string " "))))))) +;;; Other stuff. (defvar reftex-docstruct-symbol) -(defvar reftex-cite-format) (defvar org--rds) (defun org-reftex-citation () @@ -22814,131 +23732,137 @@ Export of such citations to both LaTeX and HTML is handled by the contributed package ox-bibtex by Taru Karttunen." (interactive) (let ((reftex-docstruct-symbol 'org--rds) - (reftex-cite-format "\\cite{%l}") org--rds bib) - (save-excursion - (save-restriction - (widen) - (let ((case-fold-search t) - (re "^#\\+bibliography:[ \t]+\\([^ \t\n]+\\)")) - (if (not (save-excursion - (or (re-search-forward re nil t) - (re-search-backward re nil t)))) - (error "No bibliography defined in file") - (setq bib (concat (match-string 1) ".bib") - org--rds (list (list 'bib bib))))))) + (org-with-wide-buffer + (let ((case-fold-search t) + (re "^[ \t]*#\\+BIBLIOGRAPHY:[ \t]+\\([^ \t\n]+\\)")) + (if (not (save-excursion + (or (re-search-forward re nil t) + (re-search-backward re nil t)))) + (user-error "No bibliography defined in file") + (setq bib (concat (match-string 1) ".bib") + org--rds (list (list 'bib bib)))))) (call-interactively 'reftex-citation))) ;;;; Functions extending outline functionality -(defun org-beginning-of-line (&optional arg) - "Go to the beginning of the current line. If that is invisible, continue -to a visible line beginning. This makes the function of C-a more intuitive. -If this is a headline, and `org-special-ctrl-a/e' is set, ignore tags on the -first attempt, and only move to after the tags when the cursor is already -beyond the end of the headline." - (interactive "P") - (let ((pos (point)) - (special (if (consp org-special-ctrl-a/e) - (car org-special-ctrl-a/e) - org-special-ctrl-a/e)) - deactivate-mark refpos) - (if (org-bound-and-true-p visual-line-mode) - (beginning-of-visual-line 1) - (beginning-of-line 1)) - (if (and arg (fboundp 'move-beginning-of-line)) - (call-interactively 'move-beginning-of-line) - (if (bobp) - nil - (backward-char 1) - (if (org-truely-invisible-p) - (while (and (not (bobp)) (org-truely-invisible-p)) - (backward-char 1) - (beginning-of-line 1)) - (forward-char 1)))) - (when special - (cond - ((and (looking-at org-complex-heading-regexp) - (= (char-after (match-end 1)) ?\ )) - (setq refpos (min (1+ (or (match-end 3) (match-end 2) (match-end 1))) - (point-at-eol))) - (goto-char - (if (eq special t) - (cond ((> pos refpos) refpos) - ((= pos (point)) refpos) - (t (point))) - (cond ((> pos (point)) (point)) - ((not (eq last-command this-command)) (point)) - (t refpos))))) - ((org-at-item-p) - ;; Being at an item and not looking at an the item means point - ;; was previously moved to beginning of a visual line, which - ;; doesn't contain the item. Therefore, do nothing special, - ;; just stay here. - (when (looking-at org-list-full-item-re) - ;; Set special position at first white space character after - ;; bullet, and check-box, if any. - (let ((after-bullet - (let ((box (match-end 3))) - (if (not box) (match-end 1) - (let ((after (char-after box))) - (if (and after (= after ? )) (1+ box) box)))))) - ;; Special case: Move point to special position when - ;; currently after it or at beginning of line. - (if (eq special t) - (when (or (> pos after-bullet) (= (point) pos)) - (goto-char after-bullet)) - ;; Reversed case: Move point to special position when - ;; point was already at beginning of line and command is - ;; repeated. - (when (and (= (point) pos) (eq last-command this-command)) - (goto-char after-bullet)))))))) - (org-no-warnings - (and (featurep 'xemacs) (setq zmacs-region-stays t)))) - (setq disable-point-adjustment - (or (not (invisible-p (point))) - (not (invisible-p (max (point-min) (1- (point)))))))) - -(defun org-end-of-line (&optional arg) - "Go to the end of the line. +(defun org-beginning-of-line (&optional n) + "Go to the beginning of the current visible line. + If this is a headline, and `org-special-ctrl-a/e' is set, ignore tags on the first attempt, and only move to after the tags when -the cursor is already beyond the end of the headline." - (interactive "P") - (let ((special (if (consp org-special-ctrl-a/e) (cdr org-special-ctrl-a/e) - org-special-ctrl-a/e)) - (move-fun (cond ((org-bound-and-true-p visual-line-mode) - 'end-of-visual-line) - ((fboundp 'move-end-of-line) 'move-end-of-line) - (t 'end-of-line))) +the cursor is already beyond the end of the headline. + +With argument N not nil or 1, move forward N - 1 lines first." + (interactive "^p") + (let ((origin (point)) + (special (pcase org-special-ctrl-a/e + (`(,C-a . ,_) C-a) (_ org-special-ctrl-a/e))) deactivate-mark) - (if (or (not special) arg) (call-interactively move-fun) - (let* ((element (save-excursion (beginning-of-line) - (org-element-at-point))) - (type (org-element-type element))) - (cond - ((memq type '(headline inlinetask)) - (let ((pos (point))) - (beginning-of-line 1) - (if (looking-at (org-re ".*?\\(?:\\([ \t]*\\)\\(:[[:alnum:]_@#%:]+:\\)?[ \t]*\\)?$")) - (if (eq special t) - (if (or (< pos (match-beginning 1)) (= pos (match-end 0))) - (goto-char (match-beginning 1)) - (goto-char (match-end 0))) - (if (or (< pos (match-end 0)) - (not (eq this-command last-command))) - (goto-char (match-end 0)) - (goto-char (match-beginning 1)))) - (call-interactively move-fun)))) - ((org-element-property :hiddenp element) - ;; If element is hidden, `move-end-of-line' would put point - ;; after it. Use `end-of-line' to stay on current line. - (call-interactively 'end-of-line)) - (t (call-interactively move-fun))))) - (org-no-warnings (and (featurep 'xemacs) (setq zmacs-region-stays t)))) - (setq disable-point-adjustment - (or (not (invisible-p (point))) - (not (invisible-p (max (point-min) (1- (point)))))))) + ;; First move to a visible line. + (if (bound-and-true-p visual-line-mode) + (beginning-of-visual-line n) + (move-beginning-of-line n) + ;; `move-beginning-of-line' may leave point after invisible + ;; characters if line starts with such of these (e.g., with + ;; a link at column 0). Really move to the beginning of the + ;; current visible line. + (beginning-of-line)) + (cond + ;; No special behavior. Point is already at the beginning of + ;; a line, logical or visual. + ((not special)) + ;; `beginning-of-visual-line' left point before logical beginning + ;; of line: point is at the beginning of a visual line. Bail + ;; out. + ((and (bound-and-true-p visual-line-mode) (not (bolp)))) + ((let ((case-fold-search nil)) (looking-at org-complex-heading-regexp)) + ;; At a headline, special position is before the title, but + ;; after any TODO keyword or priority cookie. + (let ((refpos (min (1+ (or (match-end 3) (match-end 2) (match-end 1))) + (line-end-position))) + (bol (point))) + (if (eq special 'reversed) + (when (and (= origin bol) (eq last-command this-command)) + (goto-char refpos)) + (when (or (> origin refpos) (= origin bol)) + (goto-char refpos))))) + ((and (looking-at org-list-full-item-re) + (memq (org-element-type (save-match-data (org-element-at-point))) + '(item plain-list))) + ;; Set special position at first white space character after + ;; bullet, and check-box, if any. + (let ((after-bullet + (let ((box (match-end 3))) + (cond ((not box) (match-end 1)) + ((eq (char-after box) ?\s) (1+ box)) + (t box))))) + (if (eq special 'reversed) + (when (and (= (point) origin) (eq last-command this-command)) + (goto-char after-bullet)) + (when (or (> origin after-bullet) (= (point) origin)) + (goto-char after-bullet))))) + ;; No special context. Point is already at beginning of line. + (t nil)))) + +(defun org-end-of-line (&optional n) + "Go to the end of the line, but before ellipsis, if any. + +If this is a headline, and `org-special-ctrl-a/e' is set, ignore +tags on the first attempt, and only move to after the tags when +the cursor is already beyond the end of the headline. + +With argument N not nil or 1, move forward N - 1 lines first." + (interactive "^p") + (let ((origin (point)) + (special (pcase org-special-ctrl-a/e + (`(,_ . ,C-e) C-e) (_ org-special-ctrl-a/e))) + deactivate-mark) + ;; First move to a visible line. + (if (bound-and-true-p visual-line-mode) + (beginning-of-visual-line n) + (move-beginning-of-line n)) + (cond + ;; At a headline, with tags. + ((and special + (save-excursion + (beginning-of-line) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp))) + (match-end 5)) + (let ((tags (save-excursion + (goto-char (match-beginning 5)) + (skip-chars-backward " \t") + (point))) + (visual-end (and (bound-and-true-p visual-line-mode) + (save-excursion + (end-of-visual-line) + (point))))) + ;; If `end-of-visual-line' brings us before end of line or + ;; even tags, i.e., the headline spans over multiple visual + ;; lines, move there. + (cond ((and visual-end + (< visual-end tags) + (<= origin visual-end)) + (goto-char visual-end)) + ((eq special 'reversed) + (if (and (= origin (line-end-position)) + (eq this-command last-command)) + (goto-char tags) + (end-of-line))) + (t + (if (or (< origin tags) (= origin (line-end-position))) + (goto-char tags) + (end-of-line)))))) + ((bound-and-true-p visual-line-mode) + (let ((bol (line-beginning-position))) + (end-of-visual-line) + ;; If `end-of-visual-line' gets us past the ellipsis at the + ;; end of a line, backtrack and use `end-of-line' instead. + (when (/= bol (line-beginning-position)) + (goto-char bol) + (end-of-line)))) + (t (end-of-line))))) (define-key org-mode-map "\C-a" 'org-beginning-of-line) (define-key org-mode-map "\C-e" 'org-end-of-line) @@ -22948,18 +23872,43 @@ the cursor is already beyond the end of the headline." This will call `backward-sentence' or `org-table-beginning-of-field', depending on context." (interactive) - (cond - ((org-at-table-p) (call-interactively 'org-table-beginning-of-field)) - (t (call-interactively 'backward-sentence)))) + (let* ((element (org-element-at-point)) + (contents-begin (org-element-property :contents-begin element)) + (table (org-element-lineage element '(table) t))) + (if (and table + (> (point) contents-begin) + (<= (point) (org-element-property :contents-end table))) + (call-interactively #'org-table-beginning-of-field) + (save-restriction + (when (and contents-begin + (< (point-min) contents-begin) + (> (point) contents-begin)) + (narrow-to-region contents-begin + (org-element-property :contents-end element))) + (call-interactively #'backward-sentence))))) (defun org-forward-sentence (&optional _arg) "Go to end of sentence, or end of table field. This will call `forward-sentence' or `org-table-end-of-field', depending on context." (interactive) - (cond - ((org-at-table-p) (call-interactively 'org-table-end-of-field)) - (t (call-interactively 'forward-sentence)))) + (let* ((element (org-element-at-point)) + (contents-end (org-element-property :contents-end element)) + (table (org-element-lineage element '(table) t))) + (if (and table + (>= (point) (org-element-property :contents-begin table)) + (< (point) contents-end)) + (call-interactively #'org-table-end-of-field) + (save-restriction + (when (and contents-end + (> (point-max) contents-end) + ;; Skip blank lines between elements. + (< (org-element-property :end element) + (save-excursion (goto-char contents-end) + (skip-chars-forward " \r\t\n")))) + (narrow-to-region (org-element-property :contents-begin element) + contents-end)) + (call-interactively #'forward-sentence))))) (define-key org-mode-map "\M-a" 'org-backward-sentence) (define-key org-mode-map "\M-e" 'org-forward-sentence) @@ -22971,14 +23920,14 @@ depending on context." ((or (not org-special-ctrl-k) (bolp) (not (org-at-heading-p))) - (if (and (get-char-property (min (point-max) (point-at-eol)) 'invisible) - org-ctrl-k-protect-subtree) - (if (or (eq org-ctrl-k-protect-subtree 'error) - (not (y-or-n-p "Kill hidden subtree along with headline? "))) - (user-error "C-k aborted as it would kill a hidden subtree"))) + (when (and (get-char-property (min (point-max) (point-at-eol)) 'invisible) + org-ctrl-k-protect-subtree + (or (eq org-ctrl-k-protect-subtree 'error) + (not (y-or-n-p "Kill hidden subtree along with headline? ")))) + (user-error "C-k aborted as it would kill a hidden subtree")) (call-interactively - (if (org-bound-and-true-p visual-line-mode) 'kill-visual-line 'kill-line))) - ((looking-at (org-re ".*?\\S-\\([ \t]+\\(:[[:alnum:]_@#%:]+:\\)\\)[ \t]*$")) + (if (bound-and-true-p visual-line-mode) 'kill-visual-line 'kill-line))) + ((looking-at ".*?\\S-\\([ \t]+\\(:[[:alnum:]_@#%:]+:\\)\\)[ \t]*$") (kill-region (point) (match-beginning 1)) (org-set-tags nil t)) (t (kill-region (point) (point-at-eol))))) @@ -22991,24 +23940,25 @@ This command will look at the current kill and check if is a single subtree, or a series of subtrees[1]. If it passes the test, and if the cursor is at the beginning of a line or after the stars of a currently empty headline, then the yank is handled specially. How exactly depends -on the value of the following variables, both set by default. +on the value of the following variables. -org-yank-folded-subtrees - When set, the subtree(s) will be folded after insertion, but only - if doing so would now swallow text after the yanked text. +`org-yank-folded-subtrees' + By default, this variable is non-nil, which results in + subtree(s) being folded after insertion, except if doing so + would swallow text after the yanked text. -org-yank-adjusted-subtrees - When set, the subtree will be promoted or demoted in order to - fit into the local outline tree structure, which means that the level - will be adjusted so that it becomes the smaller one of the two - *visible* surrounding headings. +`org-yank-adjusted-subtrees' + When non-nil (the default value is nil), the subtree will be + promoted or demoted in order to fit into the local outline tree + structure, which means that the level will be adjusted so that it + becomes the smaller one of the two *visible* surrounding headings. Any prefix to this command will cause `yank' to be called directly with -no special treatment. In particular, a simple \\[universal-argument] prefix \ +no special treatment. In particular, a simple `\\[universal-argument]' prefix \ will just plainly yank the text as it is. -[1] The test checks if the first non-white line is a heading +\[1] The test checks if the first non-white line is a heading and if there are no other headings with fewer stars." (interactive "P") (org-yank-generic 'yank arg)) @@ -23051,7 +24001,7 @@ interactive command with similar behavior." (or (looking-at org-outline-regexp) (re-search-forward org-outline-regexp-bol end t)) (while (and (< (point) end) (looking-at org-outline-regexp)) - (hide-subtree) + (outline-hide-subtree) (org-cycle-show-empty-lines 'folded) (condition-case nil (outline-forward-same-level 1) @@ -23082,11 +24032,9 @@ interactive command with similar behavior." (setq level (org-outline-level))) (goto-char end) (skip-chars-forward " \t\r\n\v\f") - (if (or (eobp) - (and (bolp) (looking-at org-outline-regexp) - (<= (org-outline-level) level))) - nil ; Nothing would be swallowed - t))))) ; something would swallow + (not (or (eobp) + (and (bolp) (looking-at-p org-outline-regexp) + (<= (org-outline-level) level)))))))) (define-key org-mode-map "\C-y" 'org-yank) @@ -23094,17 +24042,18 @@ interactive command with similar behavior." "Check if point is at a character currently not visible. This version does not only check the character property, but also `visible-mode'." - ;; Early versions of noutline don't have `outline-invisible-p'. - (if (org-bound-and-true-p visible-mode) - nil - (outline-invisible-p))) + (unless (bound-and-true-p visible-mode) + (org-invisible-p))) (defun org-invisible-p2 () - "Check if point is at a character currently not visible." + "Check if point is at a character currently not visible. + +If the point is at EOL (and not at the beginning of a buffer too), +move it back by one char before doing this check." (save-excursion - (if (and (eolp) (not (bobp))) (backward-char 1)) - ;; Early versions of noutline don't have `outline-invisible-p'. - (outline-invisible-p))) + (when (and (eolp) (not (bobp))) + (backward-char 1)) + (org-invisible-p))) (defun org-back-to-heading (&optional invisible-ok) "Call `outline-back-to-heading', but provide a better error message." @@ -23121,14 +24070,28 @@ This version does not only check the character property, but also (defun org-at-heading-p (&optional ignored) (outline-on-heading-p t)) -;; Compatibility alias with Org versions < 7.8.03 -(defalias 'org-on-heading-p 'org-at-heading-p) + +(defun org-in-commented-heading-p (&optional no-inheritance) + "Non-nil if point is under a commented heading. +This function also checks ancestors of the current headline, +unless optional argument NO-INHERITANCE is non-nil." + (cond + ((org-before-first-heading-p) nil) + ((let ((headline (nth 4 (org-heading-components)))) + (and headline + (let ((case-fold-search nil)) + (string-match-p (concat "^" org-comment-string "\\(?: \\|$\\)") + headline))))) + (no-inheritance nil) + (t + (save-excursion (and (org-up-heading-safe) (org-in-commented-heading-p)))))) (defun org-at-comment-p nil - "Is cursor in a line starting with a # character?" + "Is cursor in a commented line?" (save-excursion - (beginning-of-line) - (looking-at "^#"))) + (save-match-data + (beginning-of-line) + (looking-at "^[ \t]*# ")))) (defun org-at-drawer-p nil "Is cursor at a drawer keyword?" @@ -23146,13 +24109,13 @@ This version does not only check the character property, but also "If point is at the end of an empty headline, return t, else nil. If the heading only contains a TODO keyword, it is still still considered empty." - (and (looking-at "[ \t]*$") - (when org-todo-line-regexp + (let ((case-fold-search nil)) + (and (looking-at "[ \t]*$") + org-todo-line-regexp (save-excursion - (beginning-of-line 1) - (let ((case-fold-search nil)) - (looking-at org-todo-line-regexp) - (string= (match-string 3) "")))))) + (beginning-of-line) + (looking-at org-todo-line-regexp) + (string= (match-string 3) ""))))) (defun org-at-heading-or-item-p () (or (org-at-heading-p) (org-at-item-p))) @@ -23167,9 +24130,7 @@ empty." "Move to the heading line of which the present line is a subheading. This function considers both visible and invisible heading lines. With argument, move up ARG levels." - (if (fboundp 'outline-up-heading-all) - (outline-up-heading-all arg) ; emacs 21 version of outline.el - (outline-up-heading arg t))) ; emacs 22 version of outline.el + (outline-up-heading arg t)) (defun org-up-heading-safe () "Move to the heading line of which the present line is a subheading. @@ -23179,14 +24140,11 @@ headline found, or nil if no higher level is found. Also, this function will be a lot faster than `outline-up-heading', because it relies on stars being the outline starters. This can really make a significant difference in outlines with very many siblings." - (let (start-level re) - (org-back-to-heading t) - (setq start-level (funcall outline-level)) - (if (equal start-level 1) - nil - (setq re (concat "^\\*\\{1," (number-to-string (1- start-level)) "\\} ")) - (if (re-search-backward re nil t) - (funcall outline-level))))) + (when (ignore-errors (org-back-to-heading t)) + (let ((level-up (1- (funcall outline-level)))) + (and (> level-up 0) + (re-search-backward (format "^\\*\\{1,%d\\} " level-up) nil t) + (funcall outline-level))))) (defun org-first-sibling-p () "Is this heading the first child of its parents?" @@ -23211,7 +24169,7 @@ move point." (pos (point)) (re org-outline-regexp-bol) level l) - (when (condition-case nil (org-back-to-heading t) (error nil)) + (when (ignore-errors (org-back-to-heading t)) (setq level (funcall outline-level)) (catch 'exit (or previous (forward-char 1)) @@ -23235,7 +24193,7 @@ move point." Return t when a child was found. Otherwise don't move point and return nil." (let (level (pos (point)) (re org-outline-regexp-bol)) - (when (condition-case nil (org-back-to-heading t) (error nil)) + (when (ignore-errors (org-back-to-heading t)) (setq level (outline-level)) (forward-char 1) (if (and (re-search-forward re nil t) (> (outline-level) level)) @@ -23271,8 +24229,7 @@ This is like outline-next-sibling, but invisible headings are ok." (outline-next-heading) (while (and (not (eobp)) (> (funcall outline-level) level)) (outline-next-heading)) - (if (or (eobp) (< (funcall outline-level) level)) - nil + (unless (or (eobp) (< (funcall outline-level) level)) (point)))) (defun org-get-last-sibling () @@ -23285,8 +24242,7 @@ If there is no such heading, return nil." (while (and (> (funcall outline-level) level) (not (bobp))) (outline-previous-heading)) - (if (< (funcall outline-level) level) - nil + (unless (< (funcall outline-level) level) (point))))) (defun org-end-of-subtree (&optional invisible-ok to-heading) @@ -23302,7 +24258,7 @@ If there is no such heading, return nil." (let ((first t) (level (funcall outline-level))) (if (and (derived-mode-p 'org-mode) (< level 1000)) - ;; A true heading (not a plain list item), in Org-mode + ;; A true heading (not a plain list item), in Org ;; This means we can easily find the end by looking ;; only for the right number of stars. Using a regexp to do ;; this is so much faster than using a Lisp loop. @@ -23315,33 +24271,36 @@ If there is no such heading, return nil." (setq first nil) (outline-next-heading))) (unless to-heading - (if (memq (preceding-char) '(?\n ?\^M)) - (progn - ;; Go to end of line before heading - (forward-char -1) - (if (memq (preceding-char) '(?\n ?\^M)) - ;; leave blank line before heading - (forward-char -1)))))) + (when (memq (preceding-char) '(?\n ?\^M)) + ;; Go to end of line before heading + (forward-char -1) + (when (memq (preceding-char) '(?\n ?\^M)) + ;; leave blank line before heading + (forward-char -1))))) (point)) -(defun org-end-of-meta-data-and-drawers () - "Jump to the first text after meta data and drawers in the current entry. -This will move over empty lines, lines with planning time stamps, -clocking lines, and drawers." +(defun org-end-of-meta-data (&optional full) + "Skip planning line and properties drawer in current entry. +When optional argument FULL is non-nil, also skip empty lines, +clocking lines and regular drawers at the beginning of the +entry." (org-back-to-heading t) - (let ((end (save-excursion (outline-next-heading) (point))) - (re (concat "\\(" org-drawer-regexp "\\)" - "\\|" "[ \t]*" org-keyword-time-regexp))) - (forward-line 1) - (while (re-search-forward re end t) - (if (not (match-end 1)) - ;; empty or planning line - (forward-line 1) - ;; a drawer, find the end - (re-search-forward "^[ \t]*:END:" end 'move) - (forward-line 1))) - (and (re-search-forward "[^\n]" nil t) (backward-char 1)) - (point))) + (forward-line) + (when (looking-at-p org-planning-line-re) (forward-line)) + (when (looking-at org-property-drawer-re) + (goto-char (match-end 0)) + (forward-line)) + (when (and full (not (org-at-heading-p))) + (catch 'exit + (let ((end (save-excursion (outline-next-heading) (point))) + (re (concat "[ \t]*$" "\\|" org-clock-line-re))) + (while (not (eobp)) + (cond ((looking-at-p org-drawer-regexp) + (if (re-search-forward "^[ \t]*:END:[ \t]*$" end t) + (forward-line) + (throw 'exit t))) + ((looking-at-p re) (forward-line)) + (t (throw 'exit t)))))))) (defun org-forward-heading-same-level (arg &optional invisible-ok) "Move forward to the ARG'th subheading at same level as this one. @@ -23349,32 +24308,27 @@ Stop at the first and last subheadings of a superior heading. Normally this only looks at visible headings, but when INVISIBLE-OK is non-nil it will also look at invisible ones." (interactive "p") - (if (not (ignore-errors (org-back-to-heading invisible-ok))) - (if (and arg (< arg 0)) - (goto-char (point-min)) - (outline-next-heading)) - (org-at-heading-p) - (let ((level (- (match-end 0) (match-beginning 0) 1)) - (f (if (and arg (< arg 0)) - 're-search-backward - 're-search-forward)) - (count (if arg (abs arg) 1)) - (result (point))) - (while (and (prog1 (> count 0) - (forward-char (if (and arg (< arg 0)) -1 1))) - (funcall f org-outline-regexp-bol nil 'move)) - (let ((l (- (match-end 0) (match-beginning 0) 1))) - (cond ((< l level) (setq count 0)) - ((and (= l level) - (or invisible-ok - (progn - (goto-char (line-beginning-position)) - (not (outline-invisible-p))))) - (setq count (1- count)) - (when (eq l level) - (setq result (point))))))) - (goto-char result)) - (beginning-of-line 1))) + (let ((backward? (and arg (< arg 0)))) + (if (org-before-first-heading-p) + (if backward? (goto-char (point-min)) (outline-next-heading)) + (org-back-to-heading invisible-ok) + (unless backward? (end-of-line)) ;do not match current headline + (let ((level (- (match-end 0) (match-beginning 0) 1)) + (f (if backward? #'re-search-backward #'re-search-forward)) + (count (if arg (abs arg) 1)) + (result (point))) + (while (and (> count 0) + (funcall f org-outline-regexp-bol nil 'move)) + (let ((l (- (match-end 0) (match-beginning 0) 1))) + (cond ((< l level) (setq count 0)) + ((and (= l level) + (or invisible-ok + (not (org-invisible-p + (line-beginning-position))))) + (cl-decf count) + (when (= l level) (setq result (point))))))) + (goto-char result)) + (beginning-of-line)))) (defun org-backward-heading-same-level (arg &optional invisible-ok) "Move backward to the ARG'th subheading at same level as this one. @@ -23382,20 +24336,64 @@ Stop at the first and last subheadings of a superior heading." (interactive "p") (org-forward-heading-same-level (if arg (- arg) -1) invisible-ok)) +(defun org-next-visible-heading (arg) + "Move to the next visible heading. + +This function wraps `outline-next-visible-heading' with +`org-with-limited-levels' in order to skip over inline tasks and +respect customization of `org-odd-levels-only'." + (interactive "p") + (org-with-limited-levels + (outline-next-visible-heading arg))) + +(defun org-previous-visible-heading (arg) + "Move to the previous visible heading. + +This function wraps `outline-previous-visible-heading' with +`org-with-limited-levels' in order to skip over inline tasks and +respect customization of `org-odd-levels-only'." + (interactive "p") + (org-with-limited-levels + (outline-previous-visible-heading arg))) + (defun org-next-block (arg &optional backward block-regexp) "Jump to the next block. -With a prefix argument ARG, jump forward ARG many source blocks. + +With a prefix argument ARG, jump forward ARG many blocks. + When BACKWARD is non-nil, jump to the previous block. -When BLOCK-REGEXP is non-nil, use this regexp to find blocks." + +When BLOCK-REGEXP is non-nil, use this regexp to find blocks. +Match data is set according to this regexp when the function +returns. + +Return point at beginning of the opening line of found block. +Throw an error if no block is found." (interactive "p") - (let ((re (or block-regexp org-block-regexp)) - (re-search-fn (or (and backward 're-search-backward) - 're-search-forward))) - (if (looking-at re) (forward-char 1)) - (condition-case nil - (funcall re-search-fn re nil nil arg) - (error (error "No %s code blocks" (if backward "previous" "further" )))) - (goto-char (match-beginning 0)) (org-show-context))) + (let ((re (or block-regexp "^[ \t]*#\\+BEGIN")) + (case-fold-search t) + (search-fn (if backward #'re-search-backward #'re-search-forward)) + (count (or arg 1)) + (origin (point)) + last-element) + (if backward (beginning-of-line) (end-of-line)) + (while (and (> count 0) (funcall search-fn re nil t)) + (let ((element (save-excursion + (goto-char (match-beginning 0)) + (save-match-data (org-element-at-point))))) + (when (and (memq (org-element-type element) + '(center-block comment-block dynamic-block + example-block export-block quote-block + special-block src-block verse-block)) + (<= (match-beginning 0) + (org-element-property :post-affiliated element))) + (setq last-element element) + (cl-decf count)))) + (if (= count 0) + (prog1 (goto-char (org-element-property :post-affiliated last-element)) + (save-match-data (org-show-context))) + (goto-char origin) + (user-error "No %s code blocks" (if backward "previous" "further"))))) (defun org-previous-block (arg &optional block-regexp) "Jump to the previous block. @@ -23434,7 +24432,7 @@ item, etc. It also provides some special moves for convenience: (skip-chars-forward " \r\t\n") (or (eobp) (beginning-of-line))) ;; On affiliated keywords, move to element's beginning. - ((and post-affiliated (< (point) post-affiliated)) + ((< (point) post-affiliated) (goto-char post-affiliated)) ;; At a table row, move to the end of the table. Similarly, ;; at a node property, move to the end of the property @@ -23461,8 +24459,8 @@ item, etc. It also provides some special moves for convenience: ;; With no contents, just skip element. ((not contents-begin) (goto-char end)) ;; If contents are invisible, skip the element altogether. - ((outline-invisible-p (line-end-position)) - (case type + ((org-invisible-p (line-end-position)) + (cl-case type (headline (org-with-limited-levels (outline-next-visible-heading 1))) ;; At a plain list, make sure we move to the next item @@ -23473,7 +24471,7 @@ item, etc. It also provides some special moves for convenience: ((>= (point) contents-end) (goto-char end)) ((>= (point) contents-begin) ;; This can only happen on paragraphs and plain lists. - (case type + (cl-case type (paragraph (goto-char end)) ;; At a plain list, try to move to second element in ;; first item, if possible. @@ -23513,7 +24511,7 @@ convenience: ((= (point) begin) (backward-char) (org-backward-paragraph)) - ((and post-affiliated (<= (point) post-affiliated)) (goto-char begin)) + ((<= (point) post-affiliated) (goto-char begin)) ((memq type '(node-property table-row)) (goto-char (org-element-property :post-affiliated (org-element-property :parent element)))) @@ -23548,7 +24546,7 @@ convenience: (org-backward-paragraph)) (t (goto-char (or post-affiliated begin)))) ;; Ensure we never leave point invisible. - (when (outline-invisible-p (point)) (beginning-of-visual-line)))) + (when (org-invisible-p (point)) (beginning-of-visual-line)))) (defun org-forward-element () "Move forward by one element. @@ -23587,18 +24585,21 @@ Move to the previous element at the same level, when possible." (progn (goto-char origin) (user-error "Cannot move further up")))))) (t - (let* ((trail (org-element-at-point 'keep-trail)) - (elem (car trail)) - (prev-elem (nth 1 trail)) + (let* ((elem (org-element-at-point)) (beg (org-element-property :begin elem))) (cond ;; Move to beginning of current element if point isn't ;; there already. ((null beg) (message "No element at point")) ((/= (point) beg) (goto-char beg)) - (prev-elem (goto-char (org-element-property :begin prev-elem))) - ((org-before-first-heading-p) (goto-char (point-min))) - (t (org-back-to-heading))))))) + (t (goto-char beg) + (skip-chars-backward " \r\t\n") + (unless (bobp) + (let ((prev (org-element-at-point))) + (goto-char (org-element-property :begin prev)) + (while (and (setq prev (org-element-property :parent prev)) + (<= (org-element-property :end prev) beg)) + (goto-char (org-element-property :begin prev))))))))))) (defun org-up-element () "Move to upper element." @@ -23612,7 +24613,6 @@ Move to the previous element at the same level, when possible." (user-error "No surrounding element") (org-with-limited-levels (org-back-to-heading))))))) -(defvar org-element-greater-elements) (defun org-down-element () "Move to inner element." (interactive) @@ -23623,7 +24623,7 @@ Move to the previous element at the same level, when possible." (forward-char)) ((memq (org-element-type element) org-element-greater-elements) ;; If contents are hidden, first disclose them. - (when (org-element-property :hiddenp element) (org-cycle)) + (when (org-invisible-p (line-end-position)) (org-cycle)) (goto-char (or (org-element-property :contents-begin element) (user-error "No content for this element")))) (t (user-error "No inner element"))))) @@ -23631,24 +24631,41 @@ Move to the previous element at the same level, when possible." (defun org-drag-element-backward () "Move backward element at point." (interactive) - (if (org-with-limited-levels (org-at-heading-p)) (org-move-subtree-up) - (let* ((trail (org-element-at-point 'keep-trail)) - (elem (car trail)) - (prev-elem (nth 1 trail))) - ;; Error out if no previous element or previous element is - ;; a parent of the current one. - (if (or (not prev-elem) (org-element-nested-p elem prev-elem)) - (user-error "Cannot drag element backward") - (let ((pos (point))) - (org-element-swap-A-B prev-elem elem) - (goto-char (+ (org-element-property :begin prev-elem) - (- pos (org-element-property :begin elem))))))))) + (let ((elem (or (org-element-at-point) + (user-error "No element at point")))) + (if (eq (org-element-type elem) 'headline) + ;; Preserve point when moving a whole tree, even if point was + ;; on blank lines below the headline. + (let ((offset (skip-chars-backward " \t\n"))) + (unwind-protect (org-move-subtree-up) + (forward-char (- offset)))) + (let ((prev-elem + (save-excursion + (goto-char (org-element-property :begin elem)) + (skip-chars-backward " \r\t\n") + (unless (bobp) + (let* ((beg (org-element-property :begin elem)) + (prev (org-element-at-point)) + (up prev)) + (while (and (setq up (org-element-property :parent up)) + (<= (org-element-property :end up) beg)) + (setq prev up)) + prev))))) + ;; Error out if no previous element or previous element is + ;; a parent of the current one. + (if (or (not prev-elem) (org-element-nested-p elem prev-elem)) + (user-error "Cannot drag element backward") + (let ((pos (point))) + (org-element-swap-A-B prev-elem elem) + (goto-char (+ (org-element-property :begin prev-elem) + (- pos (org-element-property :begin elem)))))))))) (defun org-drag-element-forward () "Move forward element at point." (interactive) (let* ((pos (point)) - (elem (org-element-at-point))) + (elem (or (org-element-at-point) + (user-error "No element at point")))) (when (= (point-max) (org-element-property :end elem)) (user-error "Cannot drag element forward")) (goto-char (org-element-property :end elem)) @@ -23681,7 +24698,7 @@ Move to the previous element at the same level, when possible." (defun org-drag-line-forward (arg) "Drag the line at point ARG lines forward." (interactive "p") - (dotimes (n (abs arg)) + (dotimes (_ (abs arg)) (let ((c (current-column))) (if (< 0 arg) (progn @@ -23705,7 +24722,7 @@ mode) if the mark is active, it marks the next element after the ones already marked." (interactive) (let (deactivate-mark) - (if (and (org-called-interactively-p 'any) + (if (and (called-interactively-p 'any) (or (and (eq last-command this-command) (mark t)) (and transient-mark-mode mark-active))) (set-mark @@ -23751,13 +24768,10 @@ modified." (interactive) (unless (eq major-mode 'org-mode) (user-error "Cannot un-indent a buffer not in Org mode")) - (let* ((parse-tree (org-element-parse-buffer 'greater-element)) - unindent-tree ; For byte-compiler. - (unindent-tree - (function - (lambda (contents) - (mapc - (lambda (element) + (letrec ((parse-tree (org-element-parse-buffer 'greater-element)) + (unindent-tree + (lambda (contents) + (dolist (element (reverse contents)) (if (memq (org-element-type element) '(headline section)) (funcall unindent-tree (org-element-contents element)) (save-excursion @@ -23765,10 +24779,49 @@ modified." (narrow-to-region (org-element-property :begin element) (org-element-property :end element)) - (org-do-remove-indentation))))) - (reverse contents)))))) + (org-do-remove-indentation)))))))) (funcall unindent-tree (org-element-contents parse-tree)))) +(defun org-show-children (&optional level) + "Show all direct subheadings of this heading. +Prefix arg LEVEL is how many levels below the current level +should be shown. Default is enough to cause the following +heading to appear." + (interactive "p") + ;; If `orgstruct-mode' is active, use the slower version. + (if orgstruct-mode (call-interactively #'outline-show-children) + (save-excursion + (org-back-to-heading t) + (let* ((current-level (funcall outline-level)) + (max-level (org-get-valid-level + current-level + (if level (prefix-numeric-value level) 1))) + (end (save-excursion (org-end-of-subtree t t))) + (regexp-fmt "^\\*\\{%d,%s\\}\\(?: \\|$\\)") + (past-first-child nil) + ;; Make sure to skip inlinetasks. + (re (format regexp-fmt + current-level + (cond + ((not (featurep 'org-inlinetask)) "") + (org-odd-levels-only (- (* 2 org-inlinetask-min-level) + 3)) + (t (1- org-inlinetask-min-level)))))) + ;; Display parent heading. + (outline-flag-region (line-end-position 0) (line-end-position) nil) + (forward-line) + ;; Display children. First child may be deeper than expected + ;; MAX-LEVEL. Since we want to display it anyway, adjust + ;; MAX-LEVEL accordingly. + (while (re-search-forward re end t) + (unless past-first-child + (setq re (format regexp-fmt + current-level + (max (funcall outline-level) max-level))) + (setq past-first-child t)) + (outline-flag-region + (line-end-position 0) (line-end-position) nil)))))) + (defun org-show-subtree () "Show everything after this heading at deeper levels." (interactive) @@ -23783,58 +24836,33 @@ modified." Show the heading too, if it is currently invisible." (interactive) (save-excursion - (condition-case nil - (progn - (org-back-to-heading t) - (outline-flag-region - (max (point-min) (1- (point))) - (save-excursion - (if (re-search-forward - (concat "[\r\n]\\(" org-outline-regexp "\\)") nil t) - (match-beginning 1) - (point-max))) - nil) - (org-cycle-hide-drawers 'children)) - (error nil)))) + (ignore-errors + (org-back-to-heading t) + (outline-flag-region + (max (point-min) (1- (point))) + (save-excursion + (if (re-search-forward + (concat "[\r\n]\\(" org-outline-regexp "\\)") nil t) + (match-beginning 1) + (point-max))) + nil) + (org-cycle-hide-drawers 'children)))) (defun org-make-options-regexp (kwds &optional extra) - "Make a regular expression for keyword lines." - (concat - "^#\\+\\(" - (mapconcat 'regexp-quote kwds "\\|") - (if extra (concat "\\|" extra)) - "\\):[ \t]*\\(.*\\)")) - -;; Make isearch reveal the necessary context -(defun org-isearch-end () - "Reveal context after isearch exits." - (when isearch-success ; only if search was successful - (if (featurep 'xemacs) - ;; Under XEmacs, the hook is run in the correct place, - ;; we directly show the context. - (org-show-context 'isearch) - ;; In Emacs the hook runs *before* restoring the overlays. - ;; So we have to use a one-time post-command-hook to do this. - ;; (Emacs 22 has a special variable, see function `org-mode') - (unless (and (boundp 'isearch-mode-end-hook-quit) - isearch-mode-end-hook-quit) - ;; Only when the isearch was not quitted. - (org-add-hook 'post-command-hook 'org-isearch-post-command - 'append 'local))))) - -(defun org-isearch-post-command () - "Remove self from hook, and show context." - (remove-hook 'post-command-hook 'org-isearch-post-command 'local) - (org-show-context 'isearch)) - + "Make a regular expression for keyword lines. +KWDS is a list of keywords, as strings. Optional argument EXTRA, +when non-nil, is a regexp matching keywords names." + (concat "^[ \t]*#\\+\\(" + (regexp-opt kwds) + (and extra (concat (and kwds "\\|") extra)) + "\\):[ \t]*\\(.*\\)")) ;;;; Integration with and fixes for other packages ;;; Imenu support -(defvar org-imenu-markers nil +(defvar-local org-imenu-markers nil "All markers currently used by Imenu.") -(make-variable-buffer-local 'org-imenu-markers) (defun org-imenu-new-marker (&optional pos) "Return a new marker for use by Imenu, and remember the marker." @@ -23845,50 +24873,48 @@ Show the heading too, if it is currently invisible." (defun org-imenu-get-tree () "Produce the index for Imenu." - (mapc (lambda (x) (move-marker x nil)) org-imenu-markers) + (dolist (x org-imenu-markers) (move-marker x nil)) (setq org-imenu-markers nil) - (let* ((n org-imenu-depth) + (let* ((case-fold-search nil) + (n org-imenu-depth) (re (concat "^" (org-get-limited-outline-regexp))) (subs (make-vector (1+ n) nil)) (last-level 0) m level head0 head) - (save-excursion - (save-restriction - (widen) - (goto-char (point-max)) - (while (re-search-backward re nil t) - (setq level (org-reduced-level (funcall outline-level))) - (when (and (<= level n) - (looking-at org-complex-heading-regexp) - (setq head0 (org-match-string-no-properties 4))) - (setq head (org-link-display-format head0) - m (org-imenu-new-marker)) - (org-add-props head nil 'org-imenu-marker m 'org-imenu t) - (if (>= level last-level) - (push (cons head m) (aref subs level)) - (push (cons head (aref subs (1+ level))) (aref subs level)) - (loop for i from (1+ level) to n do (aset subs i nil))) - (setq last-level level))))) + (org-with-wide-buffer + (goto-char (point-max)) + (while (re-search-backward re nil t) + (setq level (org-reduced-level (funcall outline-level))) + (when (and (<= level n) + (looking-at org-complex-heading-regexp) + (setq head0 (match-string-no-properties 4))) + (setq head (org-link-display-format head0) + m (org-imenu-new-marker)) + (org-add-props head nil 'org-imenu-marker m 'org-imenu t) + (if (>= level last-level) + (push (cons head m) (aref subs level)) + (push (cons head (aref subs (1+ level))) (aref subs level)) + (cl-loop for i from (1+ level) to n do (aset subs i nil))) + (setq last-level level)))) (aref subs 1))) (eval-after-load "imenu" '(progn (add-hook 'imenu-after-jump-hook (lambda () - (if (derived-mode-p 'org-mode) - (org-show-context 'org-goto)))))) + (when (derived-mode-p 'org-mode) + (org-show-context 'org-goto)))))) -(defun org-link-display-format (link) - "Replace a link with its the description. +(defun org-link-display-format (s) + "Replace links in string S with their description. If there is no description, use the link target." (save-match-data - (if (string-match org-bracket-link-analytic-regexp link) - (replace-match (if (match-end 5) - (match-string 5 link) - (concat (match-string 1 link) - (match-string 3 link))) - nil t link) - link))) + (replace-regexp-in-string + org-bracket-link-analytic-regexp + (lambda (m) + (if (match-end 5) (match-string 5 m) + (concat (match-string 1 m) (match-string 3 m)))) + s nil t))) (defun org-toggle-link-display () "Toggle the literal or descriptive display of links." @@ -23909,11 +24935,11 @@ If there is no description, use the link target." 'face 'org-agenda-restriction-lock) (overlay-put org-speedbar-restriction-lock-overlay 'help-echo "Agendas are currently limited to this item.") -(org-detach-overlay org-speedbar-restriction-lock-overlay) +(delete-overlay org-speedbar-restriction-lock-overlay) (defun org-speedbar-set-agenda-restriction () "Restrict future agenda commands to the location at point in speedbar. -To get rid of the restriction, use \\[org-agenda-remove-restriction-lock]." +To get rid of the restriction, use `\\[org-agenda-remove-restriction-lock]'." (interactive) (require 'org-agenda) (let (p m tp np dir txt) @@ -23937,9 +24963,9 @@ To get rid of the restriction, use \\[org-agenda-remove-restriction-lock]." (let ((default-directory dir)) (expand-file-name txt))) (unless (derived-mode-p 'org-mode) - (user-error "Cannot restrict to non-Org-mode file")) + (user-error "Cannot restrict to non-Org mode file")) (org-agenda-set-restriction-lock 'file))) - (t (user-error "Don't know how to restrict Org-mode's agenda"))) + (t (user-error "Don't know how to restrict Org mode agenda"))) (move-overlay org-speedbar-restriction-lock-overlay (point-at-bol) (point-at-eol)) (setq current-prefix-arg nil) @@ -23959,34 +24985,98 @@ To get rid of the restriction, use \\[org-agenda-remove-restriction-lock]." ;;; Fixes and Hacks for problems with other packages -;; Make flyspell not check words in links, to not mess up our keymap -(defvar org-element-affiliated-keywords) ; From org-element.el -(defvar org-element-block-name-alist) ; From org-element.el +(defun org--flyspell-object-check-p (element) + "Non-nil when Flyspell can check object at point. +ELEMENT is the element at point." + (let ((object (save-excursion + (when (looking-at-p "\\>") (backward-char)) + (org-element-context element)))) + (cl-case (org-element-type object) + ;; Prevent checks in links due to keybinding conflict with + ;; Flyspell. + ((code entity export-snippet inline-babel-call + inline-src-block line-break latex-fragment link macro + statistics-cookie target timestamp verbatim) + nil) + (footnote-reference + ;; Only in inline footnotes, within the definition. + (and (eq (org-element-property :type object) 'inline) + (< (save-excursion + (goto-char (org-element-property :begin object)) + (search-forward ":" nil t 2)) + (point)))) + (otherwise t)))) + (defun org-mode-flyspell-verify () - "Don't let flyspell put overlays at active buttons, or on - {todo,all-time,additional-option-like}-keywords." - (require 'org-element) ; For `org-element-affiliated-keywords' - (let ((pos (max (1- (point)) (point-min))) - (word (thing-at-point 'word))) - (and (not (get-text-property pos 'keymap)) - (not (get-text-property pos 'org-no-flyspell)) - (not (member word org-todo-keywords-1)) - (not (member word org-all-time-keywords)) - (not (member word org-options-keywords)) - (not (member word (mapcar 'car org-startup-options))) - (not (member-ignore-case word org-element-affiliated-keywords)) - (not (member-ignore-case word (org-get-export-keywords))) - (not (member-ignore-case - word (mapcar 'car org-element-block-name-alist))) - (not (member-ignore-case word '("BEGIN" "END" "ATTR"))) - (not (org-in-src-block-p))))) + "Function used for `flyspell-generic-check-word-predicate'." + (if (org-at-heading-p) + ;; At a headline or an inlinetask, check title only. This is + ;; faster than relying on `org-element-at-point'. + (and (save-excursion (beginning-of-line) + (and (let ((case-fold-search t)) + (not (looking-at-p "\\*+ END[ \t]*$"))) + (let ((case-fold-search nil)) + (looking-at org-complex-heading-regexp)))) + (match-beginning 4) + (>= (point) (match-beginning 4)) + (or (not (match-beginning 5)) + (< (point) (match-beginning 5)))) + (let* ((element (org-element-at-point)) + (post-affiliated (org-element-property :post-affiliated element))) + (cond + ;; Ignore checks in all affiliated keywords but captions. + ((< (point) post-affiliated) + (and (save-excursion + (beginning-of-line) + (let ((case-fold-search t)) (looking-at "[ \t]*#\\+CAPTION:"))) + (> (point) (match-end 0)) + (org--flyspell-object-check-p element))) + ;; Ignore checks in LOGBOOK (or equivalent) drawer. + ((let ((log (org-log-into-drawer))) + (and log + (let ((drawer (org-element-lineage element '(drawer)))) + (and drawer + (eq (compare-strings + log nil nil + (org-element-property :drawer-name drawer) nil nil t) + t))))) + nil) + (t + (cl-case (org-element-type element) + ((comment quote-section) t) + (comment-block + ;; Allow checks between block markers, not on them. + (and (> (line-beginning-position) post-affiliated) + (save-excursion + (end-of-line) + (skip-chars-forward " \r\t\n") + (< (point) (org-element-property :end element))))) + ;; Arbitrary list of keywords where checks are meaningful. + ;; Make sure point is on the value part of the element. + (keyword + (and (member (org-element-property :key element) + '("DESCRIPTION" "TITLE")) + (save-excursion + (search-backward ":" (line-beginning-position) t)))) + ;; Check is globally allowed in paragraphs verse blocks and + ;; table rows (after affiliated keywords) but some objects + ;; must not be affected. + ((paragraph table-row verse-block) + (let ((cbeg (org-element-property :contents-begin element)) + (cend (org-element-property :contents-end element))) + (and cbeg (>= (point) cbeg) (< (point) cend) + (org--flyspell-object-check-p element)))))))))) +(put 'org-mode 'flyspell-mode-predicate 'org-mode-flyspell-verify) (defun org-remove-flyspell-overlays-in (beg end) "Remove flyspell overlays in region." - (and (org-bound-and-true-p flyspell-mode) + (and (bound-and-true-p flyspell-mode) (fboundp 'flyspell-delete-region-overlays) - (flyspell-delete-region-overlays beg end)) - (add-text-properties beg end '(org-no-flyspell t))) + (flyspell-delete-region-overlays beg end))) + +(defvar flyspell-delayed-commands) +(eval-after-load "flyspell" + '(add-to-list 'flyspell-delayed-commands 'org-self-insert-command)) ;; Make `bookmark-jump' shows the jump location if it was hidden. (eval-after-load "bookmark" @@ -24008,17 +25098,38 @@ To get rid of the restriction, use \\[org-agenda-remove-restriction-lock]." (eval-after-load "ecb" '(defadvice ecb-method-clicked (after esf/org-show-context activate) "Make hierarchy visible when jumping into location from ECB tree buffer." - (if (derived-mode-p 'org-mode) - (org-show-context)))) + (when (derived-mode-p 'org-mode) + (org-show-context)))) (defun org-bookmark-jump-unhide () "Unhide the current position, to show the bookmark location." (and (derived-mode-p 'org-mode) - (or (outline-invisible-p) + (or (org-invisible-p) (save-excursion (goto-char (max (point-min) (1- (point)))) - (outline-invisible-p))) + (org-invisible-p))) (org-show-context 'bookmark-jump))) +(defun org-mark-jump-unhide () + "Make the point visible with `org-show-context' after jumping to the mark." + (when (and (derived-mode-p 'org-mode) + (org-invisible-p)) + (org-show-context 'mark-goto))) + +(eval-after-load "simple" + '(defadvice pop-to-mark-command (after org-make-visible activate) + "Make the point visible with `org-show-context'." + (org-mark-jump-unhide))) + +(eval-after-load "simple" + '(defadvice exchange-point-and-mark (after org-make-visible activate) + "Make the point visible with `org-show-context'." + (org-mark-jump-unhide))) + +(eval-after-load "simple" + '(defadvice pop-global-mark (after org-make-visible activate) + "Make the point visible with `org-show-context'." + (org-mark-jump-unhide))) + ;; Make session.el ignore our circular variable (defvar session-globals-exclude) (eval-after-load "session" diff --git a/lisp/org/ox-ascii.el b/lisp/org/ox-ascii.el index 6ba70d700b..2a129e9de7 100644 --- a/lisp/org/ox-ascii.el +++ b/lisp/org/ox-ascii.el @@ -1,4 +1,4 @@ -;;; ox-ascii.el --- ASCII Back-End for Org Export Engine +;;; ox-ascii.el --- ASCII Back-End for Org Export Engine -*- lexical-binding: t; -*- ;; Copyright (C) 2012-2017 Free Software Foundation, Inc. @@ -27,9 +27,9 @@ ;;; Code: -(eval-when-compile (require 'cl)) (require 'ox) (require 'ox-publish) +(require 'cl-lib) (declare-function aa2u "ext:ascii-art-to-unicode" ()) @@ -49,8 +49,6 @@ (center-block . org-ascii-center-block) (clock . org-ascii-clock) (code . org-ascii-code) - (comment . (lambda (&rest args) "")) - (comment-block . (lambda (&rest args) "")) (drawer . org-ascii-drawer) (dynamic-block . org-ascii-dynamic-block) (entity . org-ascii-entity) @@ -71,12 +69,13 @@ (latex-fragment . org-ascii-latex-fragment) (line-break . org-ascii-line-break) (link . org-ascii-link) + (node-property . org-ascii-node-property) (paragraph . org-ascii-paragraph) (plain-list . org-ascii-plain-list) (plain-text . org-ascii-plain-text) (planning . org-ascii-planning) + (property-drawer . org-ascii-property-drawer) (quote-block . org-ascii-quote-block) - (quote-section . org-ascii-quote-section) (radio-target . org-ascii-radio-target) (section . org-ascii-section) (special-block . org-ascii-special-block) @@ -94,7 +93,6 @@ (underline . org-ascii-underline) (verbatim . org-ascii-verbatim) (verse-block . org-ascii-verse-block)) - :export-block "ASCII" :menu-entry '(?t "Export to Plain Text" ((?A "As ASCII buffer" @@ -119,7 +117,30 @@ (:filter-parse-tree org-ascii-filter-paragraph-spacing org-ascii-filter-comment-spacing) (:filter-section . org-ascii-filter-headline-blank-lines)) - :options-alist '((:ascii-charset nil nil org-ascii-charset))) + :options-alist + '((:subtitle "SUBTITLE" nil nil parse) + (:ascii-bullets nil nil org-ascii-bullets) + (:ascii-caption-above nil nil org-ascii-caption-above) + (:ascii-charset nil nil org-ascii-charset) + (:ascii-global-margin nil nil org-ascii-global-margin) + (:ascii-format-drawer-function nil nil org-ascii-format-drawer-function) + (:ascii-format-inlinetask-function + nil nil org-ascii-format-inlinetask-function) + (:ascii-headline-spacing nil nil org-ascii-headline-spacing) + (:ascii-indented-line-width nil nil org-ascii-indented-line-width) + (:ascii-inlinetask-width nil nil org-ascii-inlinetask-width) + (:ascii-inner-margin nil nil org-ascii-inner-margin) + (:ascii-links-to-notes nil nil org-ascii-links-to-notes) + (:ascii-list-margin nil nil org-ascii-list-margin) + (:ascii-paragraph-spacing nil nil org-ascii-paragraph-spacing) + (:ascii-quote-margin nil nil org-ascii-quote-margin) + (:ascii-table-keep-all-vertical-lines + nil nil org-ascii-table-keep-all-vertical-lines) + (:ascii-table-use-ascii-art nil nil org-ascii-table-use-ascii-art) + (:ascii-table-widen-columns nil nil org-ascii-table-widen-columns) + (:ascii-text-width nil nil org-ascii-text-width) + (:ascii-underline nil nil org-ascii-underline) + (:ascii-verbatim-format nil nil org-ascii-verbatim-format))) @@ -162,6 +183,15 @@ This margin is applied on both sides of the text." :package-version '(Org . "8.0") :type 'integer) +(defcustom org-ascii-list-margin 0 + "Width of margin used for plain lists, in characters. +This margin applies to top level list only, not to its +sub-lists." + :group 'org-export-ascii + :version "26.1" + :package-version '(Org . "8.3") + :type 'integer) + (defcustom org-ascii-inlinetask-width 30 "Width of inline tasks, in number of characters. This number ignores any margin." @@ -339,7 +369,7 @@ Otherwise, place it right after it." :type 'string) (defcustom org-ascii-format-drawer-function - (lambda (name contents width) contents) + (lambda (_name contents _width) contents) "Function called to format a drawer in ASCII. The function must accept three parameters: @@ -384,14 +414,18 @@ nil to ignore the inline task." ;; Internal functions fall into three categories. -;; The first one is about text formatting. The core function is -;; `org-ascii--current-text-width', which determines the current -;; text width allowed to a given element. In other words, it helps -;; keeping each line width within maximum text width defined in -;; `org-ascii-text-width'. Once this information is known, -;; `org-ascii--fill-string', `org-ascii--justify-string', -;; `org-ascii--box-string' and `org-ascii--indent-string' can -;; operate on a given output string. +;; The first one is about text formatting. The core functions are +;; `org-ascii--current-text-width' and +;; `org-ascii--current-justification', which determine, respectively, +;; the current text width allowed to a given element and its expected +;; justification. Once this information is known, +;; `org-ascii--fill-string', `org-ascii--justify-lines', +;; `org-ascii--justify-element' `org-ascii--box-string' and +;; `org-ascii--indent-string' can operate on a given output string. +;; In particular, justification happens at the regular (i.e., +;; non-greater) element level, which means that when the exporting +;; process reaches a container (e.g., a center block) content are +;; already justified. ;; The second category contains functions handling elements listings, ;; triggered by "#+TOC:" keyword. As such, `org-ascii--build-toc' @@ -420,7 +454,8 @@ a communication channel. Optional argument JUSTIFY can specify any type of justification among `left', `center', `right' or `full'. A nil value is equivalent to `left'. For a justification that doesn't also fill -string, see `org-ascii--justify-string'. +string, see `org-ascii--justify-lines' and +`org-ascii--justify-block'. Return nil if S isn't a string." (when (stringp s) @@ -435,8 +470,8 @@ Return nil if S isn't a string." (fill-region (point-min) (point-max) justify)) (buffer-string))))) -(defun org-ascii--justify-string (s text-width how) - "Justify string S. +(defun org-ascii--justify-lines (s text-width how) + "Justify all lines in string S. TEXT-WIDTH is an integer specifying maximum length of a line. HOW determines the type of justification: it can be `left', `right', `full' or `center'." @@ -452,6 +487,48 @@ HOW determines the type of justification: it can be `left', (forward-line))) (buffer-string))) +(defun org-ascii--justify-element (contents element info) + "Justify CONTENTS of ELEMENT. +INFO is a plist used as a communication channel. Justification +is done according to the type of element. More accurately, +paragraphs are filled and other elements are justified as blocks, +that is according to the widest non blank line in CONTENTS." + (if (not (org-string-nw-p contents)) contents + (let ((text-width (org-ascii--current-text-width element info)) + (how (org-ascii--current-justification element))) + (cond + ((eq (org-element-type element) 'paragraph) + ;; Paragraphs are treated specially as they need to be filled. + (org-ascii--fill-string contents text-width info how)) + ((eq how 'left) contents) + (t (with-temp-buffer + (insert contents) + (goto-char (point-min)) + (catch 'exit + (let ((max-width 0)) + ;; Compute maximum width. Bail out if it is greater + ;; than page width, since no justification is + ;; possible. + (save-excursion + (while (not (eobp)) + (unless (looking-at-p "[ \t]*$") + (end-of-line) + (let ((column (current-column))) + (cond + ((>= column text-width) (throw 'exit contents)) + ((> column max-width) (setq max-width column))))) + (forward-line))) + ;; Justify every line according to TEXT-WIDTH and + ;; MAX-WIDTH. + (let ((offset (/ (- text-width max-width) + (if (eq how 'right) 1 2)))) + (if (zerop offset) (throw 'exit contents) + (while (not (eobp)) + (unless (looking-at-p "[ \t]*$") + (indent-to-column offset)) + (forward-line))))) + (buffer-string)))))))) + (defun org-ascii--indent-string (s width) "Indent string S by WIDTH white spaces. Empty lines are not indented." @@ -472,26 +549,28 @@ INFO is a plist used as a communication channel." (defun org-ascii--current-text-width (element info) "Return maximum text width for ELEMENT's contents. INFO is a plist used as a communication channel." - (case (org-element-type element) + (pcase (org-element-type element) ;; Elements with an absolute width: `headline' and `inlinetask'. - (inlinetask org-ascii-inlinetask-width) - (headline - (- org-ascii-text-width + (`inlinetask (plist-get info :ascii-inlinetask-width)) + (`headline + (- (plist-get info :ascii-text-width) (let ((low-level-rank (org-export-low-level-p element info))) - (if low-level-rank (* low-level-rank 2) org-ascii-global-margin)))) + (if low-level-rank (* low-level-rank 2) + (plist-get info :ascii-global-margin))))) ;; Elements with a relative width: store maximum text width in ;; TOTAL-WIDTH. - (otherwise - (let* ((genealogy (cons element (org-export-get-genealogy element))) + (_ + (let* ((genealogy (org-element-lineage element nil t)) ;; Total width is determined by the presence, or not, of an ;; inline task among ELEMENT parents. (total-width - (if (loop for parent in genealogy - thereis (eq (org-element-type parent) 'inlinetask)) - org-ascii-inlinetask-width + (if (cl-some (lambda (parent) + (eq (org-element-type parent) 'inlinetask)) + genealogy) + (plist-get info :ascii-inlinetask-width) ;; No inlinetask: Remove global margin from text width. - (- org-ascii-text-width - org-ascii-global-margin + (- (plist-get info :ascii-text-width) + (plist-get info :ascii-global-margin) (let ((parent (org-export-get-parent-headline element))) ;; Inner margin doesn't apply to text before first ;; headline. @@ -502,41 +581,67 @@ INFO is a plist used as a communication channel." ;; low level headlines, since they've got their ;; own indentation mechanism. (if low-level-rank (* low-level-rank 2) - org-ascii-inner-margin)))))))) + (plist-get info :ascii-inner-margin))))))))) (- total-width - ;; Each `quote-block', `quote-section' and `verse-block' above - ;; narrows text width by twice the standard margin size. - (+ (* (loop for parent in genealogy - when (memq (org-element-type parent) - '(quote-block quote-section verse-block)) - count parent) - 2 org-ascii-quote-margin) + ;; Each `quote-block' and `verse-block' above narrows text + ;; width by twice the standard margin size. + (+ (* (cl-count-if (lambda (parent) + (memq (org-element-type parent) + '(quote-block verse-block))) + genealogy) + 2 + (plist-get info :ascii-quote-margin)) + ;; Apply list margin once per "top-level" plain-list + ;; containing current line + (* (cl-count-if + (lambda (e) + (and (eq (org-element-type e) 'plain-list) + (not (eq (org-element-type (org-export-get-parent e)) + 'item)))) + genealogy) + (plist-get info :ascii-list-margin)) ;; Text width within a plain-list is restricted by ;; indentation of current item. If that's the case, ;; compute it with the help of `:structure' property from ;; parent item, if any. - (let ((parent-item + (let ((item (if (eq (org-element-type element) 'item) element - (loop for parent in genealogy - when (eq (org-element-type parent) 'item) - return parent)))) - (if (not parent-item) 0 + (cl-find-if (lambda (parent) + (eq (org-element-type parent) 'item)) + genealogy)))) + (if (not item) 0 ;; Compute indentation offset of the current item, ;; that is the sum of the difference between its ;; indentation and the indentation of the top item in ;; the list and current item bullet's length. Also ;; remove checkbox length, and tag length (for ;; description lists) or bullet length. - (let ((struct (org-element-property :structure parent-item)) - (beg-item (org-element-property :begin parent-item))) + (let ((struct (org-element-property :structure item)) + (beg-item (org-element-property :begin item))) (+ (- (org-list-get-ind beg-item struct) (org-list-get-ind (org-list-get-top-point struct) struct)) - (string-width (or (org-ascii--checkbox parent-item info) + (string-width (or (org-ascii--checkbox item info) "")) (string-width - (or (org-list-get-tag beg-item struct) - (org-list-get-bullet beg-item struct))))))))))))) + (let ((tag (org-element-property :tag item))) + (if tag (org-export-data tag info) + (org-element-property :bullet item)))))))))))))) + +(defun org-ascii--current-justification (element) + "Return expected justification for ELEMENT's contents. +Return value is a symbol among `left', `center', `right' and +`full'." + (let (justification) + (while (and (not justification) + (setq element (org-element-property :parent element))) + (pcase (org-element-type element) + (`center-block (setq justification 'center)) + (`special-block + (let ((name (org-element-property :type element))) + (cond ((string= name "JUSTIFYRIGHT") (setq justification 'right)) + ((string= name "JUSTIFYLEFT") (setq justification 'left))))))) + (or justification 'left))) (defun org-ascii--build-title (element info text-width &optional underline notags toc) @@ -601,14 +706,14 @@ possible. It doesn't apply to `inlinetask' elements." (let ((under-char (nth (1- (org-export-get-relative-level element info)) (cdr (assq (plist-get info :ascii-charset) - org-ascii-underline))))) + (plist-get info :ascii-underline)))))) (and under-char (concat "\n" (make-string (/ (string-width first-part) (char-width under-char)) under-char)))))))) -(defun org-ascii--has-caption-p (element info) +(defun org-ascii--has-caption-p (element _info) "Non-nil when ELEMENT has a caption affiliated keyword. INFO is a plist used as a communication channel. This function is meant to be used as a predicate for `org-export-get-ordinal'." @@ -630,9 +735,9 @@ caption keyword." (org-export-get-ordinal element info nil 'org-ascii--has-caption-p)) (title-fmt (org-ascii--translate - (case (org-element-type element) - (table "Table %d:") - (src-block "Listing %d:")) + (pcase (org-element-type element) + (`table "Table %d:") + (`src-block "Listing %d:")) info))) (org-ascii--fill-string (concat (format title-fmt reference) @@ -640,7 +745,7 @@ caption keyword." (org-export-data caption info)) (org-ascii--current-text-width element info) info))))) -(defun org-ascii--build-toc (info &optional n keyword) +(defun org-ascii--build-toc (info &optional n keyword local) "Return a table of contents. INFO is a plist used as a communication channel. @@ -649,28 +754,34 @@ Optional argument N, when non-nil, is an integer specifying the depth of the table. Optional argument KEYWORD specifies the TOC keyword, if any, from -which the table of contents generation has been initiated." - (let ((title (org-ascii--translate "Table of Contents" info))) - (concat - title "\n" - (make-string (string-width title) - (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_)) - "\n\n" - (let ((text-width - (if keyword (org-ascii--current-text-width keyword info) - (- org-ascii-text-width org-ascii-global-margin)))) - (mapconcat - (lambda (headline) - (let* ((level (org-export-get-relative-level headline info)) - (indent (* (1- level) 3))) - (concat - (unless (zerop indent) (concat (make-string (1- indent) ?.) " ")) - (org-ascii--build-title - headline info (- text-width indent) nil - (or (not (plist-get info :with-tags)) - (eq (plist-get info :with-tags) 'not-in-toc)) - 'toc)))) - (org-export-collect-headlines info n) "\n"))))) +which the table of contents generation has been initiated. + +When optional argument LOCAL is non-nil, build a table of +contents according to the current headline." + (concat + (unless local + (let ((title (org-ascii--translate "Table of Contents" info))) + (concat title "\n" + (make-string + (string-width title) + (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_)) + "\n\n"))) + (let ((text-width + (if keyword (org-ascii--current-text-width keyword info) + (- (plist-get info :ascii-text-width) + (plist-get info :ascii-global-margin))))) + (mapconcat + (lambda (headline) + (let* ((level (org-export-get-relative-level headline info)) + (indent (* (1- level) 3))) + (concat + (unless (zerop indent) (concat (make-string (1- indent) ?.) " ")) + (org-ascii--build-title + headline info (- text-width indent) nil + (or (not (plist-get info :with-tags)) + (eq (plist-get info :with-tags) 'not-in-toc)) + 'toc)))) + (org-export-collect-headlines info n (and local keyword)) "\n")))) (defun org-ascii--list-listings (keyword info) "Return a list of listings. @@ -685,7 +796,8 @@ generation. INFO is a plist used as a communication channel." "\n\n" (let ((text-width (if keyword (org-ascii--current-text-width keyword info) - (- org-ascii-text-width org-ascii-global-margin))) + (- (plist-get info :ascii-text-width) + (plist-get info :ascii-global-margin)))) ;; Use a counter instead of retrieving ordinal of each ;; src-block. (count 0)) @@ -696,7 +808,7 @@ generation. INFO is a plist used as a communication channel." ;; filling (like contents of a description list item). (let* ((initial-text (format (org-ascii--translate "Listing %d:" info) - (incf count))) + (cl-incf count))) (initial-width (string-width initial-text))) (concat initial-text " " @@ -724,7 +836,8 @@ generation. INFO is a plist used as a communication channel." "\n\n" (let ((text-width (if keyword (org-ascii--current-text-width keyword info) - (- org-ascii-text-width org-ascii-global-margin))) + (- (plist-get info :ascii-text-width) + (plist-get info :ascii-global-margin)))) ;; Use a counter instead of retrieving ordinal of each ;; src-block. (count 0)) @@ -735,7 +848,7 @@ generation. INFO is a plist used as a communication channel." ;; filling (like contents of a description list item). (let* ((initial-text (format (org-ascii--translate "Table %d:" info) - (incf count))) + (cl-incf count))) (initial-width (string-width initial-text))) (concat initial-text " " @@ -756,69 +869,105 @@ ELEMENT is either a headline element or a section element. INFO is a plist used as a communication channel." (let* (seen (unique-link-p - (function - ;; Return LINK if it wasn't referenced so far, or nil. - ;; Update SEEN links along the way. - (lambda (link) - (let ((footprint - ;; Normalize description in footprints. - (cons (org-element-property :raw-link link) - (let ((contents (org-element-contents link))) - (and contents - (replace-regexp-in-string - "[ \r\t\n]+" " " - (org-trim - (org-element-interpret-data contents)))))))) - ;; Ignore LINK if it hasn't been translated already. - ;; It can happen if it is located in an affiliated - ;; keyword that was ignored. - (when (and (org-string-nw-p - (gethash link (plist-get info :exported-data))) - (not (member footprint seen))) - (push footprint seen) link))))) - ;; If at a section, find parent headline, if any, in order to - ;; count links that might be in the title. - (headline - (if (eq (org-element-type element) 'headline) element - (or (org-export-get-parent-headline element) element)))) - ;; Get all links in HEADLINE. - (org-element-map headline 'link - (lambda (l) (funcall unique-link-p l)) info nil nil t))) + ;; Return LINK if it wasn't referenced so far, or nil. + ;; Update SEEN links along the way. + (lambda (link) + (let ((footprint + ;; Normalize description in footprints. + (cons (org-element-property :raw-link link) + (let ((contents (org-element-contents link))) + (and contents + (replace-regexp-in-string + "[ \r\t\n]+" " " + (org-trim + (org-element-interpret-data contents)))))))) + ;; Ignore LINK if it hasn't been translated already. It + ;; can happen if it is located in an affiliated keyword + ;; that was ignored. + (when (and (org-string-nw-p + (gethash link (plist-get info :exported-data))) + (not (member footprint seen))) + (push footprint seen) link))))) + (org-element-map (if (eq (org-element-type element) 'section) + element + ;; In a headline, only retrieve links in title + ;; and relative section, not in children. + (list (org-element-property :title element) + (car (org-element-contents element)))) + 'link unique-link-p info nil 'headline t))) + +(defun org-ascii--describe-datum (datum info) + "Describe DATUM object or element. +If DATUM is a string, consider it to be a file name, per +`org-export-resolve-id-link'. INFO is the communication channel, +as a plist." + (pcase (org-element-type datum) + (`plain-text (format "See file %s" datum)) ;External file + (`headline + (format (org-ascii--translate "See section %s" info) + (if (org-export-numbered-headline-p datum info) + (mapconcat #'number-to-string + (org-export-get-headline-number datum info) + ".") + (org-export-data (org-element-property :title datum) info)))) + (_ + (let ((number (org-export-get-ordinal + datum info nil #'org-ascii--has-caption-p)) + ;; If destination is a target, make sure we can name the + ;; container it refers to. + (enumerable + (org-element-lineage datum '(headline paragrah src-block table) t))) + (pcase (org-element-type enumerable) + (`headline + (format (org-ascii--translate "See section %s" info) + (if (org-export-numbered-headline-p enumerable info) + (mapconcat #'number-to-string number ".") + (org-export-data + (org-element-property :title enumerable) info)))) + ((guard (not number)) + (org-ascii--translate "Unknown reference" info)) + (`paragraph + (format (org-ascii--translate "See figure %s" info) number)) + (`src-block + (format (org-ascii--translate "See listing %s" info) number)) + (`table + (format (org-ascii--translate "See table %s" info) number)) + (_ (org-ascii--translate "Unknown reference" info))))))) (defun org-ascii--describe-links (links width info) "Return a string describing a list of links. - LINKS is a list of link type objects, as returned by `org-ascii--unique-links'. WIDTH is the text width allowed for the output string. INFO is a plist used as a communication channel." (mapconcat (lambda (link) - (let ((type (org-element-property :type link)) - (anchor (let ((desc (org-element-contents link))) - (if desc (org-export-data desc info) - (org-element-property :raw-link link))))) + (let* ((type (org-element-property :type link)) + (description (org-element-contents link)) + (anchor (org-export-data + (or description (org-element-property :raw-link link)) + info))) (cond - ;; Coderefs, radio links and fuzzy links are ignored. - ((member type '("coderef" "radio" "fuzzy")) nil) - ;; Id and custom-id links: Headlines refer to their numbering. - ((member type '("custom-id" "id")) - (let ((dest (org-export-resolve-id-link link info))) - (concat - (org-ascii--fill-string - (format - "[%s] %s" - anchor - (if (not dest) (org-ascii--translate "Unknown reference" info) - (format - (org-ascii--translate "See section %s" info) - (mapconcat 'number-to-string - (org-export-get-headline-number dest info) ".")))) - width info) "\n\n"))) + ((member type '("coderef" "radio")) nil) + ((member type '("custom-id" "fuzzy" "id")) + ;; Only links with a description need an entry. Other are + ;; already handled in `org-ascii-link'. + (when description + (let ((dest (if (equal type "fuzzy") + (org-export-resolve-fuzzy-link link info) + (org-export-resolve-id-link link info)))) + (concat + (org-ascii--fill-string + (format "[%s] %s" anchor (org-ascii--describe-datum dest info)) + width info) + "\n\n")))) ;; Do not add a link that cannot be resolved and doesn't have ;; any description: destination is already visible in the ;; paragraph. ((not (org-element-contents link)) nil) + ;; Do not add a link already handled by custom export + ;; functions. + ((org-export-custom-protocol-maybe link anchor 'ascii) nil) (t (concat (org-ascii--fill-string @@ -831,10 +980,10 @@ channel." "Return checkbox string for ITEM or nil. INFO is a plist used as a communication channel." (let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))) - (case (org-element-property :checkbox item) - (on (if utf8p "☑ " "[X] ")) - (off (if utf8p "☐ " "[ ] ")) - (trans (if utf8p "☒ " "[-] "))))) + (pcase (org-element-property :checkbox item) + (`on (if utf8p "☑ " "[X] ")) + (`off (if utf8p "☐ " "[ ] ")) + (`trans (if utf8p "☒ " "[-] "))))) @@ -843,11 +992,15 @@ INFO is a plist used as a communication channel." (defun org-ascii-template--document-title (info) "Return document title, as a string. INFO is a plist used as a communication channel." - (let* ((text-width org-ascii-text-width) + (let* ((text-width (plist-get info :ascii-text-width)) ;; Links in the title will not be resolved later, so we make ;; sure their path is located right after them. - (org-ascii-links-to-notes nil) - (title (org-export-data (plist-get info :title) info)) + (info (org-combine-plists info '(:ascii-links-to-notes nil))) + (with-title (plist-get info :with-title)) + (title (org-export-data + (when with-title (plist-get info :title)) info)) + (subtitle (org-export-data + (when with-title (plist-get info :subtitle)) info)) (author (and (plist-get info :with-author) (let ((auth (plist-get info :author))) (and auth (org-export-data auth info))))) @@ -878,7 +1031,7 @@ INFO is a plist used as a communication channel." date "\n\n\n")) ((org-string-nw-p date) (concat - (org-ascii--justify-string date text-width 'right) + (org-ascii--justify-lines date text-width 'right) "\n\n\n")) ((and (org-string-nw-p author) (org-string-nw-p email)) (concat author "\n" email "\n\n\n")) @@ -890,8 +1043,14 @@ INFO is a plist used as a communication channel." (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)) ;; Format TITLE. It may be filled if it is too wide, ;; that is wider than the two thirds of the total width. - (title-len (min (length title) (/ (* 2 text-width) 3))) + (title-len (min (apply #'max + (mapcar #'length + (org-split-string + (concat title "\n" subtitle) "\n"))) + (/ (* 2 text-width) 3))) (formatted-title (org-ascii--fill-string title title-len info)) + (formatted-subtitle (when (org-string-nw-p subtitle) + (org-ascii--fill-string subtitle title-len info))) (line (make-string (min (+ (max title-len @@ -899,17 +1058,16 @@ INFO is a plist used as a communication channel." (string-width (or email ""))) 2) text-width) (if utf8p ?━ ?_)))) - (org-ascii--justify-string + (org-ascii--justify-lines (concat line "\n" (unless utf8p "\n") (upcase formatted-title) + (and formatted-subtitle (concat "\n" formatted-subtitle)) (cond ((and (org-string-nw-p author) (org-string-nw-p email)) - (concat (if utf8p "\n\n\n" "\n\n") author "\n" email)) - ((org-string-nw-p author) - (concat (if utf8p "\n\n\n" "\n\n") author)) - ((org-string-nw-p email) - (concat (if utf8p "\n\n\n" "\n\n") email))) + (concat "\n\n" author "\n" email)) + ((org-string-nw-p author) (concat "\n\n" author)) + ((org-string-nw-p email) (concat "\n\n" email))) "\n" line (when (org-string-nw-p date) (concat "\n\n\n" date)) "\n\n\n") text-width 'center))))) @@ -919,81 +1077,83 @@ INFO is a plist used as a communication channel." CONTENTS is the transcoded contents string. INFO is a plist holding export options." (org-element-normalize-string - (org-ascii--indent-string - (concat - ;; 1. Document's body. - contents - ;; 2. Footnote definitions. - (let ((definitions (org-export-collect-footnote-definitions - (plist-get info :parse-tree) info)) - ;; Insert full links right inside the footnote definition - ;; as they have no chance to be inserted later. - (org-ascii-links-to-notes nil)) - (when definitions - (concat - "\n\n\n" - (let ((title (org-ascii--translate "Footnotes" info))) - (concat - title "\n" - (make-string - (string-width title) - (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_)))) - "\n\n" - (let ((text-width (- org-ascii-text-width org-ascii-global-margin))) - (mapconcat - (lambda (ref) - (let ((id (format "[%s] " (car ref)))) - ;; Distinguish between inline definitions and - ;; full-fledged definitions. - (org-trim - (let ((def (nth 2 ref))) - (if (eq (org-element-type def) 'org-data) - ;; Full-fledged definition: footnote ID is - ;; inserted inside the first parsed paragraph - ;; (FIRST), if any, to be sure filling will - ;; take it into consideration. - (let ((first (car (org-element-contents def)))) - (if (not (eq (org-element-type first) 'paragraph)) - (concat id "\n" (org-export-data def info)) - (push id (nthcdr 2 first)) - (org-export-data def info))) - ;; Fill paragraph once footnote ID is inserted - ;; in order to have a correct length for first - ;; line. - (org-ascii--fill-string - (concat id (org-export-data def info)) - text-width info)))))) - definitions "\n\n")))))) - org-ascii-global-margin))) + (let ((global-margin (plist-get info :ascii-global-margin))) + (org-ascii--indent-string + (concat + ;; 1. Document's body. + contents + ;; 2. Footnote definitions. + (let ((definitions (org-export-collect-footnote-definitions info)) + ;; Insert full links right inside the footnote definition + ;; as they have no chance to be inserted later. + (info (org-combine-plists info '(:ascii-links-to-notes nil)))) + (when definitions + (concat + "\n\n\n" + (let ((title (org-ascii--translate "Footnotes" info))) + (concat + title "\n" + (make-string + (string-width title) + (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_)))) + "\n\n" + (let ((text-width (- (plist-get info :ascii-text-width) + global-margin))) + (mapconcat + (lambda (ref) + (let ((id (format "[%s] " (car ref)))) + ;; Distinguish between inline definitions and + ;; full-fledged definitions. + (org-trim + (let ((def (nth 2 ref))) + (if (org-element-map def org-element-all-elements + #'identity info 'first-match) + ;; Full-fledged definition: footnote ID is + ;; inserted inside the first parsed + ;; paragraph (FIRST), if any, to be sure + ;; filling will take it into consideration. + (let ((first (car (org-element-contents def)))) + (if (not (eq (org-element-type first) 'paragraph)) + (concat id "\n" (org-export-data def info)) + (push id (nthcdr 2 first)) + (org-export-data def info))) + ;; Fill paragraph once footnote ID is inserted + ;; in order to have a correct length for first + ;; line. + (org-ascii--fill-string + (concat id (org-export-data def info)) + text-width info)))))) + definitions "\n\n")))))) + global-margin)))) (defun org-ascii-template (contents info) "Return complete document string after ASCII conversion. CONTENTS is the transcoded contents string. INFO is a plist holding export options." - (concat - ;; 1. Build title block. - (org-ascii--indent-string - (concat (org-ascii-template--document-title info) - ;; 2. Table of contents. - (let ((depth (plist-get info :with-toc))) - (when depth - (concat - (org-ascii--build-toc info (and (wholenump depth) depth)) - "\n\n\n")))) - org-ascii-global-margin) - ;; 3. Document's body. - contents - ;; 4. Creator. Ignore `comment' value as there are no comments in - ;; ASCII. Justify it to the bottom right. - (org-ascii--indent-string - (let ((creator-info (plist-get info :with-creator)) - (text-width (- org-ascii-text-width org-ascii-global-margin))) - (unless (or (not creator-info) (eq creator-info 'comment)) - (concat - "\n\n\n" - (org-ascii--fill-string - (plist-get info :creator) text-width info 'right)))) - org-ascii-global-margin))) + (let ((global-margin (plist-get info :ascii-global-margin))) + (concat + ;; Build title block. + (org-ascii--indent-string + (concat (org-ascii-template--document-title info) + ;; 2. Table of contents. + (let ((depth (plist-get info :with-toc))) + (when depth + (concat + (org-ascii--build-toc info (and (wholenump depth) depth)) + "\n\n\n")))) + global-margin) + ;; Document's body. + contents + ;; Creator. Justify it to the bottom right. + (and (plist-get info :with-creator) + (org-ascii--indent-string + (let ((text-width + (- (plist-get info :ascii-text-width) global-margin))) + (concat + "\n\n\n" + (org-ascii--fill-string + (plist-get info :creator) text-width info 'right))) + global-margin))))) (defun org-ascii--translate (s info) "Translate string S according to specified language and charset. @@ -1007,7 +1167,7 @@ INFO is a plist used as a communication channel." ;;;; Bold -(defun org-ascii-bold (bold contents info) +(defun org-ascii-bold (_bold contents _info) "Transcode BOLD from Org to ASCII. CONTENTS is the text with bold markup. INFO is a plist holding contextual information." @@ -1016,39 +1176,41 @@ contextual information." ;;;; Center Block -(defun org-ascii-center-block (center-block contents info) +(defun org-ascii-center-block (_center-block contents _info) "Transcode a CENTER-BLOCK element from Org to ASCII. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." - (org-ascii--justify-string - contents (org-ascii--current-text-width center-block info) 'center)) + ;; Center has already been taken care of at a lower level, so + ;; there's nothing left to do. + contents) ;;;; Clock -(defun org-ascii-clock (clock contents info) +(defun org-ascii-clock (clock _contents info) "Transcode a CLOCK object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." - (concat org-clock-string " " - (org-translate-time - (org-element-property :raw-value - (org-element-property :value clock))) - (let ((time (org-element-property :duration clock))) - (and time - (concat " => " - (apply 'format - "%2s:%02s" - (org-split-string time ":"))))))) + (org-ascii--justify-element + (concat org-clock-string " " + (org-timestamp-translate (org-element-property :value clock)) + (let ((time (org-element-property :duration clock))) + (and time + (concat " => " + (apply 'format + "%2s:%02s" + (org-split-string time ":")))))) + clock info)) ;;;; Code -(defun org-ascii-code (code contents info) +(defun org-ascii-code (code _contents info) "Return a CODE object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." - (format org-ascii-verbatim-format (org-element-property :value code))) + (format (plist-get info :ascii-verbatim-format) + (org-element-property :value code))) ;;;; Drawer @@ -1059,12 +1221,13 @@ CONTENTS holds the contents of the block. INFO is a plist holding contextual information." (let ((name (org-element-property :drawer-name drawer)) (width (org-ascii--current-text-width drawer info))) - (funcall org-ascii-format-drawer-function name contents width))) + (funcall (plist-get info :ascii-format-drawer-function) + name contents width))) ;;;; Dynamic Block -(defun org-ascii-dynamic-block (dynamic-block contents info) +(defun org-ascii-dynamic-block (_dynamic-block contents _info) "Transcode a DYNAMIC-BLOCK element from Org to ASCII. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." @@ -1073,7 +1236,7 @@ holding contextual information." ;;;; Entity -(defun org-ascii-entity (entity contents info) +(defun org-ascii-entity (entity _contents info) "Transcode an ENTITY object from Org to ASCII. CONTENTS are the definition itself. INFO is a plist holding contextual information." @@ -1084,16 +1247,18 @@ contextual information." ;;;; Example Block -(defun org-ascii-example-block (example-block contents info) +(defun org-ascii-example-block (example-block _contents info) "Transcode a EXAMPLE-BLOCK element from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." - (org-ascii--box-string - (org-export-format-code-default example-block info) info)) + (org-ascii--justify-element + (org-ascii--box-string + (org-export-format-code-default example-block info) info) + example-block info)) ;;;; Export Snippet -(defun org-ascii-export-snippet (export-snippet contents info) +(defun org-ascii-export-snippet (export-snippet _contents _info) "Transcode a EXPORT-SNIPPET object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." (when (eq (org-export-snippet-backend export-snippet) 'ascii) @@ -1102,21 +1267,24 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;;; Export Block -(defun org-ascii-export-block (export-block contents info) +(defun org-ascii-export-block (export-block _contents info) "Transcode a EXPORT-BLOCK element from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." (when (string= (org-element-property :type export-block) "ASCII") - (org-remove-indentation (org-element-property :value export-block)))) + (org-ascii--justify-element + (org-element-property :value export-block) export-block info))) ;;;; Fixed Width -(defun org-ascii-fixed-width (fixed-width contents info) +(defun org-ascii-fixed-width (fixed-width _contents info) "Transcode a FIXED-WIDTH element from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." - (org-ascii--box-string - (org-remove-indentation - (org-element-property :value fixed-width)) info)) + (org-ascii--justify-element + (org-ascii--box-string + (org-remove-indentation + (org-element-property :value fixed-width)) info) + fixed-width info)) ;;;; Footnote Definition @@ -1127,7 +1295,7 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;;; Footnote Reference -(defun org-ascii-footnote-reference (footnote-reference contents info) +(defun org-ascii-footnote-reference (footnote-reference _contents info) "Transcode a FOOTNOTE-REFERENCE element from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." (format "[%s]" (org-export-get-footnote-number footnote-reference info))) @@ -1142,57 +1310,62 @@ holding contextual information." ;; Don't export footnote section, which will be handled at the end ;; of the template. (unless (org-element-property :footnote-section-p headline) - (let* ((low-level-rank (org-export-low-level-p headline info)) + (let* ((low-level (org-export-low-level-p headline info)) (width (org-ascii--current-text-width headline info)) + ;; Export title early so that any link in it can be + ;; exported and seen in `org-ascii--unique-links'. + (title (org-ascii--build-title headline info width (not low-level))) ;; Blank lines between headline and its contents. ;; `org-ascii-headline-spacing', when set, overwrites ;; original buffer's spacing. (pre-blanks - (make-string - (if org-ascii-headline-spacing (car org-ascii-headline-spacing) - (org-element-property :pre-blank headline)) ?\n)) - ;; Even if HEADLINE has no section, there might be some - ;; links in its title that we shouldn't forget to describe. - (links - (unless (or (eq (caar (org-element-contents headline)) 'section)) - (let ((title (org-element-property :title headline))) - (when (consp title) - (org-ascii--describe-links - (org-ascii--unique-links title info) width info)))))) + (make-string (or (car (plist-get info :ascii-headline-spacing)) + (org-element-property :pre-blank headline) + 0) + ?\n)) + (links (and (plist-get info :ascii-links-to-notes) + (org-ascii--describe-links + (org-ascii--unique-links headline info) width info))) + ;; Re-build contents, inserting section links at the right + ;; place. The cost is low since build results are cached. + (body + (if (not (org-string-nw-p links)) contents + (let* ((contents (org-element-contents headline)) + (section (let ((first (car contents))) + (and (eq (org-element-type first) 'section) + first)))) + (concat (and section + (concat (org-element-normalize-string + (org-export-data section info)) + "\n\n")) + links + (mapconcat (lambda (e) (org-export-data e info)) + (if section (cdr contents) contents) + "")))))) ;; Deep subtree: export it as a list item. - (if low-level-rank - (concat - ;; Bullet. - (let ((bullets (cdr (assq (plist-get info :ascii-charset) - org-ascii-bullets)))) - (char-to-string - (nth (mod (1- low-level-rank) (length bullets)) bullets))) - " " - ;; Title. - (org-ascii--build-title headline info width) "\n" - ;; Contents, indented by length of bullet. - pre-blanks - (org-ascii--indent-string - (concat contents - (when (org-string-nw-p links) (concat "\n\n" links))) - 2)) + (if low-level + (let* ((bullets (cdr (assq (plist-get info :ascii-charset) + (plist-get info :ascii-bullets)))) + (bullet + (format "%c " + (nth (mod (1- low-level) (length bullets)) bullets)))) + (concat bullet title "\n" pre-blanks + ;; Contents, indented by length of bullet. + (org-ascii--indent-string body (length bullet)))) ;; Else: Standard headline. - (concat - (org-ascii--build-title headline info width 'underline) - "\n" pre-blanks - (concat (when (org-string-nw-p links) links) contents)))))) + (concat title "\n" pre-blanks body))))) ;;;; Horizontal Rule -(defun org-ascii-horizontal-rule (horizontal-rule contents info) +(defun org-ascii-horizontal-rule (horizontal-rule _contents info) "Transcode an HORIZONTAL-RULE object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." (let ((text-width (org-ascii--current-text-width horizontal-rule info)) (spec-width (org-export-read-attribute :attr_ascii horizontal-rule :width))) - (org-ascii--justify-string + (org-ascii--justify-lines (make-string (if (and spec-width (string-match "^[0-9]+$" spec-width)) (string-to-number spec-width) text-width) @@ -1202,23 +1375,23 @@ information." ;;;; Inline Src Block -(defun org-ascii-inline-src-block (inline-src-block contents info) +(defun org-ascii-inline-src-block (inline-src-block _contents info) "Transcode an INLINE-SRC-BLOCK element from Org to ASCII. CONTENTS holds the contents of the item. INFO is a plist holding contextual information." - (format org-ascii-verbatim-format + (format (plist-get info :ascii-verbatim-format) (org-element-property :value inline-src-block))) ;;;; Inlinetask (defun org-ascii-format-inlinetask-default - (todo type priority name tags contents width inlinetask info) + (_todo _type _priority _name _tags contents width inlinetask info) "Format an inline task element for ASCII export. See `org-ascii-format-inlinetask-function' for a description of the parameters." (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)) - (width (or width org-ascii-inlinetask-width))) + (width (or width (plist-get info :ascii-inlinetask-width)))) (org-ascii--indent-string (concat ;; Top line, with an additional blank line if not in UTF-8. @@ -1236,9 +1409,9 @@ of the parameters." ;; Bottom line. (make-string width (if utf8p ?━ ?_))) ;; Flush the inlinetask to the right. - (- org-ascii-text-width org-ascii-global-margin + (- (plist-get info :ascii-text-width) (plist-get info :ascii-global-margin) (if (not (org-export-get-parent-headline inlinetask)) 0 - org-ascii-inner-margin) + (plist-get info :ascii-inner-margin)) (org-ascii--current-text-width inlinetask info))))) (defun org-ascii-inlinetask (inlinetask contents info) @@ -1246,7 +1419,7 @@ of the parameters." CONTENTS holds the contents of the block. INFO is a plist holding contextual information." (let ((width (org-ascii--current-text-width inlinetask info))) - (funcall org-ascii-format-inlinetask-function + (funcall (plist-get info :ascii-format-inlinetask-function) ;; todo. (and (plist-get info :with-todo-keywords) (let ((todo (org-element-property @@ -1268,7 +1441,7 @@ holding contextual information." ;;;; Italic -(defun org-ascii-italic (italic contents info) +(defun org-ascii-italic (_italic contents _info) "Transcode italic from Org to ASCII. CONTENTS is the text with italic markup. INFO is a plist holding contextual information." @@ -1288,12 +1461,12 @@ contextual information." ;; First parent of ITEM is always the plain-list. Get ;; `:type' property from it. (org-list-bullet-string - (case list-type - (descriptive + (pcase list-type + (`descriptive (concat checkbox (org-export-data (org-element-property :tag item) info) ": ")) - (ordered + (`ordered ;; Return correct number for ITEM, paying attention to ;; counters. (let* ((struct (org-element-property :structure item)) @@ -1305,7 +1478,7 @@ contextual information." (org-list-prevs-alist struct) (org-list-parents-alist struct))))))) (replace-regexp-in-string "[0-9]+" num bul))) - (t (let ((bul (org-element-property :bullet item))) + (_ (let ((bul (org-element-property :bullet item))) ;; Change bullets into more visible form if UTF-8 is active. (if (not utf8p) bul (replace-regexp-in-string @@ -1327,42 +1500,45 @@ contextual information." ;;;; Keyword -(defun org-ascii-keyword (keyword contents info) +(defun org-ascii-keyword (keyword _contents info) "Transcode a KEYWORD element from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." (let ((key (org-element-property :key keyword)) (value (org-element-property :value keyword))) (cond - ((string= key "ASCII") value) + ((string= key "ASCII") (org-ascii--justify-element value keyword info)) ((string= key "TOC") - (let ((value (downcase value))) - (cond - ((string-match "\\" value) - (let ((depth (or (and (string-match "[0-9]+" value) - (string-to-number (match-string 0 value))) - (plist-get info :with-toc)))) - (org-ascii--build-toc - info (and (wholenump depth) depth) keyword))) - ((string= "tables" value) - (org-ascii--list-tables keyword info)) - ((string= "listings" value) - (org-ascii--list-listings keyword info)))))))) + (org-ascii--justify-element + (let ((case-fold-search t)) + (cond + ((string-match-p "\\" value) + (let ((depth (and (string-match "\\<[0-9]+\\>" value) + (string-to-number (match-string 0 value)))) + (localp (string-match-p "\\" value))) + (org-ascii--build-toc info depth keyword localp))) + ((string-match-p "\\" value) + (org-ascii--list-tables keyword info)) + ((string-match-p "\\" value) + (org-ascii--list-listings keyword info)))) + keyword info))))) ;;;; Latex Environment -(defun org-ascii-latex-environment (latex-environment contents info) +(defun org-ascii-latex-environment (latex-environment _contents info) "Transcode a LATEX-ENVIRONMENT element from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." (when (plist-get info :with-latex) - (org-remove-indentation (org-element-property :value latex-environment)))) + (org-ascii--justify-element + (org-remove-indentation (org-element-property :value latex-environment)) + latex-environment info))) ;;;; Latex Fragment -(defun org-ascii-latex-fragment (latex-fragment contents info) +(defun org-ascii-latex-fragment (latex-fragment _contents info) "Transcode a LATEX-FRAGMENT object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." @@ -1372,7 +1548,7 @@ information." ;;;; Line Break -(defun org-ascii-line-break (line-break contents info) +(defun org-ascii-line-break (_line-break _contents _info) "Transcode a LINE-BREAK object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." hard-newline) @@ -1385,9 +1561,9 @@ CONTENTS is nil. INFO is a plist holding contextual DESC is the description part of the link, or the empty string. INFO is a plist holding contextual information." - (let ((raw-link (org-element-property :raw-link link)) - (type (org-element-property :type link))) + (let ((type (org-element-property :type link))) (cond + ((org-export-custom-protocol-maybe link desc 'ascii)) ((string= type "coderef") (let ((ref (org-element-property :path link))) (format (org-export-get-coderef-format ref desc) @@ -1395,23 +1571,51 @@ INFO is a plist holding contextual information." ;; Do not apply a special syntax on radio links. Though, use ;; transcoded target's contents as output. ((string= type "radio") desc) - ;; Do not apply a special syntax on fuzzy links pointing to - ;; targets. - ((string= type "fuzzy") - (let ((destination (org-export-resolve-fuzzy-link link info))) - (if (org-string-nw-p desc) desc - (when destination - (let ((number - (org-export-get-ordinal - destination info nil 'org-ascii--has-caption-p))) - (when number - (if (atom number) (number-to-string number) - (mapconcat 'number-to-string number ".")))))))) + ((member type '("custom-id" "fuzzy" "id")) + (let ((destination (if (string= type "fuzzy") + (org-export-resolve-fuzzy-link link info) + (org-export-resolve-id-link link info)))) + (pcase (org-element-type destination) + ((guard desc) + (if (plist-get info :ascii-links-to-notes) + (format "[%s]" desc) + (concat desc + (format " (%s)" + (org-ascii--describe-datum destination info))))) + ;; External file. + (`plain-text destination) + (`headline + (if (org-export-numbered-headline-p destination info) + (mapconcat #'number-to-string + (org-export-get-headline-number destination info) + ".") + (org-export-data (org-element-property :title destination) info))) + ;; Handle enumerable elements and targets within them. + ((and (let number (org-export-get-ordinal + destination info nil #'org-ascii--has-caption-p)) + (guard number)) + (if (atom number) (number-to-string number) + (mapconcat #'number-to-string number "."))) + ;; Don't know what to do. Signal it. + (_ "???")))) (t - (if (not (org-string-nw-p desc)) (format "[%s]" raw-link) - (concat - (format "[%s]" desc) - (unless org-ascii-links-to-notes (format " (%s)" raw-link)))))))) + (let ((raw-link (org-element-property :raw-link link))) + (if (not (org-string-nw-p desc)) (format "[%s]" raw-link) + (concat (format "[%s]" desc) + (and (not (plist-get info :ascii-links-to-notes)) + (format " (%s)" raw-link))))))))) + + +;;;; Node Properties + +(defun org-ascii-node-property (node-property _contents _info) + "Transcode a NODE-PROPERTY element from Org to ASCII. +CONTENTS is nil. INFO is a plist holding contextual +information." + (format "%s:%s" + (org-element-property :key node-property) + (let ((value (org-element-property :value node-property))) + (if value (concat " " value) "")))) ;;;; Paragraph @@ -1420,16 +1624,17 @@ INFO is a plist holding contextual information." "Transcode a PARAGRAPH element from Org to ASCII. CONTENTS is the contents of the paragraph, as a string. INFO is the plist used as a communication channel." - (org-ascii--fill-string - (if (not (wholenump org-ascii-indented-line-width)) contents - (concat - ;; Do not indent first paragraph in a section. - (unless (and (not (org-export-get-previous-element paragraph info)) - (eq (org-element-type (org-export-get-parent paragraph)) - 'section)) - (make-string org-ascii-indented-line-width ?\s)) - (replace-regexp-in-string "\\`[ \t]+" "" contents))) - (org-ascii--current-text-width paragraph info) info)) + (org-ascii--justify-element + (let ((indented-line-width (plist-get info :ascii-indented-line-width))) + (if (not (wholenump indented-line-width)) contents + (concat + ;; Do not indent first paragraph in a section. + (unless (and (not (org-export-get-previous-element paragraph info)) + (eq (org-element-type (org-export-get-parent paragraph)) + 'section)) + (make-string indented-line-width ?\s)) + (replace-regexp-in-string "\\`[ \t]+" "" contents)))) + paragraph info)) ;;;; Plain List @@ -1438,7 +1643,11 @@ the plist used as a communication channel." "Transcode a PLAIN-LIST element from Org to ASCII. CONTENTS is the contents of the list. INFO is a plist holding contextual information." - contents) + (let ((margin (plist-get info :ascii-list-margin))) + (if (or (< margin 1) + (eq (org-element-type (org-export-get-parent plain-list)) 'item)) + contents + (org-ascii--indent-string contents margin)))) ;;;; Plain Text @@ -1462,62 +1671,52 @@ INFO is a plist used as a communication channel." ;;;; Planning -(defun org-ascii-planning (planning contents info) +(defun org-ascii-planning (planning _contents info) "Transcode a PLANNING element from Org to ASCII. CONTENTS is nil. INFO is a plist used as a communication channel." - (mapconcat - 'identity - (delq nil - (list (let ((closed (org-element-property :closed planning))) - (when closed - (concat org-closed-string " " - (org-translate-time - (org-element-property :raw-value closed))))) - (let ((deadline (org-element-property :deadline planning))) - (when deadline - (concat org-deadline-string " " - (org-translate-time - (org-element-property :raw-value deadline))))) - (let ((scheduled (org-element-property :scheduled planning))) - (when scheduled - (concat org-scheduled-string " " - (org-translate-time - (org-element-property :raw-value scheduled))))))) - " ")) + (org-ascii--justify-element + (mapconcat + #'identity + (delq nil + (list (let ((closed (org-element-property :closed planning))) + (when closed + (concat org-closed-string " " + (org-timestamp-translate closed)))) + (let ((deadline (org-element-property :deadline planning))) + (when deadline + (concat org-deadline-string " " + (org-timestamp-translate deadline)))) + (let ((scheduled (org-element-property :scheduled planning))) + (when scheduled + (concat org-scheduled-string " " + (org-timestamp-translate scheduled)))))) + " ") + planning info)) + + +;;;; Property Drawer + +(defun org-ascii-property-drawer (property-drawer contents info) + "Transcode a PROPERTY-DRAWER element from Org to ASCII. +CONTENTS holds the contents of the drawer. INFO is a plist +holding contextual information." + (and (org-string-nw-p contents) + (org-ascii--justify-element contents property-drawer info))) ;;;; Quote Block -(defun org-ascii-quote-block (quote-block contents info) +(defun org-ascii-quote-block (_quote-block contents info) "Transcode a QUOTE-BLOCK element from Org to ASCII. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." - (org-ascii--indent-string contents org-ascii-quote-margin)) - - -;;;; Quote Section - -(defun org-ascii-quote-section (quote-section contents info) - "Transcode a QUOTE-SECTION element from Org to ASCII. -CONTENTS is nil. INFO is a plist holding contextual information." - (let ((width (org-ascii--current-text-width quote-section info)) - (value - (org-export-data - (org-remove-indentation (org-element-property :value quote-section)) - info))) - (org-ascii--indent-string - value - (+ org-ascii-quote-margin - ;; Don't apply inner margin if parent headline is low level. - (let ((headline (org-export-get-parent-headline quote-section))) - (if (org-export-low-level-p headline info) 0 - org-ascii-inner-margin)))))) + (org-ascii--indent-string contents (plist-get info :ascii-quote-margin))) ;;;; Radio Target -(defun org-ascii-radio-target (radio-target contents info) +(defun org-ascii-radio-target (_radio-target contents _info) "Transcode a RADIO-TARGET object from Org to ASCII. CONTENTS is the contents of the target. INFO is a plist holding contextual information." @@ -1530,50 +1729,56 @@ contextual information." "Transcode a SECTION element from Org to ASCII. CONTENTS is the contents of the section. INFO is a plist holding contextual information." - (org-ascii--indent-string - (concat - contents - (when org-ascii-links-to-notes - ;; Add list of links at the end of SECTION. - (let ((links (org-ascii--describe-links - (org-ascii--unique-links section info) - (org-ascii--current-text-width section info) info))) - ;; Separate list of links and section contents. - (when (org-string-nw-p links) (concat "\n\n" links))))) - ;; Do not apply inner margin if parent headline is low level. - (let ((headline (org-export-get-parent-headline section))) - (if (or (not headline) (org-export-low-level-p headline info)) 0 - org-ascii-inner-margin)))) + (let ((links + (and (plist-get info :ascii-links-to-notes) + ;; Take care of links in first section of the document. + (not (org-element-lineage section '(headline))) + (org-ascii--describe-links + (org-ascii--unique-links section info) + (org-ascii--current-text-width section info) + info)))) + (org-ascii--indent-string + (if (not (org-string-nw-p links)) contents + (concat (org-element-normalize-string contents) "\n\n" links)) + ;; Do not apply inner margin if parent headline is low level. + (let ((headline (org-export-get-parent-headline section))) + (if (or (not headline) (org-export-low-level-p headline info)) 0 + (plist-get info :ascii-inner-margin)))))) ;;;; Special Block -(defun org-ascii-special-block (special-block contents info) +(defun org-ascii-special-block (_special-block contents _info) "Transcode a SPECIAL-BLOCK element from Org to ASCII. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." + ;; "JUSTIFYLEFT" and "JUSTFYRIGHT" have already been taken care of + ;; at a lower level. There is no other special block type to + ;; handle. contents) ;;;; Src Block -(defun org-ascii-src-block (src-block contents info) +(defun org-ascii-src-block (src-block _contents info) "Transcode a SRC-BLOCK element from Org to ASCII. CONTENTS holds the contents of the item. INFO is a plist holding contextual information." (let ((caption (org-ascii--build-caption src-block info)) + (caption-above-p (plist-get info :ascii-caption-above)) (code (org-export-format-code-default src-block info))) (if (equal code "") "" - (concat - (when (and caption org-ascii-caption-above) (concat caption "\n")) - (org-ascii--box-string code info) - (when (and caption (not org-ascii-caption-above)) - (concat "\n" caption)))))) + (org-ascii--justify-element + (concat + (and caption caption-above-p (concat caption "\n")) + (org-ascii--box-string code info) + (and caption (not caption-above-p) (concat "\n" caption))) + src-block info)))) ;;;; Statistics Cookie -(defun org-ascii-statistics-cookie (statistics-cookie contents info) +(defun org-ascii-statistics-cookie (statistics-cookie _contents _info) "Transcode a STATISTICS-COOKIE object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." (org-element-property :value statistics-cookie)) @@ -1581,7 +1786,7 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;;; Subscript -(defun org-ascii-subscript (subscript contents info) +(defun org-ascii-subscript (subscript contents _info) "Transcode a SUBSCRIPT object from Org to ASCII. CONTENTS is the contents of the object. INFO is a plist holding contextual information." @@ -1592,7 +1797,7 @@ contextual information." ;;;; Superscript -(defun org-ascii-superscript (superscript contents info) +(defun org-ascii-superscript (superscript contents _info) "Transcode a SUPERSCRIPT object from Org to ASCII. CONTENTS is the contents of the object. INFO is a plist holding contextual information." @@ -1603,7 +1808,7 @@ contextual information." ;;;; Strike-through -(defun org-ascii-strike-through (strike-through contents info) +(defun org-ascii-strike-through (_strike-through contents _info) "Transcode STRIKE-THROUGH from Org to ASCII. CONTENTS is text with strike-through markup. INFO is a plist holding contextual information." @@ -1616,26 +1821,29 @@ holding contextual information." "Transcode a TABLE element from Org to ASCII. CONTENTS is the contents of the table. INFO is a plist holding contextual information." - (let ((caption (org-ascii--build-caption table info))) - (concat - ;; Possibly add a caption string above. - (when (and caption org-ascii-caption-above) (concat caption "\n")) - ;; Insert table. Note: "table.el" tables are left unmodified. - (cond ((eq (org-element-property :type table) 'org) contents) - ((and org-ascii-table-use-ascii-art - (eq (plist-get info :ascii-charset) 'utf-8) - (require 'ascii-art-to-unicode nil t)) - (with-temp-buffer - (insert (org-remove-indentation - (org-element-property :value table))) - (goto-char (point-min)) - (aa2u) - (goto-char (point-max)) - (skip-chars-backward " \r\t\n") - (buffer-substring (point-min) (point)))) - (t (org-remove-indentation (org-element-property :value table)))) - ;; Possible add a caption string below. - (and (not org-ascii-caption-above) caption)))) + (let ((caption (org-ascii--build-caption table info)) + (caption-above-p (plist-get info :ascii-caption-above))) + (org-ascii--justify-element + (concat + ;; Possibly add a caption string above. + (and caption caption-above-p (concat caption "\n")) + ;; Insert table. Note: "table.el" tables are left unmodified. + (cond ((eq (org-element-property :type table) 'org) contents) + ((and (plist-get info :ascii-table-use-ascii-art) + (eq (plist-get info :ascii-charset) 'utf-8) + (require 'ascii-art-to-unicode nil t)) + (with-temp-buffer + (insert (org-remove-indentation + (org-element-property :value table))) + (goto-char (point-min)) + (aa2u) + (goto-char (point-max)) + (skip-chars-backward " \r\t\n") + (buffer-substring (point-min) (point)))) + (t (org-remove-indentation (org-element-property :value table)))) + ;; Possible add a caption string below. + (and (not caption-above-p) caption)) + table info))) ;;;; Table Cell @@ -1661,12 +1869,13 @@ are ignored." (plist-put info :ascii-table-cell-width-cache (make-hash-table :test 'equal))) :ascii-table-cell-width-cache))) - (key (cons table col))) + (key (cons table col)) + (widenp (plist-get info :ascii-table-widen-columns))) (or (gethash key cache) (puthash key (let ((cookie-width (org-export-table-cell-width table-cell info))) - (or (and (not org-ascii-table-widen-columns) cookie-width) + (or (and (not widenp) cookie-width) (let ((contents-width (let ((max-width 0)) (org-element-map table 'table-row @@ -1681,8 +1890,7 @@ are ignored." info) max-width))) (cond ((not cookie-width) contents-width) - (org-ascii-table-widen-columns - (max cookie-width contents-width)) + (widenp (max cookie-width contents-width)) (t cookie-width))))) cache)))) @@ -1696,14 +1904,14 @@ a communication channel." ;; each cell in the column. (let ((width (org-ascii--table-cell-width table-cell info))) ;; When contents are too large, truncate them. - (unless (or org-ascii-table-widen-columns + (unless (or (plist-get info :ascii-table-widen-columns) (<= (string-width (or contents "")) width)) (setq contents (concat (substring contents 0 (- width 2)) "=>"))) ;; Align contents correctly within the cell. (let* ((indent-tabs-mode nil) (data (when contents - (org-ascii--justify-string + (org-ascii--justify-lines contents width (org-export-table-cell-alignment table-cell info))))) (setq contents @@ -1770,7 +1978,7 @@ a communication channel." ;;;; Timestamp -(defun org-ascii-timestamp (timestamp contents info) +(defun org-ascii-timestamp (timestamp _contents info) "Transcode a TIMESTAMP object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." (org-ascii-plain-text (org-timestamp-translate timestamp) info)) @@ -1778,7 +1986,7 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;;; Underline -(defun org-ascii-underline (underline contents info) +(defun org-ascii-underline (_underline contents _info) "Transcode UNDERLINE from Org to ASCII. CONTENTS is the text with underline markup. INFO is a plist holding contextual information." @@ -1787,10 +1995,10 @@ holding contextual information." ;;;; Verbatim -(defun org-ascii-verbatim (verbatim contents info) +(defun org-ascii-verbatim (verbatim _contents info) "Return a VERBATIM object from Org to ASCII. CONTENTS is nil. INFO is a plist holding contextual information." - (format org-ascii-verbatim-format + (format (plist-get info :ascii-verbatim-format) (org-element-property :value verbatim))) @@ -1800,48 +2008,48 @@ CONTENTS is nil. INFO is a plist holding contextual information." "Transcode a VERSE-BLOCK element from Org to ASCII. CONTENTS is verse block contents. INFO is a plist holding contextual information." - (let ((verse-width (org-ascii--current-text-width verse-block info))) - (org-ascii--indent-string - (org-ascii--justify-string contents verse-width 'left) - org-ascii-quote-margin))) + (org-ascii--indent-string + (org-ascii--justify-element contents verse-block info) + (plist-get info :ascii-quote-margin))) ;;; Filters -(defun org-ascii-filter-headline-blank-lines (headline back-end info) +(defun org-ascii-filter-headline-blank-lines (headline _backend info) "Filter controlling number of blank lines after a headline. -HEADLINE is a string representing a transcoded headline. -BACK-END is symbol specifying back-end used for export. INFO is -plist containing the communication channel. +HEADLINE is a string representing a transcoded headline. BACKEND +is symbol specifying back-end used for export. INFO is plist +containing the communication channel. This function only applies to `ascii' back-end. See `org-ascii-headline-spacing' for information." - (if (not org-ascii-headline-spacing) headline - (let ((blanks (make-string (1+ (cdr org-ascii-headline-spacing)) ?\n))) - (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline)))) + (let ((headline-spacing (plist-get info :ascii-headline-spacing))) + (if (not headline-spacing) headline + (let ((blanks (make-string (1+ (cdr headline-spacing)) ?\n))) + (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline))))) -(defun org-ascii-filter-paragraph-spacing (tree back-end info) +(defun org-ascii-filter-paragraph-spacing (tree _backend info) "Filter controlling number of blank lines between paragraphs. -TREE is the parse tree. BACK-END is the symbol specifying +TREE is the parse tree. BACKEND is the symbol specifying back-end used for export. INFO is a plist used as a communication channel. See `org-ascii-paragraph-spacing' for information." - (when (wholenump org-ascii-paragraph-spacing) - (org-element-map tree 'paragraph - (lambda (p) - (when (eq (org-element-type (org-export-get-next-element p info)) - 'paragraph) - (org-element-put-property - p :post-blank org-ascii-paragraph-spacing))))) + (let ((paragraph-spacing (plist-get info :ascii-paragraph-spacing))) + (when (wholenump paragraph-spacing) + (org-element-map tree 'paragraph + (lambda (p) + (when (eq (org-element-type (org-export-get-next-element p info)) + 'paragraph) + (org-element-put-property p :post-blank paragraph-spacing)))))) tree) -(defun org-ascii-filter-comment-spacing (tree backend info) +(defun org-ascii-filter-comment-spacing (tree _backend info) "Filter removing blank lines between comments. -TREE is the parse tree. BACK-END is the symbol specifying +TREE is the parse tree. BACKEND is the symbol specifying back-end used for export. INFO is a plist used as a communication channel." (org-element-map tree '(comment comment-block) diff --git a/lisp/org/ox-beamer.el b/lisp/org/ox-beamer.el index a8d48b6718..82651d3848 100644 --- a/lisp/org/ox-beamer.el +++ b/lisp/org/ox-beamer.el @@ -1,4 +1,4 @@ -;;; ox-beamer.el --- Beamer Back-End for Org Export Engine +;;; ox-beamer.el --- Beamer Back-End for Org Export Engine -*- lexical-binding: t; -*- ;; Copyright (C) 2007-2017 Free Software Foundation, Inc. @@ -29,7 +29,7 @@ ;;; Code: -(eval-when-compile (require 'cl)) +(require 'cl-lib) (require 'ox-latex) ;; Install a default set-up for Beamer export. @@ -105,7 +105,9 @@ key Selection key for `org-beamer-select-environment' open The opening template for the environment, with the following escapes %a the action/overlay specification %A the default action/overlay specification - %o the options argument of the template + %R the raw BEAMER_act value + %o the options argument, with square brackets + %O the raw BEAMER_opt value %h the headline text %r the raw headline text (i.e. without any processing) %H if there is headline text, that raw text in {} braces @@ -133,6 +135,15 @@ You might want to put e.g. \"allowframebreaks=0.9\" here." :type '(string :tag "Outline frame options")) +(defcustom org-beamer-subtitle-format "\\subtitle{%s}" + "Format string used for transcoded subtitle. +The format string should have at most one \"%s\"-expression, +which is replaced with the subtitle." + :group 'org-export-beamer + :version "26.1" + :package-version '(Org . "8.3") + :type '(string :tag "Format string")) + ;;; Internal Variables @@ -191,19 +202,14 @@ TYPE is a symbol among the following: `defaction' Return ARGUMENT within both square and angular brackets. `option' Return ARGUMENT within square brackets." (if (not (string-match "\\S-" argument)) "" - (case type - (action (if (string-match "\\`<.*>\\'" argument) argument - (format "<%s>" argument))) - (defaction (cond - ((string-match "\\`\\[<.*>\\]\\'" argument) argument) - ((string-match "\\`<.*>\\'" argument) - (format "[%s]" argument)) - ((string-match "\\`\\[\\(.*\\)\\]\\'" argument) - (format "[<%s>]" (match-string 1 argument))) - (t (format "[<%s>]" argument)))) - (option (if (string-match "\\`\\[.*\\]\\'" argument) argument - (format "[%s]" argument))) - (otherwise argument)))) + (cl-case type + (action (format "<%s>" (org-unbracket-string "<" ">" argument))) + (defaction + (format "[<%s>]" + (org-unbracket-string "<" ">" (org-unbracket-string "[" "]" argument)))) + (option (format "[%s]" (org-unbracket-string "[" "]" argument))) + (otherwise (error "Invalid `type' argument to `org-beamer--normalize-argument': %s" + type))))) (defun org-beamer--element-has-overlay-p (element) "Non-nil when ELEMENT has an overlay specified. @@ -213,14 +219,14 @@ Return overlay specification, as a string, or nil." (let ((first-object (car (org-element-contents element)))) (when (eq (org-element-type first-object) 'export-snippet) (let ((value (org-element-property :value first-object))) - (and (string-match "\\`<.*>\\'" value) value))))) + (and (string-prefix-p "<" value) (string-suffix-p ">" value) + value))))) ;;; Define Back-End (org-export-define-derived-backend 'beamer 'latex - :export-block "BEAMER" :menu-entry '(?l 1 ((?B "As LaTeX buffer (Beamer)" org-beamer-export-as-latex) @@ -231,15 +237,20 @@ Return overlay specification, as a string, or nil." (if a (org-beamer-export-to-pdf t s v b) (org-open-file (org-beamer-export-to-pdf nil s v b))))))) :options-alist - '((:beamer-theme "BEAMER_THEME" nil org-beamer-theme) + '((:headline-levels nil "H" org-beamer-frame-level) + (:latex-class "LATEX_CLASS" nil "beamer" t) + (:beamer-subtitle-format nil nil org-beamer-subtitle-format) + (:beamer-column-view-format "COLUMNS" nil org-beamer-column-view-format) + (:beamer-theme "BEAMER_THEME" nil org-beamer-theme) (:beamer-color-theme "BEAMER_COLOR_THEME" nil nil t) (:beamer-font-theme "BEAMER_FONT_THEME" nil nil t) (:beamer-inner-theme "BEAMER_INNER_THEME" nil nil t) (:beamer-outer-theme "BEAMER_OUTER_THEME" nil nil t) - (:beamer-header-extra "BEAMER_HEADER" nil nil newline) - ;; Modify existing properties. - (:headline-levels nil "H" org-beamer-frame-level) - (:latex-class "LATEX_CLASS" nil "beamer" t)) + (:beamer-header "BEAMER_HEADER" nil nil newline) + (:beamer-environments-extra nil nil org-beamer-environments-extra) + (:beamer-frame-default-options nil nil org-beamer-frame-default-options) + (:beamer-outline-frame-options nil nil org-beamer-outline-frame-options) + (:beamer-outline-frame-title nil nil org-beamer-outline-frame-title)) :translate-alist '((bold . org-beamer-bold) (export-block . org-beamer-export-block) (export-snippet . org-beamer-export-snippet) @@ -249,7 +260,6 @@ Return overlay specification, as a string, or nil." (link . org-beamer-link) (plain-list . org-beamer-plain-list) (radio-target . org-beamer-radio-target) - (target . org-beamer-target) (template . org-beamer-template))) @@ -258,7 +268,7 @@ Return overlay specification, as a string, or nil." ;;;; Bold -(defun org-beamer-bold (bold contents info) +(defun org-beamer-bold (bold contents _info) "Transcode BLOCK object into Beamer code. CONTENTS is the text being bold. INFO is a plist used as a communication channel." @@ -269,7 +279,7 @@ a communication channel." ;;;; Export Block -(defun org-beamer-export-block (export-block contents info) +(defun org-beamer-export-block (export-block _contents _info) "Transcode an EXPORT-BLOCK element into Beamer code. CONTENTS is nil. INFO is a plist used as a communication channel." @@ -279,7 +289,7 @@ channel." ;;;; Export Snippet -(defun org-beamer-export-snippet (export-snippet contents info) +(defun org-beamer-export-snippet (export-snippet _contents info) "Transcode an EXPORT-SNIPPET object into Beamer code. CONTENTS is nil. INFO is a plist used as a communication channel." @@ -315,16 +325,21 @@ channel." INFO is a plist used as a communication channel. The value is either the label specified in \"BEAMER_opt\" -property, or a fallback value built from headline's number. This -function assumes HEADLINE will be treated as a frame." - (let ((opt (org-element-property :BEAMER_OPT headline))) - (if (and (org-string-nw-p opt) - (string-match "\\(?:^\\|,\\)label=\\(.*?\\)\\(?:$\\|,\\)" opt)) - (match-string 1 opt) - (format "sec-%s" - (mapconcat 'number-to-string - (org-export-get-headline-number headline info) - "-"))))) +property, the custom ID, if there is one and +`:latex-prefer-user-labels' property has a non nil value, or +a unique internal label. This function assumes HEADLINE will be +treated as a frame." + (cond + ((let ((opt (org-element-property :BEAMER_OPT headline))) + (and (stringp opt) + (string-match "\\(?:^\\|,\\)label=\\(.*?\\)\\(?:$\\|,\\)" opt) + (let ((label (match-string 1 opt))) + (if (string-match-p "\\`{.*}\\'" label) + (substring label 1 -1) + label))))) + ((and (plist-get info :latex-prefer-user-labels) + (org-element-property :CUSTOM_ID headline))) + (t (format "sec:%s" (org-export-get-reference headline info))))) (defun org-beamer--frame-level (headline info) "Return frame level in subtree containing HEADLINE. @@ -333,12 +348,10 @@ INFO is a plist used as a communication channel." ;; 1. Look for "frame" environment in parents, starting from the ;; farthest. (catch 'exit - (mapc (lambda (parent) - (let ((env (org-element-property :BEAMER_ENV parent))) - (when (and env (member-ignore-case env '("frame" "fullframe"))) - (throw 'exit (org-export-get-relative-level parent info))))) - (nreverse (org-export-get-genealogy headline))) - nil) + (dolist (parent (nreverse (org-element-lineage headline))) + (let ((env (org-element-property :BEAMER_ENV parent))) + (when (and env (member-ignore-case env '("frame" "fullframe"))) + (throw 'exit (org-export-get-relative-level parent info)))))) ;; 2. Look for "frame" environment in HEADLINE. (let ((env (org-element-property :BEAMER_ENV headline))) (and env (member-ignore-case env '("frame" "fullframe")) @@ -413,7 +426,8 @@ used as a communication channel." ;; Collect options from default value and headline's ;; properties. Also add a label for links. (append - (org-split-string org-beamer-frame-default-options ",") + (org-split-string + (plist-get info :beamer-frame-default-options) ",") (and beamer-opt (org-split-string ;; Remove square brackets if user provided @@ -422,12 +436,20 @@ used as a communication channel." (match-string 1 beamer-opt)) ",")) ;; Provide an automatic label for the frame - ;; unless the user specified one. + ;; unless the user specified one. Also refrain + ;; from labeling `allowframebreaks' frames; this + ;; is not allowed by beamer. (unless (and beamer-opt - (string-match "\\(^\\|,\\)label=" beamer-opt)) + (or (string-match "\\(^\\|,\\)label=" beamer-opt) + (string-match "allowframebreaks" beamer-opt))) (list - (format "label=%s" - (org-beamer--get-label headline info))))))) + (let ((label (org-beamer--get-label headline info))) + ;; Labels containing colons need to be + ;; wrapped within braces. + (format (if (string-match-p ":" label) + "label={%s}" + "label=%s") + label))))))) ;; Change options list into a string. (org-beamer--normalize-argument (mapconcat @@ -475,14 +497,15 @@ used as a communication channel." (env-format (cond ((member environment '("column" "columns")) nil) ((assoc environment - (append org-beamer-environments-extra + (append (plist-get info :beamer-environments-extra) org-beamer-environments-default))) (t (user-error "Wrong block type at a headline named \"%s\"" raw-title)))) (title (org-export-data (org-element-property :title headline) info)) - (options (let ((options (org-element-property :BEAMER_OPT headline))) - (if (not options) "" - (org-beamer--normalize-argument options 'option)))) + (raw-options (org-element-property :BEAMER_OPT headline)) + (options (if raw-options + (org-beamer--normalize-argument raw-options 'option) + "")) ;; Start a "columns" environment when explicitly requested or ;; when there is no previous headline or the previous ;; headline do not have a BEAMER_column property. @@ -521,7 +544,7 @@ used as a communication channel." ;; One can specify placement for column only when ;; HEADLINE stands for a column on its own. (if (equal environment "column") options "") - (format "%s\\textwidth" column-width))) + (format "%s\\columnwidth" column-width))) ;; Block's opening string. (when (nth 2 env-format) (concat @@ -534,15 +557,19 @@ used as a communication channel." ;; overlay specification and the default one is nil. (let ((action (org-element-property :BEAMER_ACT headline))) (cond - ((not action) (list (cons "a" "") (cons "A" ""))) - ((string-match "\\`\\[.*\\]\\'" action) + ((not action) (list (cons "a" "") (cons "A" "") (cons "R" ""))) + ((and (string-prefix-p "[" action) + (string-suffix-p "]" action)) (list (cons "A" (org-beamer--normalize-argument action 'defaction)) - (cons "a" ""))) + (cons "a" "") + (cons "R" action))) (t (list (cons "a" (org-beamer--normalize-argument action 'action)) - (cons "A" ""))))) + (cons "A" "") + (cons "R" action))))) (list (cons "o" options) + (cons "O" (or raw-options "")) (cons "h" title) (cons "r" raw-title) (cons "H" (if (equal raw-title "") "" @@ -578,28 +605,27 @@ as a communication channel." (when overlay (org-beamer--normalize-argument overlay - (if (string-match "^\\[.*\\]$" overlay) 'defaction + (if (string-match "\\`\\[.*\\]\\'" overlay) 'defaction 'action)))) ;; Options. (let ((options (org-element-property :BEAMER_OPT headline))) (when options (org-beamer--normalize-argument options 'option))) ;; Resolve reference provided by "BEAMER_ref" - ;; property. This is done by building a minimal fake - ;; link and calling the appropriate resolve function, - ;; depending on the reference syntax. - (let* ((type - (progn - (string-match "^\\(id:\\|#\\|\\*\\)?\\(.*\\)" ref) - (cond - ((or (not (match-string 1 ref)) - (equal (match-string 1 ref) "*")) 'fuzzy) - ((equal (match-string 1 ref) "id:") 'id) - (t 'custom-id)))) - (link (list 'link (list :path (match-string 2 ref)))) - (target (if (eq type 'fuzzy) - (org-export-resolve-fuzzy-link link info) - (org-export-resolve-id-link link info)))) + ;; property. This is done by building a minimal + ;; fake link and calling the appropriate resolve + ;; function, depending on the reference syntax. + (let ((target + (if (string-match "\\`\\(id:\\|#\\)" ref) + (org-export-resolve-id-link + `(link (:path ,(substring ref (match-end 0)))) + info) + (org-export-resolve-fuzzy-link + `(link (:path + ;; Look for headlines only. + ,(if (eq (string-to-char ref) ?*) ref + (concat "*" ref)))) + info)))) ;; Now use user-defined label provided in TARGET ;; headline, or fallback to standard one. (format "{%s}" (org-beamer--get-label target info))))))) @@ -640,15 +666,27 @@ as a communication channel." "Transcode an ITEM element into Beamer code. CONTENTS holds the contents of the item. INFO is a plist holding contextual information." - (let ((action (let ((first-element (car (org-element-contents item)))) - (and (eq (org-element-type first-element) 'paragraph) - (org-beamer--element-has-overlay-p first-element)))) - (output (org-export-with-backend 'latex item contents info))) - (if (or (not action) (not (string-match "\\\\item" output))) output - ;; If the item starts with a paragraph and that paragraph starts - ;; with an export snippet specifying an overlay, insert it after - ;; \item command. - (replace-match (concat "\\\\item" action) nil nil output)))) + (org-export-with-backend + ;; Delegate item export to `latex'. However, we use `beamer' + ;; transcoders for objects in the description tag. + (org-export-create-backend + :parent 'beamer + :transcoders + (list + (cons + 'item + (lambda (item _c _i) + (let ((action + (let ((first (car (org-element-contents item)))) + (and (eq (org-element-type first) 'paragraph) + (org-beamer--element-has-overlay-p first)))) + (output (org-latex-item item contents info))) + (if (not (and action (string-match "\\\\item" output))) output + ;; If the item starts with a paragraph and that paragraph + ;; starts with an export snippet specifying an overlay, + ;; append it to the \item command. + (replace-match (concat "\\\\item" action) nil nil output))))))) + item contents info)) ;;;; Keyword @@ -681,46 +719,16 @@ channel." "Transcode a LINK object into Beamer code. CONTENTS is the description part of the link. INFO is a plist used as a communication channel." - (let ((type (org-element-property :type link)) - (path (org-element-property :path link))) - ;; Use \hyperlink command for all internal links. - (cond - ((equal type "radio") - (let ((destination (org-export-resolve-radio-link link info))) - (if (not destination) contents - (format "\\hyperlink%s{%s}{%s}" - (or (org-beamer--element-has-overlay-p link) "") - (org-export-solidify-link-text - (org-element-property :value destination)) - contents)))) - ((and (member type '("custom-id" "fuzzy" "id")) - (let ((destination (if (string= type "fuzzy") - (org-export-resolve-fuzzy-link link info) - (org-export-resolve-id-link link info)))) - (case (org-element-type destination) - (headline - (let ((label - (format "sec-%s" - (mapconcat - 'number-to-string - (org-export-get-headline-number - destination info) - "-")))) - (if (and (plist-get info :section-numbers) (not contents)) - (format "\\ref{%s}" label) - (format "\\hyperlink%s{%s}{%s}" - (or (org-beamer--element-has-overlay-p link) "") - label - contents)))) - (target - (let ((path (org-export-solidify-link-text path))) - (if (not contents) (format "\\ref{%s}" path) - (format "\\hyperlink%s{%s}{%s}" - (or (org-beamer--element-has-overlay-p link) "") - path - contents)))))))) - ;; Otherwise, use `latex' back-end. - (t (org-export-with-backend 'latex link contents info))))) + (or (org-export-custom-protocol-maybe link contents 'beamer) + ;; Fall-back to LaTeX export. However, prefer "\hyperlink" over + ;; "\hyperref" since the former handles overlay specifications. + (let ((latex-link (org-export-with-backend 'latex link contents info))) + (if (string-match "\\`\\\\hyperref\\[\\(.*?\\)\\]" latex-link) + (replace-match + (format "\\\\hyperlink%s{\\1}" + (or (org-beamer--element-has-overlay-p link) "")) + nil nil latex-link) + latex-link)))) ;;;; Plain List @@ -755,7 +763,8 @@ contextual information." 'option) ;; Eventually insert contents and close environment. contents - latex-type)))) + latex-type) + info))) ;;;; Radio Target @@ -766,21 +775,10 @@ TEXT is the text of the target. INFO is a plist holding contextual information." (format "\\hypertarget%s{%s}{%s}" (or (org-beamer--element-has-overlay-p radio-target) "") - (org-export-solidify-link-text - (org-element-property :value radio-target)) + (org-export-get-reference radio-target info) text)) -;;;; Target - -(defun org-beamer-target (target contents info) - "Transcode a TARGET object into Beamer code. -CONTENTS is nil. INFO is a plist holding contextual -information." - (format "\\hypertarget{%s}{}" - (org-export-solidify-link-text (org-element-property :value target)))) - - ;;;; Template ;; ;; Template used is similar to the one used in `latex' back-end, @@ -790,37 +788,17 @@ information." "Return complete document string after Beamer conversion. CONTENTS is the transcoded contents string. INFO is a plist holding export options." - (let ((title (org-export-data (plist-get info :title) info))) + (let ((title (org-export-data (plist-get info :title) info)) + (subtitle (org-export-data (plist-get info :subtitle) info))) (concat - ;; 1. Time-stamp. + ;; Time-stamp. (and (plist-get info :time-stamp-file) (format-time-string "%% Created %Y-%m-%d %a %H:%M\n")) - ;; 2. Document class and packages. - (let* ((class (plist-get info :latex-class)) - (class-options (plist-get info :latex-class-options)) - (header (nth 1 (assoc class org-latex-classes))) - (document-class-string - (and (stringp header) - (if (not class-options) header - (replace-regexp-in-string - "^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)" - class-options header t nil 1))))) - (if (not document-class-string) - (user-error "Unknown LaTeX class `%s'" class) - (org-latex-guess-babel-language - (org-latex-guess-inputenc - (org-element-normalize-string - (org-splice-latex-header - document-class-string - org-latex-default-packages-alist - org-latex-packages-alist nil - (concat (org-element-normalize-string - (plist-get info :latex-header)) - (org-element-normalize-string - (plist-get info :latex-header-extra)) - (plist-get info :beamer-header-extra))))) - info))) - ;; 3. Insert themes. + ;; LaTeX compiler + (org-latex--insert-compiler info) + ;; Document class and packages. + (org-latex-make-preamble info) + ;; Insert themes. (let ((format-theme (function (lambda (prop command) @@ -840,11 +818,11 @@ holding export options." (:beamer-inner-theme "\\useinnertheme") (:beamer-outer-theme "\\useoutertheme")) "")) - ;; 4. Possibly limit depth for headline numbering. + ;; Possibly limit depth for headline numbering. (let ((sec-num (plist-get info :section-numbers))) (when (integerp sec-num) (format "\\setcounter{secnumdepth}{%d}\n" sec-num))) - ;; 5. Author. + ;; Author. (let ((author (and (plist-get info :with-author) (let ((auth (plist-get info :author))) (and auth (org-export-data auth info))))) @@ -852,52 +830,52 @@ holding export options." (org-export-data (plist-get info :email) info)))) (cond ((and author email (not (string= "" email))) (format "\\author{%s\\thanks{%s}}\n" author email)) - (author (format "\\author{%s}\n" author)) - (t "\\author{}\n"))) - ;; 6. Date. + ((or author email) (format "\\author{%s}\n" (or author email))))) + ;; Date. (let ((date (and (plist-get info :with-date) (org-export-get-date info)))) (format "\\date{%s}\n" (org-export-data date info))) - ;; 7. Title + ;; Title (format "\\title{%s}\n" title) - ;; 8. Hyperref options. - (when (plist-get info :latex-hyperref-p) - (format "\\hypersetup{\n pdfkeywords={%s},\n pdfsubject={%s},\n pdfcreator={%s}}\n" - (or (plist-get info :keywords) "") - (or (plist-get info :description) "") - (if (not (plist-get info :with-creator)) "" - (plist-get info :creator)))) - ;; 9. Document start. + (when (org-string-nw-p subtitle) + (concat (format (plist-get info :beamer-subtitle-format) subtitle) "\n")) + ;; Beamer-header + (let ((beamer-header (plist-get info :beamer-header))) + (when beamer-header + (format "%s\n" (plist-get info :beamer-header)))) + ;; 9. Hyperref options. + (let ((template (plist-get info :latex-hyperref-template))) + (and (stringp template) + (format-spec template (org-latex--format-spec info)))) + ;; Document start. "\\begin{document}\n\n" - ;; 10. Title command. + ;; Title command. (org-element-normalize-string - (cond ((string= "" title) nil) + (cond ((not (plist-get info :with-title)) nil) + ((string= "" title) nil) ((not (stringp org-latex-title-command)) nil) ((string-match "\\(?:[^%]\\|^\\)%s" org-latex-title-command) (format org-latex-title-command title)) (t org-latex-title-command))) - ;; 11. Table of contents. + ;; Table of contents. (let ((depth (plist-get info :with-toc))) (when depth (concat (format "\\begin{frame}%s{%s}\n" (org-beamer--normalize-argument - org-beamer-outline-frame-options 'option) - org-beamer-outline-frame-title) + (plist-get info :beamer-outline-frame-options) 'option) + (plist-get info :beamer-outline-frame-title)) (when (wholenump depth) (format "\\setcounter{tocdepth}{%d}\n" depth)) "\\tableofcontents\n" "\\end{frame}\n\n"))) - ;; 12. Document's body. + ;; Document's body. contents - ;; 13. Creator. - (let ((creator-info (plist-get info :with-creator))) - (cond - ((not creator-info) "") - ((eq creator-info 'comment) - (format "%% %s\n" (plist-get info :creator))) - (t (concat (plist-get info :creator) "\n")))) - ;; 14. Document end. + ;; Creator. + (if (plist-get info :with-creator) + (concat (plist-get info :creator) "\n") + "") + ;; Document end. "\\end{document}"))) @@ -933,7 +911,7 @@ value." (save-excursion (org-back-to-heading t) ;; Filter out Beamer-related tags and install environment tag. - (let ((tags (org-remove-if (lambda (x) (string-match "^B_" x)) + (let ((tags (cl-remove-if (lambda (x) (string-match "^B_" x)) (org-get-tags))) (env-tag (and (org-string-nw-p value) (concat "B_" value)))) (org-set-tags-to (if env-tag (cons env-tag tags) tags)) @@ -1085,7 +1063,7 @@ aid, but the tag does not have any semantic meaning." (let* ((envs (append org-beamer-environments-special org-beamer-environments-extra org-beamer-environments-default)) - (org-tag-alist + (org-current-tag-alist (append '((:startgroup)) (mapcar (lambda (e) (cons (concat "B_" (car e)) (string-to-char (nth 1 e)))) @@ -1120,30 +1098,6 @@ aid, but the tag does not have any semantic meaning." (org-entry-put nil "BEAMER_env" (match-string 1 tags))) (t (org-entry-delete nil "BEAMER_env")))))) -;;;###autoload -(defun org-beamer-insert-options-template (&optional kind) - "Insert a settings template, to make sure users do this right." - (interactive (progn - (message "Current [s]ubtree or [g]lobal?") - (if (eq (read-char-exclusive) ?g) (list 'global) - (list 'subtree)))) - (if (eq kind 'subtree) - (progn - (org-back-to-heading t) - (org-reveal) - (org-entry-put nil "EXPORT_LaTeX_CLASS" "beamer") - (org-entry-put nil "EXPORT_LaTeX_CLASS_OPTIONS" "[presentation]") - (org-entry-put nil "EXPORT_FILE_NAME" "presentation.pdf") - (when org-beamer-column-view-format - (org-entry-put nil "COLUMNS" org-beamer-column-view-format)) - (org-entry-put nil "BEAMER_col_ALL" org-beamer-column-widths)) - (insert "#+LaTeX_CLASS: beamer\n") - (insert "#+LaTeX_CLASS_OPTIONS: [presentation]\n") - (when org-beamer-theme (insert "#+BEAMER_THEME: " org-beamer-theme "\n")) - (when org-beamer-column-view-format - (insert "#+COLUMNS: " org-beamer-column-view-format "\n")) - (insert "#+PROPERTY: BEAMER_col_ALL " org-beamer-column-widths "\n"))) - ;;;###autoload (defun org-beamer-publish-to-latex (plist filename pub-dir) "Publish an Org file to a Beamer presentation (LaTeX). @@ -1168,9 +1122,13 @@ Return output file name." ;; working directory and then moved to publishing directory. (org-publish-attachment plist - (org-latex-compile - (org-publish-org-to - 'beamer filename ".tex" plist (file-name-directory filename))) + ;; Default directory could be anywhere when this function is + ;; called. We ensure it is set to source file directory during + ;; compilation so as to not break links to external documents. + (let ((default-directory (file-name-directory filename))) + (org-latex-compile + (org-publish-org-to + 'beamer filename ".tex" plist (file-name-directory filename)))) pub-dir)) diff --git a/lisp/org/ox-html.el b/lisp/org/ox-html.el index 86ca3a6bb2..9c0ba65398 100644 --- a/lisp/org/ox-html.el +++ b/lisp/org/ox-html.el @@ -1,4 +1,4 @@ -;;; ox-html.el --- HTML Back-End for Org Export Engine +;;; ox-html.el --- HTML Back-End for Org Export Engine -*- lexical-binding: t; -*- ;; Copyright (C) 2011-2017 Free Software Foundation, Inc. @@ -30,20 +30,24 @@ ;;; Dependencies +(require 'cl-lib) +(require 'format-spec) (require 'ox) (require 'ox-publish) -(require 'format-spec) -(eval-when-compile (require 'cl) (require 'table nil 'noerror)) +(require 'table) ;;; Function Declarations (declare-function org-id-find-id-file "org-id" (id)) (declare-function htmlize-region "ext:htmlize" (beg end)) -(declare-function org-pop-to-buffer-same-window - "org-compat" (&optional buffer-or-name norecord label)) (declare-function mm-url-decode-entities "mm-url" ()) +(defvar htmlize-css-name-prefix) +(defvar htmlize-output-type) +(defvar htmlize-output-type) +(defvar htmlize-css-name-prefix) + ;;; Define Back-End (org-export-define-backend 'html @@ -72,13 +76,13 @@ (latex-fragment . org-html-latex-fragment) (line-break . org-html-line-break) (link . org-html-link) + (node-property . org-html-node-property) (paragraph . org-html-paragraph) (plain-list . org-html-plain-list) (plain-text . org-html-plain-text) (planning . org-html-planning) (property-drawer . org-html-property-drawer) (quote-block . org-html-quote-block) - (quote-section . org-html-quote-section) (radio-target . org-html-radio-target) (section . org-html-section) (special-block . org-html-special-block) @@ -96,7 +100,6 @@ (underline . org-html-underline) (verbatim . org-html-verbatim) (verse-block . org-html-verse-block)) - :export-block "HTML" :filters-alist '((:filter-options . org-html-infojs-install-script) (:filter-final-output . org-html-final-function)) :menu-entry @@ -108,10 +111,10 @@ (if a (org-html-export-to-html t s v b) (org-open-file (org-html-export-to-html nil s v b))))))) :options-alist - '((:html-extension nil nil org-html-extension) - (:html-link-org-as-html nil nil org-html-link-org-files-as-html) - (:html-doctype "HTML_DOCTYPE" nil org-html-doctype) + '((:html-doctype "HTML_DOCTYPE" nil org-html-doctype) (:html-container "HTML_CONTAINER" nil org-html-container-element) + (:description "DESCRIPTION" nil nil newline) + (:keywords "KEYWORDS" nil nil space) (:html-html5-fancy nil "html5-fancy" org-html-html5-fancy) (:html-link-use-abs-url nil "html-link-use-abs-url" org-html-link-use-abs-url) (:html-link-home "HTML_LINK_HOME" nil org-html-link-home) @@ -121,12 +124,52 @@ (:html-preamble nil "html-preamble" org-html-preamble) (:html-head "HTML_HEAD" nil org-html-head newline) (:html-head-extra "HTML_HEAD_EXTRA" nil org-html-head-extra newline) - (:html-head-include-default-style nil "html-style" org-html-head-include-default-style) + (:subtitle "SUBTITLE" nil nil parse) + (:html-head-include-default-style + nil "html-style" org-html-head-include-default-style) (:html-head-include-scripts nil "html-scripts" org-html-head-include-scripts) + (:html-allow-name-attribute-in-anchors + nil nil org-html-allow-name-attribute-in-anchors) + (:html-divs nil nil org-html-divs) + (:html-checkbox-type nil nil org-html-checkbox-type) + (:html-extension nil nil org-html-extension) + (:html-footnote-format nil nil org-html-footnote-format) + (:html-footnote-separator nil nil org-html-footnote-separator) + (:html-footnotes-section nil nil org-html-footnotes-section) + (:html-format-drawer-function nil nil org-html-format-drawer-function) + (:html-format-headline-function nil nil org-html-format-headline-function) + (:html-format-inlinetask-function + nil nil org-html-format-inlinetask-function) + (:html-home/up-format nil nil org-html-home/up-format) + (:html-indent nil nil org-html-indent) + (:html-infojs-options nil nil org-html-infojs-options) + (:html-infojs-template nil nil org-html-infojs-template) + (:html-inline-image-rules nil nil org-html-inline-image-rules) + (:html-link-org-files-as-html nil nil org-html-link-org-files-as-html) + (:html-mathjax-options nil nil org-html-mathjax-options) + (:html-mathjax-template nil nil org-html-mathjax-template) + (:html-metadata-timestamp-format nil nil org-html-metadata-timestamp-format) + (:html-postamble-format nil nil org-html-postamble-format) + (:html-preamble-format nil nil org-html-preamble-format) + (:html-table-align-individual-fields + nil nil org-html-table-align-individual-fields) + (:html-table-caption-above nil nil org-html-table-caption-above) + (:html-table-data-tags nil nil org-html-table-data-tags) + (:html-table-header-tags nil nil org-html-table-header-tags) + (:html-table-use-header-tags-for-first-column + nil nil org-html-table-use-header-tags-for-first-column) + (:html-tag-class-prefix nil nil org-html-tag-class-prefix) + (:html-text-markup-alist nil nil org-html-text-markup-alist) + (:html-todo-kwd-class-prefix nil nil org-html-todo-kwd-class-prefix) + (:html-toplevel-hlevel nil nil org-html-toplevel-hlevel) + (:html-use-infojs nil nil org-html-use-infojs) + (:html-validation-link nil nil org-html-validation-link) + (:html-viewport nil nil org-html-viewport) + (:html-inline-images nil nil org-html-inline-images) (:html-table-attributes nil nil org-html-table-default-attributes) - (:html-table-row-tags nil nil org-html-table-row-tags) + (:html-table-row-open-tag nil nil org-html-table-row-open-tag) + (:html-table-row-close-tag nil nil org-html-table-row-close-tag) (:html-xml-declaration nil nil org-html-xml-declaration) - (:html-inline-images nil nil org-html-inline-images) (:infojs-opt "INFOJS_OPT" nil nil) ;; Redefine regular options. (:creator "CREATOR" nil org-html-creator-string) @@ -186,7 +229,7 @@ property on the headline itself.") @licstart The following is the entire license notice for the JavaScript code in this tag. -Copyright (C) 2012-2013 Free Software Foundation, Inc. +Copyright (C) 2012-2017 Free Software Foundation, Inc. The JavaScript code in this tag is free software: you can redistribute it and/or modify it under the terms of the GNU @@ -232,16 +275,22 @@ for the JavaScript code in this tag. (defconst org-html-style-default "" "The default style specification for exported HTML files. @@ -447,23 +580,24 @@ Option settings will replace the %MANAGER-OPTIONS cookie." :package-version '(Org . "8.0") :type 'string) -(defun org-html-infojs-install-script (exp-plist backend) +(defun org-html-infojs-install-script (exp-plist _backend) "Install script in export options when appropriate. EXP-PLIST is a plist containing export options. BACKEND is the export back-end currently used." (unless (or (memq 'body-only (plist-get exp-plist :export-options)) - (not org-html-use-infojs) - (and (eq org-html-use-infojs 'when-configured) - (or (not (plist-get exp-plist :infojs-opt)) - (string= "" (plist-get exp-plist :infojs-opt)) - (string-match "\\" - (plist-get exp-plist :infojs-opt))))) - (let* ((template org-html-infojs-template) + (not (plist-get exp-plist :html-use-infojs)) + (and (eq (plist-get exp-plist :html-use-infojs) 'when-configured) + (let ((opt (plist-get exp-plist :infojs-opt))) + (or (not opt) + (string= "" opt) + (string-match "\\" opt))))) + (let* ((template (plist-get exp-plist :html-infojs-template)) (ptoc (plist-get exp-plist :with-toc)) (hlevels (plist-get exp-plist :headline-levels)) (sdepth hlevels) (tdepth (if (integerp ptoc) (min ptoc hlevels) hlevels)) (options (plist-get exp-plist :infojs-opt)) + (infojs-opt (plist-get exp-plist :html-infojs-options)) (table org-html-infojs-opts-table) style) (dolist (entry table) @@ -472,7 +606,7 @@ export back-end currently used." ;; Compute default values for script option OPT from ;; `org-html-infojs-options' variable. (default - (let ((default (cdr (assq opt org-html-infojs-options)))) + (let ((default (cdr (assq opt infojs-opt)))) (if (and (symbolp default) (not (memq default '(t nil)))) (plist-get exp-plist default) default))) @@ -483,21 +617,21 @@ export back-end currently used." options)) (match-string 1 options) default))) - (case opt - (path (setq template - (replace-regexp-in-string - "%SCRIPT_PATH" val template t t))) - (sdepth (when (integerp (read val)) - (setq sdepth (min (read val) sdepth)))) - (tdepth (when (integerp (read val)) - (setq tdepth (min (read val) tdepth)))) - (otherwise (setq val - (cond - ((or (eq val t) (equal val "t")) "1") - ((or (eq val nil) (equal val "nil")) "0") - ((stringp val) val) - (t (format "%s" val)))) - (push (cons var val) style))))) + (pcase opt + (`path (setq template + (replace-regexp-in-string + "%SCRIPT_PATH" val template t t))) + (`sdepth (when (integerp (read val)) + (setq sdepth (min (read val) sdepth)))) + (`tdepth (when (integerp (read val)) + (setq tdepth (min (read val) tdepth)))) + (_ (setq val + (cond + ((or (eq val t) (equal val "t")) "1") + ((or (eq val nil) (equal val "nil")) "0") + ((stringp val) val) + (t (format "%s" val)))) + (push (cons var val) style))))) ;; Now we set the depth of the *generated* TOC to SDEPTH, ;; because the toc will actually determine the splitting. How ;; much of the toc will actually be displayed is governed by the @@ -509,9 +643,9 @@ export back-end currently used." (push (cons "TOC_DEPTH" tdepth) style) ;; Build style string. (setq style (mapconcat - (lambda (x) (format "org_html_manager.set(\"%s\", \"%s\");" - (car x) - (cdr x))) + (lambda (x) + (format "org_html_manager.set(\"%s\", \"%s\");" + (car x) (cdr x))) style "\n")) (when (and style (> (length style) 0)) (and (string-match "%MANAGER_OPTIONS" template) @@ -561,17 +695,9 @@ Warning: non-nil may break indentation of source code blocks." :package-version '(Org . "8.0") :type 'boolean) -(defcustom org-html-use-unicode-chars nil - "Non-nil means to use unicode characters instead of HTML entities." - :group 'org-export-html - :version "24.4" - :package-version '(Org . "8.0") - :type 'boolean) - ;;;; Drawers -(defcustom org-html-format-drawer-function - (lambda (name contents) contents) +(defcustom org-html-format-drawer-function (lambda (_name contents) contents) "Function called to format a drawer in HTML code. The function must accept two parameters: @@ -628,28 +754,30 @@ document title." :group 'org-export-html :type 'integer) -(defcustom org-html-format-headline-function 'ignore +(defcustom org-html-format-headline-function + 'org-html-format-headline-default-function "Function to format headline text. -This function will be called with 5 arguments: +This function will be called with six arguments: TODO the todo keyword (string or nil). TODO-TYPE the type of todo (symbol: `todo', `done', nil) PRIORITY the priority of the headline (integer or nil) TEXT the main headline text (string). TAGS the tags (string or nil). +INFO the export options (plist). The function result will be used in the section format string." :group 'org-export-html - :version "24.4" - :package-version '(Org . "8.0") + :version "26.1" + :package-version '(Org . "8.3") :type 'function) ;;;; HTML-specific -(defcustom org-html-allow-name-attribute-in-anchors t +(defcustom org-html-allow-name-attribute-in-anchors nil "When nil, do not set \"name\" attribute in anchors. -By default, anchors are formatted with both \"id\" and \"name\" -attributes, when appropriate." +By default, when appropriate, anchors are formatted with \"id\" +but without \"name\" attribute." :group 'org-export-html :version "24.4" :package-version '(Org . "8.0") @@ -657,21 +785,23 @@ attributes, when appropriate." ;;;; Inlinetasks -(defcustom org-html-format-inlinetask-function 'ignore +(defcustom org-html-format-inlinetask-function + 'org-html-format-inlinetask-default-function "Function called to format an inlinetask in HTML code. -The function must accept six parameters: +The function must accept seven parameters: TODO the todo keyword, as a string TODO-TYPE the todo type, a symbol among `todo', `done' and nil. PRIORITY the inlinetask priority, as a string NAME the inlinetask name, as a string. TAGS the inlinetask tags, as a list of strings. CONTENTS the contents of the inlinetask, as a string. + INFO the export options, as a plist The function should return the string to be exported." :group 'org-export-html - :version "24.4" - :package-version '(Org . "8.0") + :version "26.1" + :package-version '(Org . "8.3") :type 'function) ;;;; LaTeX @@ -685,24 +815,20 @@ fragments. This option can also be set with the +OPTIONS line, e.g. \"tex:mathjax\". Allowed values are: -nil Ignore math snippets. -`verbatim' Keep everything in verbatim -`dvipng' Process the LaTeX fragments to images. This will also - include processing of non-math environments. -`imagemagick' Convert the LaTeX fragments to pdf files and use - imagemagick to convert pdf files to png files. -`mathjax' Do MathJax preprocessing and arrange for MathJax.js to - be loaded. -t Synonym for `mathjax'." + nil Ignore math snippets. + `verbatim' Keep everything in verbatim + `mathjax', t Do MathJax preprocessing and arrange for MathJax.js to + be loaded. + SYMBOL Any symbol defined in `org-preview-latex-process-alist', + e.g., `dvipng'." :group 'org-export-html :version "24.4" :package-version '(Org . "8.0") :type '(choice (const :tag "Do not process math in any way" nil) - (const :tag "Use dvipng to make images" dvipng) - (const :tag "Use imagemagick to make images" imagemagick) + (const :tag "Leave math verbatim" verbatim) (const :tag "Use MathJax to display math" mathjax) - (const :tag "Leave math verbatim" verbatim))) + (symbol :tag "Convert to image to display math" :value dvipng))) ;;;; Links :: Generic @@ -710,11 +836,11 @@ t Synonym for `mathjax'." "Non-nil means make file links to `file.org' point to `file.html'. When `org-mode' is exporting an `org-mode' file to HTML, links to non-html files are directly put into a href tag in HTML. -However, links to other Org-mode files (recognized by the -extension `.org') should become links to the corresponding html +However, links to other Org files (recognized by the extension +\".org\") should become links to the corresponding HTML file, assuming that the linked `org-mode' file will also be converted to HTML. -When nil, the links still point to the plain `.org' file." +When nil, the links still point to the plain \".org\" file." :group 'org-export-html :type 'boolean) @@ -745,22 +871,20 @@ link's path." ;;;; Plain Text -(defcustom org-html-protect-char-alist +(defvar org-html-protect-char-alist '(("&" . "&") ("<" . "<") (">" . ">")) - "Alist of characters to be converted by `org-html-protect'." - :group 'org-export-html - :type '(repeat (cons (string :tag "Character") - (string :tag "HTML equivalent")))) + "Alist of characters to be converted by `org-html-encode-plain-text'.") ;;;; Src Block (defcustom org-html-htmlize-output-type 'inline-css "Output type to be used by htmlize when formatting code snippets. -Choices are `css', to export the CSS selectors only, or `inline-css', to -export the CSS attribute values inline in the HTML. We use as default -`inline-css', in order to make the resulting HTML self-containing. +Choices are `css' to export the CSS selectors only,`inline-css' +to export the CSS attribute values inline in the HTML or `nil' to +export plain text. We use as default `inline-css', in order to +make the resulting HTML self-containing. However, this will fail when using Emacs in batch mode for export, because then no rich font definitions are in place. It will also not be good if @@ -771,9 +895,9 @@ a style file to define the look of these classes. To get a start for your css file, start Emacs session and make sure that all the faces you are interested in are defined, for example by loading files in all modes you want. Then, use the command -\\[org-html-htmlize-generate-css] to extract class definitions." +`\\[org-html-htmlize-generate-css]' to extract class definitions." :group 'org-export-html - :type '(choice (const css) (const inline-css))) + :type '(choice (const css) (const inline-css) (const nil))) (defcustom org-html-htmlize-font-prefix "org-" "The prefix for CSS class names for htmlize font specifications." @@ -796,7 +920,7 @@ When exporting to HTML5, these values will be disregarded." :value-type (string :tag "Value"))) (defcustom org-html-table-header-tags '("" . "") - "The opening tag for table header fields. + "The opening and ending tags for table header fields. This is customizable so that alignment options can be specified. The first %s will be filled with the scope of the field, either row or col. The second %s will be replaced by a style entry to align the field. @@ -806,7 +930,7 @@ See also the variable `org-html-table-align-individual-fields'." :type '(cons (string :tag "Opening tag") (string :tag "Closing tag"))) (defcustom org-html-table-data-tags '("" . "") - "The opening tag for table data fields. + "The opening and ending tags for table data fields. This is customizable so that alignment options can be specified. The first %s will be filled with the scope of the field, either row or col. The second %s will be replaced by a style entry to align the field. @@ -814,43 +938,50 @@ See also the variable `org-html-table-align-individual-fields'." :group 'org-export-html :type '(cons (string :tag "Opening tag") (string :tag "Closing tag"))) -(defcustom org-html-table-row-tags '("" . "") - "The opening and ending tags for table rows. +(defcustom org-html-table-row-open-tag "" + "The opening tag for table rows. This is customizable so that alignment options can be specified. -Instead of strings, these can be Lisp forms that will be +Instead of strings, these can be a Lisp function that will be evaluated for each row in order to construct the table row tags. -During evaluation, these variables will be dynamically bound so that -you can reuse them: +The function will be called with these arguments: - `row-number': row number (0 is the first row) - `rowgroup-number': group number of current row - `start-rowgroup-p': non-nil means the row starts a group - `end-rowgroup-p': non-nil means the row ends a group - `top-row-p': non-nil means this is the top row - `bottom-row-p': non-nil means this is the bottom row + `number': row number (0 is the first row) + `group-number': group number of current row + `start-group?': non-nil means the row starts a group + `end-group?': non-nil means the row ends a group + `top?': non-nil means this is the top row + `bottom?': non-nil means this is the bottom row For example: -\(setq org-html-table-row-tags - (cons \\='(cond (top-row-p \"\") - (bottom-row-p \"\") - (t (if (= (mod row-number 2) 1) - \"\" - \"\"))) - \"\")) + (setq org-html-table-row-open-tag + (lambda (number group-number start-group? end-group-p top? bottom?) + (cond (top? \"\") + (bottom? \"\") + (t (if (= (mod number 2) 1) + \"\" + \"\"))))) will use the \"tr-top\" and \"tr-bottom\" classes for the top row and the bottom row, and otherwise alternate between \"tr-odd\" and \"tr-even\" for odd and even rows." :group 'org-export-html - :type '(cons - (choice :tag "Opening tag" - (string :tag "Specify") - (sexp)) - (choice :tag "Closing tag" - (string :tag "Specify") - (sexp)))) + :type '(choice :tag "Opening tag" + (string :tag "Specify") + (function))) + +(defcustom org-html-table-row-close-tag "" + "The closing tag for table rows. +This is customizable so that alignment options can be specified. +Instead of strings, this can be a Lisp function that will be +evaluated for each row in order to construct the table row tags. + +See documentation of `org-html-table-row-open-tag'." + :group 'org-export-html + :type '(choice :tag "Closing tag" + (string :tag "Specify") + (function))) (defcustom org-html-table-align-individual-fields t "Non-nil means attach style attributes for alignment to each table field. @@ -921,7 +1052,10 @@ publishing, with :html-doctype." :group 'org-export-html :version "24.4" :package-version '(Org . "8.0") - :type 'string) + :type (append + '(choice) + (mapcar (lambda (x) `(const ,(car x))) org-html-doctype-alist) + '((string :tag "Custom doctype" )))) (defcustom org-html-html5-fancy nil "Non-nil means using new HTML5 elements. @@ -954,7 +1088,7 @@ org-info.js for your website." (content "div" "content") (postamble "div" "postamble")) "Alist of the three section elements for HTML export. -The car of each entry is one of 'preamble, 'content or 'postamble. +The car of each entry is one of `preamble', `content' or `postamble'. The cdrs of each entry are the ELEMENT_TYPE and ID for each section of the exported document. @@ -973,6 +1107,41 @@ org-info.js for your website." (list :tag "Postamble" (const :format "" postamble) (string :tag " id") (string :tag "element")))) +(defconst org-html-checkbox-types + '((unicode . + ((on . "☑") (off . "☐") (trans . "☐"))) + (ascii . + ((on . "[X]") + (off . "[ ]") + (trans . "[-]"))) + (html . + ((on . "") + (off . "") + (trans . "")))) + "Alist of checkbox types. +The cdr of each entry is an alist list three checkbox types for +HTML export: `on', `off' and `trans'. + +The choices are: + `unicode' Unicode characters (HTML entities) + `ascii' ASCII characters + `html' HTML checkboxes + +Note that only the ascii characters implement tri-state +checkboxes. The other two use the `off' checkbox for `trans'.") + +(defcustom org-html-checkbox-type 'ascii + "The type of checkboxes to use for HTML export. +See `org-html-checkbox-types' for for the values used for each +option." + :group 'org-export-html + :version "24.4" + :package-version '(Org . "8.0") + :type '(choice + (const :tag "ASCII characters" ascii) + (const :tag "Unicode characters" unicode) + (const :tag "HTML checkboxes" html))) + (defcustom org-html-metadata-timestamp-format "%Y-%m-%d %a %H:%M" "Format used for timestamps in preamble, postamble and metadata. See `format-time-string' for more information on its components." @@ -984,82 +1153,107 @@ See `format-time-string' for more information on its components." ;;;; Template :: Mathjax (defcustom org-html-mathjax-options - '((path "http://orgmode.org/mathjax/MathJax.js") + '((path "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_HTML" ) (scale "100") (align "center") - (indent "2em") - (mathml nil)) + (font "TeX") + (linebreaks "false") + (autonumber "AMS") + (indent "0em") + (multlinewidth "85%") + (tagindent ".8em") + (tagside "right")) "Options for MathJax setup. -path The path where to find MathJax -scale Scaling for the HTML-CSS backend, usually between 100 and 133 -align How to align display math: left, center, or right -indent If align is not center, how far from the left/right side? -mathml Should a MathML player be used if available? - This is faster and reduces bandwidth use, but currently - sometimes has lower spacing quality. Therefore, the default is - nil. When browsers get better, this switch can be flipped. +Alist of the following elements. All values are strings. + +path The path to MathJax. +scale Scaling with HTML-CSS, MathML and SVG output engines. +align How to align display math: left, center, or right. +font The font to use with HTML-CSS and SVG output. As of MathJax 2.5 + the following values are understood: \"TeX\", \"STIX-Web\", + \"Asana-Math\", \"Neo-Euler\", \"Gyre-Pagella\", + \"Gyre-Termes\", and \"Latin-Modern\". +linebreaks Let MathJax perform automatic linebreaks. Valid values + are \"true\" and \"false\". +indent If align is not center, how far from the left/right side? + Valid values are \"left\" and \"right\" +multlinewidth The width of the multline environment. +autonumber How to number equations. Valid values are \"None\", + \"all\" and \"AMS Math\". +tagindent The amount tags are indented. +tagside Which side to show tags/labels on. Valid values are + \"left\" and \"right\" You can also customize this for each buffer, using something like -#+MATHJAX: scale:\"133\" align:\"right\" mathml:t path:\"/MathJax/\"" +#+HTML_MATHJAX: align: left indent: 5em tagside: left font: Neo-Euler + +For further information about MathJax options, see the MathJax documentation: + + http://docs.mathjax.org/" :group 'org-export-html + :package-version '(Org . "8.3") :type '(list :greedy t - (list :tag "path (the path from where to load MathJax.js)" - (const :format " " path) (string)) - (list :tag "scale (scaling for the displayed math)" - (const :format " " scale) (string)) - (list :tag "align (alignment of displayed equations)" - (const :format " " align) (string)) - (list :tag "indent (indentation with left or right alignment)" - (const :format " " indent) (string)) - (list :tag "mathml (should MathML display be used is possible)" - (const :format " " mathml) (boolean)))) + (list :tag "path (the path from where to load MathJax.js)" + (const :format " " path) (string)) + (list :tag "scale (scaling for the displayed math)" + (const :format " " scale) (string)) + (list :tag "align (alignment of displayed equations)" + (const :format " " align) (string)) + (list :tag "font (used to display math)" + (const :format " " font) + (choice (const "TeX") + (const "STIX-Web") + (const "Asana-Math") + (const "Neo-Euler") + (const "Gyre-Pagella") + (const "Gyre-Termes") + (const "Latin-Modern"))) + (list :tag "linebreaks (automatic line-breaking)" + (const :format " " linebreaks) + (choice (const "true") + (const "false"))) + (list :tag "autonumber (when should equations be numbered)" + (const :format " " autonumber) + (choice (const "AMS") + (const "None") + (const "All"))) + (list :tag "indent (indentation with left or right alignment)" + (const :format " " indent) (string)) + (list :tag "multlinewidth (width to use for the multline environment)" + (const :format " " multlinewidth) (string)) + (list :tag "tagindent (the indentation of tags from left or right)" + (const :format " " tagindent) (string)) + (list :tag "tagside (location of tags)" + (const :format " " tagside) + (choice (const "left") + (const "right"))))) (defcustom org-html-mathjax-template - " -" - "The MathJax setup for XHTML files." +}); + +" + "The MathJax template. See also `org-html-mathjax-options'." :group 'org-export-html :type 'string) @@ -1068,7 +1262,7 @@ You can also customize this for each buffer, using something like (defcustom org-html-postamble 'auto "Non-nil means insert a postamble in HTML export. -When set to 'auto, check against the +When set to `auto', check against the `org-export-with-author/email/creator/date' variables to set the content of the postamble. When set to a string, use this string as the postamble. When t, insert a string as defined by the @@ -1101,6 +1295,7 @@ The second element of each list is a format string to format the postamble itself. This format string can contain these elements: %t stands for the title. + %s stands for the subtitle. %a stands for the author's name. %e stands for the author's email. %d stands for the date. @@ -1165,6 +1360,7 @@ The second element of each list is a format string to format the preamble itself. This format string can contain these elements: %t stands for the title. + %s stands for the subtitle. %a stands for the author's name. %e stands for the author's email. %d stands for the date. @@ -1216,8 +1412,6 @@ ignored." ;;;; Template :: Scripts -(define-obsolete-variable-alias - 'org-html-style-include-scripts 'org-html-head-include-scripts "24.4") (defcustom org-html-head-include-scripts t "Non-nil means include the JavaScript snippets in exported HTML files. The actual script is defined in `org-html-scripts' and should @@ -1229,8 +1423,6 @@ not be modified." ;;;; Template :: Styles -(define-obsolete-variable-alias - 'org-html-style-include-default 'org-html-head-include-default-style "24.4") (defcustom org-html-head-include-default-style t "Non-nil means include the default style in exported HTML files. The actual style is defined in `org-html-style-default' and @@ -1243,7 +1435,6 @@ style information." ;;;###autoload (put 'org-html-head-include-default-style 'safe-local-variable 'booleanp) -(define-obsolete-variable-alias 'org-html-style 'org-html-head "24.4") (defcustom org-html-head "" "Org-wide head definitions for exported HTML files. @@ -1293,6 +1484,54 @@ or for publication projects using the :html-head-extra property." ;;;###autoload (put 'org-html-head-extra 'safe-local-variable 'stringp) +;;;; Template :: Viewport + +(defcustom org-html-viewport '((width "device-width") + (initial-scale "1") + (minimum-scale "") + (maximum-scale "") + (user-scalable "")) + "Viewport options for mobile-optimized sites. + +The following values are recognized + +width Size of the viewport. +initial-scale Zoom level when the page is first loaded. +minimum-scale Minimum allowed zoom level. +maximum-scale Maximum allowed zoom level. +user-scalable Whether zoom can be changed. + +The viewport meta tag is inserted if this variable is non-nil. + +See the following site for a reference: +https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag" + :group 'org-export-html + :version "26.1" + :package-version '(Org . "8.3") + :type '(choice (const :tag "Disable" nil) + (list :tag "Enable" + (list :tag "Width of viewport" + (const :format " " width) + (choice (const :tag "unset" "") + (string))) + (list :tag "Initial scale" + (const :format " " initial-scale) + (choice (const :tag "unset" "") + (string))) + (list :tag "Minimum scale/zoom" + (const :format " " minimum-scale) + (choice (const :tag "unset" "") + (string))) + (list :tag "Maximum scale/zoom" + (const :format " " maximum-scale) + (choice (const :tag "unset" "") + (string))) + (list :tag "User scalable/zoomable" + (const :format " " user-scalable) + (choice (const :tag "unset" "") + (const "true") + (const "false")))))) + ;;;; Todos (defcustom org-html-todo-kwd-class-prefix "" @@ -1315,22 +1554,33 @@ CSS classes, then this prefix can be very useful." (let ((dt (downcase (plist-get info :html-doctype)))) (member dt '("html5" "xhtml5" "")))) +(defun org-html--html5-fancy-p (info) + "Non-nil when exporting to HTML5 with fancy elements. +INFO is the current state of the export process, as a plist." + (and (plist-get info :html-html5-fancy) + (org-html-html5-p info))) + (defun org-html-close-tag (tag attr info) - (concat "<" tag " " attr + "Return close-tag for string TAG. +ATTR specifies additional attributes. INFO is a property list +containing current export state." + (concat "<" tag + (org-string-nw-p (concat " " attr)) (if (org-html-xhtml-p info) " />" ">"))) (defun org-html-doctype (info) - "Return correct html doctype tag from `org-html-doctype-alist', -or the literal value of :html-doctype from INFO if :html-doctype -is not found in the alist. -INFO is a plist used as a communication channel." + "Return correct HTML doctype tag. +INFO is a plist used as a communication channel. Doctype tag is +extracted from `org-html-doctype-alist', or the literal value +of :html-doctype from INFO if :html-doctype is not found in the +alist." (let ((dt (plist-get info :html-doctype))) (or (cdr (assoc dt org-html-doctype-alist)) dt))) (defun org-html--make-attribute-string (attributes) "Return a list of attributes, as a string. -ATTRIBUTES is a plist where values are either strings or nil. An -attributes with a nil value will be omitted from the result." +ATTRIBUTES is a plist where values are either strings or nil. An +attribute with a nil value will be omitted from the result." (let (output) (dolist (item attributes (mapconcat 'identity (nreverse output) " ")) (cond ((null item) (pop output)) @@ -1345,15 +1595,13 @@ attributes with a nil value will be omitted from the result." INFO is a plist used as a communication channel. When optional arguments CAPTION and LABEL are given, use them for caption and \"id\" attribute." - (let ((html5-fancy (and (org-html-html5-p info) - (plist-get info :html-html5-fancy)))) - (format (if html5-fancy "\n%s%s\n" - "\n%s%s\n") + (let ((html5-fancy (org-html--html5-fancy-p info))) + (format (if html5-fancy "\n\n%s%s\n" + "\n\n%s%s\n") ;; ID. - (if (not (org-string-nw-p label)) "" - (format " id=\"%s\"" (org-export-solidify-link-text label))) + (if (org-string-nw-p label) (format " id=\"%s\"" label) "") ;; Contents. - (format "\n

%s

" contents) + (if html5-fancy contents (format "

%s

" contents)) ;; Caption. (if (not (org-string-nw-p caption)) "" (format (if html5-fancy "\n
%s
" @@ -1366,17 +1614,42 @@ SOURCE is a string specifying the location of the image. ATTRIBUTES is a plist, as returned by `org-export-read-attribute'. INFO is a plist used as a communication channel." - (org-html-close-tag - "img" - (org-html--make-attribute-string - (org-combine-plists - (list :src source - :alt (if (string-match-p "^ltxpng/" source) - (org-html-encode-plain-text - (org-find-text-property-in-string 'org-latex-src source)) - (file-name-nondirectory source))) - attributes)) - info)) + (if (string= "svg" (file-name-extension source)) + (org-html--svg-image source attributes info) + (org-html-close-tag + "img" + (org-html--make-attribute-string + (org-combine-plists + (list :src source + :alt (if (string-match-p "^ltxpng/" source) + (org-html-encode-plain-text + (org-find-text-property-in-string 'org-latex-src source)) + (file-name-nondirectory source))) + attributes)) + info))) + +(defun org-html--svg-image (source attributes info) + "Return \"object\" embedding svg file SOURCE with given ATTRIBUTES. +INFO is a plist used as a communication channel. + +The special attribute \"fallback\" can be used to specify a +fallback image file to use if the object embedding is not +supported. CSS class \"org-svg\" is assigned as the class of the +object unless a different class is specified with an attribute." + (let ((fallback (plist-get attributes :fallback)) + (attrs (org-html--make-attribute-string + (org-combine-plists + ;; Remove fallback attribute, which is not meant to + ;; appear directly in the attributes string, and + ;; provide a default class if none is set. + '(:class "org-svg") attributes '(:fallback nil))))) + (format "\n%s" + source + attrs + (if fallback + (org-html-close-tag + "img" (format "src=\"%s\" %s" fallback attrs) info) + "Sorry, your browser does not support SVG.")))) (defun org-html--textarea-block (element) "Transcode ELEMENT into a textarea block. @@ -1388,7 +1661,7 @@ ELEMENT is either a src block or an example block." (or (plist-get attr :height) (org-count-lines code)) code))) -(defun org-html--has-caption-p (element &optional info) +(defun org-html--has-caption-p (element &optional _info) "Non-nil when ELEMENT has a caption affiliated keyword. INFO is a plist used as a communication channel. This function is meant to be used as a predicate for `org-export-get-ordinal' or @@ -1435,7 +1708,7 @@ produce code that uses these same face definitions." (when (and (symbolp f) (or (not i) (not (listp i)))) (insert (org-add-props (copy-sequence "1") nil 'face f)))) (htmlize-region (point-min) (point-max)))) - (org-pop-to-buffer-same-window "*html*") + (pop-to-buffer-same-window "*html*") (goto-char (point-min)) (if (re-search-forward "%s %s\n" - (format org-html-footnote-format - (let* ((id (format "fn.%s" n)) - (href (format " href=\"#fnr.%s\"" n)) - (attributes (concat " class=\"footnum\"" href))) - (org-html--anchor id n attributes))) - def))) + (replace-regexp-in-string "[^a-zA-Z0-9_]" "_" kwd nil t)) (defun org-html-footnote-section (info) "Format the footnote section. INFO is a plist used as a communication channel." - (let* ((fn-alist (org-export-collect-footnote-definitions - (plist-get info :parse-tree) info)) + (let* ((fn-alist (org-export-collect-footnote-definitions info)) (fn-alist - (loop for (n type raw) in fn-alist collect - (cons n (if (eq (org-element-type raw) 'org-data) - (org-trim (org-export-data raw info)) - (format "

%s

" - (org-trim (org-export-data raw info)))))))) + (cl-loop for (n _type raw) in fn-alist collect + (cons n (if (eq (org-element-type raw) 'org-data) + (org-trim (org-export-data raw info)) + (format "
%s
" + (org-trim (org-export-data raw info)))))))) (when fn-alist - (org-html-format-footnotes-section + (format + (plist-get info :html-footnotes-section) (org-html--translate "Footnotes" info) (format "\n%s\n" - (mapconcat 'org-html-format-footnote-definition fn-alist "\n")))))) + (mapconcat + (lambda (fn) + (let ((n (car fn)) (def (cdr fn))) + (format + "
%s %s
\n" + (format + (plist-get info :html-footnote-format) + (org-html--anchor + (format "fn.%d" n) + n + (format " class=\"footnum\" href=\"#fnr.%d\"" n) + info)) + def))) + fn-alist + "\n")))))) ;;; Template @@ -1529,37 +1787,52 @@ INFO is a plist used as a communication channel." 'mime-charset)) "iso-8859-1"))) (concat - (format "%s\n" title) (when (plist-get info :time-stamp-file) (format-time-string - (concat "\n"))) + (concat "\n"))) (format (if (org-html-html5-p info) - (org-html-close-tag "meta" " charset=\"%s\"" info) + (org-html-close-tag "meta" "charset=\"%s\"" info) (org-html-close-tag - "meta" " http-equiv=\"Content-Type\" content=\"text/html;charset=%s\"" + "meta" "http-equiv=\"Content-Type\" content=\"text/html;charset=%s\"" info)) charset) "\n" - (org-html-close-tag "meta" " name=\"generator\" content=\"Org-mode\"" info) + (let ((viewport-options + (cl-remove-if-not (lambda (cell) (org-string-nw-p (cadr cell))) + (plist-get info :html-viewport)))) + (and viewport-options + (concat + (org-html-close-tag + "meta" + (format "name=\"viewport\" content=\"%s\"" + (mapconcat + (lambda (elm) (format "%s=%s" (car elm) (cadr elm))) + viewport-options ", ")) + info) + "\n"))) + (format "%s\n" title) + (org-html-close-tag "meta" "name=\"generator\" content=\"Org mode\"" info) "\n" (and (org-string-nw-p author) (concat (org-html-close-tag "meta" - (format " name=\"author\" content=\"%s\"" + (format "name=\"author\" content=\"%s\"" (funcall protect-string author)) info) "\n")) (and (org-string-nw-p description) (concat (org-html-close-tag "meta" - (format " name=\"description\" content=\"%s\"\n" + (format "name=\"description\" content=\"%s\"\n" (funcall protect-string description)) info) "\n")) (and (org-string-nw-p keywords) (concat (org-html-close-tag "meta" - (format " name=\"keywords\" content=\"%s\"" + (format "name=\"keywords\" content=\"%s\"" (funcall protect-string keywords)) info) "\n"))))) @@ -1576,7 +1849,7 @@ INFO is a plist used as a communication channel." (when (and (plist-get info :html-htmlized-css-url) (eq org-html-htmlize-output-type 'css)) (org-html-close-tag "link" - (format " rel=\"stylesheet\" href=\"%s\" type=\"text/css\"" + (format "rel=\"stylesheet\" href=\"%s\" type=\"text/css\"" (plist-get info :html-htmlized-css-url)) info)) (when (plist-get info :html-head-include-scripts) org-html-scripts)))) @@ -1587,55 +1860,43 @@ INFO is a plist used as a communication channel." (when (and (memq (plist-get info :with-latex) '(mathjax t)) (org-element-map (plist-get info :parse-tree) '(latex-fragment latex-environment) 'identity info t)) - (let ((template org-html-mathjax-template) - (options org-html-mathjax-options) - (in-buffer (or (plist-get info :html-mathjax) "")) - name val (yes " ") (no "// ") x) - (mapc - (lambda (e) - (setq name (car e) val (nth 1 e)) - (if (string-match (concat "\\<" (symbol-name name) ":") in-buffer) - (setq val (car (read-from-string - (substring in-buffer (match-end 0)))))) - (if (not (stringp val)) (setq val (format "%s" val))) - (if (string-match (concat "%" (upcase (symbol-name name))) template) - (setq template (replace-match val t t template)))) - options) - (setq val (nth 1 (assq 'mathml options))) - (if (string-match (concat "\\%s" e e)) - (split-string (plist-get info :email) ",+ *") - ", ")) - (?c . ,(plist-get info :creator)) - (?C . ,(let ((file (plist-get info :input-file))) - (format-time-string org-html-metadata-timestamp-format - (if file (nth 5 (file-attributes file)))))) - (?v . ,(or org-html-validation-link "")))) + "Return format specification for preamble and postamble. +INFO is a plist used as a communication channel." + (let ((timestamp-format (plist-get info :html-metadata-timestamp-format))) + `((?t . ,(org-export-data (plist-get info :title) info)) + (?s . ,(org-export-data (plist-get info :subtitle) info)) + (?d . ,(org-export-data (org-export-get-date info timestamp-format) + info)) + (?T . ,(format-time-string timestamp-format)) + (?a . ,(org-export-data (plist-get info :author) info)) + (?e . ,(mapconcat + (lambda (e) (format "%s" e e)) + (split-string (plist-get info :email) ",+ *") + ", ")) + (?c . ,(plist-get info :creator)) + (?C . ,(let ((file (plist-get info :input-file))) + (format-time-string timestamp-format + (and file (nth 5 (file-attributes file)))))) + (?v . ,(or (plist-get info :html-validation-link) ""))))) (defun org-html--build-pre/postamble (type info) "Return document preamble or postamble as a string, or nil. -TYPE is either 'preamble or 'postamble, INFO is a plist used as a +TYPE is either `preamble' or `postamble', INFO is a plist used as a communication channel." (let ((section (plist-get info (intern (format ":html-%s" type)))) (spec (org-html-format-spec info))) @@ -1649,7 +1910,6 @@ communication channel." (author (cdr (assq ?a spec))) (email (cdr (assq ?e spec))) (creator (cdr (assq ?c spec))) - (timestamp (cdr (assq ?T spec))) (validation-link (cdr (assq ?v spec)))) (concat (when (and (plist-get info :with-date) @@ -1671,30 +1931,34 @@ communication channel." (format "

%s: %s

\n" (org-html--translate "Created" info) - (format-time-string org-html-metadata-timestamp-format))) + (format-time-string + (plist-get info :html-metadata-timestamp-format)))) (when (plist-get info :with-creator) (format "

%s

\n" creator)) (format "

%s

\n" validation-link)))) (t (format-spec - (or (cadr (assoc + (or (cadr (assoc-string (plist-get info :language) (eval (intern - (format "org-html-%s-format" type))))) + (format "org-html-%s-format" type))) + t)) (cadr - (assoc + (assoc-string "en" (eval - (intern (format "org-html-%s-format" type)))))) + (intern (format "org-html-%s-format" type))) + t))) spec)))))) - (when (org-string-nw-p section-contents) - (concat - (format "<%s id=\"%s\" class=\"%s\">\n" - (nth 1 (assq type org-html-divs)) - (nth 2 (assq type org-html-divs)) - org-html--pre/postamble-class) - (org-element-normalize-string section-contents) - (format "\n" (nth 1 (assq type org-html-divs))))))))) + (let ((div (assq type (plist-get info :html-divs)))) + (when (org-string-nw-p section-contents) + (concat + (format "<%s id=\"%s\" class=\"%s\">\n" + (nth 1 div) + (nth 2 div) + org-html--pre/postamble-class) + (org-element-normalize-string section-contents) + (format "\n" (nth 1 div))))))))) (defun org-html-inner-template (contents info) "Return body of document string after HTML conversion. @@ -1715,27 +1979,28 @@ CONTENTS is the transcoded contents string. INFO is a plist holding export options." (concat (when (and (not (org-html-html5-p info)) (org-html-xhtml-p info)) - (let ((decl (or (and (stringp org-html-xml-declaration) - org-html-xml-declaration) - (cdr (assoc (plist-get info :html-extension) - org-html-xml-declaration)) - (cdr (assoc "html" org-html-xml-declaration)) - - ""))) - (when (not (or (eq nil decl) (string= "" decl))) + (let* ((xml-declaration (plist-get info :html-xml-declaration)) + (decl (or (and (stringp xml-declaration) xml-declaration) + (cdr (assoc (plist-get info :html-extension) + xml-declaration)) + (cdr (assoc "html" xml-declaration)) + ""))) + (when (not (or (not decl) (string= "" decl))) (format "%s\n" (format decl - (or (and org-html-coding-system - (fboundp 'coding-system-get) - (coding-system-get org-html-coding-system 'mime-charset)) - "iso-8859-1")))))) + (or (and org-html-coding-system + (fboundp 'coding-system-get) + (coding-system-get org-html-coding-system 'mime-charset)) + "iso-8859-1")))))) (org-html-doctype info) "\n" (concat "\n") "\n" (org-html--build-meta-info info) @@ -1746,21 +2011,34 @@ holding export options." (let ((link-up (org-trim (plist-get info :html-link-up))) (link-home (org-trim (plist-get info :html-link-home)))) (unless (and (string= link-up "") (string= link-home "")) - (format org-html-home/up-format + (format (plist-get info :html-home/up-format) (or link-up link-home) (or link-home link-up)))) ;; Preamble. (org-html--build-pre/postamble 'preamble info) ;; Document contents. - (format "<%s id=\"%s\">\n" - (nth 1 (assq 'content org-html-divs)) - (nth 2 (assq 'content org-html-divs))) + (let ((div (assq 'content (plist-get info :html-divs)))) + (format "<%s id=\"%s\">\n" (nth 1 div) (nth 2 div))) ;; Document title. - (let ((title (plist-get info :title))) - (format "

%s

\n" (org-export-data (or title "") info))) + (when (plist-get info :with-title) + (let ((title (plist-get info :title)) + (subtitle (plist-get info :subtitle)) + (html5-fancy (org-html--html5-fancy-p info))) + (when title + (format + (if html5-fancy + "
\n

%s

\n%s
" + "

%s%s

\n") + (org-export-data title info) + (if subtitle + (format + (if html5-fancy + "

%s

\n" + "\n
\n%s\n") + (org-export-data subtitle info)) + ""))))) contents - (format "\n" - (nth 1 (assq 'content org-html-divs))) + (format "\n" (nth 1 (assq 'content (plist-get info :html-divs)))) ;; Postamble. (org-html--build-pre/postamble 'postamble info) ;; Closing document. @@ -1773,9 +2051,9 @@ INFO is a plist used as a communication channel." ;;;; Anchor -(defun org-html--anchor (&optional id desc attributes) +(defun org-html--anchor (id desc attributes info) "Format a HTML anchor." - (let* ((name (and org-html-allow-name-attribute-in-anchors id)) + (let* ((name (and (plist-get info :html-allow-name-attribute-in-anchors) id)) (attributes (concat (and id (format " id=\"%s\"" id)) (and name (format " name=\"%s\"" name)) attributes))) @@ -1783,43 +2061,38 @@ INFO is a plist used as a communication channel." ;;;; Todo -(defun org-html--todo (todo) +(defun org-html--todo (todo info) "Format TODO keywords into HTML." (when todo (format "%s" (if (member todo org-done-keywords) "done" "todo") - org-html-todo-kwd-class-prefix (org-html-fix-class-name todo) + (or (plist-get info :html-todo-kwd-class-prefix) "") + (org-html-fix-class-name todo) todo))) +;;;; Priority + +(defun org-html--priority (priority _info) + "Format a priority into HTML. +PRIORITY is the character code of the priority or nil. INFO is +a plist containing export options." + (and priority (format "[%c]" priority))) + ;;;; Tags -(defun org-html--tags (tags) - "Format TAGS into HTML." +(defun org-html--tags (tags info) + "Format TAGS into HTML. +INFO is a plist containing export options." (when tags (format "%s" (mapconcat (lambda (tag) (format "%s" - (concat org-html-tag-class-prefix + (concat (plist-get info :html-tag-class-prefix) (org-html-fix-class-name tag)) tag)) tags " ")))) -;;;; Headline - -(defun* org-html-format-headline - (todo todo-type priority text tags - &key level section-number headline-label &allow-other-keys) - "Format a headline in HTML." - (let ((section-number - (when section-number - (format "%s " - level section-number))) - (todo (org-html--todo todo)) - (tags (org-html--tags tags))) - (concat section-number todo (and todo " ") text - (and tags "   ") tags))) - ;;;; Src Code (defun org-html-fontify-code (code lang) @@ -1838,6 +2111,10 @@ is the language used for CODE, as a string, or nil." (message "Cannot fontify src block (htmlize.el >= 1.34 required)") ;; Simple transcoding. (org-html-encode-plain-text code)) + ;; Case 3: plain text explicitly set + ((not org-html-htmlize-output-type) + ;; Simple transcoding. + (org-html-encode-plain-text code)) (t ;; Map language (setq lang (or (assoc-default lang org-src-lang-modes) lang)) @@ -1850,25 +2127,30 @@ is the language used for CODE, as a string, or nil." ;; Case 2: Default. Fontify code. (t ;; htmlize - (setq code (with-temp-buffer - ;; Switch to language-specific mode. - (funcall lang-mode) - (insert code) - ;; Fontify buffer. - (org-font-lock-ensure) - ;; Remove formatting on newline characters. - (save-excursion - (let ((beg (point-min)) - (end (point-max))) - (goto-char beg) - (while (progn (end-of-line) (< (point) end)) - (put-text-property (point) (1+ (point)) 'face nil) - (forward-char 1)))) - (org-src-mode) - (set-buffer-modified-p nil) - ;; Htmlize region. - (org-html-htmlize-region-for-paste - (point-min) (point-max)))) + (setq code + (let ((output-type org-html-htmlize-output-type) + (font-prefix org-html-htmlize-font-prefix)) + (with-temp-buffer + ;; Switch to language-specific mode. + (funcall lang-mode) + (insert code) + ;; Fontify buffer. + (org-font-lock-ensure) + ;; Remove formatting on newline characters. + (save-excursion + (let ((beg (point-min)) + (end (point-max))) + (goto-char beg) + (while (progn (end-of-line) (< (point) end)) + (put-text-property (point) (1+ (point)) 'face nil) + (forward-char 1)))) + (org-src-mode) + (set-buffer-modified-p nil) + ;; Htmlize region. + (let ((org-html-htmlize-output-type output-type) + (org-html-htmlize-font-prefix font-prefix)) + (org-html-htmlize-region-for-paste + (point-min) (point-max)))))) ;; Strip any enclosing
 tags.
 	  (let* ((beg (and (string-match "\\`]*>\n*" code) (match-end 0)))
 		 (end (and beg (string-match "
\\'" code)))) @@ -1921,38 +2203,39 @@ a plist used as a communication channel." ;; Does the src block contain labels? (retain-labels (org-element-property :retain-labels element)) ;; Does it have line numbers? - (num-start (case (org-element-property :number-lines element) - (continued (org-export-get-loc element info)) - (new 0)))) + (num-start (org-export-get-loc element info))) (org-html-do-format-code code lang refs retain-labels num-start))) ;;; Tables of Contents -(defun org-html-toc (depth info) +(defun org-html-toc (depth info &optional scope) "Build a table of contents. -DEPTH is an integer specifying the depth of the table. INFO is a -plist used as a communication channel. Return the table of -contents as a string, or nil if it is empty." +DEPTH is an integer specifying the depth of the table. INFO is +a plist used as a communication channel. Optional argument SCOPE +is an element defining the scope of the table. Return the table +of contents as a string, or nil if it is empty." (let ((toc-entries (mapcar (lambda (headline) (cons (org-html--format-toc-headline headline info) (org-export-get-relative-level headline info))) - (org-export-collect-headlines info depth))) - (outer-tag (if (and (org-html-html5-p info) - (plist-get info :html-html5-fancy)) - "nav" - "div"))) + (org-export-collect-headlines info depth scope)))) (when toc-entries - (concat (format "<%s id=\"table-of-contents\">\n" outer-tag) - (format "%s\n" - org-html-toplevel-hlevel - (org-html--translate "Table of Contents" info) - org-html-toplevel-hlevel) - "
" - (org-html--toc-text toc-entries) - "
\n" - (format "\n" outer-tag))))) + (let ((toc (concat "
" + (org-html--toc-text toc-entries) + "
\n"))) + (if scope toc + (let ((outer-tag (if (org-html--html5-fancy-p info) + "nav" + "div"))) + (concat (format "<%s id=\"table-of-contents\">\n" outer-tag) + (let ((top-level (plist-get info :html-toplevel-hlevel))) + (format "%s\n" + top-level + (org-html--translate "Table of Contents" info) + top-level)) + toc + (format "\n" outer-tag)))))))) (defun org-html--toc-text (toc-entries) "Return innards of a table of contents, as a string. @@ -1967,8 +2250,7 @@ and value is its relative level, as an integer." (level (cdr entry))) (concat (let* ((cnt (- level prev-level)) - (times (if (> cnt 0) (1- cnt) (- cnt))) - rtn) + (times (if (> cnt 0) (1- cnt) (- cnt)))) (setq prev-level level) (concat (org-html--make-string @@ -2005,21 +2287,15 @@ INFO is a plist used as a communication channel." (org-export-get-tags headline info)))) (format "%s" ;; Label. - (org-export-solidify-link-text - (or (org-element-property :CUSTOM_ID headline) - (concat "sec-" - (mapconcat #'number-to-string headline-number "-")))) + (or (org-element-property :CUSTOM_ID headline) + (org-export-get-reference headline info)) ;; Body. (concat (and (not (org-export-low-level-p headline info)) (org-export-numbered-headline-p headline info) (concat (mapconcat #'number-to-string headline-number ".") ". ")) - (apply (if (not (eq org-html-format-headline-function 'ignore)) - (lambda (todo todo-type priority text tags &rest ignore) - (funcall org-html-format-headline-function - todo todo-type priority text tags)) - #'org-html-format-headline) + (apply (plist-get info :html-format-headline-function) todo todo-type priority text tags :section-number nil))))) (defun org-html-list-of-listings (info) @@ -2029,17 +2305,19 @@ of listings as a string, or nil if it is empty." (let ((lol-entries (org-export-collect-listings info))) (when lol-entries (concat "
\n" - (format "%s\n" - org-html-toplevel-hlevel - (org-html--translate "List of Listings" info) - org-html-toplevel-hlevel) + (let ((top-level (plist-get info :html-toplevel-hlevel))) + (format "%s\n" + top-level + (org-html--translate "List of Listings" info) + top-level)) "
\n
    \n" (let ((count 0) (initial-fmt (format "%s" (org-html--translate "Listing %d:" info)))) (mapconcat (lambda (entry) - (let ((label (org-element-property :name entry)) + (let ((label (and (org-element-property :name entry) + (org-export-get-reference entry info))) (title (org-trim (org-export-data (or (org-export-get-caption entry t) @@ -2048,10 +2326,12 @@ of listings as a string, or nil if it is empty." (concat "
  • " (if (not label) - (concat (format initial-fmt (incf count)) " " title) + (concat (format initial-fmt (cl-incf count)) + " " + title) (format "%s %s" - (org-export-solidify-link-text label) - (format initial-fmt (incf count)) + label + (format initial-fmt (cl-incf count)) title)) "
  • "))) lol-entries "\n")) @@ -2064,17 +2344,19 @@ of tables as a string, or nil if it is empty." (let ((lol-entries (org-export-collect-tables info))) (when lol-entries (concat "
    \n" - (format "%s\n" - org-html-toplevel-hlevel - (org-html--translate "List of Tables" info) - org-html-toplevel-hlevel) + (let ((top-level (plist-get info :html-toplevel-hlevel))) + (format "%s\n" + top-level + (org-html--translate "List of Tables" info) + top-level)) "
    \n
      \n" (let ((count 0) (initial-fmt (format "%s" (org-html--translate "Table %d:" info)))) (mapconcat (lambda (entry) - (let ((label (org-element-property :name entry)) + (let ((label (and (org-element-property :name entry) + (org-export-get-reference entry info))) (title (org-trim (org-export-data (or (org-export-get-caption entry t) @@ -2083,10 +2365,12 @@ of tables as a string, or nil if it is empty." (concat "
    • " (if (not label) - (concat (format initial-fmt (incf count)) " " title) + (concat (format initial-fmt (cl-incf count)) + " " + title) (format "%s %s" - (org-export-solidify-link-text label) - (format initial-fmt (incf count)) + label + (format initial-fmt (cl-incf count)) title)) "
    • "))) lol-entries "\n")) @@ -2097,24 +2381,24 @@ of tables as a string, or nil if it is empty." ;;;; Bold -(defun org-html-bold (bold contents info) +(defun org-html-bold (_bold contents info) "Transcode BOLD from Org to HTML. CONTENTS is the text with bold markup. INFO is a plist holding contextual information." - (format (or (cdr (assq 'bold org-html-text-markup-alist)) "%s") + (format (or (cdr (assq 'bold (plist-get info :html-text-markup-alist))) "%s") contents)) ;;;; Center Block -(defun org-html-center-block (center-block contents info) +(defun org-html-center-block (_center-block contents _info) "Transcode a CENTER-BLOCK element from Org to HTML. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." - (format "
      \n%s
      " contents)) + (format "
      \n%s
      " contents)) ;;;; Clock -(defun org-html-clock (clock contents info) +(defun org-html-clock (clock _contents _info) "Transcode a CLOCK element from Org to HTML. CONTENTS is nil. INFO is a plist used as a communication channel." @@ -2124,19 +2408,17 @@ channel."

      " org-clock-string - (org-translate-time - (org-element-property :raw-value - (org-element-property :value clock))) + (org-timestamp-translate (org-element-property :value clock)) (let ((time (org-element-property :duration clock))) (and time (format " (%s)" time))))) ;;;; Code -(defun org-html-code (code contents info) +(defun org-html-code (code _contents info) "Transcode CODE from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." - (format (or (cdr (assq 'code org-html-text-markup-alist)) "%s") + (format (or (cdr (assq 'code (plist-get info :html-text-markup-alist))) "%s") (org-html-encode-plain-text (org-element-property :value code)))) ;;;; Drawer @@ -2145,17 +2427,13 @@ information." "Transcode a DRAWER element from Org to HTML. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." - (if (functionp org-html-format-drawer-function) - (funcall org-html-format-drawer-function - (org-element-property :drawer-name drawer) - contents) - ;; If there's no user defined function: simply - ;; display contents of the drawer. - contents)) + (funcall (plist-get info :html-format-drawer-function) + (org-element-property :drawer-name drawer) + contents)) ;;;; Dynamic Block -(defun org-html-dynamic-block (dynamic-block contents info) +(defun org-html-dynamic-block (_dynamic-block contents _info) "Transcode a DYNAMIC-BLOCK element from Org to HTML. CONTENTS holds the contents of the block. INFO is a plist holding contextual information. See `org-export-data'." @@ -2163,7 +2441,7 @@ holding contextual information. See `org-export-data'." ;;;; Entity -(defun org-html-entity (entity contents info) +(defun org-html-entity (entity _contents _info) "Transcode an ENTITY object from Org to HTML. CONTENTS are the definition itself. INFO is a plist holding contextual information." @@ -2171,18 +2449,25 @@ contextual information." ;;;; Example Block -(defun org-html-example-block (example-block contents info) +(defun org-html-example-block (example-block _contents info) "Transcode a EXAMPLE-BLOCK element from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." - (if (org-export-read-attribute :attr_html example-block :textarea) - (org-html--textarea-block example-block) - (format "
      \n%s
      " - (org-html-format-code example-block info)))) + (let ((attributes (org-export-read-attribute :attr_html example-block))) + (if (plist-get attributes :textarea) + (org-html--textarea-block example-block) + (format "
      \n%s
      " + (let* ((name (org-element-property :name example-block)) + (a (org-html--make-attribute-string + (if (or (not name) (plist-member attributes :id)) + attributes + (plist-put attributes :id name))))) + (if (org-string-nw-p a) (concat " " a) "")) + (org-html-format-code example-block info))))) ;;;; Export Snippet -(defun org-html-export-snippet (export-snippet contents info) +(defun org-html-export-snippet (export-snippet _contents _info) "Transcode a EXPORT-SNIPPET object from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." @@ -2191,7 +2476,7 @@ information." ;;;; Export Block -(defun org-html-export-block (export-block contents info) +(defun org-html-export-block (export-block _contents _info) "Transcode a EXPORT-BLOCK element from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (when (string= (org-element-property :type export-block) "HTML") @@ -2199,7 +2484,7 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;;; Fixed Width -(defun org-html-fixed-width (fixed-width contents info) +(defun org-html-fixed-width (fixed-width _contents _info) "Transcode a FIXED-WIDTH element from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (format "
      \n%s
      " @@ -2209,135 +2494,116 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;;; Footnote Reference -(defun org-html-footnote-reference (footnote-reference contents info) +(defun org-html-footnote-reference (footnote-reference _contents info) "Transcode a FOOTNOTE-REFERENCE element from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (concat ;; Insert separator between two footnotes in a row. (let ((prev (org-export-get-previous-element footnote-reference info))) (when (eq (org-element-type prev) 'footnote-reference) - org-html-footnote-separator)) - (cond - ((not (org-export-footnote-first-reference-p footnote-reference info)) - (org-html-format-footnote-reference - (org-export-get-footnote-number footnote-reference info) - "IGNORED" 100)) - ;; Inline definitions are secondary strings. - ((eq (org-element-property :type footnote-reference) 'inline) - (org-html-format-footnote-reference - (org-export-get-footnote-number footnote-reference info) - "IGNORED" 1)) - ;; Non-inline footnotes definitions are full Org data. - (t (org-html-format-footnote-reference - (org-export-get-footnote-number footnote-reference info) - "IGNORED" 1))))) + (plist-get info :html-footnote-separator))) + (let* ((n (org-export-get-footnote-number footnote-reference info)) + (id (format "fnr.%d%s" + n + (if (org-export-footnote-first-reference-p + footnote-reference info) + "" + ".100")))) + (format + (plist-get info :html-footnote-format) + (org-html--anchor + id n (format " class=\"footref\" href=\"#fn.%d\"" n) info))))) ;;;; Headline -(defun org-html-format-headline--wrap - (headline info &optional format-function &rest extra-keys) - "Transcode a HEADLINE element from Org to HTML. -CONTENTS holds the contents of the headline. INFO is a plist -holding contextual information." - (let* ((level (+ (org-export-get-relative-level headline info) - (1- org-html-toplevel-hlevel))) - (headline-number (org-export-get-headline-number headline info)) - (section-number (and (not (org-export-low-level-p headline info)) - (org-export-numbered-headline-p headline info) - (mapconcat 'number-to-string - headline-number "."))) - (todo (and (plist-get info :with-todo-keywords) - (let ((todo (org-element-property :todo-keyword headline))) - (and todo (org-export-data todo info))))) - (todo-type (and todo (org-element-property :todo-type headline))) - (priority (and (plist-get info :with-priority) - (org-element-property :priority headline))) - (text (org-export-data (org-element-property :title headline) info)) - (tags (and (plist-get info :with-tags) - (org-export-get-tags headline info))) - (headline-label (or (org-element-property :CUSTOM_ID headline) - (concat "sec-" (mapconcat 'number-to-string - headline-number "-")))) - (format-function - (cond ((functionp format-function) format-function) - ((not (eq org-html-format-headline-function 'ignore)) - (lambda (todo todo-type priority text tags &rest ignore) - (funcall org-html-format-headline-function - todo todo-type priority text tags))) - (t 'org-html-format-headline)))) - (apply format-function - todo todo-type priority text tags - :headline-label headline-label :level level - :section-number section-number extra-keys))) - (defun org-html-headline (headline contents info) "Transcode a HEADLINE element from Org to HTML. CONTENTS holds the contents of the headline. INFO is a plist holding contextual information." (unless (org-element-property :footnote-section-p headline) - (let* ((contents (or contents "")) - (numberedp (org-export-numbered-headline-p headline info)) - (level (org-export-get-relative-level headline info)) - (text (org-export-data (org-element-property :title headline) info)) - (todo (and (plist-get info :with-todo-keywords) - (let ((todo (org-element-property :todo-keyword headline))) - (and todo (org-export-data todo info))))) - (todo-type (and todo (org-element-property :todo-type headline))) - (tags (and (plist-get info :with-tags) - (org-export-get-tags headline info))) - (priority (and (plist-get info :with-priority) - (org-element-property :priority headline))) - (section-number (mapconcat #'number-to-string - (org-export-get-headline-number - headline info) "-")) - (ids (delq 'nil - (list (org-element-property :CUSTOM_ID headline) - (concat "sec-" section-number) - (org-element-property :ID headline)))) - (preferred-id (car ids)) - (extra-ids (mapconcat - (lambda (id) - (org-html--anchor - (org-export-solidify-link-text - (if (org-uuidgen-p id) (concat "ID-" id) id)))) - (cdr ids) "")) - ;; Create the headline text. - (full-text (org-html-format-headline--wrap headline info))) + (let* ((numberedp (org-export-numbered-headline-p headline info)) + (numbers (org-export-get-headline-number headline info)) + (level (+ (org-export-get-relative-level headline info) + (1- (plist-get info :html-toplevel-hlevel)))) + (todo (and (plist-get info :with-todo-keywords) + (let ((todo (org-element-property :todo-keyword headline))) + (and todo (org-export-data todo info))))) + (todo-type (and todo (org-element-property :todo-type headline))) + (priority (and (plist-get info :with-priority) + (org-element-property :priority headline))) + (text (org-export-data (org-element-property :title headline) info)) + (tags (and (plist-get info :with-tags) + (org-export-get-tags headline info))) + (full-text (funcall (plist-get info :html-format-headline-function) + todo todo-type priority text tags info)) + (contents (or contents "")) + (ids (delq nil + (list (org-element-property :CUSTOM_ID headline) + (org-export-get-reference headline info) + (org-element-property :ID headline)))) + (preferred-id (car ids)) + (extra-ids + (mapconcat + (lambda (id) + (org-html--anchor + (if (org-uuidgen-p id) (concat "ID-" id) id) + nil nil info)) + (cdr ids) ""))) (if (org-export-low-level-p headline info) - ;; This is a deep sub-tree: export it as a list item. - (let* ((type (if numberedp 'ordered 'unordered)) - (itemized-body - (org-html-format-list-item - contents type nil info nil - (concat (org-html--anchor preferred-id) extra-ids - full-text)))) - (concat - (and (org-export-first-sibling-p headline info) - (org-html-begin-plain-list type)) - itemized-body - (and (org-export-last-sibling-p headline info) - (org-html-end-plain-list type)))) - ;; Standard headline. Export it as a section. - (let ((extra-class (org-element-property :HTML_CONTAINER_CLASS headline)) - (level1 (+ level (1- org-html-toplevel-hlevel))) - (first-content (car (org-element-contents headline)))) - (format "<%s id=\"%s\" class=\"%s\">%s%s\n" - (org-html--container headline info) - (format "outline-container-%s" - (or (org-element-property :CUSTOM_ID headline) - (concat "sec-" section-number))) - (concat (format "outline-%d" level1) (and extra-class " ") - extra-class) - (format "\n%s%s\n" - level1 preferred-id extra-ids full-text level1) - ;; When there is no section, pretend there is an - ;; empty one to get the correct
      %s%s\n" + (org-html--container headline info) + (concat "outline-container-" + (org-export-get-reference headline info)) + (concat (format "outline-%d" level) + (and extra-class " ") + extra-class) + (format "\n%s%s\n" + level + preferred-id + extra-ids + (concat + (and numberedp + (format + "%s " + level + (mapconcat #'number-to-string numbers "."))) + full-text) + level) + ;; When there is no section, pretend there is an + ;; empty one to get the correct
      %s" lang label code))) ;;;; Inlinetask -(defun org-html-format-section (text class &optional id) - "Format a section with TEXT into a HTML div with CLASS and ID." - (let ((extra (concat (when id (format " id=\"%s\"" id))))) - (concat (format "
      \n" class extra) text "
      \n"))) - (defun org-html-inlinetask (inlinetask contents info) "Transcode an INLINETASK element from Org to HTML. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." - (cond - ;; If `org-html-format-inlinetask-function' is not 'ignore, call it - ;; with appropriate arguments. - ((not (eq org-html-format-inlinetask-function 'ignore)) - (let ((format-function - (function* - (lambda (todo todo-type priority text tags - &key contents &allow-other-keys) - (funcall org-html-format-inlinetask-function - todo todo-type priority text tags contents))))) - (org-html-format-headline--wrap - inlinetask info format-function :contents contents))) - ;; Otherwise, use a default template. - (t (format "
      \n%s%s\n%s
      " - (org-html-format-headline--wrap inlinetask info) - (org-html-close-tag "br" nil info) - contents)))) + (let* ((todo (and (plist-get info :with-todo-keywords) + (let ((todo (org-element-property :todo-keyword inlinetask))) + (and todo (org-export-data todo info))))) + (todo-type (and todo (org-element-property :todo-type inlinetask))) + (priority (and (plist-get info :with-priority) + (org-element-property :priority inlinetask))) + (text (org-export-data (org-element-property :title inlinetask) info)) + (tags (and (plist-get info :with-tags) + (org-export-get-tags inlinetask info)))) + (funcall (plist-get info :html-format-inlinetask-function) + todo todo-type priority text tags contents info))) + +(defun org-html-format-inlinetask-default-function + (todo todo-type priority text tags contents info) + "Default format function for a inlinetasks. +See `org-html-format-inlinetask-function' for details." + (format "
      \n%s%s\n%s
      " + (org-html-format-headline-default-function + todo todo-type priority text tags info) + (org-html-close-tag "br" nil info) + contents)) ;;;; Italic -(defun org-html-italic (italic contents info) +(defun org-html-italic (_italic contents info) "Transcode ITALIC from Org to HTML. CONTENTS is the text with italic markup. INFO is a plist holding contextual information." - (format (or (cdr (assq 'italic org-html-text-markup-alist)) "%s") contents)) + (format + (or (cdr (assq 'italic (plist-get info :html-text-markup-alist))) "%s") + contents)) ;;;; Item -(defun org-html-checkbox (checkbox) - "Format CHECKBOX into HTML." - (case checkbox (on "[X]") - (off "[ ]") - (trans "[-]") - (t ""))) +(defun org-html-checkbox (checkbox info) + "Format CHECKBOX into HTML. +INFO is a plist holding contextual information. See +`org-html-checkbox-type' for customization options." + (cdr (assq checkbox + (cdr (assq (plist-get info :html-checkbox-type) + org-html-checkbox-types))))) (defun org-html-format-list-item (contents type checkbox info - &optional term-counter-id - headline) + &optional term-counter-id + headline) "Format a list item into HTML." - (let ((checkbox (concat (org-html-checkbox checkbox) (and checkbox " "))) + (let ((class (if checkbox + (format " class=\"%s\"" + (symbol-name checkbox)) "")) + (checkbox (concat (org-html-checkbox checkbox info) + (and checkbox " "))) (br (org-html-close-tag "br" nil info))) (concat - (case type - (ordered + (pcase type + (`ordered (let* ((counter term-counter-id) (extra (if counter (format " value=\"%s\"" counter) ""))) (concat - (format "" extra) + (format "" class extra) (when headline (concat headline br))))) - (unordered + (`unordered (let* ((id term-counter-id) (extra (if id (format " id=\"%s\"" id) ""))) (concat - (format "" extra) + (format "" class extra) (when headline (concat headline br))))) - (descriptive + (`descriptive (let* ((term term-counter-id)) (setq term (or term "(no term)")) ;; Check-boxes in descriptive lists are associated to tag. - (concat (format "
      %s
      " - (concat checkbox term)) + (concat (format "%s" + class (concat checkbox term)) "
      ")))) (unless (eq type 'descriptive) checkbox) - contents - (case type - (ordered "") - (unordered "") - (descriptive "
      "))))) + (and contents (org-trim contents)) + (pcase type + (`ordered "") + (`unordered "") + (`descriptive ""))))) (defun org-html-item (item contents info) "Transcode an ITEM element from Org to HTML. @@ -2457,7 +2735,7 @@ contextual information." ;;;; Keyword -(defun org-html-keyword (keyword contents info) +(defun org-html-keyword (keyword _contents info) "Transcode a KEYWORD element from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let ((key (org-element-property :key keyword)) @@ -2465,13 +2743,13 @@ CONTENTS is nil. INFO is a plist holding contextual information." (cond ((string= key "HTML") value) ((string= key "TOC") - (let ((value (downcase value))) + (let ((case-fold-search t)) (cond ((string-match "\\" value) - (let ((depth (or (and (string-match "[0-9]+" value) - (string-to-number (match-string 0 value))) - (plist-get info :with-toc)))) - (org-html-toc depth info))) + (let ((depth (and (string-match "\\<[0-9]+\\>" value) + (string-to-number (match-string 0 value)))) + (localp (string-match-p "\\" value))) + (org-html-toc depth info (and localp keyword)))) ((string= "listings" value) (org-html-list-of-listings info)) ((string= "tables" value) (org-html-list-of-tables info)))))))) @@ -2479,10 +2757,11 @@ CONTENTS is nil. INFO is a plist holding contextual information." (defun org-html-format-latex (latex-frag processing-type info) "Format a LaTeX fragment LATEX-FRAG into HTML. -PROCESSING-TYPE designates the tool used for conversion. It is -a symbol among `mathjax', `dvipng', `imagemagick', `verbatim' nil -and t. See `org-html-with-latex' for more information. INFO is -a plist containing export properties." +PROCESSING-TYPE designates the tool used for conversion. It can +be `mathjax', `verbatim', nil, t or symbols in +`org-preview-latex-process-alist', e.g., `dvipng', `dvisvgm' or +`imagemagick'. See `org-html-with-latex' for more information. +INFO is a plist containing export properties." (let ((cache-relpath "") (cache-dir "")) (unless (eq processing-type 'mathjax) (let ((bfn (or (buffer-file-name) @@ -2497,7 +2776,7 @@ a plist containing export properties." "\n") "\n"))))) (setq cache-relpath - (concat "ltxpng/" + (concat (file-name-as-directory org-preview-latex-image-directory) (file-name-sans-extension (file-name-nondirectory bfn))) cache-dir (file-name-directory bfn)) @@ -2507,51 +2786,51 @@ a plist containing export properties." (setq latex-frag (concat latex-header latex-frag)))) (with-temp-buffer (insert latex-frag) - (org-format-latex cache-relpath cache-dir nil "Creating LaTeX Image..." - nil nil processing-type) + (org-format-latex cache-relpath nil nil cache-dir nil + "Creating LaTeX Image..." nil processing-type) (buffer-string)))) -(defun org-html-latex-environment (latex-environment contents info) +(defun org-html-latex-environment (latex-environment _contents info) "Transcode a LATEX-ENVIRONMENT element from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let ((processing-type (plist-get info :with-latex)) (latex-frag (org-remove-indentation (org-element-property :value latex-environment))) (attributes (org-export-read-attribute :attr_html latex-environment))) - (case processing-type - ((t mathjax) - (org-html-format-latex latex-frag 'mathjax info)) - ((dvipng imagemagick) - (let ((formula-link - (org-html-format-latex latex-frag processing-type info))) - (when (and formula-link (string-match "file:\\([^]]*\\)" formula-link)) - ;; Do not provide a caption or a name to be consistent with - ;; `mathjax' handling. - (org-html--wrap-image - (org-html--format-image - (match-string 1 formula-link) attributes info) info)))) - (t latex-frag)))) + (cond + ((memq processing-type '(t mathjax)) + (org-html-format-latex latex-frag 'mathjax info)) + ((assq processing-type org-preview-latex-process-alist) + (let ((formula-link + (org-html-format-latex latex-frag processing-type info))) + (when (and formula-link (string-match "file:\\([^]]*\\)" formula-link)) + ;; Do not provide a caption or a name to be consistent with + ;; `mathjax' handling. + (org-html--wrap-image + (org-html--format-image + (match-string 1 formula-link) attributes info) info)))) + (t latex-frag)))) ;;;; Latex Fragment -(defun org-html-latex-fragment (latex-fragment contents info) +(defun org-html-latex-fragment (latex-fragment _contents info) "Transcode a LATEX-FRAGMENT object from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let ((latex-frag (org-element-property :value latex-fragment)) (processing-type (plist-get info :with-latex))) - (case processing-type - ((t mathjax) - (org-html-format-latex latex-frag 'mathjax info)) - ((dvipng imagemagick) - (let ((formula-link - (org-html-format-latex latex-frag processing-type info))) - (when (and formula-link (string-match "file:\\([^]]*\\)" formula-link)) - (org-html--format-image (match-string 1 formula-link) nil info)))) - (t latex-frag)))) + (cond + ((memq processing-type '(t mathjax)) + (org-html-format-latex latex-frag 'mathjax info)) + ((assq processing-type org-preview-latex-process-alist) + (let ((formula-link + (org-html-format-latex latex-frag processing-type info))) + (when (and formula-link (string-match "file:\\([^]]*\\)" formula-link)) + (org-html--format-image (match-string 1 formula-link) nil info)))) + (t latex-frag)))) ;;;; Line Break -(defun org-html-line-break (line-break contents info) +(defun org-html-line-break (_line-break _contents info) "Transcode a LINE-BREAK object from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (concat (org-html-close-tag "br" nil info) "\n")) @@ -2565,19 +2844,20 @@ inline image when it has no description and targets an image file (see `org-html-inline-image-rules' for more information), or if its description is a single link targeting an image file." (if (not (org-element-contents link)) - (org-export-inline-image-p link org-html-inline-image-rules) + (org-export-inline-image-p + link (plist-get info :html-inline-image-rules)) (not (let ((link-count 0)) (org-element-map (org-element-contents link) (cons 'plain-text org-element-all-objects) (lambda (obj) - (case (org-element-type obj) - (plain-text (org-string-nw-p obj)) - (link (if (= link-count 1) t - (incf link-count) - (not (org-export-inline-image-p - obj org-html-inline-image-rules)))) - (otherwise t))) + (pcase (org-element-type obj) + (`plain-text (org-string-nw-p obj)) + (`link (if (= link-count 1) t + (cl-incf link-count) + (not (org-export-inline-image-p + obj (plist-get info :html-inline-image-rules))))) + (_ t))) info t))))) (defvar org-html-standalone-image-predicate) @@ -2599,9 +2879,9 @@ further. For example, to check for only captioned standalone images, set it to: (lambda (paragraph) (org-element-property :caption paragraph))" - (let ((paragraph (case (org-element-type element) - (paragraph element) - (link (org-export-get-parent element))))) + (let ((paragraph (pcase (org-element-type element) + (`paragraph element) + (`link (org-export-get-parent element))))) (and (eq (org-element-type paragraph) 'paragraph) (or (not (fboundp 'org-html-standalone-image-predicate)) (funcall org-html-standalone-image-predicate paragraph)) @@ -2609,19 +2889,18 @@ images, set it to: (let ((link-count 0)) (org-element-map (org-element-contents paragraph) (cons 'plain-text org-element-all-objects) - #'(lambda (obj) - (when (case (org-element-type obj) - (plain-text (org-string-nw-p obj)) - (link (or (> (incf link-count) 1) - (not (org-html-inline-image-p obj info)))) - (otherwise t)) - (throw 'exit nil))) + (lambda (obj) + (when (pcase (org-element-type obj) + (`plain-text (org-string-nw-p obj)) + (`link (or (> (cl-incf link-count) 1) + (not (org-html-inline-image-p obj info)))) + (_ t)) + (throw 'exit nil))) info nil 'link) (= link-count 1)))))) (defun org-html-link (link desc info) "Transcode a LINK object from Org to HTML. - DESC is the description part of the link, or the empty string. INFO is a plist holding contextual information. See `org-export-data'." @@ -2629,56 +2908,49 @@ INFO is a plist holding contextual information. See (org-trim (plist-get info :html-link-home)))) (use-abs-url (plist-get info :html-link-use-abs-url)) (link-org-files-as-html-maybe - (function - (lambda (raw-path info) - "Treat links to `file.org' as links to `file.html', if needed. - See `org-html-link-org-files-as-html'." - (cond - ((and org-html-link-org-files-as-html - (string= ".org" - (downcase (file-name-extension raw-path ".")))) - (concat (file-name-sans-extension raw-path) "." - (plist-get info :html-extension))) - (t raw-path))))) + (lambda (raw-path info) + ;; Treat links to `file.org' as links to `file.html', if + ;; needed. See `org-html-link-org-files-as-html'. + (cond + ((and (plist-get info :html-link-org-files-as-html) + (string= ".org" + (downcase (file-name-extension raw-path ".")))) + (concat (file-name-sans-extension raw-path) "." + (plist-get info :html-extension))) + (t raw-path)))) (type (org-element-property :type link)) (raw-path (org-element-property :path link)) ;; Ensure DESC really exists, or set it to nil. (desc (org-string-nw-p desc)) (path (cond - ((member type '("http" "https" "ftp" "mailto")) - (org-link-escape - (org-link-unescape - (concat type ":" raw-path)) org-link-escape-chars-browser)) + ((member type '("http" "https" "ftp" "mailto" "news")) + (url-encode-url (org-link-unescape (concat type ":" raw-path)))) ((string= type "file") ;; Treat links to ".org" files as ".html", if needed. (setq raw-path (funcall link-org-files-as-html-maybe raw-path info)) ;; If file path is absolute, prepend it with protocol - ;; component - "file:". + ;; component - "file://". (cond ((file-name-absolute-p raw-path) - (setq raw-path (concat "file:" raw-path))) + (setq raw-path (org-export-file-uri raw-path))) ((and home use-abs-url) (setq raw-path (concat (file-name-as-directory home) raw-path)))) ;; Add search option, if any. A search option can be - ;; relative to a custom-id or a headline title. Any other - ;; option is ignored. + ;; relative to a custom-id, a headline title, a name or + ;; a target. (let ((option (org-element-property :search-option link))) (cond ((not option) raw-path) - ((eq (aref option 0) ?#) (concat raw-path option)) - ;; External fuzzy link: try to resolve it if path - ;; belongs to current project, if any. - ((eq (aref option 0) ?*) - (concat - raw-path - (let ((numbers - (org-publish-resolve-external-fuzzy-link - (org-element-property :path link) option))) - (and numbers (concat "#sec-" - (mapconcat 'number-to-string - numbers "-")))))) - (t raw-path)))) + ;; Since HTML back-end use custom-id value as-is, + ;; resolving is them is trivial. + ((eq (string-to-char option) ?#) (concat raw-path option)) + (t + (concat raw-path + "#" + (org-publish-resolve-external-link + option + (org-element-property :path link))))))) (t raw-path))) ;; Extract attributes from parent's paragraph. HACK: Only do ;; this for the first link in parent (inner image link for @@ -2695,12 +2967,14 @@ INFO is a plist holding contextual information. See (org-export-read-attribute :attr_html parent)))) (attributes (let ((attr (org-html--make-attribute-string attributes-plist))) - (if (org-string-nw-p attr) (concat " " attr) ""))) - protocol) + (if (org-string-nw-p attr) (concat " " attr) "")))) (cond + ;; Link type is handled by a special function. + ((org-export-custom-protocol-maybe link desc 'html)) ;; Image file. - ((and org-html-inline-images - (org-export-inline-image-p link org-html-inline-image-rules)) + ((and (plist-get info :html-inline-images) + (org-export-inline-image-p + link (plist-get info :html-inline-image-rules))) (org-html--format-image path attributes-plist info)) ;; Radio target: Transcode target's contents and use them as ;; link's description. @@ -2708,18 +2982,18 @@ INFO is a plist holding contextual information. See (let ((destination (org-export-resolve-radio-link link info))) (if (not destination) desc (format "%s" - (org-export-solidify-link-text - (org-element-property :value destination)) - attributes desc)))) + (org-export-get-reference destination info) + attributes + desc)))) ;; Links pointing to a headline: Find destination and build ;; appropriate referencing command. ((member type '("custom-id" "fuzzy" "id")) (let ((destination (if (string= type "fuzzy") (org-export-resolve-fuzzy-link link info) (org-export-resolve-id-link link info)))) - (case (org-element-type destination) + (pcase (org-element-type destination) ;; ID link points to an external file. - (plain-text + (`plain-text (let ((fragment (concat "ID-" path)) ;; Treat links to ".org" files as ".html", if needed. (path (funcall link-org-files-as-html-maybe @@ -2727,86 +3001,87 @@ INFO is a plist holding contextual information. See (format "%s" path fragment attributes (or desc destination)))) ;; Fuzzy link points nowhere. - ((nil) + (`nil (format "%s" (or desc (org-export-data (org-element-property :raw-link link) info)))) ;; Link points to a headline. - (headline - (let ((href - ;; What href to use? - (cond - ;; Case 1: Headline is linked via it's CUSTOM_ID - ;; property. Use CUSTOM_ID. - ((string= type "custom-id") - (org-element-property :CUSTOM_ID destination)) - ;; Case 2: Headline is linked via it's ID property - ;; or through other means. Use the default href. - ((member type '("id" "fuzzy")) - (format "sec-%s" - (mapconcat 'number-to-string - (org-export-get-headline-number - destination info) "-"))) - (t (error "Shouldn't reach here")))) + (`headline + (let ((href (or (org-element-property :CUSTOM_ID destination) + (org-export-get-reference destination info))) ;; What description to use? (desc ;; Case 1: Headline is numbered and LINK has no ;; description. Display section number. (if (and (org-export-numbered-headline-p destination info) (not desc)) - (mapconcat 'number-to-string + (mapconcat #'number-to-string (org-export-get-headline-number destination info) ".") ;; Case 2: Either the headline is un-numbered or ;; LINK has a custom description. Display LINK's ;; description or headline's title. - (or desc (org-export-data (org-element-property - :title destination) info))))) - (format "%s" - (org-export-solidify-link-text href) attributes desc))) + (or desc + (org-export-data + (org-element-property :title destination) info))))) + (format "%s" href attributes desc))) ;; Fuzzy link points to a target or an element. - (t - (let* ((path (org-export-solidify-link-text path)) - (org-html-standalone-image-predicate 'org-html--has-caption-p) + (_ + (let* ((ref (org-export-get-reference destination info)) + (org-html-standalone-image-predicate + #'org-html--has-caption-p) (number (cond (desc nil) ((org-html-standalone-image-p destination info) (org-export-get-ordinal (org-element-map destination 'link - 'identity info t) + #'identity info t) info 'link 'org-html-standalone-image-p)) (t (org-export-get-ordinal destination info nil 'org-html--has-caption-p)))) (desc (cond (desc) ((not number) "No description for this link") ((numberp number) (number-to-string number)) - (t (mapconcat 'number-to-string number "."))))) - (format "%s" path attributes desc)))))) + (t (mapconcat #'number-to-string number "."))))) + (format "%s" ref attributes desc)))))) ;; Coderef: replace link with the reference name or the ;; equivalent line number. ((string= type "coderef") - (let ((fragment (concat "coderef-" path))) - (format "%s" + (let ((fragment (concat "coderef-" (org-html-encode-plain-text path)))) + (format "%s" fragment - (org-trim - (format (concat "class=\"coderef\"" - " onmouseover=\"CodeHighlightOn(this, '%s');\"" - " onmouseout=\"CodeHighlightOff(this, '%s');\"") - fragment fragment)) + (format "class=\"coderef\" onmouseover=\"CodeHighlightOn(this, \ +'%s');\" onmouseout=\"CodeHighlightOff(this, '%s');\"" + fragment fragment) attributes (format (org-export-get-coderef-format path desc) (org-export-resolve-coderef path info))))) - ;; Link type is handled by a special function. - ((functionp (setq protocol (nth 2 (assoc type org-link-protocols)))) - (funcall protocol (org-link-unescape path) desc 'html)) ;; External link with a description part. - ((and path desc) (format "%s" path attributes desc)) + ((and path desc) (format "%s" + (org-html-encode-plain-text path) + attributes + desc)) ;; External link without a description part. - (path (format "%s" path attributes path)) + (path (let ((path (org-html-encode-plain-text path))) + (format "%s" + path + attributes + (org-link-unescape path)))) ;; No path, only description. Try to do something useful. (t (format "%s" desc))))) +;;;; Node Property + +(defun org-html-node-property (node-property _contents _info) + "Transcode a NODE-PROPERTY element from Org to HTML. +CONTENTS is nil. INFO is a plist holding contextual +information." + (format "%s:%s" + (org-element-property :key node-property) + (let ((value (org-element-property :value node-property))) + (if value (concat " " value) "")))) + ;;;; Paragraph (defun org-html-paragraph (paragraph contents info) @@ -2815,13 +3090,19 @@ CONTENTS is the contents of the paragraph, as a string. INFO is the plist used as a communication channel." (let* ((parent (org-export-get-parent paragraph)) (parent-type (org-element-type parent)) - (style '((footnote-definition " class=\"footpara\""))) - (extra (or (cadr (assoc parent-type style)) ""))) + (style '((footnote-definition " class=\"footpara\"") + (org-data " class=\"footpara\""))) + (attributes (org-html--make-attribute-string + (org-export-read-attribute :attr_html paragraph))) + (extra (or (cadr (assq parent-type style)) ""))) (cond - ((and (eq (org-element-type parent) 'item) - (= (org-element-property :begin paragraph) - (org-element-property :contents-begin parent))) - ;; Leading paragraph in a list item have no tags. + ((and (eq parent-type 'item) + (not (org-export-get-previous-element paragraph info)) + (let ((followers (org-export-get-next-element paragraph info 2))) + (and (not (cdr followers)) + (memq (org-element-type (car followers)) '(nil plain-list))))) + ;; First paragraph in an item has no tag if it is alone or + ;; followed, at most, by a sub-list. contents) ((org-html-standalone-image-p paragraph info) ;; Standalone image. @@ -2829,20 +3110,24 @@ the plist used as a communication channel." (let ((raw (org-export-data (org-export-get-caption paragraph) info)) (org-html-standalone-image-predicate - 'org-html--has-caption-p)) + #'org-html--has-caption-p)) (if (not (org-string-nw-p raw)) raw - (concat - "" - (format (org-html--translate "Figure %d:" info) - (org-export-get-ordinal - (org-element-map paragraph 'link - 'identity info t) - info nil 'org-html-standalone-image-p)) - " " raw)))) - (label (org-element-property :name paragraph))) + (concat "" + (format (org-html--translate "Figure %d:" info) + (org-export-get-ordinal + (org-element-map paragraph 'link + #'identity info t) + info nil #'org-html-standalone-image-p)) + " " + raw)))) + (label (and (org-element-property :name paragraph) + (org-export-get-reference paragraph info)))) (org-html--wrap-image contents info caption label))) ;; Regular paragraph. - (t (format "\n%s

      " extra contents))))) + (t (format "\n%s

      " + (if (org-string-nw-p attributes) + (concat " " attributes) "") + extra contents))))) ;;;; Plain List @@ -2852,26 +3137,25 @@ the plist used as a communication channel." "Insert the beginning of the HTML list depending on TYPE. When ARG1 is a string, use it as the start parameter for ordered lists." - (case type - (ordered + (pcase type + (`ordered (format "
        " (if arg1 (format " start=\"%d\"" arg1) ""))) - (unordered "
          ") - (descriptive "
          "))) + (`unordered "
            ") + (`descriptive "
            "))) (defun org-html-end-plain-list (type) "Insert the end of the HTML list depending on TYPE." - (case type - (ordered "
      ") - (unordered "
    ") - (descriptive ""))) + (pcase type + (`ordered "") + (`unordered "
") + (`descriptive ""))) -(defun org-html-plain-list (plain-list contents info) +(defun org-html-plain-list (plain-list contents _info) "Transcode a PLAIN-LIST element from Org to HTML. CONTENTS is the contents of the list. INFO is a plist holding contextual information." - (let* (arg1 ;; (assoc :counter (org-element-map plain-list 'item - (type (org-element-property :type plain-list))) + (let ((type (org-element-property :type plain-list))) (format "%s\n%s%s" (org-html-begin-plain-list type) contents (org-html-end-plain-list type)))) @@ -2880,22 +3164,16 @@ contextual information." (defun org-html-convert-special-strings (string) "Convert special characters in STRING to HTML." - (let ((all org-html-special-string-regexps) - e a re rpl start) - (while (setq a (pop all)) - (setq re (car a) rpl (cdr a) start 0) - (while (string-match re string start) - (setq string (replace-match rpl t nil string)))) - string)) + (dolist (a org-html-special-string-regexps string) + (let ((re (car a)) + (rpl (cdr a))) + (setq string (replace-regexp-in-string re rpl string t))))) (defun org-html-encode-plain-text (text) "Convert plain text characters from TEXT to HTML equivalent. Possible conversions are set in `org-html-protect-char-alist'." - (mapc - (lambda (pair) - (setq text (replace-regexp-in-string (car pair) (cdr pair) text t t))) - org-html-protect-char-alist) - text) + (dolist (pair org-html-protect-char-alist text) + (setq text (replace-regexp-in-string (car pair) (cdr pair) text t t)))) (defun org-html-plain-text (text info) "Transcode a TEXT string from Org to HTML. @@ -2923,60 +3201,52 @@ contextual information." ;; Planning -(defun org-html-planning (planning contents info) +(defun org-html-planning (planning _contents info) "Transcode a PLANNING element from Org to HTML. CONTENTS is nil. INFO is a plist used as a communication channel." - (let ((span-fmt "%s %s")) - (format - "

%s

" - (mapconcat - 'identity - (delq nil - (list - (let ((closed (org-element-property :closed planning))) - (when closed - (format span-fmt org-closed-string - (org-translate-time - (org-element-property :raw-value closed))))) - (let ((deadline (org-element-property :deadline planning))) - (when deadline - (format span-fmt org-deadline-string - (org-translate-time - (org-element-property :raw-value deadline))))) - (let ((scheduled (org-element-property :scheduled planning))) - (when scheduled - (format span-fmt org-scheduled-string - (org-translate-time - (org-element-property :raw-value scheduled))))))) - " ")))) + (format + "

%s

" + (org-trim + (mapconcat + (lambda (pair) + (let ((timestamp (cdr pair))) + (when timestamp + (let ((string (car pair))) + (format "%s \ +%s " + string + (org-html-plain-text (org-timestamp-translate timestamp) + info)))))) + `((,org-closed-string . ,(org-element-property :closed planning)) + (,org-deadline-string . ,(org-element-property :deadline planning)) + (,org-scheduled-string . ,(org-element-property :scheduled planning))) + "")))) ;;;; Property Drawer -(defun org-html-property-drawer (property-drawer contents info) +(defun org-html-property-drawer (_property-drawer contents _info) "Transcode a PROPERTY-DRAWER element from Org to HTML. -CONTENTS is nil. INFO is a plist holding contextual -information." - ;; The property drawer isn't exported but we want separating blank - ;; lines nonetheless. - "") +CONTENTS holds the contents of the drawer. INFO is a plist +holding contextual information." + (and (org-string-nw-p contents) + (format "
\n%s
" contents))) ;;;; Quote Block -(defun org-html-quote-block (quote-block contents info) +(defun org-html-quote-block (quote-block contents _info) "Transcode a QUOTE-BLOCK element from Org to HTML. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." - (format "
\n%s
" contents)) - -;;;; Quote Section - -(defun org-html-quote-section (quote-section contents info) - "Transcode a QUOTE-SECTION element from Org to HTML. -CONTENTS is nil. INFO is a plist holding contextual information." - (let ((value (org-remove-indentation - (org-element-property :value quote-section)))) - (when value (format "
\n%s
" value)))) + (format "\n%s" + (let* ((name (org-element-property :name quote-block)) + (attributes (org-export-read-attribute :attr_html quote-block)) + (a (org-html--make-attribute-string + (if (or (not name) (plist-member attributes :id)) + attributes + (plist-put attributes :id name))))) + (if (org-string-nw-p a) (concat " " a) "")) + contents)) ;;;; Section @@ -2989,16 +3259,19 @@ holding contextual information." (if (not parent) contents ;; Get div's class and id references. (let* ((class-num (+ (org-export-get-relative-level parent info) - (1- org-html-toplevel-hlevel))) + (1- (plist-get info :html-toplevel-hlevel)))) (section-number - (mapconcat - 'number-to-string - (org-export-get-headline-number parent info) "-"))) + (and (org-export-numbered-headline-p parent info) + (mapconcat + #'number-to-string + (org-export-get-headline-number parent info) "-")))) ;; Build return value. (format "
\n%s
" class-num - (or (org-element-property :CUSTOM_ID parent) section-number) - contents))))) + (or (org-element-property :CUSTOM_ID parent) + section-number + (org-export-get-reference parent info)) + (or contents "")))))) ;;;; Radio Target @@ -3006,9 +3279,8 @@ holding contextual information." "Transcode a RADIO-TARGET object from Org to HTML. TEXT is the text of the target. INFO is a plist holding contextual information." - (let ((id (org-export-solidify-link-text - (org-element-property :value radio-target)))) - (org-html--anchor id text))) + (let ((ref (org-export-get-reference radio-target info))) + (org-html--anchor ref text nil info))) ;;;; Special Block @@ -3016,52 +3288,61 @@ contextual information." "Transcode a SPECIAL-BLOCK element from Org to HTML. CONTENTS holds the contents of the block. INFO is a plist holding contextual information." - (let* ((block-type (downcase - (org-element-property :type special-block))) - (contents (or contents "")) - (html5-fancy (and (org-html-html5-p info) - (plist-get info :html-html5-fancy) - (member block-type org-html-html5-elements))) - (attributes (org-export-read-attribute :attr_html special-block))) + (let* ((block-type (org-element-property :type special-block)) + (html5-fancy (and (org-html--html5-fancy-p info) + (member block-type org-html-html5-elements))) + (attributes (org-export-read-attribute :attr_html special-block))) (unless html5-fancy (let ((class (plist-get attributes :class))) - (setq attributes (plist-put attributes :class - (if class (concat class " " block-type) - block-type))))) - (setq attributes (org-html--make-attribute-string attributes)) - (when (not (equal attributes "")) - (setq attributes (concat " " attributes))) - (if html5-fancy - (format "<%s%s>\n%s" block-type attributes - contents block-type) - (format "\n%s\n
" attributes contents)))) + (setq attributes (plist-put attributes :class + (if class (concat class " " block-type) + block-type))))) + (let* ((contents (or contents "")) + (name (org-element-property :name special-block)) + (a (org-html--make-attribute-string + (if (or (not name) (plist-member attributes :id)) + attributes + (plist-put attributes :id name)))) + (str (if (org-string-nw-p a) (concat " " a) ""))) + (if html5-fancy + (format "<%s%s>\n%s" block-type str contents block-type) + (format "\n%s\n
" str contents))))) ;;;; Src Block -(defun org-html-src-block (src-block contents info) +(defun org-html-src-block (src-block _contents info) "Transcode a SRC-BLOCK element from Org to HTML. CONTENTS holds the contents of the item. INFO is a plist holding contextual information." (if (org-export-read-attribute :attr_html src-block :textarea) (org-html--textarea-block src-block) (let ((lang (org-element-property :language src-block)) - (caption (org-export-get-caption src-block)) (code (org-html-format-code src-block info)) - (label (let ((lbl (org-element-property :name src-block))) - (if (not lbl) "" - (format " id=\"%s\"" - (org-export-solidify-link-text lbl)))))) + (label (let ((lbl (and (org-element-property :name src-block) + (org-export-get-reference src-block info)))) + (if lbl (format " id=\"%s\"" lbl) "")))) (if (not lang) (format "
\n%s
" label code) - (format - "
\n%s%s\n
" - (if (not caption) "" - (format "" - (org-export-data caption info))) - (format "\n
%s
" lang label code)))))) + (format "
\n%s%s\n
" + ;; Build caption. + (let ((caption (org-export-get-caption src-block))) + (if (not caption) "" + (let ((listing-number + (format + "%s " + (format + (org-html--translate "Listing %d:" info) + (org-export-get-ordinal + src-block info nil #'org-html--has-caption-p))))) + (format "" + listing-number + (org-trim (org-export-data caption info)))))) + ;; Contents. + (format "
%s
" + lang label code)))))) ;;;; Statistics Cookie -(defun org-html-statistics-cookie (statistics-cookie contents info) +(defun org-html-statistics-cookie (statistics-cookie _contents _info) "Transcode a STATISTICS-COOKIE object from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." (let ((cookie-value (org-element-property :value statistics-cookie))) @@ -3069,16 +3350,18 @@ CONTENTS is nil. INFO is a plist holding contextual information." ;;;; Strike-Through -(defun org-html-strike-through (strike-through contents info) +(defun org-html-strike-through (_strike-through contents info) "Transcode STRIKE-THROUGH from Org to HTML. CONTENTS is the text with strike-through markup. INFO is a plist holding contextual information." - (format (or (cdr (assq 'strike-through org-html-text-markup-alist)) "%s") - contents)) + (format + (or (cdr (assq 'strike-through (plist-get info :html-text-markup-alist))) + "%s") + contents)) ;;;; Subscript -(defun org-html-subscript (subscript contents info) +(defun org-html-subscript (_subscript contents _info) "Transcode a SUBSCRIPT object from Org to HTML. CONTENTS is the contents of the object. INFO is a plist holding contextual information." @@ -3086,7 +3369,7 @@ contextual information." ;;;; Superscript -(defun org-html-superscript (superscript contents info) +(defun org-html-superscript (_superscript contents _info) "Transcode a SUPERSCRIPT object from Org to HTML. CONTENTS is the contents of the object. INFO is a plist holding contextual information." @@ -3101,24 +3384,30 @@ channel." (let* ((table-row (org-export-get-parent table-cell)) (table (org-export-get-parent-table table-cell)) (cell-attrs - (if (not org-html-table-align-individual-fields) "" + (if (not (plist-get info :html-table-align-individual-fields)) "" (format (if (and (boundp 'org-html-format-table-no-css) org-html-format-table-no-css) - " align=\"%s\"" " class=\"%s\"") + " align=\"%s\"" " class=\"org-%s\"") (org-export-table-cell-alignment table-cell info))))) (when (or (not contents) (string= "" (org-trim contents))) (setq contents " ")) (cond ((and (org-export-table-has-header-p table info) (= 1 (org-export-table-row-group table-row info))) - (concat "\n" (format (car org-html-table-header-tags) "col" cell-attrs) - contents (cdr org-html-table-header-tags))) - ((and org-html-table-use-header-tags-for-first-column + (let ((header-tags (plist-get info :html-table-header-tags))) + (concat "\n" (format (car header-tags) "col" cell-attrs) + contents + (cdr header-tags)))) + ((and (plist-get info :html-table-use-header-tags-for-first-column) (zerop (cdr (org-export-table-cell-address table-cell info)))) - (concat "\n" (format (car org-html-table-header-tags) "row" cell-attrs) - contents (cdr org-html-table-header-tags))) - (t (concat "\n" (format (car org-html-table-data-tags) cell-attrs) - contents (cdr org-html-table-data-tags)))))) + (let ((header-tags (plist-get info :html-table-header-tags))) + (concat "\n" (format (car header-tags) "row" cell-attrs) + contents + (cdr header-tags)))) + (t (let ((data-tags (plist-get info :html-table-data-tags))) + (concat "\n" (format (car data-tags) cell-attrs) + contents + (cdr data-tags))))))) ;;;; Table Row @@ -3129,40 +3418,45 @@ communication channel." ;; Rules are ignored since table separators are deduced from ;; borders of the current row. (when (eq (org-element-property :type table-row) 'standard) - (let* ((rowgroup-number (org-export-table-row-group table-row info)) - (row-number (org-export-table-row-number table-row info)) - (start-rowgroup-p + (let* ((group (org-export-table-row-group table-row info)) + (number (org-export-table-row-number table-row info)) + (start-group-p (org-export-table-row-starts-rowgroup-p table-row info)) - (end-rowgroup-p + (end-group-p (org-export-table-row-ends-rowgroup-p table-row info)) - ;; `top-row-p' and `end-rowgroup-p' are not used directly - ;; but should be set so that `org-html-table-row-tags' can - ;; use them (see the docstring of this variable.) - (top-row-p (and (equal start-rowgroup-p '(top)) - (equal end-rowgroup-p '(below top)))) - (bottom-row-p (and (equal start-rowgroup-p '(above)) - (equal end-rowgroup-p '(bottom above)))) - (rowgroup-tags + (topp (and (equal start-group-p '(top)) + (equal end-group-p '(below top)))) + (bottomp (and (equal start-group-p '(above)) + (equal end-group-p '(bottom above)))) + (row-open-tag + (pcase (plist-get info :html-table-row-open-tag) + ((and accessor (pred functionp)) + (funcall accessor + number group start-group-p end-group-p topp bottomp)) + (accessor accessor))) + (row-close-tag + (pcase (plist-get info :html-table-row-close-tag) + ((and accessor (pred functionp)) + (funcall accessor + number group start-group-p end-group-p topp bottomp)) + (accessor accessor))) + (group-tags (cond - ;; Case 1: Row belongs to second or subsequent rowgroups. - ((not (= 1 rowgroup-number)) - '("" . "\n")) - ;; Case 2: Row is from first rowgroup. Table has >=1 rowgroups. + ;; Row belongs to second or subsequent groups. + ((not (= 1 group)) '("" . "\n")) + ;; Row is from first group. Table has >=1 groups. ((org-export-table-has-header-p (org-export-get-parent-table table-row) info) '("" . "\n")) - ;; Case 2: Row is from first and only row group. + ;; Row is from first and only group. (t '("" . "\n"))))) - (concat - ;; Begin a rowgroup? - (when start-rowgroup-p (car rowgroup-tags)) - ;; Actual table row - (concat "\n" (eval (car org-html-table-row-tags)) - contents - "\n" - (eval (cdr org-html-table-row-tags))) - ;; End a rowgroup? - (when end-rowgroup-p (cdr rowgroup-tags)))))) + (concat (and start-group-p (car group-tags)) + (concat "\n" + row-open-tag + contents + "\n" + row-close-tag) + (and end-group-p (cdr group-tags)))))) ;;;; Table @@ -3178,7 +3472,7 @@ INFO is a plist used as a communication channel." (if (not special-column-p) (org-element-contents table-row) (cdr (org-element-contents table-row))))) -(defun org-html-table--table.el-table (table info) +(defun org-html-table--table.el-table (table _info) "Format table.el tables into HTML. INFO is a plist used as a communication channel." (when (eq (org-element-property :type table) 'table.el) @@ -3199,134 +3493,123 @@ INFO is a plist used as a communication channel." "Transcode a TABLE element from Org to HTML. CONTENTS is the contents of the table. INFO is a plist holding contextual information." - (case (org-element-property :type table) - ;; Case 1: table.el table. Convert it using appropriate tools. - (table.el (org-html-table--table.el-table table info)) - ;; Case 2: Standard table. - (t - (let* ((label (org-element-property :name table)) - (caption (org-export-get-caption table)) - (number (org-export-get-ordinal - table info nil 'org-html--has-caption-p)) - (attributes - (org-html--make-attribute-string - (org-combine-plists - (and label (list :id (org-export-solidify-link-text label))) - (and (not (org-html-html5-p info)) - (plist-get info :html-table-attributes)) - (org-export-read-attribute :attr_html table)))) - (alignspec - (if (and (boundp 'org-html-format-table-no-css) - org-html-format-table-no-css) - "align=\"%s\"" "class=\"%s\"")) - (table-column-specs - (function - (lambda (table info) - (mapconcat - (lambda (table-cell) - (let ((alignment (org-export-table-cell-alignment - table-cell info))) - (concat - ;; Begin a colgroup? - (when (org-export-table-cell-starts-colgroup-p - table-cell info) - "\n") - ;; Add a column. Also specify it's alignment. - (format "\n%s" - (org-html-close-tag - "col" (concat " " (format alignspec alignment)) info)) - ;; End a colgroup? - (when (org-export-table-cell-ends-colgroup-p - table-cell info) - "\n")))) - (org-html-table-first-row-data-cells table info) "\n"))))) - (format "\n%s\n%s\n%s" - (if (equal attributes "") "" (concat " " attributes)) - (if (not caption) "" - (format (if org-html-table-caption-above - "%s" - "%s") - (concat - "" - (format (org-html--translate "Table %d:" info) number) - " " (org-export-data caption info)))) - (funcall table-column-specs table info) - contents))))) + (if (eq (org-element-property :type table) 'table.el) + ;; "table.el" table. Convert it using appropriate tools. + (org-html-table--table.el-table table info) + ;; Standard table. + (let* ((caption (org-export-get-caption table)) + (number (org-export-get-ordinal + table info nil #'org-html--has-caption-p)) + (attributes + (org-html--make-attribute-string + (org-combine-plists + (and (org-element-property :name table) + (list :id (org-export-get-reference table info))) + (and (not (org-html-html5-p info)) + (plist-get info :html-table-attributes)) + (org-export-read-attribute :attr_html table)))) + (alignspec + (if (bound-and-true-p org-html-format-table-no-css) + "align=\"%s\"" + "class=\"org-%s\"")) + (table-column-specs + (lambda (table info) + (mapconcat + (lambda (table-cell) + (let ((alignment (org-export-table-cell-alignment + table-cell info))) + (concat + ;; Begin a colgroup? + (when (org-export-table-cell-starts-colgroup-p + table-cell info) + "\n") + ;; Add a column. Also specify its alignment. + (format "\n%s" + (org-html-close-tag + "col" (concat " " (format alignspec alignment)) info)) + ;; End a colgroup? + (when (org-export-table-cell-ends-colgroup-p + table-cell info) + "\n")))) + (org-html-table-first-row-data-cells table info) "\n")))) + (format "\n%s\n%s\n%s" + (if (equal attributes "") "" (concat " " attributes)) + (if (not caption) "" + (format (if (plist-get info :html-table-caption-above) + "%s" + "%s") + (concat + "" + (format (org-html--translate "Table %d:" info) number) + " " (org-export-data caption info)))) + (funcall table-column-specs table info) + contents)))) ;;;; Target -(defun org-html-target (target contents info) +(defun org-html-target (target _contents info) "Transcode a TARGET object from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." - (let ((id (org-export-solidify-link-text - (org-element-property :value target)))) - (org-html--anchor id))) + (let ((ref (org-export-get-reference target info))) + (org-html--anchor ref nil nil info))) ;;;; Timestamp -(defun org-html-timestamp (timestamp contents info) +(defun org-html-timestamp (timestamp _contents info) "Transcode a TIMESTAMP object from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." - (let ((value (org-html-plain-text - (org-timestamp-translate timestamp) info))) + (let ((value (org-html-plain-text (org-timestamp-translate timestamp) info))) (format "%s" (replace-regexp-in-string "--" "–" value)))) ;;;; Underline -(defun org-html-underline (underline contents info) +(defun org-html-underline (_underline contents info) "Transcode UNDERLINE from Org to HTML. CONTENTS is the text with underline markup. INFO is a plist holding contextual information." - (format (or (cdr (assq 'underline org-html-text-markup-alist)) "%s") + (format (or (cdr (assq 'underline (plist-get info :html-text-markup-alist))) + "%s") contents)) ;;;; Verbatim -(defun org-html-verbatim (verbatim contents info) +(defun org-html-verbatim (verbatim _contents info) "Transcode VERBATIM from Org to HTML. CONTENTS is nil. INFO is a plist holding contextual information." - (format (or (cdr (assq 'verbatim org-html-text-markup-alist)) "%s") + (format (or (cdr (assq 'verbatim (plist-get info :html-text-markup-alist))) "%s") (org-html-encode-plain-text (org-element-property :value verbatim)))) ;;;; Verse Block -(defun org-html-verse-block (verse-block contents info) +(defun org-html-verse-block (_verse-block contents info) "Transcode a VERSE-BLOCK element from Org to HTML. CONTENTS is verse block contents. INFO is a plist holding contextual information." - ;; Replace each newline character with line break. Also replace - ;; each blank line with a line break. - (setq contents (replace-regexp-in-string - "^ *\\\\\\\\$" (format "%s\n" (org-html-close-tag "br" nil info)) - (replace-regexp-in-string - "\\(\\\\\\\\\\)?[ \t]*\n" - (format "%s\n" (org-html-close-tag "br" nil info)) contents))) - ;; Replace each white space at beginning of a line with a - ;; non-breaking space. - (while (string-match "^[ \t]+" contents) - (let* ((num-ws (length (match-string 0 contents))) - (ws (let (out) (dotimes (i num-ws out) - (setq out (concat out " ")))))) - (setq contents (replace-match ws nil t contents)))) - (format "

\n%s

" contents)) + (format "

\n%s

" + ;; Replace leading white spaces with non-breaking spaces. + (replace-regexp-in-string + "^[ \t]+" (lambda (m) (org-html--make-string (length m) " ")) + ;; Replace each newline character with line break. Also + ;; remove any trailing "br" close-tag so as to avoid + ;; duplicates. + (let* ((br (org-html-close-tag "br" nil info)) + (re (format "\\(?:%s\\)?[ \t]*\n" (regexp-quote br)))) + (replace-regexp-in-string re (concat br "\n") contents))))) ;;; Filter Functions -(defun org-html-final-function (contents backend info) +(defun org-html-final-function (contents _backend info) "Filter to indent the HTML and convert HTML entities." (with-temp-buffer (insert contents) (set-auto-mode t) - (if org-html-indent + (if (plist-get info :html-indent) (indent-region (point-min) (point-max))) - (when org-html-use-unicode-chars - (require 'mm-url) - (mm-url-decode-entities)) (buffer-substring-no-properties (point-min) (point-max)))) @@ -3370,10 +3653,10 @@ is non-nil." ;;;###autoload (defun org-html-convert-region-to-html () - "Assume the current region has org-mode syntax, and convert it to HTML. + "Assume the current region has Org syntax, and convert it to HTML. This can be used in any buffer. For example, you can write an -itemized list in org-mode syntax in an HTML buffer and use this -command to convert it." +itemized list in Org syntax in an HTML buffer and use this command +to convert it." (interactive) (org-export-replace-region-by 'html)) @@ -3407,7 +3690,9 @@ file-local settings. Return output file's name." (interactive) - (let* ((extension (concat "." org-html-extension)) + (let* ((extension (concat "." (or (plist-get ext-plist :html-extension) + org-html-extension + "html"))) (file (org-export-output-file-name extension subtreep)) (org-export-coding-system org-html-coding-system)) (org-export-to-file 'html file @@ -3424,7 +3709,8 @@ publishing directory. Return output file name." (org-publish-org-to 'html filename (concat "." (or (plist-get plist :html-extension) - org-html-extension "html")) + org-html-extension + "html")) plist pub-dir)) diff --git a/lisp/org/ox-icalendar.el b/lisp/org/ox-icalendar.el index fe6d08a85b..9ccbb27244 100644 --- a/lisp/org/ox-icalendar.el +++ b/lisp/org/ox-icalendar.el @@ -1,4 +1,4 @@ -;;; ox-icalendar.el --- iCalendar Back-End for Org Export Engine +;;; ox-icalendar.el --- iCalendar Back-End for Org Export Engine -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2017 Free Software Foundation, Inc. @@ -31,7 +31,7 @@ ;;; Code: -(eval-when-compile (require 'cl)) +(require 'cl-lib) (require 'ox-ascii) (declare-function org-bbdb-anniv-export-ical "org-bbdb" nil) @@ -46,7 +46,7 @@ (defcustom org-icalendar-combined-agenda-file "~/org.ics" "The file name for the iCalendar file covering all agenda files. -This file is created with the command \\[org-icalendar-combine-agenda-files]. +This file is created with the command `\\[org-icalendar-combine-agenda-files]'. The file name should be absolute. It will be overwritten without warning." :group 'org-export-icalendar :type 'file) @@ -77,7 +77,7 @@ for timed events. If non-zero, alarms are created. (defcustom org-icalendar-exclude-tags nil "Tags that exclude a tree from export. This variable allows specifying different exclude tags from other -back-ends. It can also be set with the ICAL_EXCLUDE_TAGS +back-ends. It can also be set with the ICALENDAR_EXCLUDE_TAGS keyword." :group 'org-export-icalendar :type '(repeat (string :tag "Tag"))) @@ -85,10 +85,11 @@ keyword." (defcustom org-icalendar-use-deadline '(event-if-not-todo todo-due) "Contexts where iCalendar export should use a deadline time stamp. -This is a list with several symbols in it. Valid symbol are: +This is a list with possibly several symbols in it. Valid symbols are: + `event-if-todo' Deadlines in TODO entries become calendar events. `event-if-not-todo' Deadlines in non-TODO entries become calendar events. -`todo-due' Use deadlines in TODO entries as due-dates" +`todo-due' Use deadlines in TODO entries as due-dates." :group 'org-export-icalendar :type '(set :greedy t (const :tag "Deadlines in non-TODO entries become events" @@ -101,7 +102,8 @@ This is a list with several symbols in it. Valid symbol are: (defcustom org-icalendar-use-scheduled '(todo-start) "Contexts where iCalendar export should use a scheduling time stamp. -This is a list with several symbols in it. Valid symbol are: +This is a list with possibly several symbols in it. Valid symbols are: + `event-if-todo' Scheduling time stamps in TODO entries become an event. `event-if-not-todo' Scheduling time stamps in non-TODO entries become an event. `todo-start' Scheduling time stamps in TODO entries become start date. @@ -256,11 +258,18 @@ re-read the iCalendar file.") '((:exclude-tags "ICALENDAR_EXCLUDE_TAGS" nil org-icalendar-exclude-tags split) (:with-timestamps nil "<" org-icalendar-with-timestamps) - (:with-vtodo nil nil org-icalendar-include-todo) - ;; The following property will be non-nil when export has been - ;; started from org-agenda-mode. In this case, any entry without - ;; a non-nil "ICALENDAR_MARK" property will be ignored. - (:icalendar-agenda-view nil nil nil)) + ;; Other variables. + (:icalendar-alarm-time nil nil org-icalendar-alarm-time) + (:icalendar-categories nil nil org-icalendar-categories) + (:icalendar-date-time-format nil nil org-icalendar-date-time-format) + (:icalendar-include-bbdb-anniversaries nil nil org-icalendar-include-bbdb-anniversaries) + (:icalendar-include-body nil nil org-icalendar-include-body) + (:icalendar-include-sexps nil nil org-icalendar-include-sexps) + (:icalendar-include-todo nil nil org-icalendar-include-todo) + (:icalendar-store-UID nil nil org-icalendar-store-UID) + (:icalendar-timezone nil nil org-icalendar-timezone) + (:icalendar-use-deadline nil nil org-icalendar-use-deadline) + (:icalendar-use-scheduled nil nil org-icalendar-use-scheduled)) :filters-alist '((:filter-headline . org-icalendar-clear-blank-lines)) :menu-entry @@ -275,22 +284,18 @@ re-read the iCalendar file.") ;;; Internal Functions -(defun org-icalendar-create-uid (file &optional bell h-markers) +(defun org-icalendar-create-uid (file &optional bell) "Set ID property on headlines missing it in FILE. When optional argument BELL is non-nil, inform the user with -a message if the file was modified. With optional argument -H-MARKERS non-nil, it is a list of markers for the headlines -which will be updated." - (let ((pt (if h-markers (goto-char (car h-markers)) (point-min))) - modified-flag) +a message if the file was modified." + (let (modified-flag) (org-map-entries (lambda () (let ((entry (org-element-at-point))) - (unless (or (< (point) pt) (org-element-property :ID entry)) + (unless (org-element-property :ID entry) (org-id-get-create) (setq modified-flag t) - (forward-line)) - (when h-markers (setq org-map-continue-from (pop h-markers))))) + (forward-line)))) nil nil 'comment) (when (and bell modified-flag) (message "ID properties created in file \"%s\"" file) @@ -318,19 +323,17 @@ A headline is blocked when either ;; Check :ORDERED: node property. (catch 'blockedp (let ((current headline)) - (mapc (lambda (parent) - (cond - ((not (org-element-property :todo-keyword parent)) - (throw 'blockedp nil)) - ((org-not-nil (org-element-property :ORDERED parent)) - (let ((sibling current)) - (while (setq sibling (org-export-get-previous-element - sibling info)) - (when (eq (org-element-property :todo-type sibling) 'todo) - (throw 'blockedp t))))) - (t (setq current parent)))) - (org-export-get-genealogy headline)) - nil)))) + (dolist (parent (org-element-lineage headline)) + (cond + ((not (org-element-property :todo-keyword parent)) + (throw 'blockedp nil)) + ((org-not-nil (org-element-property :ORDERED parent)) + (let ((sibling current)) + (while (setq sibling (org-export-get-previous-element + sibling info)) + (when (eq (org-element-property :todo-type sibling) 'todo) + (throw 'blockedp t))))) + (t (setq current parent)))))))) (defun org-icalendar-use-UTC-date-time-p () "Non-nil when `org-icalendar-date-time-format' requires UTC time." @@ -393,8 +396,8 @@ Universal Time, ignoring `org-icalendar-date-time-format'." ;; Convert timestamp into internal time in order to use ;; `format-time-string' and fix any mistake (i.e. MI >= 60). (encode-time 0 mi h d m y) - (not (not (or utc (and with-time-p - (org-icalendar-use-UTC-date-time-p))))))))) + (and (or utc (and with-time-p (org-icalendar-use-UTC-date-time-p))) + t))))) (defun org-icalendar-dtstamp () "Return DTSTAMP property, as a string." @@ -405,27 +408,25 @@ Universal Time, ignoring `org-icalendar-date-time-format'." ENTRY is a headline or an inlinetask element. INFO is a plist used as a communication channel." (mapconcat - 'identity + #'identity (org-uniquify (let (categories) - (mapc (lambda (type) - (case type - (category - (push (org-export-get-category entry info) categories)) - (todo-state - (let ((todo (org-element-property :todo-keyword entry))) - (and todo (push todo categories)))) - (local-tags - (setq categories - (append (nreverse (org-export-get-tags entry info)) - categories))) - (all-tags - (setq categories - (append (nreverse (org-export-get-tags entry info nil t)) - categories))))) - org-icalendar-categories) - ;; Return list of categories, following specified order. - (nreverse categories))) ",")) + (dolist (type org-icalendar-categories (nreverse categories)) + (cl-case type + (category + (push (org-export-get-category entry info) categories)) + (todo-state + (let ((todo (org-element-property :todo-keyword entry))) + (and todo (push todo categories)))) + (local-tags + (setq categories + (append (nreverse (org-export-get-tags entry info)) + categories))) + (all-tags + (setq categories + (append (nreverse (org-export-get-tags entry info nil t)) + categories))))))) + ",")) (defun org-icalendar-transcode-diary-sexp (sexp uid summary) "Transcode a diary sexp into iCalendar format. @@ -457,7 +458,7 @@ or subject for the event." (mapconcat (lambda (line) ;; Limit each line to a maximum of 75 characters. If it is - ;; longer, fold it by using "\n " as a continuation marker. + ;; longer, fold it by using "\r\n " as a continuation marker. (let ((len (length line))) (if (<= len 75) line (let ((folded-line (substring line 0 75)) @@ -467,17 +468,17 @@ or subject for the event." ;; line, real contents must be split at 74 chars. (while (< (setq chunk-end (+ chunk-start 74)) len) (setq folded-line - (concat folded-line "\n " + (concat folded-line "\r\n " (substring line chunk-start chunk-end)) chunk-start chunk-end)) - (concat folded-line "\n " (substring line chunk-start)))))) - (org-split-string s "\n") "\n"))) + (concat folded-line "\r\n " (substring line chunk-start)))))) + (org-split-string s "\n") "\r\n"))) ;;; Filters -(defun org-icalendar-clear-blank-lines (headline back-end info) +(defun org-icalendar-clear-blank-lines (headline _back-end _info) "Remove blank lines in HEADLINE export. HEADLINE is a string representing a transcoded headline. BACK-END and INFO are ignored." @@ -522,99 +523,97 @@ inlinetask within the section." (cons 'org-data (cons nil (org-element-contents first)))))))) (concat - (unless (and (plist-get info :icalendar-agenda-view) - (not (org-element-property :ICALENDAR-MARK entry))) - (let ((todo-type (org-element-property :todo-type entry)) - (uid (or (org-element-property :ID entry) (org-id-new))) - (summary (org-icalendar-cleanup-string - (or (org-element-property :SUMMARY entry) - (org-export-data - (org-element-property :title entry) info)))) - (loc (org-icalendar-cleanup-string - (org-element-property :LOCATION entry))) - ;; Build description of the entry from associated - ;; section (headline) or contents (inlinetask). - (desc - (org-icalendar-cleanup-string - (or (org-element-property :DESCRIPTION entry) - (let ((contents (org-export-data inside info))) - (cond - ((not (org-string-nw-p contents)) nil) - ((wholenump org-icalendar-include-body) - (let ((contents (org-trim contents))) - (substring - contents 0 (min (length contents) - org-icalendar-include-body)))) - (org-icalendar-include-body (org-trim contents))))))) - (cat (org-icalendar-get-categories entry info))) - (concat - ;; Events: Delegate to `org-icalendar--vevent' to - ;; generate "VEVENT" component from scheduled, deadline, - ;; or any timestamp in the entry. - (let ((deadline (org-element-property :deadline entry))) - (and deadline - (memq (if todo-type 'event-if-todo 'event-if-not-todo) - org-icalendar-use-deadline) - (org-icalendar--vevent - entry deadline (concat "DL-" uid) - (concat "DL: " summary) loc desc cat))) - (let ((scheduled (org-element-property :scheduled entry))) - (and scheduled - (memq (if todo-type 'event-if-todo 'event-if-not-todo) - org-icalendar-use-scheduled) - (org-icalendar--vevent - entry scheduled (concat "SC-" uid) - (concat "S: " summary) loc desc cat))) - ;; When collecting plain timestamps from a headline and - ;; its title, skip inlinetasks since collection will - ;; happen once ENTRY is one of them. + (let ((todo-type (org-element-property :todo-type entry)) + (uid (or (org-element-property :ID entry) (org-id-new))) + (summary (org-icalendar-cleanup-string + (or (org-element-property :SUMMARY entry) + (org-export-data + (org-element-property :title entry) info)))) + (loc (org-icalendar-cleanup-string + (org-element-property :LOCATION entry))) + ;; Build description of the entry from associated section + ;; (headline) or contents (inlinetask). + (desc + (org-icalendar-cleanup-string + (or (org-element-property :DESCRIPTION entry) + (let ((contents (org-export-data inside info))) + (cond + ((not (org-string-nw-p contents)) nil) + ((wholenump org-icalendar-include-body) + (let ((contents (org-trim contents))) + (substring + contents 0 (min (length contents) + org-icalendar-include-body)))) + (org-icalendar-include-body (org-trim contents))))))) + (cat (org-icalendar-get-categories entry info))) + (concat + ;; Events: Delegate to `org-icalendar--vevent' to generate + ;; "VEVENT" component from scheduled, deadline, or any + ;; timestamp in the entry. + (let ((deadline (org-element-property :deadline entry))) + (and deadline + (memq (if todo-type 'event-if-todo 'event-if-not-todo) + org-icalendar-use-deadline) + (org-icalendar--vevent + entry deadline (concat "DL-" uid) + (concat "DL: " summary) loc desc cat))) + (let ((scheduled (org-element-property :scheduled entry))) + (and scheduled + (memq (if todo-type 'event-if-todo 'event-if-not-todo) + org-icalendar-use-scheduled) + (org-icalendar--vevent + entry scheduled (concat "SC-" uid) + (concat "S: " summary) loc desc cat))) + ;; When collecting plain timestamps from a headline and its + ;; title, skip inlinetasks since collection will happen once + ;; ENTRY is one of them. + (let ((counter 0)) + (mapconcat + #'identity + (org-element-map (cons (org-element-property :title entry) + (org-element-contents inside)) + 'timestamp + (lambda (ts) + (when (let ((type (org-element-property :type ts))) + (cl-case (plist-get info :with-timestamps) + (active (memq type '(active active-range))) + (inactive (memq type '(inactive inactive-range))) + ((t) t))) + (let ((uid (format "TS%d-%s" (cl-incf counter) uid))) + (org-icalendar--vevent + entry ts uid summary loc desc cat)))) + info nil (and (eq type 'headline) 'inlinetask)) + "")) + ;; Task: First check if it is appropriate to export it. If + ;; so, call `org-icalendar--vtodo' to transcode it into + ;; a "VTODO" component. + (when (and todo-type + (cl-case (plist-get info :icalendar-include-todo) + (all t) + (unblocked + (and (eq type 'headline) + (not (org-icalendar-blocked-headline-p + entry info)))) + ((t) (eq todo-type 'todo)))) + (org-icalendar--vtodo entry uid summary loc desc cat)) + ;; Diary-sexp: Collect every diary-sexp element within ENTRY + ;; and its title, and transcode them. If ENTRY is + ;; a headline, skip inlinetasks: they will be handled + ;; separately. + (when org-icalendar-include-sexps (let ((counter 0)) - (mapconcat - #'identity - (org-element-map (cons (org-element-property :title entry) - (org-element-contents inside)) - 'timestamp - (lambda (ts) - (when (let ((type (org-element-property :type ts))) - (case (plist-get info :with-timestamps) - (active (memq type '(active active-range))) - (inactive (memq type '(inactive inactive-range))) - ((t) t))) - (let ((uid (format "TS%d-%s" (incf counter) uid))) - (org-icalendar--vevent - entry ts uid summary loc desc cat)))) - info nil (and (eq type 'headline) 'inlinetask)) - "")) - ;; Task: First check if it is appropriate to export it. - ;; If so, call `org-icalendar--vtodo' to transcode it - ;; into a "VTODO" component. - (when (and todo-type - (case (plist-get info :with-vtodo) - (all t) - (unblocked - (and (eq type 'headline) - (not (org-icalendar-blocked-headline-p - entry info)))) - ((t) (eq todo-type 'todo)))) - (org-icalendar--vtodo entry uid summary loc desc cat)) - ;; Diary-sexp: Collect every diary-sexp element within - ;; ENTRY and its title, and transcode them. If ENTRY is - ;; a headline, skip inlinetasks: they will be handled - ;; separately. - (when org-icalendar-include-sexps - (let ((counter 0)) - (mapconcat #'identity - (org-element-map - (cons (org-element-property :title entry) - (org-element-contents inside)) - 'diary-sexp - (lambda (sexp) - (org-icalendar-transcode-diary-sexp - (org-element-property :value sexp) - (format "DS%d-%s" (incf counter) uid) - summary)) - info nil (and (eq type 'headline) 'inlinetask)) - "")))))) + (mapconcat #'identity + (org-element-map + (cons (org-element-property :title entry) + (org-element-contents inside)) + 'diary-sexp + (lambda (sexp) + (org-icalendar-transcode-diary-sexp + (org-element-property :value sexp) + (format "DS%d-%s" (cl-incf counter) uid) + summary)) + info nil (and (eq type 'headline) 'inlinetask)) + ""))))) ;; If ENTRY is a headline, call current function on every ;; inlinetask within it. In agenda export, this is independent ;; from the mark (or lack thereof) on the entry. @@ -627,7 +626,7 @@ inlinetask within the section." contents)))) (defun org-icalendar--vevent - (entry timestamp uid summary location description categories) + (entry timestamp uid summary location description categories) "Create a VEVENT component. ENTRY is either a headline or an inlinetask element. TIMESTAMP @@ -651,7 +650,7 @@ Return VEVENT component as a string." ;; RRULE. (when (org-element-property :repeater-type timestamp) (format "RRULE:FREQ=%s;INTERVAL=%d\n" - (case (org-element-property :repeater-unit timestamp) + (cl-case (org-element-property :repeater-unit timestamp) (hour "HOURLY") (day "DAILY") (week "WEEKLY") (month "MONTHLY") (year "YEARLY")) (org-element-property :repeater-value timestamp))) @@ -821,7 +820,8 @@ Return ICS file name." ;; links will not be collected at the end of sections. (let ((outfile (org-export-output-file-name ".ics" subtreep))) (org-export-to-file 'icalendar outfile - async subtreep visible-only body-only '(:ascii-charset utf-8) + async subtreep visible-only body-only + '(:ascii-charset utf-8 :ascii-links-to-notes nil) (lambda (file) (run-hook-with-args 'org-icalendar-after-save-hook file) nil)))) @@ -835,27 +835,23 @@ external process." ;; Asynchronous export is not interactive, so we will not call ;; `org-check-agenda-file'. Instead we remove any non-existent ;; agenda file from the list. - (let ((files (org-remove-if-not 'file-exists-p (org-agenda-files t)))) + (let ((files (cl-remove-if-not #'file-exists-p (org-agenda-files t)))) (org-export-async-start (lambda (results) - (mapc (lambda (f) (org-export-add-to-stack f 'icalendar)) - results)) + (dolist (f results) (org-export-add-to-stack f 'icalendar))) `(let (output-files) - (mapc (lambda (file) - (with-current-buffer (org-get-agenda-file-buffer file) - (push (expand-file-name (org-icalendar-export-to-ics)) - output-files))) - ',files) - output-files))) + (dolist (file ',files outputfiles) + (with-current-buffer (org-get-agenda-file-buffer file) + (push (expand-file-name (org-icalendar-export-to-ics)) + output-files)))))) (let ((files (org-agenda-files t))) (org-agenda-prepare-buffers files) (unwind-protect - (mapc (lambda (file) - (catch 'nextfile - (org-check-agenda-file file) - (with-current-buffer (org-get-agenda-file-buffer file) - (org-icalendar-export-to-ics)))) - files) + (dolist (file files) + (catch 'nextfile + (org-check-agenda-file file) + (with-current-buffer (org-get-agenda-file-buffer file) + (org-icalendar-export-to-ics)))) (org-release-buffers org-agenda-new-buffers))))) ;;;###autoload @@ -870,56 +866,52 @@ The file is stored under the name chosen in `org-icalendar-combined-agenda-file'." (interactive) (if async - (let ((files (org-remove-if-not 'file-exists-p (org-agenda-files t)))) + (let ((files (cl-remove-if-not #'file-exists-p (org-agenda-files t)))) (org-export-async-start - (lambda (dummy) + (lambda (_) (org-export-add-to-stack (expand-file-name org-icalendar-combined-agenda-file) 'icalendar)) - `(apply 'org-icalendar--combine-files nil ',files))) - (apply 'org-icalendar--combine-files nil (org-agenda-files t)))) + `(apply #'org-icalendar--combine-files ',files))) + (apply #'org-icalendar--combine-files (org-agenda-files t)))) (defun org-icalendar-export-current-agenda (file) "Export current agenda view to an iCalendar FILE. This function assumes major mode for current buffer is `org-agenda-mode'." - (let (org-export-babel-evaluate ; Don't evaluate Babel block - (org-icalendar-combined-agenda-file file) - (marker-list - ;; Collect the markers pointing to entries in the current - ;; agenda buffer. - (let (markers) - (save-excursion - (goto-char (point-min)) - (while (not (eobp)) - (let ((m (or (org-get-at-bol 'org-hd-marker) - (org-get-at-bol 'org-marker)))) - (and m (push m markers))) - (beginning-of-line 2))) - (nreverse markers)))) - (apply 'org-icalendar--combine-files - ;; Build restriction alist. - (let (restriction) - ;; Sort markers in each association within RESTRICTION. - (mapcar (lambda (x) (setcdr x (sort (copy-sequence (cdr x)) '<)) x) - (dolist (m marker-list restriction) - (let* ((pos (marker-position m)) - (file (buffer-file-name - (org-base-buffer (marker-buffer m)))) - (file-markers (assoc file restriction))) - ;; Add POS in FILE association if one exists - ;; or create a new association for FILE. - (if file-markers (push pos (cdr file-markers)) - (push (list file pos) restriction)))))) - (org-agenda-files nil 'ifmode)))) - -(defun org-icalendar--combine-files (restriction &rest files) + (let* ((org-export-babel-evaluate) ; Don't evaluate Babel block. + (contents + (org-export-string-as + (with-output-to-string + (save-excursion + (let ((p (point-min))) + (while (setq p (next-single-property-change p 'org-hd-marker)) + (let ((m (get-text-property p 'org-hd-marker))) + (when m + (with-current-buffer (marker-buffer m) + (org-with-wide-buffer + (goto-char (marker-position m)) + (princ + (org-element-normalize-string + (buffer-substring + (point) (progn (outline-next-heading) (point))))))))) + (forward-line))))) + 'icalendar t + '(:ascii-charset utf-8 :ascii-links-to-notes nil + :icalendar-include-todo all)))) + (with-temp-file file + (insert + (org-icalendar--vcalendar + org-icalendar-combined-name + user-full-name + (or (org-string-nw-p org-icalendar-timezone) (cadr (current-time-zone))) + org-icalendar-combined-description + contents))) + (run-hook-with-args 'org-icalendar-after-save-hook file))) + +(defun org-icalendar--combine-files (&rest files) "Combine entries from multiple files into an iCalendar file. -RESTRICTION, when non-nil, is an alist where key is a file name -and value a list of buffer positions pointing to entries that -should appear in the calendar. It only makes sense if the -function was called from an agenda buffer. FILES is a list of -files to build the calendar from." +FILES is a list of files to build the calendar from." (org-agenda-prepare-buffers files) (unwind-protect (progn @@ -943,29 +935,12 @@ files to build the calendar from." (catch 'nextfile (org-check-agenda-file file) (with-current-buffer (org-get-agenda-file-buffer file) - (let ((marks (cdr (assoc (expand-file-name file) - restriction)))) - ;; Create ID if necessary. - (when org-icalendar-store-UID - (org-icalendar-create-uid file t marks)) - (unless (and restriction (not marks)) - ;; Add a hook adding :ICALENDAR_MARK: property - ;; to each entry appearing in agenda view. - ;; Use `apply-partially' because the function - ;; still has to accept one argument. - (let ((org-export-before-processing-hook - (cons (apply-partially - (lambda (m-list dummy) - (mapc (lambda (m) - (org-entry-put - m "ICALENDAR-MARK" "t")) - m-list)) - (sort marks '>)) - org-export-before-processing-hook))) - (org-export-as - 'icalendar nil nil t - (list :ascii-charset 'utf-8 - :icalendar-agenda-view restriction)))))))) + ;; Create ID if necessary. + (when org-icalendar-store-UID + (org-icalendar-create-uid file t)) + (org-export-as + 'icalendar nil nil t + '(:ascii-charset utf-8 :ascii-links-to-notes nil))))) files "") ;; BBDB anniversaries. (when (and org-icalendar-include-bbdb-anniversaries diff --git a/lisp/org/ox-latex.el b/lisp/org/ox-latex.el index 3eee86a3ae..f11a8a63a2 100644 --- a/lisp/org/ox-latex.el +++ b/lisp/org/ox-latex.el @@ -1,4 +1,4 @@ -;;; ox-latex.el --- LaTeX Back-End for Org Export Engine +;;; ox-latex.el --- LaTeX Back-End for Org Export Engine -*- lexical-binding: t; -*- ;; Copyright (C) 2011-2017 Free Software Foundation, Inc. @@ -26,7 +26,7 @@ ;;; Code: -(eval-when-compile (require 'cl)) +(require 'cl-lib) (require 'ox) (require 'ox-publish) @@ -43,8 +43,6 @@ (center-block . org-latex-center-block) (clock . org-latex-clock) (code . org-latex-code) - (comment . (lambda (&rest args) "")) - (comment-block . (lambda (&rest args) "")) (drawer . org-latex-drawer) (dynamic-block . org-latex-dynamic-block) (entity . org-latex-entity) @@ -65,13 +63,13 @@ (latex-fragment . org-latex-latex-fragment) (line-break . org-latex-line-break) (link . org-latex-link) + (node-property . org-latex-node-property) (paragraph . org-latex-paragraph) (plain-list . org-latex-plain-list) (plain-text . org-latex-plain-text) (planning . org-latex-planning) - (property-drawer . (lambda (&rest args) "")) + (property-drawer . org-latex-property-drawer) (quote-block . org-latex-quote-block) - (quote-section . org-latex-quote-section) (radio-target . org-latex-radio-target) (section . org-latex-section) (special-block . org-latex-special-block) @@ -88,8 +86,10 @@ (timestamp . org-latex-timestamp) (underline . org-latex-underline) (verbatim . org-latex-verbatim) - (verse-block . org-latex-verse-block)) - :export-block '("LATEX" "TEX") + (verse-block . org-latex-verse-block) + ;; Pseudo objects and elements. + (latex-math-block . org-latex-math-block) + (latex-matrices . org-latex-matrices)) :menu-entry '(?l "Export to LaTeX" ((?L "As LaTeX buffer" org-latex-export-as-latex) @@ -99,13 +99,57 @@ (lambda (a s v b) (if a (org-latex-export-to-pdf t s v b) (org-open-file (org-latex-export-to-pdf nil s v b))))))) - :options-alist '((:latex-class "LATEX_CLASS" nil org-latex-default-class t) - (:latex-class-options "LATEX_CLASS_OPTIONS" nil nil t) - (:latex-header "LATEX_HEADER" nil nil newline) - (:latex-header-extra "LATEX_HEADER_EXTRA" nil nil newline) - (:latex-hyperref-p nil "texht" org-latex-with-hyperref t) - ;; Redefine regular options. - (:date "DATE" nil "\\today" t))) + :filters-alist '((:filter-options . org-latex-math-block-options-filter) + (:filter-paragraph . org-latex-clean-invalid-line-breaks) + (:filter-parse-tree org-latex-math-block-tree-filter + org-latex-matrices-tree-filter) + (:filter-verse-block . org-latex-clean-invalid-line-breaks)) + :options-alist + '((:latex-class "LATEX_CLASS" nil org-latex-default-class t) + (:latex-class-options "LATEX_CLASS_OPTIONS" nil nil t) + (:latex-header "LATEX_HEADER" nil nil newline) + (:latex-header-extra "LATEX_HEADER_EXTRA" nil nil newline) + (:description "DESCRIPTION" nil nil parse) + (:keywords "KEYWORDS" nil nil parse) + (:subtitle "SUBTITLE" nil nil parse) + ;; Other variables. + (:latex-active-timestamp-format nil nil org-latex-active-timestamp-format) + (:latex-caption-above nil nil org-latex-caption-above) + (:latex-classes nil nil org-latex-classes) + (:latex-default-figure-position nil nil org-latex-default-figure-position) + (:latex-default-table-environment nil nil org-latex-default-table-environment) + (:latex-default-table-mode nil nil org-latex-default-table-mode) + (:latex-diary-timestamp-format nil nil org-latex-diary-timestamp-format) + (:latex-footnote-defined-format nil nil org-latex-footnote-defined-format) + (:latex-footnote-separator nil nil org-latex-footnote-separator) + (:latex-format-drawer-function nil nil org-latex-format-drawer-function) + (:latex-format-headline-function nil nil org-latex-format-headline-function) + (:latex-format-inlinetask-function nil nil org-latex-format-inlinetask-function) + (:latex-hyperref-template nil nil org-latex-hyperref-template t) + (:latex-image-default-height nil nil org-latex-image-default-height) + (:latex-image-default-option nil nil org-latex-image-default-option) + (:latex-image-default-width nil nil org-latex-image-default-width) + (:latex-images-centered nil nil org-latex-images-centered) + (:latex-inactive-timestamp-format nil nil org-latex-inactive-timestamp-format) + (:latex-inline-image-rules nil nil org-latex-inline-image-rules) + (:latex-link-with-unknown-path-format nil nil org-latex-link-with-unknown-path-format) + (:latex-listings nil nil org-latex-listings) + (:latex-listings-langs nil nil org-latex-listings-langs) + (:latex-listings-options nil nil org-latex-listings-options) + (:latex-minted-langs nil nil org-latex-minted-langs) + (:latex-minted-options nil nil org-latex-minted-options) + (:latex-prefer-user-labels nil nil org-latex-prefer-user-labels) + (:latex-subtitle-format nil nil org-latex-subtitle-format) + (:latex-subtitle-separate nil nil org-latex-subtitle-separate) + (:latex-table-scientific-notation nil nil org-latex-table-scientific-notation) + (:latex-tables-booktabs nil nil org-latex-tables-booktabs) + (:latex-tables-centered nil nil org-latex-tables-centered) + (:latex-text-markup-alist nil nil org-latex-text-markup-alist) + (:latex-title-command nil nil org-latex-title-command) + (:latex-toc-command nil nil org-latex-toc-command) + (:latex-compiler "LATEX_COMPILER" nil org-latex-compiler) + ;; Redefine regular options. + (:date "DATE" nil "\\today" parse))) @@ -164,11 +208,112 @@ ("uk" . "ukrainian")) "Alist between language code and corresponding Babel option.") +(defconst org-latex-polyglossia-language-alist + '(("am" "amharic") + ("ast" "asturian") + ("ar" "arabic") + ("bo" "tibetan") + ("bn" "bengali") + ("bg" "bulgarian") + ("br" "breton") + ("bt-br" "brazilian") + ("ca" "catalan") + ("cop" "coptic") + ("cs" "czech") + ("cy" "welsh") + ("da" "danish") + ("de" "german" "german") + ("de-at" "german" "austrian") + ("de-de" "german" "german") + ("dv" "divehi") + ("el" "greek") + ("en" "english" "usmax") + ("en-au" "english" "australian") + ("en-gb" "english" "uk") + ("en-nz" "english" "newzealand") + ("en-us" "english" "usmax") + ("eo" "esperanto") + ("es" "spanish") + ("et" "estonian") + ("eu" "basque") + ("fa" "farsi") + ("fi" "finnish") + ("fr" "french") + ("fu" "friulan") + ("ga" "irish") + ("gd" "scottish") + ("gl" "galician") + ("he" "hebrew") + ("hi" "hindi") + ("hr" "croatian") + ("hu" "magyar") + ("hy" "armenian") + ("id" "bahasai") + ("ia" "interlingua") + ("is" "icelandic") + ("it" "italian") + ("kn" "kannada") + ("la" "latin" "modern") + ("la-modern" "latin" "modern") + ("la-classic" "latin" "classic") + ("la-medieval" "latin" "medieval") + ("lo" "lao") + ("lt" "lithuanian") + ("lv" "latvian") + ("mr" "maranthi") + ("ml" "malayalam") + ("nl" "dutch") + ("nb" "norsk") + ("nn" "nynorsk") + ("nko" "nko") + ("no" "norsk") + ("oc" "occitan") + ("pl" "polish") + ("pms" "piedmontese") + ("pt" "portuges") + ("rm" "romansh") + ("ro" "romanian") + ("ru" "russian") + ("sa" "sanskrit") + ("hsb" "usorbian") + ("dsb" "lsorbian") + ("sk" "slovak") + ("sl" "slovenian") + ("se" "samin") + ("sq" "albanian") + ("sr" "serbian") + ("sv" "swedish") + ("syr" "syriac") + ("ta" "tamil") + ("te" "telugu") + ("th" "thai") + ("tk" "turkmen") + ("tr" "turkish") + ("uk" "ukrainian") + ("ur" "urdu") + ("vi" "vietnamese")) + "Alist between language code and corresponding Polyglossia option") + + + (defconst org-latex-table-matrix-macros '(("bordermatrix" . "\\cr") - ("qbordermatrix" . "\\cr") - ("kbordermatrix" . "\\\\")) + ("qbordermatrix" . "\\cr") + ("kbordermatrix" . "\\\\")) "Alist between matrix macros and their row ending.") +(defconst org-latex-math-environments-re + (format + "\\`[ \t]*\\\\begin{%s\\*?}" + (regexp-opt + '("equation" "eqnarray" "math" "displaymath" + "align" "gather" "multline" "flalign" "alignat" + "xalignat" "xxalignat" + "subequations" + ;; breqn + "dmath" "dseries" "dgroup" "darray" + ;; empheq + "empheq"))) + "Regexp of LaTeX math environments.") ;;; User Configurable Variables @@ -178,6 +323,79 @@ :tag "Org Export LaTeX" :group 'org-export) +;;;; Generic + +(defcustom org-latex-caption-above '(table) + "When non-nil, place caption string at the beginning of elements. +Otherwise, place it near the end. When value is a list of +symbols, put caption above selected elements only. Allowed +symbols are: `image', `table', `src-block' and `special-block'." + :group 'org-export-latex + :version "26.1" + :package-version '(Org . "8.3") + :type '(choice + (const :tag "For all elements" t) + (const :tag "For no element" nil) + (set :tag "For the following elements only" :greedy t + (const :tag "Images" image) + (const :tag "Tables" table) + (const :tag "Source code" src-block) + (const :tag "Special blocks" special-block)))) + +(defcustom org-latex-prefer-user-labels nil + "Use user-provided labels instead of internal ones when non-nil. + +When this variable is non-nil, Org will use the value of +CUSTOM_ID property, NAME keyword or Org target as the key for the +\\label commands generated. + +By default, Org generates its own internal labels during LaTeX +export. This process ensures that the \\label keys are unique +and valid, but it means the keys are not available in advance of +the export process. + +Setting this variable gives you control over how Org generates +labels during LaTeX export, so that you may know their keys in +advance. One reason to do this is that it allows you to refer to +various elements using a single label both in Org's link syntax +and in embedded LaTeX code. + +For example, when this variable is non-nil, a headline like this: + + ** Some section + :PROPERTIES: + :CUSTOM_ID: sec:foo + :END: + This is section [[#sec:foo]]. + #+BEGIN_EXPORT latex + And this is still section \\ref{sec:foo}. + #+END_EXPORT + +will be exported to LaTeX as: + + \\subsection{Some section} + \\label{sec:foo} + This is section \\ref{sec:foo}. + And this is still section \\ref{sec:foo}. + +Note, however, that setting this variable introduces a limitation +on the possible values for CUSTOM_ID and NAME. When this +variable is non-nil, Org passes their value to \\label unchanged. +You are responsible for ensuring that the value is a valid LaTeX +\\label key, and that no other \\label commands with the same key +appear elsewhere in your document. (Keys may contain letters, +numbers, and the following punctuation: '_' '.' '-' ':'.) There +are no such limitations on CUSTOM_ID and NAME when this variable +is nil. + +For headlines that do not define the CUSTOM_ID property or +elements without a NAME, Org will continue to use its default +labeling scheme to generate labels and resolve links into proper +references." + :group 'org-export-latex + :type 'boolean + :version "26.1" + :package-version '(Org . "8.3")) ;;;; Preamble @@ -264,11 +482,15 @@ AUTO will automatically be replaced with a coding system derived from `buffer-file-coding-system'. See also the variable `org-latex-inputenc-alist' for a way to influence this mechanism. -Likewise, if your header contains \"\\usepackage[AUTO]{babel}\", -AUTO will be replaced with the language related to the language -code specified by `org-export-default-language', which see. Note -that constructions such as \"\\usepackage[french,AUTO,english]{babel}\" -are permitted. +Likewise, if your header contains \"\\usepackage[AUTO]{babel}\" +or \"\\usepackage[AUTO]{polyglossia}\", AUTO will be replaced +with the language related to the language code specified by +`org-export-default-language'. Note that constructions such as +\"\\usepackage[french,AUTO,english]{babel}\" are permitted. For +Polyglossia the language will be set via the macros +\"\\setmainlanguage\" and \"\\setotherlanguage\". See also +`org-latex-guess-babel-language' and +`org-latex-guess-polyglossia-language'. The sectioning structure ------------------------ @@ -328,11 +550,42 @@ are written as utf8 files." (defcustom org-latex-title-command "\\maketitle" "The command used to insert the title just after \\begin{document}. -If this string contains the formatting specification \"%s\" then -it will be used as a formatting string, passing the title as an -argument." + +This format string may contain these elements: + + %a for AUTHOR keyword + %t for TITLE keyword + %s for SUBTITLE keyword + %k for KEYWORDS line + %d for DESCRIPTION line + %c for CREATOR line + %l for Language keyword + %L for capitalized language keyword + %D for DATE keyword + +If you need to use a \"%\" character, you need to escape it +like that: \"%%\". + +Setting :latex-title-command in publishing projects will take +precedence over this variable." :group 'org-export-latex - :type 'string) + :type '(string :tag "Format string")) + +(defcustom org-latex-subtitle-format "\\\\\\medskip\n\\large %s" + "Format string used for transcoded subtitle. +The format string should have at most one \"%s\"-expression, +which is replaced with the subtitle." + :group 'org-export-latex + :version "26.1" + :package-version '(Org . "8.3") + :type '(string :tag "Format string")) + +(defcustom org-latex-subtitle-separate nil + "Non-nil means the subtitle is not typeset as part of title." + :group 'org-export-latex + :version "26.1" + :package-version '(Org . "8.3") + :type 'boolean) (defcustom org-latex-toc-command "\\tableofcontents\n\n" "LaTeX command to set the table of contents, list of figures, etc. @@ -341,10 +594,36 @@ the toc:nil option, not to those generated with #+TOC keyword." :group 'org-export-latex :type 'string) -(defcustom org-latex-with-hyperref t - "Toggle insertion of \\hypersetup{...} in the preamble." +(defcustom org-latex-hyperref-template + "\\hypersetup{\n pdfauthor={%a},\n pdftitle={%t},\n pdfkeywords={%k}, + pdfsubject={%d},\n pdfcreator={%c}, \n pdflang={%L}}\n" + "Template for hyperref package options. + +This format string may contain these elements: + + %a for AUTHOR keyword + %t for TITLE keyword + %s for SUBTITLE keyword + %k for KEYWORDS line + %d for DESCRIPTION line + %c for CREATOR line + %l for Language keyword + %L for capitalized language keyword + %D for DATE keyword + +If you need to use a \"%\" character, you need to escape it +like that: \"%%\". + +As a special case, a nil value prevents template from being +inserted. + +Setting :latex-hyperref-template in publishing projects will take +precedence over this variable." :group 'org-export-latex - :type 'boolean) + :version "26.1" + :package-version '(Org . "8.3") + :type '(choice (const :tag "No template" nil) + (string :tag "Format string"))) ;;;; Headline @@ -352,17 +631,15 @@ the toc:nil option, not to those generated with #+TOC keyword." 'org-latex-format-headline-default-function "Function for formatting the headline's text. -This function will be called with 5 arguments: -TODO the todo keyword (string or nil). +This function will be called with six arguments: +TODO the todo keyword (string or nil) TODO-TYPE the type of todo (symbol: `todo', `done', nil) PRIORITY the priority of the headline (integer or nil) -TEXT the main headline text (string). -TAGS the tags as a list of strings (list of strings or nil). - -The function result will be used in the section format string. +TEXT the main headline text (string) +TAGS the tags (list of strings or nil) +INFO the export options (plist) -Use `org-latex-format-headline-default-function' by default, -which format headlines like for Org version prior to 8.0." +The function result will be used in the section format string." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") @@ -376,6 +653,16 @@ which format headlines like for Org version prior to 8.0." :group 'org-export-latex :type 'string) +(defcustom org-latex-footnote-defined-format "\\textsuperscript{\\ref{%s}}" + "Format string used to format reference to footnote already defined. +%s will be replaced by the label of the referred footnote." + :group 'org-export-latex + :type '(choice + (const :tag "Use plain superscript (default)" "\\textsuperscript{\\ref{%s}}") + (const :tag "Use Memoir/KOMA-Script footref" "\\footref{%s}") + (string :tag "Other format string")) + :version "26.1" + :package-version '(Org . "9.0")) ;;;; Timestamps @@ -397,6 +684,14 @@ which format headlines like for Org version prior to 8.0." ;;;; Links +(defcustom org-latex-images-centered t + "When non-nil, images are centered." + :group 'org-export-latex + :version "26.1" + :package-version '(Org . "9.0") + :type 'boolean + :safe #'booleanp) + (defcustom org-latex-image-default-option "" "Default option for images." :group 'org-export-latex @@ -422,10 +717,13 @@ environment." :package-version '(Org . "8.0") :type 'string) -(defcustom org-latex-default-figure-position "htb" - "Default position for latex figures." +(defcustom org-latex-default-figure-position "htbp" + "Default position for LaTeX figures." :group 'org-export-latex - :type 'string) + :type 'string + :version "26.1" + :package-version '(Org . "9.0") + :safe #'stringp) (defcustom org-latex-inline-image-rules '(("file" . "\\.\\(pdf\\|jpeg\\|jpg\\|png\\|ps\\|eps\\|tikz\\|pgf\\|svg\\)\\'")) @@ -489,12 +787,14 @@ When modifying this variable, it may be useful to change :type '(choice (const :tag "Table" table) (const :tag "Matrix" math) (const :tag "Inline matrix" inline-math) - (const :tag "Verbatim" verbatim))) + (const :tag "Verbatim" verbatim)) + :safe (lambda (s) (memq s '(table math inline-math verbatim)))) (defcustom org-latex-tables-centered t "When non-nil, tables are exported in a center environment." :group 'org-export-latex - :type 'boolean) + :type 'boolean + :safe #'booleanp) (defcustom org-latex-tables-booktabs nil "When non-nil, display tables in a formal \"booktabs\" style. @@ -505,13 +805,8 @@ attributes." :group 'org-export-latex :version "24.4" :package-version '(Org . "8.0") - :type 'boolean) - -(defcustom org-latex-table-caption-above t - "When non-nil, place caption string at the beginning of the table. -Otherwise, place it near the end." - :group 'org-export-latex - :type 'boolean) + :type 'boolean + :safe #'booleanp) (defcustom org-latex-table-scientific-notation "%s\\,(%s)" "Format string to display numbers in scientific notation. @@ -526,11 +821,10 @@ When nil, no transformation is made." (string :tag "Format string") (const :tag "No formatting" nil))) - ;;;; Text markup (defcustom org-latex-text-markup-alist '((bold . "\\textbf{%s}") - (code . verb) + (code . protectedtexttt) (italic . "\\emph{%s}") (strike-through . "\\sout{%s}") (underline . "\\uline{%s}") @@ -550,14 +844,15 @@ to typeset and try to protect special characters. If no association can be found for a given markup, text will be returned as-is." :group 'org-export-latex + :version "26.1" + :package-version '(Org . "8.3") :type 'alist :options '(bold code italic strike-through underline verbatim)) ;;;; Drawers -(defcustom org-latex-format-drawer-function - (lambda (name contents) contents) +(defcustom org-latex-format-drawer-function (lambda (_ contents) contents) "Function called to format a drawer in LaTeX code. The function must accept two parameters: @@ -575,44 +870,24 @@ The default function simply returns the value of CONTENTS." ;;;; Inlinetasks -(defcustom org-latex-format-inlinetask-function 'ignore +(defcustom org-latex-format-inlinetask-function + 'org-latex-format-inlinetask-default-function "Function called to format an inlinetask in LaTeX code. -The function must accept six parameters: - TODO the todo keyword, as a string - TODO-TYPE the todo type, a symbol among `todo', `done' and nil. - PRIORITY the inlinetask priority, as a string - NAME the inlinetask name, as a string. - TAGS the inlinetask tags, as a list of strings. - CONTENTS the contents of the inlinetask, as a string. - -The function should return the string to be exported. +The function must accept seven parameters: + TODO the todo keyword (string or nil) + TODO-TYPE the todo type (symbol: `todo', `done', nil) + PRIORITY the inlinetask priority (integer or nil) + NAME the inlinetask name (string) + TAGS the inlinetask tags (list of strings or nil) + CONTENTS the contents of the inlinetask (string or nil) + INFO the export options (plist) -For example, the variable could be set to the following function -in order to mimic default behavior: - -\(defun org-latex-format-inlinetask (todo type priority name tags contents) -\"Format an inline task element for LaTeX export.\" - (let ((full-title - (concat - (when todo - (format \"\\\\textbf{\\\\textsf{\\\\textsc{%s}}} \" todo)) - (when priority (format \"\\\\framebox{\\\\#%c} \" priority)) - title - (when tags - (format \"\\\\hfill{}\\\\textsc{:%s:}\" - (mapconcat \\='identity tags \":\"))))) - (format (concat \"\\\\begin{center}\\n\" - \"\\\\fbox{\\n\" - \"\\\\begin{minipage}[c]{.6\\\\textwidth}\\n\" - \"%s\\n\\n\" - \"\\\\rule[.8em]{\\\\textwidth}{2pt}\\n\\n\" - \"%s\" - \"\\\\end{minipage}}\" - \"\\\\end{center}\") - full-title contents))" +The function should return the string to be exported." :group 'org-export-latex - :type 'function) + :type 'function + :version "26.1" + :package-version '(Org . "8.3")) ;; Src blocks @@ -640,7 +915,7 @@ the minted package to `org-latex-packages-alist', for example using customize, or with (require \\='ox-latex) - (add-to-list \\='org-latex-packages-alist \\='(\"\" \"minted\")) + (add-to-list \\='org-latex-packages-alist \\='(\"newfloat\" \"minted\")) In addition, it is necessary to install pygments \(http://pygments.org), and to configure the variable @@ -656,7 +931,8 @@ into previewing problems, please consult :type '(choice (const :tag "Use listings" t) (const :tag "Use minted" minted) - (const :tag "Export verbatim" nil))) + (const :tag "Export verbatim" nil)) + :safe (lambda (s) (memq s '(t nil minted)))) (defcustom org-latex-listings-langs '((emacs-lisp "Lisp") (lisp "Lisp") (clojure "Lisp") @@ -668,7 +944,9 @@ into previewing problems, please consult (shell-script "bash") (gnuplot "Gnuplot") (ocaml "Caml") (caml "Caml") - (sql "SQL") (sqlite "sql")) + (sql "SQL") (sqlite "sql") + (makefile "make") + (R "r")) "Alist mapping languages to their listing language counterpart. The key is a symbol, the major mode symbol without the \"-mode\". The value is the string that should be inserted as the language @@ -676,6 +954,8 @@ parameter for the listings package. If the mode name and the listings name are the same, the language does not need an entry in this list - but it does not hurt if it is present." :group 'org-export-latex + :version "24.4" + :package-version '(Org . "8.3") :type '(repeat (list (symbol :tag "Major mode ") @@ -697,7 +977,13 @@ will typeset the code in a small size font with underlined, bold black keywords. Note that the same options will be applied to blocks of all -languages." +languages. If you need block-specific options, you may use the +following syntax: + + #+ATTR_LATEX: :options key1=value1,key2=value2 + #+BEGIN_SRC + ... + #+END_SRC" :group 'org-export-latex :type '(repeat (list @@ -744,41 +1030,132 @@ will result in src blocks being exported with \\begin{minted}[bgcolor=bg,frame=lines]{} as the start of the minted environment. Note that the same -options will be applied to blocks of all languages." +options will be applied to blocks of all languages. If you need +block-specific options, you may use the following syntax: + + #+ATTR_LATEX: :options key1=value1,key2=value2 + #+BEGIN_SRC + ... + #+END_SRC" :group 'org-export-latex :type '(repeat (list (string :tag "Minted option name ") (string :tag "Minted option value")))) -(defvar org-latex-custom-lang-environments nil +(defcustom org-latex-custom-lang-environments nil "Alist mapping languages to language-specific LaTeX environments. It is used during export of src blocks by the listings and minted -latex packages. For example, +latex packages. The environment may be a simple string, composed of +only letters and numbers. In this case, the string is directly the +name of the latex environment to use. The environment may also be +a format string. In this case the format string will be directly +exported. This format string may contain these elements: + + %s for the formatted source + %c for the caption + %f for the float attribute + %l for an appropriate label + %o for the LaTeX attributes + +For example, (setq org-latex-custom-lang-environments - \\='((python \"pythoncode\"))) + \\='((python \"pythoncode\") + (ocaml \"\\\\begin{listing} +\\\\begin{minted}[%o]{ocaml} +%s\\\\end{minted} +\\\\caption{%c} +\\\\label{%l}\"))) -would have the effect that if org encounters begin_src python -during latex export it will output +would have the effect that if Org encounters a Python source block +during LaTeX export it will produce \\begin{pythoncode} - \\end{pythoncode}") + \\end{pythoncode} + +and if Org encounters an Ocaml source block during LaTeX export it +will produce + + \\begin{listing} + \\begin{minted}[]{ocaml} + + \\end{minted} + \\caption{} + \\label{